abletoolz 2.0.0__tar.gz → 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {abletoolz-2.0.0 → abletoolz-2.1.0}/PKG-INFO +228 -37
- {abletoolz-2.0.0 → abletoolz-2.1.0}/README.md +225 -36
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/__init__.py +1 -1
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/__main__.py +1 -3
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/alc.py +1 -3
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/asd/analysis.py +12 -1
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/asd/parser.py +1 -1
- abletoolz-2.1.0/abletoolz/cli.py +764 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/color_tools.py +6 -8
- abletoolz-2.1.0/abletoolz/console.py +287 -0
- abletoolz-2.1.0/abletoolz/data/fabfilter/pro-c-2.json +594 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/decode_encode.py +4 -6
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/dj_crates.py +90 -29
- abletoolz-2.1.0/abletoolz/live_set/apply_ops.py +395 -0
- abletoolz-2.1.0/abletoolz/live_set/clips.py +978 -0
- abletoolz-2.1.0/abletoolz/live_set/describe.py +379 -0
- abletoolz-2.1.0/abletoolz/live_set/devices.py +226 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/document.py +33 -3
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/persistence.py +52 -17
- abletoolz-2.1.0/abletoolz/live_set/plugins.py +642 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/sample_ref.py +99 -31
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/samples.py +18 -5
- abletoolz-2.1.0/abletoolz/live_set/sections.py +340 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/tracks.py +17 -4
- abletoolz-2.1.0/abletoolz/live_set/xml_edit.py +129 -0
- abletoolz-2.1.0/abletoolz/meta.py +181 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/misc.py +49 -2
- abletoolz-2.1.0/abletoolz/plugin_parsers/__init__.py +64 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/plugin_parsers/base.py +42 -20
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/plugin_parsers/config.py +22 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/format_translation.py +536 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/mapping.py +556 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/parsers/__init__.py +1 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/parsers/maschine.py +195 -0
- abletoolz-2.0.0/abletoolz/plugin_parsers/serato_sample_parser.py → abletoolz-2.1.0/abletoolz/plugin_parsers/parsers/serato.py +9 -8
- abletoolz-2.1.0/abletoolz/plugin_parsers/parsers/xfadelooper.py +188 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/plugin_db.py +341 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/read_plugin_files.py +295 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/plugin_parsers/registry.py +5 -6
- abletoolz-2.1.0/abletoolz/plugin_parsers/repair.py +370 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/__init__.py +353 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/derived.py +305 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/fabfilter.py +825 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/families.py +306 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/fxbk.py +82 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/izotope.py +34 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/state/measured.py +185 -0
- abletoolz-2.1.0/abletoolz/plugin_parsers/uid_sources.py +431 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/plugin_parsers/upgrade_rules.py +4 -3
- abletoolz-2.1.0/abletoolz/report.py +274 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/sample_databaser/create_db.py +7 -2
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/sample_matcher.py +5 -4
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/utils.py +25 -20
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/versioning.py +6 -6
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz.egg-info/PKG-INFO +228 -37
- abletoolz-2.1.0/abletoolz.egg-info/SOURCES.txt +101 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz.egg-info/requires.txt +2 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/pyproject.toml +17 -3
- abletoolz-2.1.0/test/test_apply_ops.py +510 -0
- abletoolz-2.1.0/test/test_au.py +122 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_cli.py +53 -0
- abletoolz-2.1.0/test/test_cli_battery.py +306 -0
- abletoolz-2.1.0/test/test_clips.py +179 -0
- abletoolz-2.1.0/test/test_clips_write.py +522 -0
- abletoolz-2.1.0/test/test_describe.py +272 -0
- abletoolz-2.1.0/test/test_devices.py +173 -0
- abletoolz-2.1.0/test/test_devices_write.py +358 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_dj_crates.py +50 -18
- abletoolz-2.1.0/test/test_fabfilter.py +773 -0
- abletoolz-2.1.0/test/test_families.py +271 -0
- abletoolz-2.1.0/test/test_format_translation.py +406 -0
- abletoolz-2.1.0/test/test_mapping.py +555 -0
- abletoolz-2.1.0/test/test_maschine.py +167 -0
- abletoolz-2.1.0/test/test_meta.py +264 -0
- abletoolz-2.1.0/test/test_persistence.py +110 -0
- abletoolz-2.1.0/test/test_plugin_db.py +408 -0
- abletoolz-2.1.0/test/test_read_plugin_files.py +248 -0
- abletoolz-2.1.0/test/test_repair.py +989 -0
- abletoolz-2.1.0/test/test_report.py +230 -0
- abletoolz-2.1.0/test/test_sample_fix.py +181 -0
- abletoolz-2.1.0/test/test_sections.py +547 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_serato_sample.py +1 -1
- abletoolz-2.1.0/test/test_state.py +356 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_version_matrix.py +67 -5
- abletoolz-2.1.0/test/test_vst3.py +266 -0
- abletoolz-2.1.0/test/test_xfadelooper.py +230 -0
- abletoolz-2.1.0/test/test_xml_edit.py +113 -0
- abletoolz-2.0.0/abletoolz/cli.py +0 -462
- abletoolz-2.0.0/abletoolz/console.py +0 -80
- abletoolz-2.0.0/abletoolz/live_set/plugins.py +0 -246
- abletoolz-2.0.0/abletoolz/plugin_parsers/__init__.py +0 -68
- abletoolz-2.0.0/abletoolz/plugin_parsers/read_plugin_files.py +0 -173
- abletoolz-2.0.0/abletoolz.egg-info/SOURCES.txt +0 -55
- abletoolz-2.0.0/test/test_cli_battery.py +0 -167
- abletoolz-2.0.0/test/test_persistence.py +0 -52
- abletoolz-2.0.0/test/test_sample_fix.py +0 -95
- {abletoolz-2.0.0 → abletoolz-2.1.0}/LICENSE +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/asd/__init__.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/asd/writer.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/data/__init__.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/data/clip_template.alc +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/__init__.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/live_set/transport.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/sample_databaser/__init__.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz/schema.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz.egg-info/dependency_links.txt +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz.egg-info/entry_points.txt +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/abletoolz.egg-info/top_level.txt +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/setup.cfg +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_alc.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_asd.py +0 -0
- {abletoolz-2.0.0 → abletoolz-2.1.0}/test/test_versioning.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: abletoolz
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Tools made for editing, fixing and analyzing Ableton Live sets.
|
|
5
5
|
Author: ElixirBeats
|
|
6
6
|
License: GNU GENERAL PUBLIC LICENSE
|
|
@@ -707,23 +707,24 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
707
707
|
Requires-Dist: mypy; extra == "dev"
|
|
708
708
|
Requires-Dist: ruff; extra == "dev"
|
|
709
709
|
Requires-Dist: types-PyYAML; extra == "dev"
|
|
710
|
+
Requires-Dist: types-colorama; extra == "dev"
|
|
711
|
+
Requires-Dist: types-tqdm; extra == "dev"
|
|
710
712
|
Dynamic: license-file
|
|
711
713
|
|
|
712
714
|

|
|
713
715
|
# Abletoolz
|
|
714
716
|
|
|
715
|
-
Abletoolz is a
|
|
716
|
-
automate things that aren't available in Live and make your life easier.
|
|
717
|
+
Abletoolz is a command line tool to edit, fix and analyze Ableton Live sets. Primarily the purpose is
|
|
718
|
+
to automate things that aren't available in Live and make your life easier. It is also a Python library —
|
|
719
|
+
if you write Python, jump down to [Using as a library](#using-as-a-library).
|
|
717
720
|
It can:
|
|
718
721
|
- Run on one set, or an entire directory of sets. So you can fix/analyze everything with one command.
|
|
719
722
|
- Color all your tracks/clips with random color gradients.
|
|
720
723
|
- Create a sample database of all your sample folders, which can then be used to automatically fix any broken samples in your ableton sets.
|
|
721
724
|
- Set all your Master/Cue outputs to a specific output, so if you buy a new audio interface you can fix all your master outs to point to 7/8 in one go.
|
|
722
|
-
- Validate all plugins in a set are installed.
|
|
723
|
-
- Analyze plugin state with plugin-specific parsers, and fix what they understand
|
|
725
|
+
- Validate all plugins in a set are installed.
|
|
726
|
+
- Analyze plugin state with plugin-specific parsers, and fix what they understand — like sample paths inside a plugin's saved state that no longer exist on disk. `--list-parsers` shows what's registered.
|
|
724
727
|
- Fold/Unfold all tracks, and/or set track height and widths.
|
|
725
|
-
- Prepend the set version name to the beginning of the file.
|
|
726
|
-
- Append the number of bars of the track, and the bpm to the end of the file.
|
|
727
728
|
- Dump the XML of the set, in case you want to dissect how they are structured or contribute to this project : )
|
|
728
729
|
|
|
729
730
|
It also:
|
|
@@ -732,6 +733,27 @@ It also:
|
|
|
732
733
|
- Works on sets from Ableton 8.2 through Live 12, including current Live 12 betas (not every command works with the oldest versions).
|
|
733
734
|
- Preserves the original set modification time.
|
|
734
735
|
|
|
736
|
+
## What's new in 2.1
|
|
737
|
+
|
|
738
|
+
- **Repair plugins Live can no longer load.** `--repair-plugins` rewrites dead VST2 devices as the VST3 you
|
|
739
|
+
have installed and brings the saved patch along, so an old set comes back with its settings instead of
|
|
740
|
+
blank devices. You decide what becomes what: `--suggest-plugin-mappings` drafts that table from the
|
|
741
|
+
plugins on your machine and `--plugin-db` scans them once so it's quick.
|
|
742
|
+
- **Every run writes down what it found.** Each set gets a `.meta.yaml` sidecar — Live version, bars, bpm,
|
|
743
|
+
what was missing, what was fixed — with `status` and `notes` fields that are yours to fill in and are
|
|
744
|
+
never overwritten. Each run also writes a JSON report of everything it did. The sidecar doubles as a
|
|
745
|
+
cache, so scanning a big library a second time is much faster, and `--output` keeps it all in a folder
|
|
746
|
+
of your choosing instead of next to your sets.
|
|
747
|
+
- **Plugin checks work properly on macOS.** VST3s are found as bundles and Audio Units are matched by their
|
|
748
|
+
component identity, so a plugin that moved or got renamed is still found.
|
|
749
|
+
- **A lot more you can do from Python.** `AbletonSet` picked up `clips` and `devices`, so writing MIDI notes,
|
|
750
|
+
copying a section of the arrangement and grafting a device chain from one set onto another are all
|
|
751
|
+
possible now. See [Using as a library](#using-as-a-library).
|
|
752
|
+
- **More sampler parsers**, so more plugins can have the broken sample and kit paths inside their own saved
|
|
753
|
+
state found and fixed. `--list-parsers` shows the current roster.
|
|
754
|
+
- `--append-bars-bpm` and `--prepend-version` are deprecated — they still work, but the bars, bpm and Live
|
|
755
|
+
version now land in each set's sidecar without renaming your files.
|
|
756
|
+
|
|
735
757
|
## What's new in 2.0
|
|
736
758
|
|
|
737
759
|
- **Live 12 support.** Live 12 renamed core elements (`MasterTrack` → `MainTrack`, and it finally fixed the
|
|
@@ -746,7 +768,6 @@ It also:
|
|
|
746
768
|
projects: a set full of dead 32-bit plugins can be retargeted at the modern versions you actually have
|
|
747
769
|
installed, and where a translator exists for the plugin's state format, your old settings come along instead
|
|
748
770
|
of being lost. It can even translate one plugin into a different one once someone decodes both formats.
|
|
749
|
-
Serato Sample is the first supported parser (finds and fixes its broken sample paths).
|
|
750
771
|
- **Safer saves.** Sets are serialized before anything on disk is touched — a failure can no longer leave you
|
|
751
772
|
without your original file (which was always backed up, but still).
|
|
752
773
|
- **Honest batch results.** Directory runs process sets concurrently (`--jobs`), report `N ok, M failed`, and
|
|
@@ -775,9 +796,10 @@ Once you verify you have python 3.12+, install with pip:
|
|
|
775
796
|
pip install abletoolz
|
|
776
797
|
```
|
|
777
798
|
This will install abletoolz as a command in your command line, you can now call `abletoolz` from anywhere if the
|
|
778
|
-
installation completed successfully
|
|
799
|
+
installation completed successfully, but make sure to open a new shell before trying.
|
|
779
800
|
|
|
780
801
|
## Usage:
|
|
802
|
+
|
|
781
803
|
`-h` Print argument usage.
|
|
782
804
|
`-v` Verbosity. For some commands, displays more information.
|
|
783
805
|
|
|
@@ -808,15 +830,15 @@ without quotes, backslashes are fine (but you'll need to use quotes if you have
|
|
|
808
830
|
sample as long as one of the two are valid. If relative path doesn't exist(Not collected and saved) only absolute path
|
|
809
831
|
is checked. By default only missing samples are displayed to reduce clutter, use `-v` to show all found samples as well.
|
|
810
832
|
|
|
811
|
-
`--check-plugins` Checks
|
|
833
|
+
`--check-plugins` Checks installed VST and Audio Unit references. **Note**: When loading a set, if Ableton finds the
|
|
812
834
|
same plugin name in a different path it will automatically fix any broken paths the next time you save your project. This
|
|
813
835
|
command attempts to find missing VSTs and show an updated path if it finds one that Ableton will most likely load.
|
|
814
|
-
Mac Audio Units/AU are not stored with paths, just plugin names. Mac OS is not supported for this yet.
|
|
815
836
|
```
|
|
816
837
|
[MidiTrack: 1-Serum] Plugin: Serum_x64.dll, Path: C:\Program Files\VstPlugins\Xfer\Serum_x64.dll, Exists: True
|
|
817
838
|
[AudioTrack: 2-Audio] Plugin: Effectrix.dll, Path: C:\Program Files\VstPlugins\Effectrix.dll, Exists: True
|
|
818
839
|
[AudioTrack: 3-Audio] Plugin: DrumLeveler.dll, Path: None, Exists: False
|
|
819
|
-
|
|
840
|
+
[AudioTrack: 4-Audio] Plugin: Apple: AUBandpass, Path: None, Exists: True
|
|
841
|
+
[MidiTrack: 5-MIDI] Plugin: Arturia: Analog Lab V, Path: /Library/Audio/Plug-Ins/Components/Analog Lab V.component, Exists: True
|
|
820
842
|
```
|
|
821
843
|
|
|
822
844
|
`--list-tracks` List track information.
|
|
@@ -832,17 +854,58 @@ Track type ReturnTrack, Name A-Reverb, Id 2, Group id -1, Color 60, Wid
|
|
|
832
854
|
|
|
833
855
|
Plugin state inside a set is an opaque buffer per plugin; parsers teach abletoolz specific formats.
|
|
834
856
|
|
|
857
|
+
Only a handful of plugins are covered currently, so out of the box this won't fix your sets.. but the value is in the framework being able to manipulate those plugin buffers/shapes easily so it is possible to reverse engineer and recover old sets or turn them into newer plugins even. This depends entirely on if you'll be able to decode the buffer. Once you figure out the format (and I recommend using agents to help you do this), from then on every device of that plugin across every set you own is fixable
|
|
858
|
+
with one command. Old collections tend to be broken by the same few plugins over and over, so each one
|
|
859
|
+
cracked recovers sets in bulk.
|
|
860
|
+
|
|
835
861
|
`--list-parsers` List registered plugin parsers and their buffer formats.
|
|
836
862
|
|
|
837
|
-
`--analyze-plugins` Deep analysis using the registered parsers — reports issues like
|
|
838
|
-
|
|
863
|
+
`--analyze-plugins` Deep analysis using the registered parsers — reports issues like sample or kit
|
|
864
|
+
references inside a plugin's state that are no longer on disk.
|
|
839
865
|
|
|
840
|
-
`--fix-plugins` Fix what a parser knows how to fix
|
|
841
|
-
sample database. Use with `-s` to write changes.
|
|
866
|
+
`--fix-plugins` Fix what a parser knows how to fix — typically broken sample paths inside a plugin's
|
|
867
|
+
state — using the sample database. Use with `-s` to write changes.
|
|
842
868
|
|
|
843
869
|
`--upgrade-plugins` Upgrade plugin paths when a rule and an installed target exist. Rules live in a config file
|
|
844
870
|
so nothing is guessed.
|
|
845
871
|
|
|
872
|
+
`--repair-plugins` Replace the plugin devices Live can no longer load with what your mappings say they
|
|
873
|
+
become, keeping the patch they were saved with. A device that still loads is left alone, and a broken device
|
|
874
|
+
with no mapping is reported by name rather than guessed at. Use with `-s` to write changes.
|
|
875
|
+
|
|
876
|
+
There is no format to choose on the command line, because the mapping says which way each plugin goes:
|
|
877
|
+
|
|
878
|
+
```yaml
|
|
879
|
+
plugin_translation:
|
|
880
|
+
targets:
|
|
881
|
+
"Some Plugin.64": # the name the VST2 device stores
|
|
882
|
+
to: vst3 # the format it becomes (this is the default)
|
|
883
|
+
name: "Some Plugin" # the name the VST3 goes by
|
|
884
|
+
state: verbatim # or a named state policy a parser provides
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
`--plugin-db` Scan every plugin installed on this machine into a local plugin database — the plugin half of
|
|
888
|
+
`--db`. `--repair-plugins` and `--suggest-plugin-mappings` read it, so run this once first (and again after
|
|
889
|
+
installing plugins). Takes no set and no folders; add folders under `plugin_database.paths` in your config,
|
|
890
|
+
the way `sample_database.paths` works.
|
|
891
|
+
|
|
892
|
+
```yaml
|
|
893
|
+
plugin_database:
|
|
894
|
+
paths:
|
|
895
|
+
- "D:/VstPlugins"
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
`--suggest-plugin-mappings [file.yaml]` Write that mapping table for you, or most of it. It takes no set: it
|
|
899
|
+
pairs the names that look like the same plugin in two formats and writes a YAML file for you to read. Every
|
|
900
|
+
suggestion aims at something you actually have installed; where nothing can perform a translation yet, it
|
|
901
|
+
says so.
|
|
902
|
+
|
|
903
|
+
**Everything it writes comes out commented, including the suggestions that look certain.** Uncommenting a
|
|
904
|
+
line is how you enable it, and that is meant to be a decision you make one plugin at a time. Each line says
|
|
905
|
+
how sure the match is and flags the ones to watch — a version change that won't load the old patch, or two
|
|
906
|
+
plugins whose names agree and whose vendors do not, which is a different plugin wearing a familiar name.
|
|
907
|
+
Nothing is in force until you move a line into your own config file, which this never touches.
|
|
908
|
+
|
|
846
909
|
`--dump-plugins` Dump plugin buffer hex + decoded preview — the starting point for writing a new parser.
|
|
847
910
|
Contributions welcome.
|
|
848
911
|
|
|
@@ -919,12 +982,39 @@ development. You can edit this xml file, rename it from `.xml` to `.als` and Ab
|
|
|
919
982
|
option multiple times, the previous xml file will be moved into the `abletoolz_backup`
|
|
920
983
|
folder with the same renaming behavior as `-s/--save`.
|
|
921
984
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
985
|
+
### Records - what a run writes down
|
|
986
|
+
|
|
987
|
+
Runs also save their results to files, so you can look back later or process them with other tools.
|
|
988
|
+
|
|
989
|
+
**Per-set sidecars.** Any run that checks or fixes plugins or samples leaves a `<set name>.meta.yaml` beside
|
|
990
|
+
each set it looked at: the Live version, bars and bpm, the missing plugins by device count, what was fixed,
|
|
991
|
+
the missing samples, and the hash of the set as it was read. Underneath there are two fields that are yours —
|
|
992
|
+
`status` and `notes` — plus anything else you care to add. abletoolz reads them, carries them across every
|
|
993
|
+
rescan and never writes them itself.
|
|
994
|
+
```yaml
|
|
995
|
+
status: needs work
|
|
996
|
+
notes: reverb tail runs long in the breakdown
|
|
997
|
+
scan:
|
|
998
|
+
scanned: '2026-08-17T10:16:07.959317-07:00'
|
|
999
|
+
scanned_with: abletoolz 2.0.0
|
|
1000
|
+
set_hash: 499af899b43a5ad850688c93ddbdeaae30602101fc005f7d2a7adec1e329eea9
|
|
1001
|
+
live_version: Ableton Live 11.3.42
|
|
1002
|
+
bars: 128
|
|
1003
|
+
bpm: 174.0
|
|
1004
|
+
plugins_missing:
|
|
1005
|
+
Serum_x64: 2
|
|
1006
|
+
samples_missing: 3
|
|
1007
|
+
```
|
|
1008
|
+
The sidecar also acts as a cache: if a set hasn't changed since the last scan, the run reuses the stored
|
|
1009
|
+
results and prints `Cached`, which makes a second pass over a big library much faster. Edit the set and it
|
|
1010
|
+
gets scanned again from scratch. `--no-meta` turns the whole thing off.
|
|
926
1011
|
|
|
927
|
-
|
|
1012
|
+
**Run reports.** Every run writes `abletoolz_report_<timestamp>.json` into the folder it processed: one
|
|
1013
|
+
record per set (what changed, what was fixed, what was missing, what was refused and why, what failed to
|
|
1014
|
+
parse), plus totals for the whole run — like every missing plugin across your library with counts. Useful
|
|
1015
|
+
when you want to script against results instead of reading console output.
|
|
1016
|
+
|
|
1017
|
+
With `--output`, both land in the output folder instead. Nothing is written next to the originals.
|
|
928
1018
|
|
|
929
1019
|
## Examples
|
|
930
1020
|
Check all samples in sets
|
|
@@ -967,35 +1057,136 @@ Saved set to D:\all_sets\myset.als
|
|
|
967
1057
|
Took 0:00:00.371096 to process 1 set(s): 1 ok, 0 failed
|
|
968
1058
|
```
|
|
969
1059
|
|
|
1060
|
+
## Using as a library
|
|
1061
|
+
|
|
1062
|
+
abletoolz started life as a command line tool, but the more powerful way to use it now is as a Python
|
|
1063
|
+
library. The command line above is this library pointed at a whole collection. A set is an object: open
|
|
1064
|
+
one, read what is in it, change it, save it or create one from scratch. `AbletonSet` is the
|
|
1065
|
+
document, and what a set holds hangs off it as `transport`, `tracks`, `clips`, `devices`, `samples` and
|
|
1066
|
+
`plugins`. Nothing reaches disk until you call `save_set()`, which moves the original into the backup
|
|
1067
|
+
folder first, exactly as `-s/--save` does.
|
|
1068
|
+
|
|
1069
|
+
**Read a set's tempo, length and tracks.**
|
|
1070
|
+
|
|
1071
|
+
```python
|
|
1072
|
+
import pathlib
|
|
1073
|
+
|
|
1074
|
+
from abletoolz.live_set import AbletonSet
|
|
1075
|
+
|
|
1076
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
1077
|
+
live_set.parse()
|
|
1078
|
+
length = live_set.transport.length()
|
|
1079
|
+
print(f"{live_set.version}: {length.bpm} bpm, {length.bars} bars, {length}")
|
|
1080
|
+
for track in live_set.tracks.load():
|
|
1081
|
+
print(track.type, track.name, "height", track.height, "color", track.color)
|
|
970
1082
|
```
|
|
971
|
-
|
|
1083
|
+
|
|
1084
|
+
**Find missing samples and fix them.** Build the sample database once — this is what `--db` does — and hand it to
|
|
1085
|
+
any set.
|
|
1086
|
+
|
|
1087
|
+
```python
|
|
1088
|
+
import pathlib
|
|
1089
|
+
|
|
1090
|
+
from abletoolz.live_set import AbletonSet
|
|
1091
|
+
from abletoolz.sample_databaser import create_db
|
|
1092
|
+
|
|
1093
|
+
create_db.create_or_update_db(["D:/samples"], db_path=pathlib.Path("sample_db.json"))
|
|
1094
|
+
db = create_db.load_db(pathlib.Path("sample_db.json"))
|
|
1095
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
1096
|
+
live_set.parse()
|
|
1097
|
+
live_set.find_project_root_folder() # where collect-and-save copies to
|
|
1098
|
+
for ref in live_set.samples.check():
|
|
1099
|
+
print("missing:", ref.name, ref.absolute)
|
|
1100
|
+
if live_set.samples.fix(db, collect_and_save=True):
|
|
1101
|
+
live_set.save_set()
|
|
972
1102
|
```
|
|
1103
|
+
|
|
1104
|
+
**Check that every plugin a set uses is still installed.** Each reference comes back with where it points, whether
|
|
1105
|
+
that file is there, and the closest thing found on this machine when it is not.
|
|
1106
|
+
|
|
1107
|
+
```python
|
|
1108
|
+
import pathlib
|
|
1109
|
+
|
|
1110
|
+
from abletoolz.live_set import AbletonSet
|
|
1111
|
+
|
|
1112
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
1113
|
+
live_set.parse()
|
|
1114
|
+
for ref in live_set.plugins.scan([pathlib.Path("D:/VstPlugins")]):
|
|
1115
|
+
where = ref.path if ref.exists else ref.alternative or "nowhere on this machine"
|
|
1116
|
+
print(f"{ref.track_location}: {ref.name} ({ref.kind}) -> {where}")
|
|
973
1117
|
```
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1118
|
+
|
|
1119
|
+
**Write MIDI notes into a clip.** A note is pitch, start and duration in beats; everything else defaults to what
|
|
1120
|
+
Live gives a freshly drawn note.
|
|
1121
|
+
|
|
1122
|
+
```python
|
|
1123
|
+
import pathlib
|
|
1124
|
+
|
|
1125
|
+
from abletoolz.live_set import AbletonSet
|
|
1126
|
+
from abletoolz.live_set.clips import MidiNote
|
|
1127
|
+
|
|
1128
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
1129
|
+
live_set.parse()
|
|
1130
|
+
clip = live_set.clips.midi()[0]
|
|
1131
|
+
bar = [MidiNote(pitch, start=float(beat), duration=0.5) for beat, pitch in enumerate((36, 42, 38, 42))]
|
|
1132
|
+
live_set.clips.set_notes(clip, bar)
|
|
1133
|
+
live_set.save_set()
|
|
977
1134
|
```
|
|
978
1135
|
|
|
979
|
-
|
|
1136
|
+
**Copy a section of the arrangement somewhere else.** Clips and track automation inside the window travel
|
|
1137
|
+
together, and a clip crossing either edge is reported rather than trimmed.
|
|
980
1138
|
|
|
981
|
-
|
|
1139
|
+
```python
|
|
1140
|
+
import pathlib
|
|
1141
|
+
|
|
1142
|
+
from abletoolz.live_set import AbletonSet
|
|
1143
|
+
from abletoolz.live_set.sections import SectionMode
|
|
1144
|
+
|
|
1145
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
1146
|
+
live_set.parse()
|
|
1147
|
+
report = live_set.clips.copy_section(64.0, 128.0, 448.0, mode=SectionMode.REFUSE) # beats
|
|
1148
|
+
for line in report.lines():
|
|
1149
|
+
print(line)
|
|
1150
|
+
live_set.save_set()
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
**Graft a device chain from one set onto a track in another.** Both sets have to be the same major Live version,
|
|
1154
|
+
and the chain arrives as a copy Live will open rather than a paste that takes the set down with it.
|
|
982
1155
|
|
|
983
1156
|
```python
|
|
1157
|
+
import pathlib
|
|
1158
|
+
|
|
984
1159
|
from abletoolz.live_set import AbletonSet
|
|
985
1160
|
|
|
986
|
-
|
|
1161
|
+
donor = AbletonSet(pathlib.Path("donor_set.als"))
|
|
1162
|
+
donor.parse()
|
|
1163
|
+
chain = next(track for track in donor.devices.inventory() if track.track_name == "Kick")
|
|
1164
|
+
live_set = AbletonSet(pathlib.Path("my_set.als"))
|
|
987
1165
|
live_set.parse()
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1166
|
+
for device in live_set.devices.graft_chain(chain, "2-Audio", mode="append"):
|
|
1167
|
+
print("grafted", device.display_name)
|
|
1168
|
+
live_set.save_set()
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
**Read back the sidecar a scan left.** It comes out as an object rather than a blob of YAML, your own `status` and
|
|
1172
|
+
`notes` included — enough to triage a whole library without opening a set.
|
|
1173
|
+
|
|
1174
|
+
```python
|
|
1175
|
+
import pathlib
|
|
1176
|
+
|
|
1177
|
+
from abletoolz import meta
|
|
1178
|
+
|
|
1179
|
+
sidecar = meta.read(pathlib.Path("my_set.als"))
|
|
1180
|
+
if sidecar is not None and sidecar.scan is not None:
|
|
1181
|
+
print(sidecar.scan.live_version, sidecar.scan.bars, sidecar.scan.bpm)
|
|
1182
|
+
print("missing plugins:", sidecar.scan.plugins_missing)
|
|
1183
|
+
print("missing samples:", sidecar.scan.samples_missing)
|
|
1184
|
+
print("yours:", sidecar.status, "--", sidecar.notes)
|
|
992
1185
|
```
|
|
993
1186
|
|
|
994
1187
|
## Future plans:
|
|
995
|
-
- Export sets as an older Live
|
|
996
|
-
are mechanical).
|
|
1188
|
+
- Export sets as an any / older Live versions (Live's compatibility is one-directional; most of the schema differences
|
|
1189
|
+
are mechanical). Some of this is partially implemented.
|
|
997
1190
|
- More plugin parsers — the upgrade framework is in place, formats need decoding one plugin at a time.
|
|
998
|
-
- Figure out way to verify AU plugins on MacOs.
|
|
999
|
-
- Analyze audio clips and color them based on a Serato like gradient(red for bass, turqoise for hi end etc...)
|
|
1000
1191
|
- Figure out how ableton calculates CRC's for samples and use it to make perfect sample fixing. The current algorithm has a very low probability of being wrong, but this would guarantee each result is correct.
|
|
1001
|
-
-
|
|
1192
|
+
- More AI agentic tool interaction interfacing
|