Manage Yamaha CP88/CP73 Live Set data over MIDI and YSFC (.X9A/.X9L/.X9P/.X9S) files
Project description
cp-liveset
cp-liveset is a Python library and command-line tool for reading,
converting, and writing Yamaha CP88/CP73 Live Set data. It moves Live
Set Sounds losslessly between the device's own representations — SysEx Bulk
Dumps over MIDI, .X9A/.X9L/.X9P/.X9S files, and a live USB-MIDI
connection — and editable JSON and YAML, so you can back up, inspect, edit,
diff, and restore your sounds with ordinary tools.
Features
- Byte-exact round trips between every supported format: each of the 19 SysEx Bulk Dump messages that make up a Live Set Sound is decoded into named fields and re-encoded identically.
- Editable JSON and a concise YAML format that shows only the fields differing from the factory defaults.
- Live MIDI I/O: read, write, and switch Live Set Sounds on a connected CP88/CP73.
- The device's file-menu formats: read
.X9Abackups and read/write.X9L,.X9P, and.X9Sfiles. - A fully typed public API (
import cp_liveset) with thecp-livesetcommand-line tool layered on top of it.
Installation
pip install .
# optional live-MIDI backend (rtmidi: endpoints):
pip install ".[rtmidi]"
# editable install for development:
pip install -e .
This installs the cp-liveset console script and the importable
cp_liveset package, so cp-liveset ..., python -m cp_liveset ..., and
import cp_liveset all work.
Requirements
- Python 3.9 or newer.
mido,pydanticv2, andruamel.yaml(installed automatically).python-rtmidiforrtmidi:endpoints (thertmidioptional extra, above). On Windows this needs the MSVC build tools if no prebuilt wheel is available for your Python version.
Quick start
Command line:
# Convert a Live Set Sound JSON file to the concise YAML format
cp-liveset convert -i myset.json -o myset.yaml
# Read page 1 from a connected CP88 and save it as JSON
cp-liveset convert -i rtmidi:CP88 1:* -o page1.json
Python:
import mido, cp_liveset
from cp_liveset import LiveSetCollection
with mido.open_input("CP88/CP73-1 0") as inp, \
mido.open_output("CP88/CP73-1 1") as out:
group = cp_liveset.request_group(inp, out, [(1, 1), (1, 2)])
group[(2, 1)] = group.pop((1, 1)) # remap 1:1 -> 2:1
cp_liveset.send_group(out, group) # write it back to the device
See docs/api.md for the full library reference, and the
Commands section below for the command-line tool.
Data model
A Live Set Sound is addressed as page (1-40, "User Live Set Page") and
set (1-8, the Program Change number within the page). Each Live Set Sound is
made up of 19 SysEx Bulk Dump blocks: a header/footer pair, a soundmondo
(format version) block, a master_eq block, a common block, an
additional (tempo/delay) block, 4 zones, and 3 sections (piano,
epiano, sub), each with common/specific/additional sub-blocks.
codec.py decodes every byte of these blocks into named fields (see
paramap.py); unused/reserved bytes are kept as reserved_0xNN so a JSON
file can always be converted back into byte-identical SysEx. The Live Set Sound
name (a fixed 15-character field on the device) is decoded with trailing
NUL bytes stripped — any other padding, such as trailing spaces, is kept —
and re-padded to 15 bytes with NULs on write, so the round trip is
byte-exact whatever padding the field used. (Which padding it uses isn't
documented by Yamaha: in practice real instruments are space-padded, so
their names keep the full 15 characters, while the factory "Init Sound" of
an empty slot is NUL-padded and reads back shorter.)
See docs/json-format.md for the complete field
reference. The JSON file produced/consumed by this tool looks like:
{
"format": "cp88-cp73-liveset-v1",
"pages": {
"1": { "5": { "soundmondo": {...}, "master_eq": {...}, "common": {...},
"additional": {...}, "zones": [...], "sections": {...} } },
"2": { "1": {...}, "2": {...} }
}
}
See docs/implementation-notes.md for notes
on SysEx Bulk Dump framing and other device behavior verified against a real
CP88.
Python API
Everything the CLI does is built on the public cp_liveset package, which
you can use directly for plumbing Live Set data into your own code. The
central types are LiveSetSound (one Live Set Sound, a pydantic model) and
LiveSetCollection (a dict-like (page, set) -> LiveSetSound mapping with
conversions to/from plain JSON dicts, ruamel.yaml documents, mido SysEx
messages, and .X9* file bytes), plus live-MIDI functions
(request_sound/request_group, send_sound/send_group,
select_sound) that work on mido ports you open yourself:
import cp_liveset
from cp_liveset import LiveSetCollection
# read a backup, keep only its page 2, save that as concise YAML
with open("CP BackUp.X9A", "rb") as f:
group = LiveSetCollection.from_x9(f.read())
page2 = LiveSetCollection({k: v for k, v in group.items() if k[0] == 2})
with open("page2.yaml", "w", encoding="utf-8") as f:
cp_liveset.make_yaml().dump(page2.to_yaml(), f)
LiveSetCollection is an ordinary MutableMapping, so selecting, merging,
and remapping sounds are plain dict operations; the live-MIDI functions work
on mido ports you open yourself. See docs/api.md for the
full reference.
Commands
The command-line tool centers on convert, which moves Live Set data
between one or more input endpoints (-i/--input) and one or more output
endpoints
(-o/--output), each written as FORMAT:LOCATION:
| Format | Location | Description |
|---|---|---|
json |
path to a .json file |
the cp88-cp73-liveset-v1 format above |
yaml |
path to a .yaml file |
concise human-editable format, see below |
midi |
path to a .mid file |
a SysEx Bulk Dump recorded as a MIDI file |
syx |
path to a .syx file |
raw SysEx Bulk Dumps (bare F0..F7 messages) |
rtmidi |
PORT[:DEVICE_ID] or IN:OUT[:DEVICE_ID] |
a connected CP88/CP73 (requires python-rtmidi) |
x9a |
path to a .X9A file |
"Back Up" file (read-only) |
x9l |
path to a .X9L file |
"Live Set All" file, 160 sets (read/write) |
x9p |
path to a .X9P file |
"Live Set Page" file, 8 sets (read/write) |
x9s |
path to a .X9S file |
"Live Set Sound" file, 1 set (read/write) |
For rtmidi endpoints, LOCATION is one of:
IN:OUT[:DEVICE_ID], whereINandOUTare port numbers (the indexes shown bylist-midi-ports) for the input port and output port to use, given separately - useful if the device exposes different port indexes for input and output. This form is used wheneverLOCATIONstarts with a digit.PORT[:DEVICE_ID], wherePORTis a port name (or a unique prefix of one) as shown bylist-midi-ports, not starting with a digit, used for both input and output. This form is used wheneverLOCATIONdoes not start with a digit.
In both forms, DEVICE_ID (0-15, the MIDI SysEx device number to address) is
optional and defaults to 0. Which of the input/output port(s) is actually
used depends on context: convert -i only opens the input, convert -o and
select only open the output; reading a Live Set Sound from a device (convert -i rtmidi:...) needs both, since it sends the request on the output and
receives the reply on the input.
For the PORT[:DEVICE_ID] form, a trailing :<digits> is taken as
DEVICE_ID and stripped from the end - greedily, so only the last
:<digits> group is stripped. A port name can therefore itself contain or
end in :<digits>: if it does, append an extra :<device_id> to address
it (e.g. for a port literally named Foo:1, use rtmidi:Foo:1:0 for device
ID 0, or rtmidi:Foo:1:5 for device ID 5).
For example, rtmidi:CP88 means port CP88 for both input and output,
device ID 0; rtmidi:CP88:5 means port CP88, device ID 5; and rtmidi:1:2
means input port number 1, output port number 2, device ID 0 (and
rtmidi:1:2:5 adds device ID 5).
The FORMAT: prefix can be omitted for json, yaml, midi, syx, x9a,
x9l, x9p, and x9s endpoints, in which case the format is inferred from
the file's extension (.json, .yaml/.yml, .mid/.midi, .syx, .x9a,
.x9l, .x9p, .x9s, case-insensitive; a trailing :<page>[:<set>] override
suffix is ignored for this purpose), e.g. cp-liveset convert -i myset.json -o myset.yaml. rtmidi always needs its explicit prefix.
midi/syx endpoints may have a trailing :<device_id> (0-15, default 0)
giving the MIDI SysEx device number to embed when writing. As with the
rtmidi PORT[:DEVICE_ID] form, the suffix is stripped greedily — only the
last :<digits> group is taken as DEVICE_ID — and the token must be all
digits, so a normal path (e.g. myset.mid or C:\sounds\myset.mid) is left
untouched; a path that itself ends in :<digits> needs an extra
:<device_id> appended to address it. When reading a midi or syx file,
any SysEx that is not a CP88/CP73 Live Set Bulk Dump (other manufacturers,
other Yamaha models) is ignored, so a file that interleaves unrelated SysEx is
fine.
For json, yaml, midi, and syx endpoints, LOCATION may be a literal - to
mean stdin (for an input) or stdout (for an output), e.g. json:- or
yaml:-. Since a format can't be inferred from -, the FORMAT: prefix is
required in this case (a bare - is rejected).
convert produces no output on success; it only prints to stderr and exits
non-zero on error. This makes it suitable for use in scripts/pipelines, e.g.
cp-liveset convert -i json:myset.json -o yaml:- | less.
A file output (json/yaml/midi/x9a/x9l/x9p/x9s) is rewritten from
scratch, replacing its entire previous contents. A rtmidi output only
updates the addressed Live Set Sound(s) on the device; everything else on the
device is left unchanged. If a file output already exists, convert prompts
for confirmation before overwriting it; pass -y/--yes to overwrite
without prompting (e.g. in scripts), or -n/--no to fail instead of
prompting if any output file already exists.
The working Live Set group
convert works against one working Live Set group: a full, sparse,
addressable 40-page x 8-set space. Each -i reads its endpoint's incoming
group and merges it into the working group; each -o selects from the
working group into an outgoing group and writes it to its endpoint.
By default (no specs after the endpoint), the whole incoming/working group
is carried straight through, slot-for-slot. To select only some slots, or
remap them to different page/set numbers, follow -i/-o ENDPOINT with one
or more specs:
* all present (page,set) pairs, identity
<pages>:<sets> select these pairs, identity mapping
<pages>:<sets>=<pages>:<sets> select the left side, remap onto the right
<pages>/<sets> is * or a comma-separated list of numbers and/or a-b
ranges, e.g. 1, 1,3,5, 1-4, 1,3,5-7, *. On the left of = (or with
no =), * means "whatever is present"; on the right of =, * means the
full range (1-40 for pages, 1-8 for sets). The two sides of = must resolve
to the same number of (page,set) pairs, paired up in page-major order.
Multiple -i/-o (and multiple specs after one endpoint) are processed in
order; later specs overwrite earlier ones for the same destination slot.
A rtmidi device always holds all 320 Live Set Sounds, but each one requires its own
MIDI Bulk Dump Request. Regardless of the spec(s) given to a rtmidi input,
only the (page,set) pairs that the -o spec(s) actually end up needing are
downloaded — e.g. -i rtmidi:CP88 -o json:myset.json 1:5 2:1-8 downloads
just 1:5 and 2:1-8, and -i rtmidi:CP88 * -o yaml:out.yaml 1:*=2:* downloads
just 1:1-8 (since a rtmidi device always has all 8 sets of page 1
present), even though the input spec * nominally selects all 320. If the
-o spec(s) need every (page,set) pair (e.g. a bare *), all 320 are
downloaded.
Using the same endpoint (e.g. the same rtmidi device, or the same file) as
both an input and an output in one convert invocation is well-defined: all
inputs are read before any output is written, so the output sees the data as
it was before the command ran.
Writing to x9p fills any of the page's 8 sets not present in the output
with "Init Sound"; without a :PAGE override (see below), at least one Live
Set must be present, and all of them must belong to the same page. Writing to
x9l similarly fills any of its 160 sets (pages 1-20) not present with "Init
Sound". Writing to x9s requires at most one Live Set Sound; if the output is
empty, a :PAGE:SET override (see below) must be given, and "Init Sound" is
written at that identity.
# List the Live Set Sounds present in a JSON file (or x9*, rtmidi, etc.)
cp-liveset inspect json:myset.json
cp-liveset inspect x9a:CP_BackUp.X9A
# List only some Live Set Sounds, using the same '*'/<pages>:<sets> selection
# syntax as 'convert' (without '=' remapping)
cp-liveset inspect rtmidi:CP88 1:5 2:1-8
# Read Live Set Sound 1:5 and all of page 2 from a connected CP88 into JSON
cp-liveset convert -i rtmidi:CP88 1:5 2:1-8 -o json:myset.json
# Write a JSON file's Live Set Sounds back to a connected CP88
cp-liveset convert -i json:myset.json -o rtmidi:CP88
# Convert a Live Set Sound JSON file to a .mid file containing the SysEx Bulk Dump
# (play this file into the CP88's MIDI IN to restore those Live Set Sounds)
cp-liveset convert -i json:myset.json -o midi:myset.mid
# Convert a SysEx .mid file (e.g. captured from the CP88) back to JSON
cp-liveset convert -i midi:myset.mid -o json:myset.json
# Write/read a raw .syx SysEx file (bare F0..F7 dumps, no MIDI-file wrapper)
cp-liveset convert -i json:myset.json -o syx:myset.syx
cp-liveset convert -i syx:myset.syx -o json:myset.json
# Extract just page 1, set 5 from an existing JSON/MIDI file
cp-liveset convert -i json:myset.json 1:5 -o json:subset.json
# Read every Live Set Sound out of a Back Up file into JSON
cp-liveset convert -i x9a:CP_BackUp.X9A -o json:backup.json
# Write a single Live Set Sound to a .X9S file (loads into the device's
# "Live Set Sound" file slot for that page/set)
cp-liveset convert -i json:myset.json 1:5 -o x9s:myset.X9S
# Write a full page (8 sets) to a .X9P file
cp-liveset convert -i json:myset.json 1:1-8 -o x9p:mypage.X9P
# Copy a backup's page 1 onto device page 5
cp-liveset convert -i x9a:CP_BackUp.X9A 1:1-8=5:1-8 -o rtmidi:CP88
# Create blank "Init Sound" .X9P/.X9L/.X9S files (no -i needed)
cp-liveset convert -o x9p:blank.X9P:5
cp-liveset convert -o x9l:blank.X9L
cp-liveset convert -o x9s:blank.X9S:12:2
# Select Live Set page 7, set 3 (write a .mid file, or send it live)
cp-liveset select midi:switch.mid 7:3
cp-liveset select rtmidi:CP88 7:3
# List MIDI ports (requires python-rtmidi)
cp-liveset list-midi-ports
inspect prints YAML, mapping each page number to a mapping of set number to
Live Set Sound name, e.g.:
1:
5: Simple 78
2:
1: CFX+DX Legend
2: A.Bass/78Rd
3: 80s El Grand
Concise YAML format (yaml)
The yaml endpoint reads/writes the same data as json, but only encodes
values that differ from a "standard" default for that field (computed from a
factory-reset CP88's Live Set Sounds, see yamldata.FIELD_DEFAULTS). It uses
cp88-cp73-liveset-yaml-v1 as its format value, and otherwise has the same
top-level pages structure as the JSON format. See
docs/yaml-format.md for the complete field
reference.
A few conventions:
- The three sections (
piano/epiano/sub) are always written, each with at leaston,category, andinstrument. A section is off (on: false) unless its section switch is set. categoryis the section's active voice category andinstrumentits active voice by name, with the voice's within-category number as a# Ncomment — the one annotation the format emits, mirroring the number on the instrument's display. If other categories hold a non-default remembered voice,instrumentbecomes aCategory: voicemap instead; Advanced Sound Mode shows ascategory: Advanced Mode.- Most discrete/enumerated values (effect types, note numbers, split mode,
EQ frequency, the assignable-controller targets, etc.) are written by
name, with no raw-number comment (either the name or the raw number is
accepted on read; the number tables are in
docs/yaml-format.md). - Any field not otherwise represented, but which differs from its default, is
preserved exactly via a
raw:map keyedblock.field_name(e.g.raw: {section_piano_specific.bulk_format_version: 0}), guaranteeing an exactjson -> yaml -> jsonround trip. - On read, repeating a default value or omitting comments is not an error.
Re-writing a Live Set Sound as
yamlalways produces the same canonical formatting (yaml -> json -> yamlis byte-identical).
For example, a "Demo Piano" Live Set Sound looks like:
format: cp88-cp73-liveset-yaml-v1
pages:
1:
1:
name: Demo Piano
piano:
on: true
category: Grand Piano
instrument: Nashville C3 # 7
delay_depth: 20
reverb_depth: 21
damper_resonance:
on: true
effect:
on: false
depth: 31
epiano:
on: true
category: Wr
instrument: Wr Warm # 1
tone: 56
reverb_depth: 17
effect1:
on: false
type: Comp
rate: 41
effect2:
on: true
type: Fla
depth: 48
drive:
on: false
depth: 0
sub:
on: true
category: Pad/Strings
instrument: Analog Pad # 12
split_mode: L
octave_shift: 2
volume: 58
tone: 69
pitch_modulation_depth: 5
reverb_depth: 28
effect:
on: false
depth: 49
speed: 23
envelope:
attack: 50
release: 34
common:
split_point: D#2
depth_knob_section: Piano
delay:
on: false
time: 54
reverb:
on: true
time: 46
master_eq:
on: false
mid_freq: 800Hz
# Convert a Live Set Sound JSON file to the concise yaml format
cp-liveset convert -i json:myset.json -o yaml:myset.yaml
# Edit myset.yaml by hand, then convert back
cp-liveset convert -i yaml:myset.yaml -o json:myset.json
Back Up / Live Set files (.X9A / .X9L / .X9P / .X9S)
The CP88/CP73 can save/load "Back Up" (.X9A), "Live Set All" (.X9L),
"Live Set Page" (.X9P), and "Live Set Sound" (.X9S) files via its own file
menu. These are "YSFC" container files; their binary layout is not
documented in the manuals, so this support is based on reverse-engineering
real files exported from a CP88 (see x9format.py and x9data.py).
Only Live Set data is supported (the rest of .X9A's contents — global
settings, etc. — is ignored):
- Reading is supported for all four formats (
x9a,x9l,x9p,x9s)..X9A/.X9Lcontain up to 160 Live Set Sounds (pages 1-20 x 8 sets);.X9Pcontains one page (8 sets);.X9Scontains a single Live Set Sound. - Writing is supported for
x9l(pages 1-20 x 8 sets),x9p(one page, 8 sets), andx9s(at most one Live Set Sound), by patching a template file. Any (page, set) slots withinx9l/x9p's range that aren't present in the output are filled with "Init Sound" (the same factory-default sound used for unused preset slots). Forx9pwithout a:PAGEoverride (see below), the output must contain at least one Live Set, and all of them must belong to the same page (which becomes the page written); with a:PAGEoverride, the output may be empty or contain Live Sets from multiple source pages, as long as no two of them end up at the same set number on the target page. Forx9s, if the output is empty, a:PAGE:SEToverride (see below) must be given, and "Init Sound" is written at that identity..X9Ais read-only — to change a backup, extract the Live Set Sounds you need withx9a, edit them as JSON, and write them back as.X9L/.X9P/.X9Sfiles instead.
.X9P/.X9S files store their own page (and, for .X9S, set) number(s),
which is what determines where the device loads them from/to. To
assign/override that identity, append :PAGE (for x9p) or
:PAGE[:SET] (for x9s) to the endpoint, on either side of the
conversion, e.g.:
# Read "Natural CFX.X9S" as if it were page 3, set 7
cp-liveset convert -i "x9s:Natural CFX.X9S:3:7" -o json:myset.json
# Write Live Set Sound 1:5 to a .X9S file tagged as page 12, set 2
cp-liveset convert -i json:myset.json 1:5 -o x9s:myset.X9S:12:2
If :SET is omitted for x9s, only the page is overridden and the
stored/derived set number is kept.
Field coverage caveat
Of the 244 non-reserved fields in a Live Set Sound, 189 are stored as
parameters in the X9 blob (x9data.FIELD_POSITIONS, plus x9data.BIT_POSITIONS
for the per-zone transmit switches, which the device stores bit-expanded — one
blob byte per switch bit), and the Live Set Sound name is stored too (190 in
all). The remaining 54 fields are not present in the X9 format at all —
mostly the per-instrument-type effect parameters for a section's inactive
instrument types (which the device force-constants), plus the format-version
stamps, the raw tempo value, and a few advanced-mode fields. The mapping was
reverse-engineered by sending sounds with controlled, distinctive values over
MIDI, exporting them from a CP88, and correlating every blob byte against the
device's read-back model (plus all 160 factory sounds); a generated .X9P is
byte-identical to the device's own export of the same page (the 8 trailing
blob bytes are a constant device chunk).
These 54 fields are real, addressable SysEx Bulk Dump parameters (they come
from the same MIDI Data Table as everything else in paramap.py, and round
trip correctly through rtmidi:/midi:/syx:) — Yamaha's own file save just
doesn't persist them. So:
x9a/x9l/x9p/x9sreads decode these 54 fields as0, except the fixed format-version stamps (soundmondomajor/minor/bugfix and each block'sbulk_format_version), which are filled with their canonical constants (x9data.FIXED_VERSION_FIELDS) — they are deterministic for a given format version, so a read reconstructs them rather than leaving them0.x9l/x9p/x9swrites silently drop any values set for these 54 fields in the input JSON.rtmidi -> json -> x9*andx9* -> json -> x9*round trips are each internally lossless; only the cross path loses these 54 fields.- Important: writing an
.X9*-derived Live Set Sound back to the device (x9* -> json -> rtmidi) will set the ~48 non-version fields among these to0on the device (the format-version stamps are sent at their canonical values, not zeroed), not leave their existing values unchanged — since the JSON decoded from an.X9*file has0for those, and artmidi:write sends a complete Bulk Dump. If you need to preserve these fields, capture the Live Set Sound viartmidi:/midi:/syx:instead of.X9*.
Sources
All formats this tool reads and writes are taken from Yamaha's official documentation:
- the CP88/CP73 Supplementary Manual
(
CP88_CP73_supplementary_manual_En_v200_H0.pdf; its "MIDI Data Table" / "MIDI PARAMETER CHANGE TABLE (BULK CONTROL)" sections); - the CP88/CP73 Owner's Manual
(
CP88_owners_manual_En_F0.pdf); and - the text extracted from Yamaha's CP88/CP73 Data List download
(
cp88_cp73_en_om_d0_text/, distributed as a.zip).
Those direct links are to Yamaha's download CDN; if they move, all of the above are reachable from the official support/download pages (Yamaha Europe, Yamaha USA).
The .X9A/.X9L/.X9P/.X9S container layout is not documented by Yamaha.
The general YSFC container framing — the 64-byte header, the tag+offset
catalogue, and the paired Exxx/Dxxx entry/data chunks shared across the
Motif/Montage/MODX YSFC family — follows the structure described by the
ysfctools project and the
Yamaha YSFC file-format notes it
builds on. The CP88/CP73-specific details — which chunks carry Live Set
data, the 367-byte Live Set Sound blob layout, and the device's file load
behavior — were reverse-engineered here from real files exported by a CP88
(see docs/implementation-notes.md).
"CP88", "CP73", and the Voice, category, effect, and control names used to label sounds and parameters (e.g. "CFX", "DX Legend", "Pedal Wah") are Yamaha's; they appear here only as factual identifiers for interoperability, and no Yamaha preset, firmware, or other creative content is included or redistributed.
License
Released under the MIT License — free to use, modify, and distribute, provided the copyright and license notice are retained.
Disclaimer / No warranty
This software is provided "as is", without warranty of any kind, express
or implied. To the maximum extent permitted by law, the authors and copyright
holders accept no liability for any claim, damages, data loss, or other
harm arising from its use, whether in contract, tort, or otherwise (see the
full disclaimer in LICENSE).
This is an independent, privately made project, not affiliated with,
authorized, sponsored, or endorsed by Yamaha in any way. "Yamaha", "CP88",
"CP73", and related names are trademarks or product names of Yamaha Corporation,
used here only for identification and interoperability. The SysEx/YSFC formats
it uses are partly reverse-engineered (see
docs/implementation-notes.md). It can
write to your CP88/CP73 and overwrite files: sending Live Set Sounds to a
device replaces what is stored there, writing a file rewrites it from scratch,
and (as noted above) an .X9*-derived Live Set Sound zeroes the ~48 non-version
fields the X9 format does not store. Back up your data first and use at your own
risk — verify results before relying on them, especially when writing to
hardware.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cp_liveset-1.0.0.tar.gz.
File metadata
- Download URL: cp_liveset-1.0.0.tar.gz
- Upload date:
- Size: 108.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1021116ba433f21e4d19824b2a9a12aebbbff6b95f5f621d901532eed3c84ee1
|
|
| MD5 |
405bbbb5133eaf3b8567980ecf02a6a2
|
|
| BLAKE2b-256 |
365c22738ed267139eaaa42a15354d0cd070e56c9743ec9badf9dbca7e33d1bc
|
File details
Details for the file cp_liveset-1.0.0-py3-none-any.whl.
File metadata
- Download URL: cp_liveset-1.0.0-py3-none-any.whl
- Upload date:
- Size: 69.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc46ca38129d8502c7ee44e476a49ba1bc043a83ce09a61e2b6d3694c4a922c4
|
|
| MD5 |
c82a38343519cab83c186940e2c70655
|
|
| BLAKE2b-256 |
a711436cde39bae08bffd4a19492e9422474a99b6bae0721f306076b1d785fd1
|