mirror of https://github.com/Nezreka/SoulSync.git
dev
video
main
fix/disable-beatport-features
johnbaumb-discover-redesign
1.0
1.1
1.2
1.3
1.4
1.5
1.6
1.7
1.8
1.9
2.0
2.1
2.2
2.3
2.4.0
2.4.1
2.4.2
2.5.0
2.5.1
2.5.2
2.5.3
2.5.4
2.5.5
2.5.6
2.5.7
2.5.9
2.6.0
2.6.1
2.6.2
2.6.3
2.6.4
2.6.5
2.6.6
2.6.7
2.6.8
2.6.9
2.7.0
2.7.1
2.7.2
2.7.3
2.7.4
2.7.5
2.7.6
v0.65
${ noResults }
5 Commits (31ebe96f76ada26847bf0bf4c8adabc70bb04f49)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
939c660498 |
Fix #792: 'reconcile' playlist sync mode (edit in place, keep image/description)
Replace mode (default) deletes + recreates the server playlist every sync,
which wipes its custom image, description, and identity. Add an opt-in
'reconcile' sync mode that edits the existing playlist in place — adds the
tracks now in the source, removes the ones gone — without destroying the
object, so the user's custom art/description survive.
- Pure planner plan_playlist_reconcile(current, desired) -> {add, remove}.
- Per-client reconcile_playlist: Plex addItems/removeItems on the same object;
Navidrome Subsonic updatePlaylist delta (songIdToAdd / descending
songIndexToRemove); Jellyfin add + remove-by-PlaylistItemId on /Playlists/{id}/Items.
- sync_service: reconcile branch with a replace FALLBACK (if a server's in-place
edit is unavailable/fails, sync still succeeds destructively — logged loudly).
- Default stays 'replace' (no behavior change). New Settings > Playlist sync mode
picker (replace/reconcile/append) backed by playlist_sync.mode; per-request
sync_mode still overrides.
- Reconcile skips the post-sync source-image push so a custom poster isn't
re-clobbered (the bug).
Tests: planner (add/remove/dedupe/order/empty) + reconcile-or-replace dispatch
(success / false-fallback / exception-fallback / no-method). Per-server in-place
API calls need dev validation against real Plex/Jellyfin/Navidrome.
NOTE: opt-in only; default behavior unchanged.
|
3 weeks ago |
|
|
3b155411c2 |
Fix #787: Find & Add now records a durable manual match that survives a rescan
Find & Add on the playlist-sync page only wrote sync_match_cache, which is DELETEd wholesale after every DB scan — so the source->library pairing (and the user's manual matches) reverted to 'extra'/red-dot on the next shallow scan. The three match stores (sync_match_cache, manual_library_track_matches, discovery extra_data) were disconnected and all pointed at tracks.id, which a rescan re-keys (esp. Jellyfin/Navidrome GUIDs). Unify the match so it's one durable fact, recorded once, honored everywhere: - Find & Add also writes a durable manual_library_track_matches row (one-way; the manual-match tool has no playlist to act on, so no reverse). Carries the library file path. - New library_file_path column (idempotent migration) + find_track_id_by_file_path: re-resolve a stale library_track_id after a rescan re-keys the track, and self-heal the row. - The sync compare display's override lookup now falls back to the durable manual match (resolve_durable_match_server_id) when sync_match_cache misses — so the pairing persists across a scan instead of reverting to a red dot. Purely additive: only adds matches when the cache returns nothing. Tests: durable resolver (valid / stale-reresolve+self-heal / no-match / not-in- playlist / missing-methods), file_path persistence + find_track_id_by_file_path. |
3 weeks ago |
|
|
feb6778af4 |
Address Cin review: extract helpers, indexed pool fetch, tidy nits
Three changes folded into one perf+cleanup pass: 1. Indexed fast path for the per-artist pool fetch. The previous `search_tracks(artist=name)` call hit `unidecode_lower(artists.name) LIKE ?`, a function-in-WHERE that can't use `idx_artists_name`. New `MusicDatabase.get_artist_tracks_indexed` does a two-step lookup: exact-name match (indexed) plus a case-insensitive fallback, then `tracks WHERE artist_id IN (...)` via `idx_tracks_artist_id`. Drops per-artist fetch from seconds to milliseconds for the common case. The sync helper falls back to the old LIKE-based `search_tracks` only when the indexed lookup finds nothing, preserving diacritic recall and `tracks.track_artist` feature-artist matches with zero regression. 2. Public text-normalization helper. Lifted the body of `MusicDatabase._normalize_for_comparison` into `core/text/normalize.py:normalize_for_comparison` so callers outside the database layer (matching engine, sync pool, future import-side comparisons) don't reach across the module boundary into a leading-underscore "private" method. The DB method now delegates, so existing internal call sites stay untouched. Sync's lazy pool now imports the public helper. 3. Artist-name walker extracted. `_artist_name` at module level in `services/sync_service.py` replaces two near-identical inline str-or-dict-or-fallback walkers (one in `sync_playlist`, one in `_find_track_in_media_server`). Returns `''` for None instead of the literal string `'None'`. Plus three small tidies from the same review: - `_POOL_FETCH_LIMIT = 10000` constant in place of the literal at the pool-fetch call site. - Trimmed the verbose docstring + comment block on the pool helper. - Set-intersection predicate for the trigger-shape reset in `core/automation/api.py` instead of a two-line `or` chain. Also removed the duplicate `_get_active_media_client()` call at sync_service.py:212/214 — pre-existing wart that was sitting in the same block I was editing. Tests: 21 new tests across `tests/database/`, `tests/sync/`, and `tests/text/`, plus updates to the existing pool tests to cover the new fast/fallback split. Full suite stays green (3953 passing). |
1 month ago |
|
|
687bb0ca2c |
Add tests for next_run reset and lazy candidate pool
`tests/automation/test_automation_api.py` gains three update_automation tests covering the schedule-shape reset: - trigger_config change blanks next_run - trigger_type change blanks next_run - non-trigger field (name) leaves next_run alone `tests/sync/test_sync_candidate_pool.py` is new — nine tests for the lazy artist track pool in PlaylistSyncService: - candidate_pool=None disables pooling and skips the DB call - first lookup for an artist fetches and caches - second lookup for the same artist reuses the cache (zero DB calls) - empty result still cached so the next call short-circuits without SQL - defensive None return coerced to [] - search_tracks exception returns None and does NOT poison the cache - pool key is normalized so casing variants share a single fetch - different artists get separate pool entries - server_source plumbing survives the trip to search_tracks All assertions go through fakes / MagicMock — no real DB, no web_server.py import, no AST-parsing. |
1 month ago |
|
|
083355ec8c |
Persist Find & Add selections as permanent server-playlist match overrides
Closes #585. When a Spotify source track had a versioned suffix not present in the local file ("Iron Man - 2012 - Remaster" vs "Iron Man"), the auto-matcher missed the pair. User could click Find & Add to pick the right local file — that worked, file got added to the Plex playlist — but the source track stayed in Missing while the added file appeared in Extra, because the matcher kept no record of the user-confirmed pairing. On the next sync the source track re-tried to download. Fix: every Find & Add selection now writes a (spotify_track_id → server_track_id) override into sync_match_cache at confidence=1.0. The matching algorithm runs an override pass BEFORE the existing exact and fuzzy passes, so any user-confirmed pair short-circuits straight to "matched" without going through title normalization. Covers every mismatch class — dash-suffix remasters, covers / karaoke, alt masters, cross-language titles, typo'd local files. - core/sync/match_overrides.py (new) — pure helpers resolve_match_overrides + record_manual_match. 18 boundary tests pin: cache hits, cache misses falling through to normal matching, stale-cache (server track removed) handled gracefully, str/int id coercion, partial cache hits, defensive against non-dict inputs and DB exceptions. - web_server.py — get_server_playlist_tracks runs the override pre-pass before exact/fuzzy matching. server_playlist_add_track accepts source_track_id + source_title + source_artist and persists the override after every successful add (Plex / Jellyfin / Navidrome). source_track_id added to source_tracks payload so the frontend has it. - webui/static/pages-extra.js — _serverSelectTrack sends source_track_id + source_title + source_artist when adding a track from a mirrored playlist context. - Sync match cache schema unchanged — already had UNIQUE (spotify_track_id, server_source) which fits the override semantics perfectly. Manual overrides distinguished from auto-discovered matches by confidence=1.0. Full suite: 3010 passed. |
1 month ago |