rekordbox-edit 0.13.0.dev7__tar.gz → 0.13.0.dev8__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.
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CHANGELOG.md +2 -1
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/PKG-INFO +1 -1
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/pyproject.toml +1 -1
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/README.md +8 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/beets-plugin-integration.md +557 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/decisions/converted-original-disposition.md +77 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/convert-repoint.txt +59 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/event-order.txt +69 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/file-level-encode.txt +11 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/id-stability.txt +7 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/media-field-roundtrip.txt +9 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/convert_repoint.py +194 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/event_probe.py +156 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/file_level_encode.py +85 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/id_stability.py +75 -0
- rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/media_field_roundtrip.py +145 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/uv.lock +1 -1
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.agent-style/RULES.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.agent-style/claude-code.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/build-release-notes/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/build-release-notes/build_release_notes.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commit-check/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commitizen-bump/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/e2e/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/install/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/lint/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/test/action.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/cd.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/ci.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/publish.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/release.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.gitignore +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.pre-commit-config.yaml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.python-version +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.readthedocs.yaml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/AGENTS.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CLAUDE.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CONTRIBUTING.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/LICENSE +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/Makefile +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/README.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/codecov.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docker-compose.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/api.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/convert.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/edit.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/import.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/remove.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/search.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/faqs.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/filtering.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/index.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/stylesheets/extra.css +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/mkdocs.yml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/_tag_fields.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_convert.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_edit.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_field_handlers.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_import.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_relations.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_remove.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_search.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_utils.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_click.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_progress.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_utils.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/convert.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/edit.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/import_.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/main.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/remove.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/search.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/display.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/errors.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/locking.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/logger.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/models.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/query.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/tags.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/utils.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/renovate.json5 +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/convert-export-impact-test-plan.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/convert-export-impact.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-10-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-20-postexport.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-21-postexport-full.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-10-deviceedit.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-10-deviceedit.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-40-resync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-41-reexport-full.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-11317782-r3-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-11317782-r3-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r1-10-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r1-20-postexport.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r2b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r2b-00-lib.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r2b-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-121715696-r2b-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-140193439-r3-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-140193439-r3-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r1-10-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r1-20-postexport.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r2b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r2b-00-lib.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r2b-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-258421826-r2b-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-268012584-r3-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-268012584-r3-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r1-10-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r1-20-postexport.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r2b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r2b-00-lib.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r2b-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-53013048-r2b-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r1-10-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r1-20-postexport.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r2b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r2b-20-postconvert.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/subject-54793376-r2b-30-postsync.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/scripts/export_diff.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/scripts/export_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-experiment2-plan.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-experiment2.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-test-plan.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-verification.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/decisions/convert-and-reanalysis-impact.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/decisions/convert-helper-structure.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/01-inventory.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/02-cue-schema.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/03-cue-fields-by-format.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/04-anlz-deep.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/05-census.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/06-content-columns.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/07-analysis-flags.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/exp2-00-pvb2-origin.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-00-zero.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-04.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-05.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-04.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-05.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-06.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-then-high.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-unlocked-ref.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-nth-mid-normal.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-postanalyze2.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postopen.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-postanalyze2.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-locked.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-unlocked-reanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-postlock.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-prelock.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-00-zero.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-04.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-05.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-01.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-02.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-03.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-04.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-05.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-06.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-locked.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-then-high.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-nth-mid-normal.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postanalyze.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postconv.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/README.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/analysis_flags.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/anlz_census.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/anlz_deep.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/content_columns.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/cue_fields_by_format.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/cue_schema.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/grid_drift.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/inventory.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/pvb2_origin.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/reset_fresh_to_zero.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/restore_fixtures.sh +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/row_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/convert-thread-scaling.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/evidence/thread-scaling.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/_sources.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/format_scaling.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/shipped_path_scaling.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/write_cost.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/database-concurrency.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/evidence/plan-apply-probes.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/evidence/usn-spikes.txt +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/plan-apply-window.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/block_reservation.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/concurrent_increment.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/identity_probe.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/membership_probe.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/snapshot_probe.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/wal_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/usn-maintenance.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/decisions/edit-field-handlers.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/edit-relational-fields-test-plan.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/edit-relational-fields.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-112264610-e2-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-112264610-e2-10-reuse.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-141833798-e3b-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-141833798-e3b-10-newalbum.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-145166719-e0-census.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-176388182-e1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-176388182-e1-10-reuse.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-183309432-e4-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-183309432-e4-10-orphaned.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-47413782-v-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-47413782-v-10-reuse.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-50395296-v-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-50395296-v-10-reuse.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-57701453-e3-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-57701453-e3-10-diffartist.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-71974950-v-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-71974950-v-10-reuse.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/scripts/relational_diff.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/scripts/relational_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/file-types/README.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/file-types/decisions/unknown-file-types.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/decisions/commit-semantics-and-usn.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/column-coverage-gig.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/datecreated-source-gig.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/filetype-searchstr-gig.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/filetype-searchstr-local.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/import-row-shape-gig.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/tag-key-mapping-gig.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/import-track-row-shape.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/column_coverage.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/datecreated_source.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/filetype_and_searchstr.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/import_row_shape.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/tag_key_mapping.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/decisions/remove-command-behavior.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-109570093-r8-00-imported.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-109570093-r8-10-analyzed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-114745319-r1-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-114745319-r1-10-removed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-120331280-f6-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-120331280-f6-10-control.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-137975518-f3-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-137975518-f3-10-control.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-139425629-r6-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-139425629-r6-10-removed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-14241481-r5-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-14241481-r5-10-removed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-143461275-r7-01-folder-art.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-265892207-r3-00-baseline.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-265892207-r3-10-removed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-265892207-r4-10-playlist-only.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-36337051-r7-00-folder-art.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-74039842-r8-00-imported.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/evidence/rm-74039842-r8-10-analyzed.json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/remove-track-impact-test-plan.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/remove-track-impact.md +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/build_fixtures.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/removal_diff.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/removal_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/restore.sh +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/shared/scripts/subject_diff.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/shared/scripts/subject_snapshot.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/ruff.toml +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/conftest.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_convert.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_edit.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_field_handlers.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_import.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_package.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_relations.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_remove.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_search.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_utils.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_convert.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_edit.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_import.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_locking.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_main.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_progress.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_remove.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_search.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_utils.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/conftest.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/Dockerfile +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__init__.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[macos].json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[windows].json +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__snapshots__/test_journey.ambr +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/conftest.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/01-flac-44_1k-16b.flac +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/02-flac-96k-24b.flac +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/03-alac-44_1k-16b.m4a +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/04-alac-48k-24b.m4a +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/05-aiff-44_1k-16b.aiff +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/06-wav-96k-24b.wav +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/07-mp3-44_1k-320cbr.mp3 +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/08-mp3-44_1k-v0vbr.mp3 +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/09-aac-44_1k-256kbps.m4a +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/10-/303/274/303/261/303/256c/303/266d/303/251-flac-44_1k-16b.flac" +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/macos/master.6.8.6.db +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/windows/master.6.8.6.db +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_edit_fields.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_import.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_journey.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_remove.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_display.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_locking.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_logger.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_models.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_query.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_tag_fields.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_tags.py +0 -0
- {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
## v0.13.0.
|
|
1
|
+
## v0.13.0.dev8 (2026-09-08)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
- docs(research): investigate integrating rekordbox-edit with beets
|
|
4
5
|
- fix(edit): give the records an edit creates and collects their USNs
|
|
5
6
|
- An edit that reassigned a relational field left the record it created
|
|
6
7
|
unstamped and the one it collected outside the counter, so a syncing peer
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: rekordbox-edit
|
|
3
|
-
Version: 0.13.0.
|
|
3
|
+
Version: 0.13.0.dev8
|
|
4
4
|
Summary: Tools for managing and modifying a RekordBox library en-masse
|
|
5
5
|
Project-URL: Homepage, https://github.com/jviall/rekordbox-edit
|
|
6
6
|
Project-URL: Repository, https://github.com/jviall/rekordbox-edit
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "rekordbox-edit"
|
|
7
|
-
version = "0.13.0.
|
|
7
|
+
version = "0.13.0.dev8"
|
|
8
8
|
description = "Tools for managing and modifying a RekordBox library en-masse"
|
|
9
9
|
authors = [{ name = "James Viall", email= "jamesviall@pm.me"}]
|
|
10
10
|
license = "MIT"
|
|
@@ -47,6 +47,14 @@ formal and terse, and states findings as the evidence supports them.
|
|
|
47
47
|
command deliberately diverges from that behavior.
|
|
48
48
|
- **`file-types/`** — how `convert` and the display layer should treat Rekordbox
|
|
49
49
|
`FileType` codes the tool does not map.
|
|
50
|
+
- **`beets-plugin-integration/`** — what beets' plugin architecture permits a
|
|
51
|
+
rekordbox-edit integration to do: where events fire relative to database
|
|
52
|
+
inserts and file moves, which events the autotagger gates off, why beets item
|
|
53
|
+
identifiers do not survive a reimport, and what that forces about how the two
|
|
54
|
+
databases are joined. Also what it takes for a beets library to follow an
|
|
55
|
+
`rbe convert`, why the converted file usually needs no move, and why keeping
|
|
56
|
+
the original leaves the two libraries unable to agree — which is what settles
|
|
57
|
+
the plugin deleting originals by default where the CLI does not.
|
|
50
58
|
- **`import-track-row-shape/`** — what a `DjmdContent` row holds immediately after
|
|
51
59
|
Rekordbox imports a track and before analysis runs, which columns come from
|
|
52
60
|
tags rather than the audio, and where `pyrekordbox.add_content` diverges from
|
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
# Integrating rekordbox-edit With Beets
|
|
2
|
+
|
|
3
|
+
## Question
|
|
4
|
+
|
|
5
|
+
Beets manages a music library: it tags files, relocates them under a path
|
|
6
|
+
template, and records the result in a SQLite database. Rekordbox maintains its
|
|
7
|
+
own database of the same files, keyed by path. When beets moves or transcodes a
|
|
8
|
+
file, the rekordbox row pointing at it becomes wrong.
|
|
9
|
+
|
|
10
|
+
What integration does beets' plugin architecture actually permit, and what shape
|
|
11
|
+
should a rekordbox-edit plugin take? Three workflows frame the question:
|
|
12
|
+
|
|
13
|
+
1. **Convert, then follow.** A user runs `rbe convert` over tracks rekordbox
|
|
14
|
+
manages. Each file is transcoded beside its source and its rekordbox row is
|
|
15
|
+
repointed at the output; depending on the mode, the source is then deleted or
|
|
16
|
+
kept. Beets manages the same files and still points at the sources. The new
|
|
17
|
+
paths have to reach the beets library, and where the source is kept the two
|
|
18
|
+
databases end up disagreeing about how many tracks exist.
|
|
19
|
+
2. **Repoint after a move.** A user imports files that beets relocates, and
|
|
20
|
+
wants the rekordbox rows to follow, along with any columns beets can now
|
|
21
|
+
populate better than the original tags did.
|
|
22
|
+
3. **Reimport and update.** A user re-runs the autotagger over items already in
|
|
23
|
+
both databases and wants the improved metadata propagated to rekordbox.
|
|
24
|
+
|
|
25
|
+
## Terminology
|
|
26
|
+
|
|
27
|
+
Both tools have a library, a convert, and an import, and this document discusses
|
|
28
|
+
all six. Two conventions keep them apart.
|
|
29
|
+
|
|
30
|
+
**A "row" without qualification is a `DjmdContent` row**, in rekordbox's
|
|
31
|
+
database. Beets' rows are never called rows: they are *items* and *albums*.
|
|
32
|
+
|
|
33
|
+
**A bare command name is beets'.** `import`, `convert`, and `modify` mean
|
|
34
|
+
`beet import`, the beets convert plugin, and `beet modify`. rekordbox-edit's are
|
|
35
|
+
always written `rbe convert`, `rbe edit`, `rbe import`, or named by the API
|
|
36
|
+
function behind them (`convert()`, `edit()`, `import_tracks()`).
|
|
37
|
+
|
|
38
|
+
### Beets
|
|
39
|
+
|
|
40
|
+
| Term | Meaning |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| **item** | One row of beets' SQLite library, describing one audio file: its path plus the fields read from its tags. |
|
|
43
|
+
| **album** | A row grouping items. An item's `album_id` names one; `Library.add` leaves it unset. |
|
|
44
|
+
| **singleton** | An item deliberately attached to no album. `beet import -s`, or `import.singletons: yes`. |
|
|
45
|
+
| **library directory** | The root beets relocates files under, the `directory:` config key. |
|
|
46
|
+
| **path template**, path format | The `paths:` config deciding where under the library directory a file belongs, such as `$albumartist/$album/$track $title`. `Item.destination()` evaluates it. The extension is not part of it; it comes from the file the item currently names. |
|
|
47
|
+
| **flexible attribute**, flexattr | A field held only in beets' database and never in the file's tags. Any field name beets does not recognise becomes one. |
|
|
48
|
+
| **media field** | A field bound to a real tag frame in the file, so `item.write()` stores it and `item.read()` recovers it. Plugins may declare new ones. |
|
|
49
|
+
| **the import pipeline** | The staged process `beet import` runs: task construction, tagging, plugin stages, then file relocation. Order matters throughout this document. |
|
|
50
|
+
| **import stage** | A plugin callback running *inside* that pipeline, given the session and the task, able to change what happens next. |
|
|
51
|
+
| **event**, listener | A named notification beets sends. A listener is told what happened; with a single exception it cannot alter the outcome. |
|
|
52
|
+
| **autotag** | Beets matching a file's metadata against MusicBrainz. On by default. |
|
|
53
|
+
| **as-is import**, `-A`, `autotag: no` | Importing with that lookup switched off, so beets keeps the file's existing tags. Internally the task's choice becomes `Action.ASIS` rather than `Action.APPLY`, which is what suppresses three events and tag writing. This document says **as-is import** for all three spellings. |
|
|
54
|
+
| **apply** | The opposite state, `Action.APPLY`: matched metadata is being written onto the item. `task.apply` is true only here. |
|
|
55
|
+
| **reimport** | Re-running the importer over files already in the library, `beet import -L`. It removes the existing items and inserts new ones rather than updating in place. |
|
|
56
|
+
| **the convert plugin** | Beets' bundled transcoder, `beetsplug/convert.py`. Never `rbe convert`. |
|
|
57
|
+
| **`convert.auto`**, `auto_keep`, `--keep-new` | Its modes, which differ in where the output lands and whether the library entry follows it. Tabulated under [Going Through the Beets Convert Plugin Is the Other Direction](#going-through-the-beets-convert-plugin-is-the-other-direction). |
|
|
58
|
+
|
|
59
|
+
### Rekordbox-edit
|
|
60
|
+
|
|
61
|
+
| Term | Meaning |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| **`DjmdContent`** | Rekordbox's track table, one row per track. `FolderPath` and `FileNameL` hold the location, `FileType` a numeric format code. |
|
|
64
|
+
| **`rbe convert`** | rekordbox-edit's transcode command. Encodes beside the source and repoints the existing `DjmdContent` row at the output. |
|
|
65
|
+
| **`rbe edit`** | Its field-editing command. Writes named columns onto matched rows through per-field handlers. |
|
|
66
|
+
| **`rbe import`**, `import_tracks()` | Its command for creating `DjmdContent` rows for files rekordbox does not yet know about, matched by resolved case-folded path. |
|
|
67
|
+
| **field handler** | The per-column unit `rbe edit` is built from, owning one field's validation and write. |
|
|
68
|
+
| **`--delete-originals`** | `rbe convert`'s policy for the source file once the output exists. The default `none` keeps it; `all` always deletes it; `lossless` deletes it only when the conversion lost no audio information. |
|
|
69
|
+
| **USN** | Rekordbox's change counter, global in `agentRegistry.localUpdateCount` and per-row in `rb_local_usn`. Every row rekordbox-edit writes is stamped. |
|
|
70
|
+
| **ANLZ** | Rekordbox's on-disk analysis files for a track, which embed the track's path and so must be rewritten when it moves. |
|
|
71
|
+
|
|
72
|
+
## Method
|
|
73
|
+
|
|
74
|
+
Code reading against beets 2.5.1 on Python 3.13.15, and rekordbox-edit at
|
|
75
|
+
`d920d9b`. Five probes under `scripts/` produce the files under `evidence/`:
|
|
76
|
+
|
|
77
|
+
- `event_probe.py` installs a plugin listening to every event in
|
|
78
|
+
`beets.plugins.EventType` plus the convert plugin's `after_convert`, then runs
|
|
79
|
+
an import and a reimport against a throwaway library.
|
|
80
|
+
- `id_stability.py` records item identifiers before and after a reimport, using
|
|
81
|
+
the unchanged file paths as a control.
|
|
82
|
+
- `media_field_roundtrip.py` stamps a plugin-declared media field and reads the
|
|
83
|
+
value back out of the file with mediafile alone.
|
|
84
|
+
- `convert_repoint.py` imports fixtures into a throwaway library, transcodes each
|
|
85
|
+
one beside its source the way `rbe convert` does, and reports what it costs to
|
|
86
|
+
repoint the existing item at the output or to register the output as a new one.
|
|
87
|
+
- `file_level_encode.py` builds rekordbox-edit's `_EncodeJob` by hand from a path
|
|
88
|
+
on disk and runs `_encode_one` against it, with and without a declared
|
|
89
|
+
rekordbox file type. It runs in the rekordbox-edit environment rather than
|
|
90
|
+
beets'; the rest run against `beet` on `PATH`.
|
|
91
|
+
|
|
92
|
+
Each builds its own library or output directory in a temp directory, and none
|
|
93
|
+
needs the network. The audio fixtures are `tests/e2e/fixtures/audio/`.
|
|
94
|
+
|
|
95
|
+
## Findings
|
|
96
|
+
|
|
97
|
+
### The Plugin Surface
|
|
98
|
+
|
|
99
|
+
A plugin subclasses `BeetsPlugin` and extends beets through seven mechanisms,
|
|
100
|
+
all in `beets/plugins.py`:
|
|
101
|
+
|
|
102
|
+
| Mechanism | Entry point | Purpose |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| Subcommands | `commands()` (line 270) | Add `beet <verb>` |
|
|
105
|
+
| Event listeners | `register_listener()` (line 355) | React to a named event |
|
|
106
|
+
| Import stages | `early_import_stages`, `import_stages` (lines 286, 296) | Run inside the import pipeline |
|
|
107
|
+
| Media fields | `add_media_field()` (line 339) | Bind a field to real tag frames |
|
|
108
|
+
| Flexible types | `item_types`, `album_types` attributes (line 516) | Typed database-only fields |
|
|
109
|
+
| Queries | `queries()` (line 335), `item_queries` | New query prefixes |
|
|
110
|
+
| Template fields | `template_field()` (line 379) | New `$name` in path formats |
|
|
111
|
+
|
|
112
|
+
Events are declared as a `Literal` of 29 names (line 70). `send()` returns the
|
|
113
|
+
non-None values handlers produced (line 636), but only one caller consumes them:
|
|
114
|
+
`handle_created` treats the return of `import_task_created` as replacement tasks
|
|
115
|
+
(`importer/tasks.py:345-357`). Every other event is notification only. A
|
|
116
|
+
listener cannot veto, defer, or alter what beets is about to do.
|
|
117
|
+
|
|
118
|
+
Import stages are the mechanism that can. They receive the session and the task
|
|
119
|
+
and run inside the pipeline rather than beside it.
|
|
120
|
+
|
|
121
|
+
### The Import Pipeline Inserts Items Before It Moves Files
|
|
122
|
+
|
|
123
|
+
The pipeline is assembled in `importer/session.py:195-228`. Plugin stages are
|
|
124
|
+
appended after the tagging stages and before `manipulate_files`. Because
|
|
125
|
+
`_apply_choice` calls `task.add(session.lib)` (`importer/stages.py:323`) during
|
|
126
|
+
the tagging stage, items reach beets' database well before their files are
|
|
127
|
+
relocated.
|
|
128
|
+
|
|
129
|
+
The observed order for an album import with `convert.auto_keep` enabled
|
|
130
|
+
(`evidence/event-order.txt`):
|
|
131
|
+
|
|
132
|
+
| Phase | Events |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| Startup | `pluginload`, `library_opened`, `import_begin` |
|
|
135
|
+
| Task construction | `import_task_created` |
|
|
136
|
+
| Item insertion | `database_change` per album and item |
|
|
137
|
+
| Plugin stages | convert runs here: `write`, `after_write`, `after_convert` |
|
|
138
|
+
| File relocation | `item_copied` per item, then `database_change` |
|
|
139
|
+
| Finalisation | `import_task_files`, `album_imported`, `import`, `cli_exit` |
|
|
140
|
+
|
|
141
|
+
Two consequences follow. An import stage observes items already in the database
|
|
142
|
+
at their pre-move paths, so a stage that reads `item.path` reads a path that is
|
|
143
|
+
about to become wrong. And `after_convert` fires **before** `item_copied`, so a
|
|
144
|
+
listener pairing the convert output with `item.path` pairs it with the source
|
|
145
|
+
location rather than the library destination.
|
|
146
|
+
|
|
147
|
+
### The Autotagger Gates Three Events, None of Them Load-Bearing
|
|
148
|
+
|
|
149
|
+
Under `autotag: no`, beets substitutes `import_asis` for the candidate lookup and
|
|
150
|
+
user query stages (`importer/stages.py:219-231`). Three events therefore never
|
|
151
|
+
fire: `import_task_start` and `import_task_choice`, which those stages send, and
|
|
152
|
+
`import_task_apply`, which `_apply_choice` sends only when `task.apply` is true.
|
|
153
|
+
That property is `choice_flag == Action.APPLY` (`importer/tasks.py:218`), and an
|
|
154
|
+
as-is import sets `Action.ASIS`. The probe confirms all three absent.
|
|
155
|
+
|
|
156
|
+
All three report on the tagging decision, which a rekordbox integration does not
|
|
157
|
+
need. Everything it does need survives, for both task types:
|
|
158
|
+
|
|
159
|
+
| Signal | Album, `-A` | Singleton, `-A` |
|
|
160
|
+
| --- | --- | --- |
|
|
161
|
+
| `item_copied`, with source and destination | fires | fires |
|
|
162
|
+
| Completion | `album_imported` | `item_imported` |
|
|
163
|
+
| `import_task_files`, `cli_exit` | fire | fire |
|
|
164
|
+
|
|
165
|
+
An as-is import is therefore fully observable. This matters because it is the
|
|
166
|
+
mode a derived or downstream library runs in, and the mode in which nothing
|
|
167
|
+
should be re-queried from a metadata source.
|
|
168
|
+
|
|
169
|
+
Tag writing is suppressed by the same flag. `manipulate_files` calls
|
|
170
|
+
`item.try_write()` only when `write and (self.apply or self.choice_flag ==
|
|
171
|
+
Action.RETAG)` (`importer/tasks.py:482`), so an as-is import leaves file tags
|
|
172
|
+
untouched even with `write: yes`. That is coherent rather than limiting: beets
|
|
173
|
+
has produced no new tag data, so there is nothing to write. It also costs a
|
|
174
|
+
rekordbox integration nothing, because `import_tracks` reads tags from the file
|
|
175
|
+
rather than from beets.
|
|
176
|
+
|
|
177
|
+
The gap that does exist is on the other side. `edit` never reads tags;
|
|
178
|
+
`FolderPathField` probes only audio properties (codec, duration, sample rate,
|
|
179
|
+
size). Populating other rekordbox columns from beets metadata has no path
|
|
180
|
+
through the current handler set.
|
|
181
|
+
|
|
182
|
+
### item_imported Fires for Singletons Only
|
|
183
|
+
|
|
184
|
+
`album_imported` is sent from the album task's `_emit_imported`
|
|
185
|
+
(`importer/tasks.py:343`); `item_imported` is sent from the singleton task's
|
|
186
|
+
(line 684). An album import emits no per-item completion event. A plugin needing
|
|
187
|
+
per-item notification on both paths must register for both and iterate
|
|
188
|
+
`album.items()` in the album case.
|
|
189
|
+
|
|
190
|
+
### Relocation Events Carry Source and Destination
|
|
191
|
+
|
|
192
|
+
`item_moved`, `item_copied`, `item_linked`, `item_hardlinked`, and
|
|
193
|
+
`item_reflinked` are sent from `Item.move()` with `item`, `source`, and
|
|
194
|
+
`destination` (`library/models.py:1033-1065`). These are the only events pairing
|
|
195
|
+
both paths, which makes them the natural repoint trigger: locate the rekordbox
|
|
196
|
+
row whose path equals `source`, rewrite it to `destination`.
|
|
197
|
+
|
|
198
|
+
Three cautions apply. The event fires per operation, so the mode determines which
|
|
199
|
+
name to listen for and a plugin should register all five. With `copy: no` and
|
|
200
|
+
`move: no`, none fires. And on a reimport of in-library files, `source` and
|
|
201
|
+
`destination` are equal (`evidence/event-order.txt`, events 25 to 30), so a
|
|
202
|
+
handler must compare them before writing anything.
|
|
203
|
+
|
|
204
|
+
### Beets Item Identifiers Do Not Survive a Reimport
|
|
205
|
+
|
|
206
|
+
A reimport removes the existing items and inserts new ones. `add` calls
|
|
207
|
+
`record_replaced` then `remove_replaced`, which calls `dup_item.remove()` per
|
|
208
|
+
replaced item (`importer/tasks.py:612-624`), emitting `item_removed` and
|
|
209
|
+
`album_removed`. Measured across three files whose paths did not change
|
|
210
|
+
(`evidence/id-stability.txt`):
|
|
211
|
+
|
|
212
|
+
| File | ID before | ID after |
|
|
213
|
+
| --- | --- | --- |
|
|
214
|
+
| Wave Alpha.flac | 1 | 4 |
|
|
215
|
+
| Wave Beta.flac | 2 | 5 |
|
|
216
|
+
| High Quality.mp3 | 3 | 6 |
|
|
217
|
+
|
|
218
|
+
Every identifier changed while every path was preserved. **No mapping between
|
|
219
|
+
the two databases may be keyed on the beets item identifier.** The durable keys
|
|
220
|
+
are the file path, which is what rekordbox itself matches on, and a value stamped
|
|
221
|
+
into the file.
|
|
222
|
+
|
|
223
|
+
Reimports preserve flexible attributes, with an exception list. `reimport_metadata`
|
|
224
|
+
copies the replaced item's `_values_flex` onto the new item, minus the fields in
|
|
225
|
+
`REIMPORT_FRESH_FIELDS_ITEM` (`importer/tasks.py:54-62`). A database-only
|
|
226
|
+
identifier therefore survives a reimport, provided the plugin does not add its
|
|
227
|
+
field to that list.
|
|
228
|
+
|
|
229
|
+
### A Plugin-Declared Media Field Round-Trips in the File
|
|
230
|
+
|
|
231
|
+
`add_media_field` registers a `MediaField` on `mediafile.MediaFile` and adds the
|
|
232
|
+
name to `Item._media_fields`, so `item.read()` and `item.write()` carry it like
|
|
233
|
+
any native tag. Declaring four storage styles covers the containers rekordbox
|
|
234
|
+
accepts. Stamped through `beet modify` and read back with mediafile alone, with
|
|
235
|
+
no beets database involved (`evidence/media-field-roundtrip.txt`):
|
|
236
|
+
|
|
237
|
+
| Container | Carrier | Result |
|
|
238
|
+
| --- | --- | --- |
|
|
239
|
+
| FLAC | Vorbis comment | round-trips |
|
|
240
|
+
| AIFF | ID3 chunk | round-trips |
|
|
241
|
+
| WAV | ID3 chunk | round-trips |
|
|
242
|
+
| MP3 (CBR and VBR) | TXXX | round-trips |
|
|
243
|
+
|
|
244
|
+
This is the mechanism for a durable join that survives operations beets does not
|
|
245
|
+
observe, at the cost of writing tags.
|
|
246
|
+
|
|
247
|
+
### rbe convert Repoints the Row, and the Original File May Outlive It
|
|
248
|
+
|
|
249
|
+
`_apply_converted_record` (`api/_convert.py:182`) writes the output's file name,
|
|
250
|
+
folder, and audio columns onto the **existing** `DjmdContent` row. Nothing in
|
|
251
|
+
`convert()` inserts a row. The rekordbox identifier is therefore stable across a
|
|
252
|
+
conversion, and rekordbox never holds the source and the output at the same time.
|
|
253
|
+
|
|
254
|
+
Whether the source file survives is a separate decision. `_deletes_original`
|
|
255
|
+
(`api/_convert.py:471`) consults `--delete-originals`, whose default is `none`:
|
|
256
|
+
the original is kept unless the user asks otherwise. `all` always removes it, and
|
|
257
|
+
`lossless` removes it only when the conversion lost no audio information —
|
|
258
|
+
`_classify_fidelity` counts an unknown bit depth and any down-sample as lossy, and
|
|
259
|
+
MP3 output is never lossless, so under `lossless` a hi-res source is kept and a
|
|
260
|
+
44.1 kHz 16-bit source is not.
|
|
261
|
+
|
|
262
|
+
| `--delete-originals` | Source file | `DjmdContent` rows | Beets items, if nothing is done |
|
|
263
|
+
| --- | --- | --- | --- |
|
|
264
|
+
| `none` (default) | kept | 1, at the output | 1, at the source; output unknown |
|
|
265
|
+
| `all` | deleted | 1, at the output | 1, at a path that no longer exists |
|
|
266
|
+
| `lossless`, lossless conversion | deleted | 1, at the output | 1, dangling |
|
|
267
|
+
| `lossless`, lossy conversion | kept | 1, at the output | 1, at the source; output unknown |
|
|
268
|
+
|
|
269
|
+
Rekordbox forgets the original in every row of that table. Beets can keep it. The
|
|
270
|
+
two libraries therefore agree only when the original is deleted; when it is kept,
|
|
271
|
+
a beets library that records both files holds two items where rekordbox holds one,
|
|
272
|
+
and only one of the two corresponds to a rekordbox row. Under the default that is
|
|
273
|
+
not the edge case but the ordinary outcome, so what the plugin does about it is a
|
|
274
|
+
policy question it has to answer up front rather than a corner to handle later. It
|
|
275
|
+
is answered in [What Happens to the Original When the Plugin
|
|
276
|
+
Converts](decisions/converted-original-disposition.md): the plugin keeps the
|
|
277
|
+
`none` default and records the output as the item rekordbox knows about, leaving
|
|
278
|
+
the original as a beets item with no `DjmdContent` row.
|
|
279
|
+
|
|
280
|
+
### The Converted File Already Sits Where Beets Wants It
|
|
281
|
+
|
|
282
|
+
`_get_output_path` (`api/_convert.py:389`) writes the output into the source's
|
|
283
|
+
directory, keeping the stem and swapping the extension. `Item.destination` builds
|
|
284
|
+
every path segment from the path template but takes the extension from
|
|
285
|
+
`self.filepath.suffix` (`library/models.py:1227`), so an item pointing at the
|
|
286
|
+
output resolves to the templated path carrying the new extension.
|
|
287
|
+
|
|
288
|
+
Under a template that does not mention anything the conversion changes, those are
|
|
289
|
+
the same path (`evidence/convert-repoint.txt`):
|
|
290
|
+
|
|
291
|
+
| Import, template | `rbe convert` output | `item.destination()` | Same |
|
|
292
|
+
| --- | --- | --- | --- |
|
|
293
|
+
| singletons, `$artist/$title` | `Alpha/Wave Alpha.aiff` | `Alpha/Wave Alpha.aiff` | yes |
|
|
294
|
+
| album, `$artist/$title` | `Compilations/Lossless Vol 1/00 Wave Alpha.aiff` | the same | yes |
|
|
295
|
+
| either, `$artist/$format/$title` | as above | `Alpha/FLAC/Wave Alpha.aiff` | no |
|
|
296
|
+
|
|
297
|
+
In the common case the plugin has no file to move: recording the new path in both
|
|
298
|
+
databases is the whole job. A library whose template keys on `$format`,
|
|
299
|
+
`$bitrate`, or `$samplerate` is the exception, and it fails in two ways at once.
|
|
300
|
+
The destination genuinely differs, so the file has to be moved after the fact —
|
|
301
|
+
which changes the path a second time, after `rbe convert` has already committed
|
|
302
|
+
the rekordbox row, leaving that row pointing at a file that is gone until the move
|
|
303
|
+
is pushed back.
|
|
304
|
+
|
|
305
|
+
And the `FLAC` segment above is the pre-conversion value: `destination()` reads
|
|
306
|
+
`format` from the beets item, not from the file that item now names. A plugin
|
|
307
|
+
must re-read the item before its destination means anything.
|
|
308
|
+
|
|
309
|
+
### Repointing an Item Refreshes the Audio Fields and Discards Database-Only Ones
|
|
310
|
+
|
|
311
|
+
Beets' own way of pointing an item at a transcode is
|
|
312
|
+
`item.path = converted; item.read(); item.store()`, which is what the convert
|
|
313
|
+
plugin does under `--keep-new` (`beetsplug/convert.py:457-459`). Measured against
|
|
314
|
+
a FLAC repointed at its AIFF conversion (`evidence/convert-repoint.txt`):
|
|
315
|
+
|
|
316
|
+
| | Before | After |
|
|
317
|
+
| --- | --- | --- |
|
|
318
|
+
| `id` | 1 | 1 |
|
|
319
|
+
| `format` | FLAC | AIFF |
|
|
320
|
+
| `bitrate` | 96592 | 705600 |
|
|
321
|
+
| `samplerate` | 44100 | 44100 |
|
|
322
|
+
| a flexible attribute | set | preserved |
|
|
323
|
+
| `comments`, set in the database only | `'db-only edit...'` | `''` |
|
|
324
|
+
|
|
325
|
+
The identifier survives, so a repoint is not a reimport and nothing keyed on the
|
|
326
|
+
item id breaks. Flexible attributes survive, because `read()` assigns only media
|
|
327
|
+
fields. Fixed fields do not: `read()` overwrites every media field from the
|
|
328
|
+
output's tags. Under `write: yes` beets has already written its metadata to the
|
|
329
|
+
source and ffmpeg's `map_metadata 0` carries those tags into the output, so in
|
|
330
|
+
the ordinary case the file agrees with the database and nothing is lost. A value
|
|
331
|
+
that reached the database without reaching the file is lost.
|
|
332
|
+
|
|
333
|
+
### A Registered Transcode Has No Album
|
|
334
|
+
|
|
335
|
+
Where the original is kept, the output has to be registered rather than
|
|
336
|
+
substituted. `Item.from_path` followed by `Library.add` does that: the probe's
|
|
337
|
+
output lands as id 3 with the correct format and bitrate, while the original
|
|
338
|
+
keeps id 2 and stays in the library (`evidence/convert-repoint.txt`).
|
|
339
|
+
|
|
340
|
+
Its `album_id` is `None`, including in the album import where the original's is
|
|
341
|
+
`1`. Beets attaches items to albums in the import pipeline, not in `add`, so a
|
|
342
|
+
plugin registering a transcode outside the pipeline must set `album_id` itself or
|
|
343
|
+
the new item will not appear under `beet ls -a`, will not inherit album art, and
|
|
344
|
+
will not see album-level flexible attributes.
|
|
345
|
+
|
|
346
|
+
### Going Through the Beets Convert Plugin Is the Other Direction
|
|
347
|
+
|
|
348
|
+
The remaining option for workflow 1 is to let beets do the transcoding and have
|
|
349
|
+
the plugin follow it. That inverts which tool owns the conversion, and it is worth
|
|
350
|
+
recording why it is the weaker of the two.
|
|
351
|
+
|
|
352
|
+
The convert plugin sends `after_convert` with `item`, `dest`, and `keepnew`
|
|
353
|
+
(`beetsplug/convert.py:477-483`). That name is absent from the `EventType`
|
|
354
|
+
literal, so it is a plugin-to-plugin convention rather than part of beets' typed
|
|
355
|
+
event surface, and it carries no compatibility guarantee.
|
|
356
|
+
|
|
357
|
+
Convert's three modes differ in what they leave behind:
|
|
358
|
+
|
|
359
|
+
| Mode | Output location | Library entry |
|
|
360
|
+
| --- | --- | --- |
|
|
361
|
+
| `beet convert -d DEST` | under `DEST` | unchanged, still the source |
|
|
362
|
+
| `convert.auto` | temp file, then the library path | repointed at the transcode |
|
|
363
|
+
| `convert.auto_keep` | `convert.dest` | unchanged, still the source |
|
|
364
|
+
| `beet convert --keep-new` | original moved to `-d`, transcode at the item path | repointed at the transcode |
|
|
365
|
+
|
|
366
|
+
Only the modes that repoint an existing entry put the output in the database, and
|
|
367
|
+
they do so by replacing the source entry rather than adding an item. No mode
|
|
368
|
+
registers the transcode as a new item. Nothing in beets records that the source
|
|
369
|
+
and the output are related.
|
|
370
|
+
|
|
371
|
+
Following that from a plugin means depending on `after_convert`, an event outside
|
|
372
|
+
`EventType`, and working around its firing before the item has moved. It also
|
|
373
|
+
gives the plugin less than driving `rbe convert` does: in the modes that repoint,
|
|
374
|
+
the source entry is replaced rather than kept, so the plugin sees the same
|
|
375
|
+
kept-original problem with no control over the policy, and in the modes that do
|
|
376
|
+
not repoint, the output is simply absent from the beets database. Neither is a
|
|
377
|
+
better starting point than owning the conversion.
|
|
378
|
+
|
|
379
|
+
### The Encoder Is Already Separable From Rekordbox
|
|
380
|
+
|
|
381
|
+
`rbe convert` is built around a plain dataclass rather than around a database
|
|
382
|
+
row. `_EncodeJob` carries a source path, file name, declared file type, output
|
|
383
|
+
path, temp path, and output format, and `_encode_one` is documented as
|
|
384
|
+
"touching no database state" (`api/_convert.py:275`). The thread pool, the
|
|
385
|
+
encode-to-temp-then-move, the ffmpeg argument construction, and the output probe
|
|
386
|
+
are all row-independent already.
|
|
387
|
+
|
|
388
|
+
Three seams hold the rekordbox coupling:
|
|
389
|
+
|
|
390
|
+
| Seam | Location | What a file-oriented caller supplies instead |
|
|
391
|
+
| --- | --- | --- |
|
|
392
|
+
| Job construction | `_encode_job_for` (line 260) | The five values directly |
|
|
393
|
+
| Output location | `_get_output_path` (line 389) | Its own destination, rather than the source's directory |
|
|
394
|
+
| Candidate selection | `_classify_convert` (line 404) | Its own list of files |
|
|
395
|
+
|
|
396
|
+
Driving it that way works today. Built by hand from a path on disk, with no
|
|
397
|
+
session and no row in existence, `_encode_one` encodes each fixture to the
|
|
398
|
+
conversion target and reports fidelity correctly (`evidence/file-level-encode.txt`):
|
|
399
|
+
|
|
400
|
+
| Source | Declared type | Result |
|
|
401
|
+
| --- | --- | --- |
|
|
402
|
+
| `01-flac-44_1k-16b.flac` | FLAC | `pcm_s16be` 44100 Hz 16-bit, lossless |
|
|
403
|
+
| `02-flac-96k-24b.flac` | FLAC | `pcm_s16be` 44100 Hz 16-bit, lossy (down-sampled) |
|
|
404
|
+
| `06-wav-96k-24b.wav` | WAV | `pcm_s16be` 44100 Hz 16-bit, lossy (down-sampled) |
|
|
405
|
+
|
|
406
|
+
One decision is semantic rather than mechanical. `_encode_one` calls
|
|
407
|
+
`probe_matches_file_type`, which returns `False` for a `None` file type by
|
|
408
|
+
design, so that "callers treat them as mismatches instead of converting blind"
|
|
409
|
+
(`utils.py:185`). That cross-check exists to catch a rekordbox row disagreeing
|
|
410
|
+
with the file it names. A caller converting a file it already holds has no second
|
|
411
|
+
declaration to check against, so the check is inapplicable rather than failing.
|
|
412
|
+
Run with `file_type=None`, every one of the three files above is skipped with
|
|
413
|
+
`codec_mismatch` and nothing is encoded, so the check cannot simply be left to
|
|
414
|
+
default. Supplying a declared type is the cheaper fix than skipping the check: the
|
|
415
|
+
rekordbox `FileType` code follows from the extension through
|
|
416
|
+
`get_file_type_for_format`, which is what the probe passes and what a beets caller
|
|
417
|
+
holding an `Item` can pass too.
|
|
418
|
+
|
|
419
|
+
A file-level entry point over `_EncodeJob`, with the present rekordbox-aware
|
|
420
|
+
`convert()` layered on it, would let a plugin transcode without a `DjmdContent`
|
|
421
|
+
row existing first, and would remove any dependency on the convert plugin.
|
|
422
|
+
|
|
423
|
+
### The rekordbox-edit API Carries Its Own Safety
|
|
424
|
+
|
|
425
|
+
`rekordbox_edit.api` exports `search`, `edit`, `convert`, `import_tracks`, and
|
|
426
|
+
`remove`. Each takes an open `Rekordbox6Database` and a request model, and each
|
|
427
|
+
is dry-runnable. For the three workflows, `edit` with `field="FolderPath"` repoints
|
|
428
|
+
a row and resynchronises the columns describing the file, and `import_tracks`
|
|
429
|
+
creates rows for files rekordbox does not know about, matching by resolved,
|
|
430
|
+
case-folded path so that repeated runs are idempotent.
|
|
431
|
+
|
|
432
|
+
Both guards a plugin would otherwise have to reproduce now live in the API.
|
|
433
|
+
`writing()` (`api/_utils.py:23`) raises `RekordboxRunningError` when rekordbox is
|
|
434
|
+
open, then holds the single-writer advisory lock for the duration of the write.
|
|
435
|
+
Every API write enters through it — `convert()`, `edit()`, and `import_tracks()`
|
|
436
|
+
each open `with writing(db, ...)` — and the CLI's own lock nests inside harmlessly.
|
|
437
|
+
A plugin driving the API directly therefore inherits both and must not reimplement
|
|
438
|
+
either.
|
|
439
|
+
|
|
440
|
+
The guard wraps only the writing region, so a dry run reaches neither check. That
|
|
441
|
+
is the property a plugin wants: it can plan against a library rekordbox has open,
|
|
442
|
+
and will be refused only when it tries to commit.
|
|
443
|
+
|
|
444
|
+
The refusal is an exception, not an exit, which shapes where a plugin puts its
|
|
445
|
+
write. `RekordboxRunningError` raised out of a `cli_exit` listener surfaces at the
|
|
446
|
+
end of an import that has already finished and cannot be replayed. That is an
|
|
447
|
+
argument for the subcommand below rather than against the guard.
|
|
448
|
+
|
|
449
|
+
### Rekordbox Writes Must Be Batched and Single-Threaded
|
|
450
|
+
|
|
451
|
+
Beets imports with `threaded: yes` by default, so relocation events arrive on
|
|
452
|
+
worker threads. Rekordbox-edit keeps every database touch on the main thread
|
|
453
|
+
inside `convert`, because `RekordboxAgentRegistry`'s change buffer is a class
|
|
454
|
+
attribute shared by every instance in the process
|
|
455
|
+
(`research/import-track-row-shape/decisions/commit-semantics-and-usn.md`).
|
|
456
|
+
|
|
457
|
+
A plugin must therefore not write rekordbox from an event handler. It should
|
|
458
|
+
accumulate the pairs it observes and flush once, from a single thread, at
|
|
459
|
+
`cli_exit`. Batching is also what makes the write inspectable: one transaction
|
|
460
|
+
that can be dry-run and re-run, rather than thousands that cannot.
|
|
461
|
+
|
|
462
|
+
## Conclusion
|
|
463
|
+
|
|
464
|
+
Beets permits the integration, and the architecture points at one shape for it.
|
|
465
|
+
|
|
466
|
+
**Collect during the import, write once at the end.** Relocation events are the
|
|
467
|
+
only source of paired source and destination paths, and they are notification
|
|
468
|
+
only, so a handler cannot usefully do more than record what it saw. A single
|
|
469
|
+
flush at `cli_exit` satisfies the threading constraint and produces one auditable
|
|
470
|
+
rekordbox transaction. The API supplies the guards, so the flush needs only to
|
|
471
|
+
handle being refused.
|
|
472
|
+
|
|
473
|
+
**Join on the path, never on an identifier beets controls.** Item identifiers
|
|
474
|
+
change on reimport while paths do not. For operations beets does not observe, a
|
|
475
|
+
media field carries a rekordbox identifier through any container rekordbox
|
|
476
|
+
accepts, at the cost of writing tags.
|
|
477
|
+
|
|
478
|
+
**Offer a subcommand alongside the listeners.** The import-time hook cannot see
|
|
479
|
+
anything done outside beets, and an import that has already completed cannot be
|
|
480
|
+
replayed if rekordbox was open at the time. A `beet rbe sync` style subcommand
|
|
481
|
+
that reconciles the library against rekordbox on demand covers both with the same
|
|
482
|
+
code, is re-runnable, and does not couple a cross-application write to an import
|
|
483
|
+
that has no rollback relationship with it.
|
|
484
|
+
|
|
485
|
+
Against the three framing workflows:
|
|
486
|
+
|
|
487
|
+
**Workflow 1, convert and follow**, is mechanically the easiest of the three and
|
|
488
|
+
semantically the hardest. `rbe convert` already writes its output where beets
|
|
489
|
+
wants the file, so under an ordinary path template no file moves and the plugin's
|
|
490
|
+
only filesystem concern is the one case where the template keys on something the
|
|
491
|
+
conversion changes. Both of the library-side operations are one-liners with
|
|
492
|
+
precedent: repointing is `item.path = out; item.read(); item.store()`, which keeps
|
|
493
|
+
the item id, and registering is `Item.from_path` plus `Library.add`
|
|
494
|
+
(`library/models.py:806`, `library/library.py:43`), which needs `album_id` set by
|
|
495
|
+
hand. Neither needs a second import pass and neither needs the convert plugin.
|
|
496
|
+
|
|
497
|
+
What is not mechanical is which of the two to perform, because `rbe convert`
|
|
498
|
+
repoints the rekordbox row unconditionally while keeping or deleting the source
|
|
499
|
+
file according to `--delete-originals`. Its default is `none`, so by default the
|
|
500
|
+
original survives — rekordbox has forgotten a file that still exists and that
|
|
501
|
+
beets still has an entry for, and no beets-side action reproduces the rekordbox
|
|
502
|
+
state: registering the output gives beets two items to rekordbox's one, and
|
|
503
|
+
repointing loses the surviving original from both. The deleting modes are the
|
|
504
|
+
opt-in case, and only there can the two libraries agree exactly.
|
|
505
|
+
|
|
506
|
+
**The plugin should not try to mirror rekordbox here**, and it should not buy
|
|
507
|
+
agreement by deleting more than `rbe convert` would. It keeps the `none` default
|
|
508
|
+
and records the output as the item rekordbox knows about — registering a new item
|
|
509
|
+
when the source survives, repointing the existing one when a deleting mode removed
|
|
510
|
+
it — treating a kept original as a beets item with no `DjmdContent` row, which is
|
|
511
|
+
what it now is (`decisions/converted-original-disposition.md`). A beets library
|
|
512
|
+
legitimately larger than the rekordbox one is therefore the normal state, and one
|
|
513
|
+
any reconciliation has to tolerate rather than repair.
|
|
514
|
+
|
|
515
|
+
The remaining seam is that a beets-driven conversion cannot pass `None` as the
|
|
516
|
+
source file type without every file being skipped, so a file-level entry point
|
|
517
|
+
over `_EncodeJob` must take a declared type from the caller.
|
|
518
|
+
|
|
519
|
+
**Workflow 2, repoint after a move**, is the best-supported case and needs no
|
|
520
|
+
custom field: `item_copied` and `item_moved` deliver both paths, and
|
|
521
|
+
`edit(field="FolderPath")` already resynchronises the technical columns. Its
|
|
522
|
+
second half is not supported. Pushing beets metadata into other rekordbox columns
|
|
523
|
+
needs either new field handlers or a way to pass explicit per-track values, since
|
|
524
|
+
`edit` reads tags from nothing and derives only audio properties from the file.
|
|
525
|
+
|
|
526
|
+
**Workflow 3, reimport and update**, is the case most likely to be got wrong,
|
|
527
|
+
because the reimport silently replaces items. A plugin holding beets identifiers
|
|
528
|
+
will appear to work and then quietly address the wrong tracks. Path-keyed
|
|
529
|
+
reconciliation is correct here for the same reason it is correct everywhere else.
|
|
530
|
+
|
|
531
|
+
## Open Questions
|
|
532
|
+
|
|
533
|
+
- Which rekordbox columns are worth exposing as editable beyond the current
|
|
534
|
+
handler set, and which beets fields map onto them without loss.
|
|
535
|
+
- Whether a plugin should write tags at all. The media field is the only join
|
|
536
|
+
that survives operations beets cannot see, but it modifies files that rekordbox
|
|
537
|
+
may have analysed.
|
|
538
|
+
- What `edit` costs at library scale. It classifies every candidate, applies
|
|
539
|
+
every change, and commits once, so a large reconciliation is atomic but
|
|
540
|
+
unbounded in memory, and `post_commit` rewrites ANLZ files serially for every
|
|
541
|
+
track whose filename changed.
|
|
542
|
+
- Whether the file-level encode entry point should skip the source probe's codec
|
|
543
|
+
cross-check or require a declared type from the caller. Requiring one is cheap
|
|
544
|
+
for a beets caller, which can derive it from the extension, and it keeps the
|
|
545
|
+
guard that catches a file disagreeing with what names it.
|
|
546
|
+
- Whether an original that `--delete-originals: lossless` kept should carry a
|
|
547
|
+
marker on its beets item, so a reconciliation can tell a deliberate leftover
|
|
548
|
+
from a track that has genuinely never reached rekordbox. Raised in
|
|
549
|
+
`decisions/converted-original-disposition.md`.
|
|
550
|
+
- Whether `rbe convert` should gain an output-directory option. It currently
|
|
551
|
+
writes beside the source, which is what makes the beets destination match; a
|
|
552
|
+
destination argument would break that coincidence and put the move back.
|
|
553
|
+
- How to sequence the second path change when the beets path template keys on
|
|
554
|
+
`$format` or `$bitrate`, given that `rbe convert` commits the rekordbox row
|
|
555
|
+
before beets has had a chance to move the file.
|
|
556
|
+
- How the plugin should behave when rekordbox is running, since the import that
|
|
557
|
+
triggered it cannot be replayed.
|