rekordbox-edit 0.12.0.dev17__tar.gz → 0.13.0.dev7__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 (369) hide show
  1. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/CHANGELOG.md +24 -1
  2. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/PKG-INFO +1 -1
  3. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/commands/edit.md +27 -1
  4. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/pyproject.toml +1 -1
  5. rekordbox_edit-0.13.0.dev7/rekordbox_edit/_tag_fields.py +88 -0
  6. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_edit.py +10 -2
  7. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_field_handlers.py +101 -65
  8. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_import.py +24 -49
  9. rekordbox_edit-0.13.0.dev7/rekordbox_edit/api/_relations.py +239 -0
  10. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_remove.py +4 -114
  11. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_utils.py +5 -3
  12. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/display.py +21 -0
  13. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/models.py +6 -0
  14. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_edit.py +50 -1
  15. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_field_handlers.py +222 -155
  16. rekordbox_edit-0.13.0.dev7/tests/api/test_relations.py +49 -0
  17. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_remove.py +0 -43
  18. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/conftest.py +5 -0
  19. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[macos].json +49 -9
  20. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/__snapshots__/test_journey/test_search_full_json_snapshot[windows].json +49 -9
  21. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/conftest.py +54 -21
  22. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/macos/master.6.8.6.db +0 -0
  23. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/windows/master.6.8.6.db +0 -0
  24. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/test_edit_fields.py +131 -15
  25. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/test_import.py +4 -44
  26. rekordbox_edit-0.13.0.dev7/tests/e2e/test_remove.py +300 -0
  27. rekordbox_edit-0.13.0.dev7/tests/test_tag_fields.py +73 -0
  28. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/uv.lock +1 -1
  29. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.agent-style/RULES.md +0 -0
  30. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.agent-style/claude-code.md +0 -0
  31. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/build-release-notes/action.yml +0 -0
  32. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/build-release-notes/build_release_notes.py +0 -0
  33. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/commit-check/action.yml +0 -0
  34. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/commitizen-bump/action.yml +0 -0
  35. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/commitizen-bump/commitizen-bump.sh +0 -0
  36. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/e2e/action.yml +0 -0
  37. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/install/action.yml +0 -0
  38. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/lint/action.yml +0 -0
  39. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/actions/test/action.yml +0 -0
  40. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/workflows/cd.yml +0 -0
  41. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/workflows/ci.yml +0 -0
  42. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/workflows/publish.yml +0 -0
  43. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.github/workflows/release.yml +0 -0
  44. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.gitignore +0 -0
  45. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.pre-commit-config.yaml +0 -0
  46. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.python-version +0 -0
  47. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/.readthedocs.yaml +0 -0
  48. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/AGENTS.md +0 -0
  49. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/CLAUDE.md +0 -0
  50. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/CONTRIBUTING.md +0 -0
  51. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/LICENSE +0 -0
  52. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/Makefile +0 -0
  53. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/README.md +0 -0
  54. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/codecov.yml +0 -0
  55. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docker-compose.yml +0 -0
  56. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/api.md +0 -0
  57. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/commands/convert.md +0 -0
  58. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/commands/import.md +0 -0
  59. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/commands/remove.md +0 -0
  60. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/commands/search.md +0 -0
  61. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/faqs.md +0 -0
  62. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/filtering.md +0 -0
  63. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/index.md +0 -0
  64. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/docs/stylesheets/extra.css +0 -0
  65. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/mkdocs.yml +0 -0
  66. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/__init__.py +0 -0
  67. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/__init__.py +0 -0
  68. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_convert.py +0 -0
  69. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/api/_search.py +0 -0
  70. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/__init__.py +0 -0
  71. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/_click.py +0 -0
  72. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/_progress.py +0 -0
  73. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/_utils.py +0 -0
  74. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/convert.py +0 -0
  75. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/edit.py +0 -0
  76. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/import_.py +0 -0
  77. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/main.py +0 -0
  78. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/remove.py +0 -0
  79. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/cli/search.py +0 -0
  80. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/errors.py +0 -0
  81. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/locking.py +0 -0
  82. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/logger.py +0 -0
  83. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/query.py +0 -0
  84. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/tags.py +0 -0
  85. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/rekordbox_edit/utils.py +0 -0
  86. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/renovate.json5 +0 -0
  87. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/README.md +0 -0
  88. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/convert-export-impact-test-plan.md +0 -0
  89. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/convert-export-impact.md +0 -0
  90. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r1-00-baseline.json +0 -0
  91. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r1-10-postconvert.json +0 -0
  92. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r1-20-postexport.json +0 -0
  93. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r1-21-postexport-full.json +0 -0
  94. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r2-00-baseline.json +0 -0
  95. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r2b-00-baseline.json +0 -0
  96. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r2b-10-deviceedit.json +0 -0
  97. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r2b-30-postsync.json +0 -0
  98. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r3-00-baseline.json +0 -0
  99. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r3-10-deviceedit.json +0 -0
  100. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r3-40-resync.json +0 -0
  101. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/export-r3-41-reexport-full.json +0 -0
  102. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-11317782-r3-20-postconvert.json +0 -0
  103. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-11317782-r3-30-postsync.json +0 -0
  104. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r1-00-baseline.json +0 -0
  105. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r1-10-postconvert.json +0 -0
  106. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r1-20-postexport.json +0 -0
  107. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r2-00-baseline.json +0 -0
  108. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r2b-00-baseline.json +0 -0
  109. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r2b-00-lib.json +0 -0
  110. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r2b-20-postconvert.json +0 -0
  111. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-121715696-r2b-30-postsync.json +0 -0
  112. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-140193439-r3-20-postconvert.json +0 -0
  113. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-140193439-r3-30-postsync.json +0 -0
  114. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r1-00-baseline.json +0 -0
  115. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r1-10-postconvert.json +0 -0
  116. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r1-20-postexport.json +0 -0
  117. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r2-00-baseline.json +0 -0
  118. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r2b-00-baseline.json +0 -0
  119. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r2b-00-lib.json +0 -0
  120. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r2b-20-postconvert.json +0 -0
  121. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-258421826-r2b-30-postsync.json +0 -0
  122. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-268012584-r3-20-postconvert.json +0 -0
  123. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-268012584-r3-30-postsync.json +0 -0
  124. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r1-00-baseline.json +0 -0
  125. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r1-10-postconvert.json +0 -0
  126. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r1-20-postexport.json +0 -0
  127. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r2-00-baseline.json +0 -0
  128. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r2b-00-baseline.json +0 -0
  129. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r2b-00-lib.json +0 -0
  130. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r2b-20-postconvert.json +0 -0
  131. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-53013048-r2b-30-postsync.json +0 -0
  132. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r1-00-baseline.json +0 -0
  133. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r1-10-postconvert.json +0 -0
  134. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r1-20-postexport.json +0 -0
  135. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r2-00-baseline.json +0 -0
  136. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r2b-00-baseline.json +0 -0
  137. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r2b-20-postconvert.json +0 -0
  138. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/evidence/subject-54793376-r2b-30-postsync.json +0 -0
  139. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/scripts/export_diff.py +0 -0
  140. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-export-impact/scripts/export_snapshot.py +0 -0
  141. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/convert-anlz-cue-impact-experiment2-plan.md +0 -0
  142. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/convert-anlz-cue-impact-experiment2.md +0 -0
  143. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/convert-anlz-cue-impact-test-plan.md +0 -0
  144. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/convert-anlz-cue-impact-verification.md +0 -0
  145. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/convert-anlz-cue-impact.md +0 -0
  146. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/decisions/convert-and-reanalysis-impact.md +0 -0
  147. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/decisions/convert-helper-structure.md +0 -0
  148. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/01-inventory.txt +0 -0
  149. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/02-cue-schema.txt +0 -0
  150. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/03-cue-fields-by-format.txt +0 -0
  151. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/04-anlz-deep.txt +0 -0
  152. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/05-census.txt +0 -0
  153. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/06-content-columns.txt +0 -0
  154. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/07-analysis-flags.txt +0 -0
  155. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/exp2-00-pvb2-origin.txt +0 -0
  156. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-00-zero.json +0 -0
  157. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-01.json +0 -0
  158. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-02.json +0 -0
  159. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-high-03.json +0 -0
  160. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-01.json +0 -0
  161. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-02.json +0 -0
  162. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-dyn-normal-03.json +0 -0
  163. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-01.json +0 -0
  164. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-02.json +0 -0
  165. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-03.json +0 -0
  166. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-04.json +0 -0
  167. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-05.json +0 -0
  168. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-01.json +0 -0
  169. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-02.json +0 -0
  170. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-high-auto-03.json +0 -0
  171. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-01.json +0 -0
  172. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-02.json +0 -0
  173. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-03.json +0 -0
  174. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-04.json +0 -0
  175. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-05.json +0 -0
  176. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-06.json +0 -0
  177. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-then-high.json +0 -0
  178. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-normal-unlocked-ref.json +0 -0
  179. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-174954387-nth-mid-normal.json +0 -0
  180. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-00-baseline.json +0 -0
  181. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postanalyze.json +0 -0
  182. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-aiff-postconv.json +0 -0
  183. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-baseline.json +0 -0
  184. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-postanalyze.json +0 -0
  185. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205157376-combined-ctrl-postanalyze2.json +0 -0
  186. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-00-baseline.json +0 -0
  187. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postanalyze.json +0 -0
  188. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postconv.json +0 -0
  189. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-aiff-postopen.json +0 -0
  190. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-baseline.json +0 -0
  191. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-postanalyze.json +0 -0
  192. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-ctrl-postanalyze2.json +0 -0
  193. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-flac-postconv.json +0 -0
  194. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postanalyze.json +0 -0
  195. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-205795680-mp3-postconv.json +0 -0
  196. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-00-baseline.json +0 -0
  197. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-locked.json +0 -0
  198. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-postconv.json +0 -0
  199. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-mp3-unlocked-reanalyze.json +0 -0
  200. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-postlock.json +0 -0
  201. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-230928195-prelock.json +0 -0
  202. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-baseline.json +0 -0
  203. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-postanalyze.json +0 -0
  204. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-231283665-iso-rate-postconv.json +0 -0
  205. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-00-zero.json +0 -0
  206. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-01.json +0 -0
  207. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-02.json +0 -0
  208. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-high-03.json +0 -0
  209. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-01.json +0 -0
  210. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-02.json +0 -0
  211. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-dyn-normal-03.json +0 -0
  212. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-01.json +0 -0
  213. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-02.json +0 -0
  214. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-03.json +0 -0
  215. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-04.json +0 -0
  216. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-05.json +0 -0
  217. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-01.json +0 -0
  218. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-02.json +0 -0
  219. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-high-auto-03.json +0 -0
  220. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-01.json +0 -0
  221. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-02.json +0 -0
  222. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-03.json +0 -0
  223. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-04.json +0 -0
  224. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-05.json +0 -0
  225. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-06.json +0 -0
  226. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-locked.json +0 -0
  227. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-normal-then-high.json +0 -0
  228. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-27790898-nth-mid-normal.json +0 -0
  229. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-baseline.json +0 -0
  230. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-postanalyze.json +0 -0
  231. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-34178159-iso-depth-postconv.json +0 -0
  232. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-baseline.json +0 -0
  233. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postanalyze.json +0 -0
  234. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/evidence/subject-7691309-pvb2-postconv.json +0 -0
  235. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/README.md +0 -0
  236. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/analysis_flags.py +0 -0
  237. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/anlz_census.py +0 -0
  238. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/anlz_deep.py +0 -0
  239. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/content_columns.py +0 -0
  240. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/cue_fields_by_format.py +0 -0
  241. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/cue_schema.py +0 -0
  242. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/grid_drift.py +0 -0
  243. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/inventory.py +0 -0
  244. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/pvb2_origin.py +0 -0
  245. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/reset_fresh_to_zero.py +0 -0
  246. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/restore_fixtures.sh +0 -0
  247. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-reanalysis-impact/scripts/row_snapshot.py +0 -0
  248. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/convert-thread-scaling.md +0 -0
  249. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/evidence/thread-scaling.txt +0 -0
  250. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/scripts/_sources.py +0 -0
  251. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/scripts/format_scaling.py +0 -0
  252. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/scripts/shipped_path_scaling.py +0 -0
  253. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/convert-thread-scaling/scripts/write_cost.py +0 -0
  254. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/database-concurrency.md +0 -0
  255. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/evidence/plan-apply-probes.txt +0 -0
  256. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/evidence/usn-spikes.txt +0 -0
  257. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/plan-apply-window.md +0 -0
  258. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/block_reservation.py +0 -0
  259. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/concurrent_increment.py +0 -0
  260. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/identity_probe.py +0 -0
  261. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/membership_probe.py +0 -0
  262. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/snapshot_probe.py +0 -0
  263. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/scripts/wal_snapshot.py +0 -0
  264. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/database-concurrency/usn-maintenance.md +0 -0
  265. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/decisions/edit-field-handlers.md +0 -0
  266. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/edit-relational-fields-test-plan.md +0 -0
  267. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/edit-relational-fields.md +0 -0
  268. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-112264610-e2-00-baseline.json +0 -0
  269. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-112264610-e2-10-reuse.json +0 -0
  270. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-141833798-e3b-00-baseline.json +0 -0
  271. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-141833798-e3b-10-newalbum.json +0 -0
  272. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-145166719-e0-census.json +0 -0
  273. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-176388182-e1-00-baseline.json +0 -0
  274. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-176388182-e1-10-reuse.json +0 -0
  275. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-183309432-e4-00-baseline.json +0 -0
  276. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-183309432-e4-10-orphaned.json +0 -0
  277. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-47413782-v-00-baseline.json +0 -0
  278. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-47413782-v-10-reuse.json +0 -0
  279. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-50395296-v-00-baseline.json +0 -0
  280. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-50395296-v-10-reuse.json +0 -0
  281. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-57701453-e3-00-baseline.json +0 -0
  282. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-57701453-e3-10-diffartist.json +0 -0
  283. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-71974950-v-00-baseline.json +0 -0
  284. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/evidence/rel-71974950-v-10-reuse.json +0 -0
  285. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/scripts/relational_diff.py +0 -0
  286. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/edit-relational-fields/scripts/relational_snapshot.py +0 -0
  287. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/file-types/README.md +0 -0
  288. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/file-types/decisions/unknown-file-types.md +0 -0
  289. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/decisions/commit-semantics-and-usn.md +0 -0
  290. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/column-coverage-gig.json +0 -0
  291. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/datecreated-source-gig.json +0 -0
  292. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/filetype-searchstr-gig.json +0 -0
  293. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/filetype-searchstr-local.json +0 -0
  294. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/import-row-shape-gig.json +0 -0
  295. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/evidence/tag-key-mapping-gig.json +0 -0
  296. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/import-track-row-shape.md +0 -0
  297. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/scripts/column_coverage.py +0 -0
  298. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/scripts/datecreated_source.py +0 -0
  299. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/scripts/filetype_and_searchstr.py +0 -0
  300. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/scripts/import_row_shape.py +0 -0
  301. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/import-track-row-shape/scripts/tag_key_mapping.py +0 -0
  302. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/decisions/remove-command-behavior.md +0 -0
  303. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-109570093-r8-00-imported.json +0 -0
  304. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-109570093-r8-10-analyzed.json +0 -0
  305. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-114745319-r1-00-baseline.json +0 -0
  306. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-114745319-r1-10-removed.json +0 -0
  307. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-120331280-f6-00-baseline.json +0 -0
  308. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-120331280-f6-10-control.json +0 -0
  309. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-137975518-f3-00-baseline.json +0 -0
  310. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-137975518-f3-10-control.json +0 -0
  311. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-139425629-r6-00-baseline.json +0 -0
  312. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-139425629-r6-10-removed.json +0 -0
  313. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-14241481-r5-00-baseline.json +0 -0
  314. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-14241481-r5-10-removed.json +0 -0
  315. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-143461275-r7-01-folder-art.json +0 -0
  316. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-265892207-r3-00-baseline.json +0 -0
  317. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-265892207-r3-10-removed.json +0 -0
  318. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-265892207-r4-10-playlist-only.json +0 -0
  319. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-36337051-r7-00-folder-art.json +0 -0
  320. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-74039842-r8-00-imported.json +0 -0
  321. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/evidence/rm-74039842-r8-10-analyzed.json +0 -0
  322. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/remove-track-impact-test-plan.md +0 -0
  323. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/remove-track-impact.md +0 -0
  324. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/scripts/build_fixtures.py +0 -0
  325. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/scripts/removal_diff.py +0 -0
  326. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/scripts/removal_snapshot.py +0 -0
  327. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/remove-track-impact/scripts/restore.sh +0 -0
  328. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/shared/scripts/subject_diff.py +0 -0
  329. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/research/shared/scripts/subject_snapshot.py +0 -0
  330. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/ruff.toml +0 -0
  331. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/__init__.py +0 -0
  332. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/__init__.py +0 -0
  333. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/conftest.py +0 -0
  334. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_convert.py +0 -0
  335. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_import.py +0 -0
  336. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_package.py +0 -0
  337. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_search.py +0 -0
  338. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/api/test_utils.py +0 -0
  339. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/__init__.py +0 -0
  340. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_convert.py +0 -0
  341. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_edit.py +0 -0
  342. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_import.py +0 -0
  343. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_locking.py +0 -0
  344. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_main.py +0 -0
  345. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_progress.py +0 -0
  346. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_remove.py +0 -0
  347. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_search.py +0 -0
  348. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/cli/test_utils.py +0 -0
  349. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/Dockerfile +0 -0
  350. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/__init__.py +0 -0
  351. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/__snapshots__/test_journey.ambr +0 -0
  352. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/01-flac-44_1k-16b.flac +0 -0
  353. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/02-flac-96k-24b.flac +0 -0
  354. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/03-alac-44_1k-16b.m4a +0 -0
  355. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/04-alac-48k-24b.m4a +0 -0
  356. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/05-aiff-44_1k-16b.aiff +0 -0
  357. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/06-wav-96k-24b.wav +0 -0
  358. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/07-mp3-44_1k-320cbr.mp3 +0 -0
  359. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/08-mp3-44_1k-v0vbr.mp3 +0 -0
  360. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/09-aac-44_1k-256kbps.m4a +0 -0
  361. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/fixtures/audio/10-/303/274/303/261/303/256c/303/266d/303/251-flac-44_1k-16b.flac" +0 -0
  362. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/e2e/test_journey.py +0 -0
  363. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_display.py +0 -0
  364. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_locking.py +0 -0
  365. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_logger.py +0 -0
  366. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_models.py +0 -0
  367. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_query.py +0 -0
  368. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_tags.py +0 -0
  369. {rekordbox_edit-0.12.0.dev17 → rekordbox_edit-0.13.0.dev7}/tests/test_utils.py +0 -0
@@ -1,4 +1,27 @@
1
- ## v0.12.0.dev17 (2026-09-06)
1
+ ## v0.13.0.dev7 (2026-09-08)
2
+
3
+
4
+ - fix(edit): give the records an edit creates and collects their USNs
5
+ - An edit that reassigned a relational field left the record it created
6
+ unstamped and the one it collected outside the counter, so a syncing peer
7
+ never learned about either. import and remove already did both.
8
+ - feat(edit): support the remaining audio-tag columns
9
+ - Adds Genre, Label, ComposerName, ISRC, TrackNo, DiscNo, and ReleaseYear,
10
+ built from the tag registry so edit and import cover the same set. Key is
11
+ left out: Rekordbox derives it from analysis.
12
+ - refactor: name the audio tags import reads in one registry
13
+ - Which column a tag lands in was spread across _resolve_relations and the
14
+ add_content call; edit needs the same mapping to stay in step with import.
15
+ - refactor(api): gather shared-record handling behind _relations
16
+ - edit, import, and remove each carried their own copy of which table holds a
17
+ kind, which columns count as a reference to it, and how a new one is made.
18
+ - test(e2e): cover the remove command
19
+ - test(e2e): give the fixture rows a Rekordbox-shaped ImagePath
20
+ - The fixture audio carries no embedded art, so analysis left every row with
21
+ an empty ImagePath and no e2e case could reach the artwork cleanup.
22
+ - test(e2e): share the private-DB fixtures through conftest
23
+
24
+ ## v0.12.0 (2026-09-07)
2
25
 
3
26
 
4
27
  - refactor(api)!: gather the public surface behind rekordbox_edit.api
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rekordbox-edit
3
- Version: 0.12.0.dev17
3
+ Version: 0.13.0.dev7
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
@@ -6,7 +6,33 @@ Bulk-edit a metadata field on tracks in your Rekordbox database.
6
6
  rbe edit [OPTIONS] [TRACK-IDS]... FIELD
7
7
  ```
8
8
 
9
- `FIELD` specifies the [DjmdContent](https://pyrekordbox.readthedocs.io/en/latest/formats/db6.html#djmdcontent) column to change. The editable fields are `Title`, `Comment`, `ArtistName`, `AlbumName`, `Rating`, and `FolderPath`.
9
+ `FIELD` specifies the [DjmdContent](https://pyrekordbox.readthedocs.io/en/latest/formats/db6.html#djmdcontent) column to change:
10
+
11
+ | Field | What it changes |
12
+ | --- | --- |
13
+ | `Title` | The track title. |
14
+ | `Comment` | The comment column (`Commnt`). |
15
+ | `ArtistName` | The artist, through the shared `DjmdArtist` record. |
16
+ | `AlbumName` | The album, through the shared `DjmdAlbum` record. |
17
+ | `ComposerName` | The composer, which shares the `DjmdArtist` table with the artist. |
18
+ | `Genre` | The genre, through the shared `DjmdGenre` record. |
19
+ | `Label` | The record label, through the shared `DjmdLabel` record. |
20
+ | `ISRC` | The recording's ISRC. |
21
+ | `TrackNo` | The track number. |
22
+ | `DiscNo` | The disc number. |
23
+ | `ReleaseYear` | The release year. `ReleaseDate` is left as it is. |
24
+ | `Rating` | The 0-5 star rating. |
25
+ | `FolderPath` | The audio file the track points at. See [FolderPath](#folderpath). |
26
+
27
+ Every field above except `Rating` and `FolderPath` also comes from an audio tag, and [`import`](import.md) reads the same set. Musical key is the one tag `edit` does not offer: Rekordbox derives it from its own analysis, which would overwrite whatever you set.
28
+
29
+ `Rating`, `TrackNo`, `DiscNo`, and `ReleaseYear` hold numbers, so `--match` does not apply to them; passing it warns and sets the value directly.
30
+
31
+ ## Relational fields
32
+
33
+ `ArtistName`, `AlbumName`, `ComposerName`, `Genre`, and `Label` do not live on the track's own row. Each names a record in a shared table that many tracks point at, so editing one works the way it does in Rekordbox: the track is reassigned to a record with the new name, reusing an existing one or creating it, and every other track keeps the record it had. `--replace ""` clears the field. A record nothing references any more is deleted, again matching Rekordbox.
34
+
35
+ One deliberate difference: reusing an existing album leaves that album's album-artist alone, where Rekordbox blanks it for every track on the album.
10
36
 
11
37
  `--replace` supplies the new value. On its own it overwrites the whole field; add `--match PATTERN` to find that literal text within the field and replace only that portion:
12
38
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "rekordbox-edit"
7
- version = "0.12.0.dev17"
7
+ version = "0.13.0.dev7"
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"
@@ -0,0 +1,88 @@
1
+ """The audio tags Rekordbox reads, and the columns they land in.
2
+
3
+ `import` writes these columns from a file's tags and `edit` changes them
4
+ afterward, so both commands read the mapping from here. Adding a tag to
5
+ `tags.TrackTags` without adding it here (or the reverse) fails the parity
6
+ test in tests/test_tag_fields.py.
7
+
8
+ Which mutagen key holds a tag is a separate, format-specific concern and
9
+ stays in `tags.py`.
10
+ """
11
+
12
+ from dataclasses import dataclass
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class TagField:
17
+ """One audio tag, the DjmdContent column it lands in, and how each
18
+ command reaches it."""
19
+
20
+ tag: str
21
+ """The `tags.TrackTags` key holding the value read off the file."""
22
+ column: str
23
+ """The DjmdContent column storing it: the value itself for a scalar tag,
24
+ or the foreign key for a relational one."""
25
+ relation: str | None = None
26
+ """The `api._relations.RELATIONS` kind `column` points at, or None where
27
+ the column holds the value directly."""
28
+ proxy: str | None = None
29
+ """The DjmdContent attribute reading the related record's name. Set for a
30
+ relational tag only."""
31
+ edit_field: str | None = None
32
+ """The `edit` command's name for this field, or None where the column is
33
+ not editable."""
34
+ default: str | int = ""
35
+ """What `import` writes when the file carries no such tag."""
36
+
37
+
38
+ #: Every tag both commands know about, in the order `import` reads them.
39
+ #:
40
+ #: `key` is deliberately not editable: Rekordbox derives KeyID from its own
41
+ #: analysis, so a user-supplied key would be overwritten the next time the
42
+ #: track is analyzed.
43
+ TAG_FIELDS: tuple[TagField, ...] = (
44
+ TagField(tag="title", column="Title", edit_field="Title"),
45
+ TagField(
46
+ tag="artist",
47
+ column="ArtistID",
48
+ relation="artist",
49
+ proxy="ArtistName",
50
+ edit_field="ArtistName",
51
+ ),
52
+ TagField(
53
+ tag="album",
54
+ column="AlbumID",
55
+ relation="album",
56
+ proxy="AlbumName",
57
+ edit_field="AlbumName",
58
+ ),
59
+ TagField(
60
+ tag="genre",
61
+ column="GenreID",
62
+ relation="genre",
63
+ proxy="GenreName",
64
+ edit_field="Genre",
65
+ ),
66
+ TagField(
67
+ tag="label",
68
+ column="LabelID",
69
+ relation="label",
70
+ proxy="LabelName",
71
+ edit_field="Label",
72
+ ),
73
+ TagField(
74
+ tag="composer",
75
+ column="ComposerID",
76
+ relation="artist",
77
+ proxy="ComposerName",
78
+ edit_field="ComposerName",
79
+ ),
80
+ TagField(tag="key", column="KeyID", relation="key", proxy="KeyName"),
81
+ TagField(tag="comment", column="Commnt", edit_field="Comment"),
82
+ TagField(tag="isrc", column="ISRC", edit_field="ISRC"),
83
+ TagField(tag="track_no", column="TrackNo", edit_field="TrackNo", default=0),
84
+ TagField(tag="disc_no", column="DiscNo", edit_field="DiscNo", default=0),
85
+ TagField(
86
+ tag="release_year", column="ReleaseYear", edit_field="ReleaseYear", default=0
87
+ ),
88
+ )
@@ -5,6 +5,7 @@ import logging
5
5
  from pyrekordbox import Rekordbox6Database
6
6
 
7
7
  from rekordbox_edit.api._utils import (
8
+ reserve_usns,
8
9
  stamp_usns,
9
10
  track_from_content,
10
11
  writing,
@@ -141,12 +142,19 @@ def edit(
141
142
  old_values: dict[str, str | None] = {}
142
143
  edited = []
143
144
  with writing(db, "edit"):
145
+ # Shared records the edit creates or collects along the way each take a
146
+ # USN too, the same as the tracks themselves.
147
+ created: list = []
148
+ collected = 0
144
149
  for content in contents:
145
150
  if str(content.ID) in new_values:
146
151
  old_values[str(content.ID)] = handler.current_value(content)
147
- handler.apply(db, content, new_values[str(content.ID)])
152
+ incidental = handler.apply(db, content, new_values[str(content.ID)])
153
+ created.extend(incidental.created)
154
+ collected += incidental.deleted
148
155
  edited.append(content)
149
- stamp_usns(db, edited)
156
+ stamp_usns(db, [*edited, *created])
157
+ reserve_usns(db, collected)
150
158
  db.session.commit()
151
159
  _logger.debug(f"edit committed {len(ops)} change(s) on field={args.field}")
152
160
 
@@ -8,11 +8,17 @@ own encoding or relational lookup.
8
8
 
9
9
  import logging
10
10
  import os
11
+ from typing import NamedTuple
11
12
 
12
13
  from pyrekordbox import Rekordbox6Database
13
14
  from pyrekordbox.db6 import tables as tb
14
- from sqlalchemy import or_
15
15
 
16
+ from rekordbox_edit._tag_fields import TAG_FIELDS, TagField
17
+ from rekordbox_edit.api._relations import (
18
+ RELATIONS,
19
+ delete_if_orphaned,
20
+ get_or_create,
21
+ )
16
22
  from rekordbox_edit.api._utils import _sync_audio_columns, _update_anlz_paths
17
23
  from rekordbox_edit.errors import DependencyMissingError
18
24
  from rekordbox_edit.models import EditRequest, SkipReason
@@ -28,6 +34,22 @@ from rekordbox_edit.utils import (
28
34
  _logger = logging.getLogger(__name__)
29
35
 
30
36
 
37
+ class IncidentalRows(NamedTuple):
38
+ """Rows a write touched besides the track itself.
39
+
40
+ Rekordbox gives every row it writes a USN, so the caller stamps `created`
41
+ and reserves one USN per row in `deleted`, which no longer exists to carry
42
+ a stamp of its own.
43
+ """
44
+
45
+ created: tuple = ()
46
+ deleted: int = 0
47
+
48
+
49
+ #: What a handler writing nothing but the track's own row returns.
50
+ NO_INCIDENTAL_ROWS = IncidentalRows()
51
+
52
+
31
53
  class FieldHandler:
32
54
  """Read, compute, validate, and apply one editable field."""
33
55
 
@@ -59,7 +81,9 @@ class FieldHandler:
59
81
  """New value as a string, or None to signal no change / skip."""
60
82
  raise NotImplementedError # pragma: no cover
61
83
 
62
- def apply(self, db: Rekordbox6Database, content, new_value: str) -> None:
84
+ def apply(self, db: Rekordbox6Database, content, new_value: str) -> IncidentalRows:
85
+ """Write the new value, returning the rows the write added or removed
86
+ alongside the track's own."""
63
87
  raise NotImplementedError # pragma: no cover
64
88
 
65
89
  def post_commit(
@@ -99,6 +123,7 @@ class StringField(FieldHandler):
99
123
 
100
124
  def apply(self, db, content, new_value):
101
125
  setattr(content, self.column, new_value)
126
+ return NO_INCIDENTAL_ROWS
102
127
 
103
128
 
104
129
  class RatingField(FieldHandler):
@@ -123,21 +148,55 @@ class RatingField(FieldHandler):
123
148
 
124
149
  def apply(self, db, content, new_value):
125
150
  content.Rating = star_rating_to_stored(int(new_value))
151
+ return NO_INCIDENTAL_ROWS
126
152
 
127
153
 
128
- # Every DjmdContent column pointing at a DjmdArtist row; an artist is orphaned
129
- # only when none of these, nor any album's AlbumArtistID, still reference it.
130
- _ARTIST_ROLE_COLUMNS = (
131
- tb.DjmdContent.ArtistID,
132
- tb.DjmdContent.RemixerID,
133
- tb.DjmdContent.OrgArtistID,
134
- tb.DjmdContent.ComposerID,
135
- tb.DjmdContent.Lyricist,
136
- )
154
+ class IntegerField(FieldHandler):
155
+ """A whole-number column: track number, disc number, or release year.
156
+
157
+ `--match` does not apply, so the value is validated once per request
158
+ rather than per track. A cleared value writes 0, the value an import
159
+ stores for a tag the file does not carry.
160
+ """
161
+
162
+ supports_match = False
163
+
164
+ def __init__(self, name: str, column: str):
165
+ self.name = name
166
+ self.column = column
167
+
168
+ def validate_request(self, args):
169
+ if args.match_pattern is not None:
170
+ _logger.warning(
171
+ f"--match does not apply to {self.name}; setting the value directly"
172
+ )
173
+ self._parse(args.replace_value) # raises ValueError on bad input
174
+
175
+ def _parse(self, value: str) -> int:
176
+ if value == "":
177
+ return 0
178
+ try:
179
+ return int(value)
180
+ except ValueError:
181
+ raise ValueError(
182
+ f"{self.name} must be a whole number, got {value!r}"
183
+ ) from None
184
+
185
+ def current_value(self, content):
186
+ stored = getattr(content, self.column)
187
+ return None if stored is None else str(stored)
188
+
189
+ def compute_new_value(self, current, args):
190
+ return str(self._parse(args.replace_value))
191
+
192
+ def apply(self, db, content, new_value):
193
+ setattr(content, self.column, int(new_value))
194
+ return NO_INCIDENTAL_ROWS
137
195
 
138
196
 
139
197
  class RelationalField(FieldHandler):
140
- """Artist or album: a shared record reached through a foreign key.
198
+ """A shared record reached through a foreign key: artist, album, genre, or
199
+ label.
141
200
 
142
201
  Setting the value reuses an existing record with the same name or creates
143
202
  one, then repoints the track. A record left with no references is deleted,
@@ -161,59 +220,15 @@ class RelationalField(FieldHandler):
161
220
 
162
221
  def apply(self, db, content, new_value):
163
222
  old_id = getattr(content, self.fk_column)
223
+ created: list = []
164
224
  if new_value == "":
165
- setattr(content, self.fk_column, "")
225
+ setattr(content, self.fk_column, RELATIONS[self.kind].empty_value)
166
226
  else:
167
- record = self._get_or_create(db, new_value)
227
+ record = get_or_create(db, self.kind, new_value, created)
168
228
  setattr(content, self.fk_column, record.ID)
169
229
  db.session.flush()
170
- self._delete_if_orphaned(db, old_id)
171
-
172
- def _get_or_create(self, db, name):
173
- if self.kind == "artist":
174
- existing = (
175
- db.session.query(tb.DjmdArtist)
176
- .filter_by(Name=name)
177
- .order_by(tb.DjmdArtist.ID)
178
- .first()
179
- )
180
- return existing or db.add_artist(name)
181
- existing = (
182
- db.session.query(tb.DjmdAlbum)
183
- .filter_by(Name=name)
184
- .order_by(tb.DjmdAlbum.ID)
185
- .first()
186
- )
187
- return existing or db.add_album(name)
188
-
189
- def _delete_if_orphaned(self, db, old_id):
190
- if old_id in (None, ""):
191
- return
192
- if self.kind == "artist":
193
- in_content = (
194
- db.session.query(tb.DjmdContent)
195
- .filter(or_(*(col == old_id for col in _ARTIST_ROLE_COLUMNS)))
196
- .first()
197
- )
198
- in_album = (
199
- db.session.query(tb.DjmdAlbum)
200
- .filter(tb.DjmdAlbum.AlbumArtistID == old_id)
201
- .first()
202
- )
203
- if in_content is None and in_album is None:
204
- row = db.get_artist(ID=old_id)
205
- if row is not None:
206
- db.delete(row)
207
- else:
208
- in_content = (
209
- db.session.query(tb.DjmdContent)
210
- .filter(tb.DjmdContent.AlbumID == old_id)
211
- .first()
212
- )
213
- if in_content is None:
214
- row = db.get_album(ID=old_id)
215
- if row is not None:
216
- db.delete(row)
230
+ collected = delete_if_orphaned(db, self.kind, old_id)
231
+ return IncidentalRows(tuple(created), collected)
217
232
 
218
233
 
219
234
  class FolderPathField(FieldHandler):
@@ -304,6 +319,12 @@ class FolderPathField(FieldHandler):
304
319
  content.OrgFolderPath = new_value
305
320
  content.FolderPath = new_value
306
321
  content.FileNameL = new_value.rsplit("/", 1)[-1]
322
+ self._sync_from_file(content, new_value)
323
+ return NO_INCIDENTAL_ROWS
324
+
325
+ def _sync_from_file(self, content, new_value) -> None:
326
+ """Rewrite the columns describing the audio file, when there is a
327
+ changed file to read them from."""
307
328
  if not os.path.exists(new_value):
308
329
  return
309
330
  file_size = os.path.getsize(new_value)
@@ -337,13 +358,28 @@ class FolderPathField(FieldHandler):
337
358
  return cue is not None
338
359
 
339
360
 
361
+ def _handler_for(tag_field: TagField) -> FieldHandler:
362
+ """The handler for one editable tag, chosen by how its column stores the
363
+ value: through a foreign key, as a number, or as text."""
364
+ name = tag_field.edit_field
365
+ assert name is not None and tag_field.column is not None
366
+ if tag_field.relation is not None:
367
+ assert tag_field.proxy is not None
368
+ return RelationalField(
369
+ name, tag_field.column, tag_field.proxy, tag_field.relation
370
+ )
371
+ if isinstance(tag_field.default, int):
372
+ return IntegerField(name, tag_field.column)
373
+ return StringField(name, tag_field.column)
374
+
375
+
376
+ #: Every field `edit` can change. The tag-backed ones come from TAG_FIELDS so
377
+ #: `edit` and `import` cannot drift apart; Rating and FolderPath are not audio
378
+ #: tags and are declared here.
340
379
  FIELD_HANDLERS: dict[str, FieldHandler] = {
341
380
  handler.name: handler
342
381
  for handler in (
343
- StringField("Title", "Title"),
344
- StringField("Comment", "Commnt"),
345
- RelationalField("ArtistName", "ArtistID", "ArtistName", "artist"),
346
- RelationalField("AlbumName", "AlbumID", "AlbumName", "album"),
382
+ *(_handler_for(f) for f in TAG_FIELDS if f.edit_field is not None),
347
383
  RatingField(),
348
384
  FolderPathField(),
349
385
  )
@@ -9,6 +9,8 @@ from typing import NamedTuple, cast
9
9
  from pyrekordbox import Rekordbox6Database
10
10
  from pyrekordbox.db6 import tables as tb
11
11
 
12
+ from rekordbox_edit._tag_fields import TAG_FIELDS
13
+ from rekordbox_edit.api._relations import RELATIONS, find_by_name, get_or_create
12
14
  from rekordbox_edit.api._utils import stamp_usns, track_from_content, writing
13
15
  from rekordbox_edit.errors import (
14
16
  DirectoryConfirmationRequired,
@@ -162,60 +164,38 @@ IMPORT_DEFAULTS: dict[str, object] = {
162
164
  }
163
165
 
164
166
 
165
- def _get_or_create(db, table, name: str, factory, created: list | None = None):
166
- """Reuse a row matching by name, else create one.
167
-
168
- Same query shape as RelationalField._get_or_create in api/field_handlers.py,
169
- generalized over a table and factory so one function covers all five
170
- relational columns instead of a per-kind branch.
171
-
172
- New rows are appended to `created` when given: add_content flushes,
173
- which moves them out of session.new before they can be found again.
174
- """
175
- existing = db.session.query(table).filter_by(Name=name).order_by(table.ID).first()
176
- if existing is not None:
177
- return existing
178
- row = factory(name)
179
- if created is not None:
180
- created.append(row)
181
- return row
182
-
183
-
184
167
  def _resolve_relations(
185
168
  db: Rekordbox6Database, tags: TrackTags, created: list | None = None
186
169
  ) -> dict[str, str]:
187
170
  """Foreign keys for a track's tags, creating shared rows as needed.
188
171
 
189
172
  A tag that is absent yields no key, leaving the column at add_content's
190
- default. KeyID is the exception: DjmdKey is a fixed table with no add_key
191
- helper, so an unmatched key falls back to Rekordbox's '0' sentinel.
173
+ default. A closed relation is the exception: DjmdKey cannot be added to,
174
+ so an unmatched key falls back to Rekordbox's '0' sentinel.
192
175
  """
193
- session = require_session(db)
194
176
  relations: dict[str, str] = {}
195
- for field, column, table, factory in (
196
- ("artist", "ArtistID", tb.DjmdArtist, db.add_artist),
197
- ("composer", "ComposerID", tb.DjmdArtist, db.add_artist),
198
- ("album", "AlbumID", tb.DjmdAlbum, db.add_album),
199
- ("genre", "GenreID", tb.DjmdGenre, db.add_genre),
200
- ("label", "LabelID", tb.DjmdLabel, db.add_label),
201
- ):
202
- value = tags.get(field)
203
- if value:
204
- relations[column] = _get_or_create(db, table, value, factory, created).ID
205
-
206
- key = tags.get("key")
207
- row = (
208
- session.query(tb.DjmdKey)
209
- .filter_by(ScaleName=key)
210
- .order_by(tb.DjmdKey.ID)
211
- .first()
212
- if key
213
- else None
214
- )
215
- relations["KeyID"] = row.ID if row else "0"
177
+ for tag_field in TAG_FIELDS:
178
+ if tag_field.relation is None:
179
+ continue
180
+ value = tags.get(tag_field.tag)
181
+ relation = RELATIONS[tag_field.relation]
182
+ if not relation.sweepable:
183
+ row = find_by_name(db, tag_field.relation, value) if value else None
184
+ relations[tag_field.column] = row.ID if row else relation.empty_value
185
+ elif value:
186
+ relations[tag_field.column] = get_or_create(
187
+ db, tag_field.relation, value, created
188
+ ).ID
216
189
  return relations
217
190
 
218
191
 
192
+ def _scalar_columns(tags: TrackTags) -> dict[str, str | int]:
193
+ """Column values for the tags stored on the DjmdContent row itself."""
194
+ return {
195
+ f.column: tags.get(f.tag) or f.default for f in TAG_FIELDS if f.relation is None
196
+ }
197
+
198
+
219
199
  def _created_date(path: str) -> str:
220
200
  """The file's creation date, as Rekordbox records DateCreated.
221
201
 
@@ -244,12 +224,7 @@ def _build_content(
244
224
  candidate.stored,
245
225
  **IMPORT_DEFAULTS,
246
226
  **_resolve_relations(db, tags, created),
247
- Title=tags["title"],
248
- Commnt=tags["comment"] or "",
249
- ISRC=tags["isrc"] or "",
250
- TrackNo=tags["track_no"] or 0,
251
- DiscNo=tags["disc_no"] or 0,
252
- ReleaseYear=tags["release_year"] or 0,
227
+ **_scalar_columns(tags),
253
228
  Length=tags["length"] or 0,
254
229
  )
255
230
  # add_content types by extension, mapping every .m4a to AAC, and stamps