Album.store(inherit=True) copied artpath onto items as a flexible
attribute, defeating its exclusion from item_keys and persisting it as
an absolute path that bypasses relative-path storage. Skip fixed album
fields that aren't inheritable instead of treating them as flex attrs.
- Centralize migration test setup in a shared helper and update the migration
tests to use the common pre-migration initialization flow.
- This removes repeated fixture boilerplate while keeping each test focused on
the migration behavior it verifies.
Store paths relative to the music directory in the database instead of
absolute paths. Add RelativePathMigration to handle existing absolute
paths in `path` and `artpath` fields on startup.
Also move `self.directory` assignment before `super().__init__()` so
the migration can access it.
Add a `requires_import` pytest marker and `is_importable` fixture to
conditionally skip or adjust assertions based on whether optional
dependencies are available. Apply this to `langdetect`-dependent
language detection tests in lyrics and migration test suites.
- Add `LyricsMetadataInFlexFieldsMigration` to extract legacy source
URLs and language metadata from lyrics text into flex attributes
- Add `Lyrics.from_legacy_text` to parse legacy lyrics format
- Move `with_row_factory` context manager up to base `Migration` class
- Rename `migrate_table` to `migrate_model` and pass model class
instead of table name string. This is so that the migration can access
both `_table` and `_flex_table` attributes.
- Make `langdetect` import optional in `Lyrics.__post_init__`: users may
not have have the dependency installed, and we do not want the
migration to fail because of that.
- Move `BACKEND_BY_NAME` to module level for use outside plugin class
* Move genre-to-genres migration into a dedicated Migration class and
wire it into Library._migrations for items and albums.
* Add batched SQL updates via mutate_many and share the multi-value
delimiter as a constant.
* Cover migration behavior with new tests.
I initially attempted to migrate using our model infrastructure
/ Model.store(), see the comparison below:
Durations migrating my library of ~9000 items and ~2300 albums:
1. Using our Python logic: 11 minutes
2. Using SQL directly: 4 seconds
That's why I've gone ahead with option 2.