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.
Files changed (381) hide show
  1. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CHANGELOG.md +2 -1
  2. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/PKG-INFO +1 -1
  3. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/pyproject.toml +1 -1
  4. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/README.md +8 -0
  5. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/beets-plugin-integration.md +557 -0
  6. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/decisions/converted-original-disposition.md +77 -0
  7. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/convert-repoint.txt +59 -0
  8. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/event-order.txt +69 -0
  9. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/file-level-encode.txt +11 -0
  10. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/id-stability.txt +7 -0
  11. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/evidence/media-field-roundtrip.txt +9 -0
  12. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/convert_repoint.py +194 -0
  13. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/event_probe.py +156 -0
  14. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/file_level_encode.py +85 -0
  15. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/id_stability.py +75 -0
  16. rekordbox_edit-0.13.0.dev8/research/beets-plugin-integration/scripts/media_field_roundtrip.py +145 -0
  17. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/uv.lock +1 -1
  18. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.agent-style/RULES.md +0 -0
  19. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.agent-style/claude-code.md +0 -0
  20. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/build-release-notes/action.yml +0 -0
  21. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/build-release-notes/build_release_notes.py +0 -0
  22. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commit-check/action.yml +0 -0
  23. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commitizen-bump/action.yml +0 -0
  24. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
  25. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/e2e/action.yml +0 -0
  26. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/install/action.yml +0 -0
  27. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/lint/action.yml +0 -0
  28. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/actions/test/action.yml +0 -0
  29. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/cd.yml +0 -0
  30. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/ci.yml +0 -0
  31. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/publish.yml +0 -0
  32. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.github/workflows/release.yml +0 -0
  33. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.gitignore +0 -0
  34. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.pre-commit-config.yaml +0 -0
  35. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.python-version +0 -0
  36. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/.readthedocs.yaml +0 -0
  37. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/AGENTS.md +0 -0
  38. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CLAUDE.md +0 -0
  39. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/CONTRIBUTING.md +0 -0
  40. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/LICENSE +0 -0
  41. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/Makefile +0 -0
  42. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/README.md +0 -0
  43. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/codecov.yml +0 -0
  44. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docker-compose.yml +0 -0
  45. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/api.md +0 -0
  46. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/convert.md +0 -0
  47. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/edit.md +0 -0
  48. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/import.md +0 -0
  49. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/remove.md +0 -0
  50. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/commands/search.md +0 -0
  51. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/faqs.md +0 -0
  52. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/filtering.md +0 -0
  53. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/index.md +0 -0
  54. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/docs/stylesheets/extra.css +0 -0
  55. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/mkdocs.yml +0 -0
  56. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/__init__.py +0 -0
  57. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/_tag_fields.py +0 -0
  58. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/__init__.py +0 -0
  59. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_convert.py +0 -0
  60. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_edit.py +0 -0
  61. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_field_handlers.py +0 -0
  62. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_import.py +0 -0
  63. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_relations.py +0 -0
  64. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_remove.py +0 -0
  65. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_search.py +0 -0
  66. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/api/_utils.py +0 -0
  67. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/__init__.py +0 -0
  68. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_click.py +0 -0
  69. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_progress.py +0 -0
  70. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/_utils.py +0 -0
  71. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/convert.py +0 -0
  72. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/edit.py +0 -0
  73. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/import_.py +0 -0
  74. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/main.py +0 -0
  75. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/remove.py +0 -0
  76. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/cli/search.py +0 -0
  77. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/display.py +0 -0
  78. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/errors.py +0 -0
  79. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/locking.py +0 -0
  80. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/logger.py +0 -0
  81. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/models.py +0 -0
  82. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/query.py +0 -0
  83. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/tags.py +0 -0
  84. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/rekordbox_edit/utils.py +0 -0
  85. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/renovate.json5 +0 -0
  86. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/convert-export-impact-test-plan.md +0 -0
  87. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/convert-export-impact.md +0 -0
  88. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-00-baseline.json +0 -0
  89. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-10-postconvert.json +0 -0
  90. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r1-20-postexport.json +0 -0
  91. {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
  92. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2-00-baseline.json +0 -0
  93. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-00-baseline.json +0 -0
  94. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-10-deviceedit.json +0 -0
  95. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r2b-30-postsync.json +0 -0
  96. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-00-baseline.json +0 -0
  97. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-10-deviceedit.json +0 -0
  98. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/evidence/export-r3-40-resync.json +0 -0
  99. {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
  100. {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
  101. {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
  102. {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
  103. {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
  104. {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
  105. {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
  106. {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
  107. {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
  108. {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
  109. {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
  110. {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
  111. {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
  112. {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
  113. {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
  114. {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
  115. {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
  116. {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
  117. {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
  118. {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
  119. {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
  120. {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
  121. {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
  122. {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
  123. {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
  124. {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
  125. {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
  126. {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
  127. {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
  128. {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
  129. {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
  130. {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
  131. {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
  132. {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
  133. {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
  134. {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
  135. {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
  136. {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
  137. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/scripts/export_diff.py +0 -0
  138. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-export-impact/scripts/export_snapshot.py +0 -0
  139. {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
  140. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-experiment2.md +0 -0
  141. {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
  142. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact-verification.md +0 -0
  143. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/convert-anlz-cue-impact.md +0 -0
  144. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/decisions/convert-and-reanalysis-impact.md +0 -0
  145. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/decisions/convert-helper-structure.md +0 -0
  146. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/01-inventory.txt +0 -0
  147. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/02-cue-schema.txt +0 -0
  148. {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
  149. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/04-anlz-deep.txt +0 -0
  150. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/05-census.txt +0 -0
  151. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/06-content-columns.txt +0 -0
  152. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/07-analysis-flags.txt +0 -0
  153. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/exp2-00-pvb2-origin.txt +0 -0
  154. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-00-zero.json +0 -0
  155. {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
  156. {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
  157. {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
  158. {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
  159. {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
  160. {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
  161. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-01.json +0 -0
  162. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-02.json +0 -0
  163. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-03.json +0 -0
  164. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-04.json +0 -0
  165. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-high-05.json +0 -0
  166. {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
  167. {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
  168. {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
  169. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-01.json +0 -0
  170. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-02.json +0 -0
  171. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-03.json +0 -0
  172. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-04.json +0 -0
  173. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-05.json +0 -0
  174. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-06.json +0 -0
  175. {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
  176. {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
  177. {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
  178. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-00-baseline.json +0 -0
  179. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postanalyze.json +0 -0
  180. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postconv.json +0 -0
  181. {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
  182. {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
  183. {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
  184. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-00-baseline.json +0 -0
  185. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postanalyze.json +0 -0
  186. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postconv.json +0 -0
  187. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postopen.json +0 -0
  188. {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
  189. {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
  190. {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
  191. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-postconv.json +0 -0
  192. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postanalyze.json +0 -0
  193. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postconv.json +0 -0
  194. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-00-baseline.json +0 -0
  195. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-locked.json +0 -0
  196. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-postconv.json +0 -0
  197. {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
  198. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-postlock.json +0 -0
  199. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-230928195-prelock.json +0 -0
  200. {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
  201. {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
  202. {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
  203. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-00-zero.json +0 -0
  204. {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
  205. {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
  206. {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
  207. {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
  208. {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
  209. {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
  210. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-01.json +0 -0
  211. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-02.json +0 -0
  212. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-03.json +0 -0
  213. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-04.json +0 -0
  214. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-high-05.json +0 -0
  215. {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
  216. {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
  217. {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
  218. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-01.json +0 -0
  219. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-02.json +0 -0
  220. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-03.json +0 -0
  221. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-04.json +0 -0
  222. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-05.json +0 -0
  223. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-06.json +0 -0
  224. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-locked.json +0 -0
  225. {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
  226. {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
  227. {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
  228. {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
  229. {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
  230. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-baseline.json +0 -0
  231. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postanalyze.json +0 -0
  232. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postconv.json +0 -0
  233. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/README.md +0 -0
  234. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/analysis_flags.py +0 -0
  235. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/anlz_census.py +0 -0
  236. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/anlz_deep.py +0 -0
  237. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/content_columns.py +0 -0
  238. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/cue_fields_by_format.py +0 -0
  239. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/cue_schema.py +0 -0
  240. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/grid_drift.py +0 -0
  241. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/inventory.py +0 -0
  242. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/pvb2_origin.py +0 -0
  243. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/reset_fresh_to_zero.py +0 -0
  244. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/restore_fixtures.sh +0 -0
  245. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-reanalysis-impact/scripts/row_snapshot.py +0 -0
  246. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/convert-thread-scaling.md +0 -0
  247. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/evidence/thread-scaling.txt +0 -0
  248. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/_sources.py +0 -0
  249. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/format_scaling.py +0 -0
  250. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/shipped_path_scaling.py +0 -0
  251. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/convert-thread-scaling/scripts/write_cost.py +0 -0
  252. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/database-concurrency.md +0 -0
  253. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/evidence/plan-apply-probes.txt +0 -0
  254. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/evidence/usn-spikes.txt +0 -0
  255. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/plan-apply-window.md +0 -0
  256. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/block_reservation.py +0 -0
  257. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/concurrent_increment.py +0 -0
  258. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/identity_probe.py +0 -0
  259. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/membership_probe.py +0 -0
  260. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/snapshot_probe.py +0 -0
  261. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/scripts/wal_snapshot.py +0 -0
  262. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/database-concurrency/usn-maintenance.md +0 -0
  263. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/decisions/edit-field-handlers.md +0 -0
  264. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/edit-relational-fields-test-plan.md +0 -0
  265. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/edit-relational-fields.md +0 -0
  266. {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
  267. {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
  268. {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
  269. {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
  270. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/evidence/rel-145166719-e0-census.json +0 -0
  271. {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
  272. {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
  273. {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
  274. {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
  275. {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
  276. {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
  277. {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
  278. {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
  279. {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
  280. {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
  281. {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
  282. {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
  283. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/scripts/relational_diff.py +0 -0
  284. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/edit-relational-fields/scripts/relational_snapshot.py +0 -0
  285. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/file-types/README.md +0 -0
  286. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/file-types/decisions/unknown-file-types.md +0 -0
  287. {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
  288. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/column-coverage-gig.json +0 -0
  289. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/datecreated-source-gig.json +0 -0
  290. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/filetype-searchstr-gig.json +0 -0
  291. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/evidence/filetype-searchstr-local.json +0 -0
  292. {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
  293. {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
  294. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/import-track-row-shape.md +0 -0
  295. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/column_coverage.py +0 -0
  296. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/datecreated_source.py +0 -0
  297. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/filetype_and_searchstr.py +0 -0
  298. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/import_row_shape.py +0 -0
  299. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/import-track-row-shape/scripts/tag_key_mapping.py +0 -0
  300. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/decisions/remove-command-behavior.md +0 -0
  301. {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
  302. {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
  303. {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
  304. {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
  305. {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
  306. {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
  307. {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
  308. {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
  309. {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
  310. {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
  311. {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
  312. {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
  313. {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
  314. {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
  315. {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
  316. {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
  317. {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
  318. {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
  319. {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
  320. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/remove-track-impact-test-plan.md +0 -0
  321. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/remove-track-impact.md +0 -0
  322. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/build_fixtures.py +0 -0
  323. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/removal_diff.py +0 -0
  324. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/removal_snapshot.py +0 -0
  325. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/remove-track-impact/scripts/restore.sh +0 -0
  326. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/shared/scripts/subject_diff.py +0 -0
  327. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/research/shared/scripts/subject_snapshot.py +0 -0
  328. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/ruff.toml +0 -0
  329. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/__init__.py +0 -0
  330. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/__init__.py +0 -0
  331. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/conftest.py +0 -0
  332. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_convert.py +0 -0
  333. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_edit.py +0 -0
  334. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_field_handlers.py +0 -0
  335. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_import.py +0 -0
  336. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_package.py +0 -0
  337. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_relations.py +0 -0
  338. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_remove.py +0 -0
  339. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_search.py +0 -0
  340. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/api/test_utils.py +0 -0
  341. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/__init__.py +0 -0
  342. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_convert.py +0 -0
  343. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_edit.py +0 -0
  344. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_import.py +0 -0
  345. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_locking.py +0 -0
  346. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_main.py +0 -0
  347. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_progress.py +0 -0
  348. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_remove.py +0 -0
  349. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_search.py +0 -0
  350. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/cli/test_utils.py +0 -0
  351. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/conftest.py +0 -0
  352. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/Dockerfile +0 -0
  353. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__init__.py +0 -0
  354. {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
  355. {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
  356. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/__snapshots__/test_journey.ambr +0 -0
  357. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/conftest.py +0 -0
  358. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/01-flac-44_1k-16b.flac +0 -0
  359. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/02-flac-96k-24b.flac +0 -0
  360. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/03-alac-44_1k-16b.m4a +0 -0
  361. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/04-alac-48k-24b.m4a +0 -0
  362. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/05-aiff-44_1k-16b.aiff +0 -0
  363. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/06-wav-96k-24b.wav +0 -0
  364. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/07-mp3-44_1k-320cbr.mp3 +0 -0
  365. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/08-mp3-44_1k-v0vbr.mp3 +0 -0
  366. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/audio/09-aac-44_1k-256kbps.m4a +0 -0
  367. {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
  368. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/macos/master.6.8.6.db +0 -0
  369. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/fixtures/windows/master.6.8.6.db +0 -0
  370. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_edit_fields.py +0 -0
  371. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_import.py +0 -0
  372. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_journey.py +0 -0
  373. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/e2e/test_remove.py +0 -0
  374. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_display.py +0 -0
  375. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_locking.py +0 -0
  376. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_logger.py +0 -0
  377. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_models.py +0 -0
  378. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_query.py +0 -0
  379. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_tag_fields.py +0 -0
  380. {rekordbox_edit-0.13.0.dev7 → rekordbox_edit-0.13.0.dev8}/tests/test_tags.py +0 -0
  381. {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.dev7 (2026-09-08)
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.dev7
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.dev7"
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.