GetOrAddFolder was a check-then-insert with no unique constraint behind it,
so two callers racing the same folder could both miss the lookup and both
insert. Enforce identity in the schema and make the loser adopt the winner.
- LibraryFolder gains a SHA-256 PathHash (the MediaFile.Path/PathHash
precedent): Path is MySQL longtext, which cannot be indexed without a
prefix length and collates case-insensitively, so the unique index is on
(LibraryPathId, PathHash) instead.
- GetOrAddFolder and SetEtag catch a classified unique violation via the
existing TvContext.IsUniqueConstraintViolation seam (#308) and re-read.
- Dual-provider migration audits and collapses pre-existing duplicates
(repointing MediaFile, ParentId and ImageFolderDuration) before creating
the index; legacy rows keep a null hash and heal on the next scan.
- Tests: deterministic cross-connection race, 8x10 barrier stress with an
insert-attempt vacuity guard, classifier-inversion negative control, and
a real-migration dedupe test.
Refs #488#308fix#491
Jellyfin libraries typed `mixed` were dropped by JellyfinApiClient.Project's
`_ => None` with no log line, so music and standup content could not be
ingested without a local-library workaround that bypassed Jellyfin entirely.
Adds LibraryMediaKind.Mixed, maps "mixed"/absent/blank CollectionType onto it,
and gives SynchronizeJellyfinLibraryByIdHandler a Mixed arm composing the three
existing per-kind scanners. Jellyfin classifies items server-side via
includeItemTypes, so the passes see disjoint sets; reconciliation is type-scoped
and cannot cross-delete. No new scanner and no DB migration -- MediaItem is TPT
keyed on LibraryPathId, so heterogeneous contents were already legal.
Segregation falls out of the model: a library is a place (one path <-> one
Jellyfin library <-> one ErsatzTV library), so music/standup cannot leak into
Movies or TV Shows.
Also removes the silent-success `_ => Unit.Default` from both scanner
dispatchers, which returned Right for an unhandled kind and stamped LastScan as
though a scan had run, and rejects Mixed for local libraries at the API.
Deliberately Jellyfin-only: local scanners share one video extension list and
would claim each other's files, and LibraryFolder etags are keyed by
LibraryPathId with no notion of kind.
Verified by live E2E against a real Jellyfin, including the interaction with
#494's reconciliation sweep. Four cold review rounds, all MERGEABLE.
fixes#489
Co-authored-by: Timothy <timothy.look@gmail.com>
Co-committed-by: Timothy <timothy.look@gmail.com>
* initial plumbing
* scan for plex networks
* save network contents to db as tags
* eliminate network tag id churn
* add network and show_network to search index
* update last networks scan
* show networks on tv show page
* update changelog
* use etags to optimize local movie scanner
* use etags to optimize local television scanner
* use etags to optimize local music video scanner
* code cleanup
* wip
* start to add jellyfin tables to db
* code cleanup
* finish adding jellyfin media source
* sync jellyfin libraries
* display list of jellyfin libraries
* toggle jellyfin library sync
* edit jellyfin path replacements
* noop jellyfin scanners
* get jellyfin admin user id on startup
* implement jellyfin disconnect
* add jellyfin libraries to list; start to query jellyfin library items
* code cleanup
* start to project jellyfin movies
* save new jellyfin movies to db
* basic jellyfin movie update
* load jellyfin actor artwork
* load jellyfin movie poster and fan art
* more jellyfin artwork fixes, sync audio streams
* jellyfin playback sort of works
* skip jellyfin movies that are inaccessible
* use ffprobe for jellyfin movie statistics
* code cleanup
* store jellyfin operating system
* more jellyfin movie updates
* update jellyfin movie poster and fan art
* add jellyfin tv types
* sync jellyfin shows
* sync jellyfin seasons
* sync jellyfin episodes
* remove missing jellyfin television items
* delete empty jellyfin seasons and shows
* fix jellyfin updates
* fix indexing jellyfin movie and show languages
* add music videos library
* add music video tables
* first pass at music video library scan
* support music videos in playouts
* display music videos in search results and collections
* fix music video thumbnails
* remove some obsolete fields
* starting database redesign
* set season and episode numbers
* use datetimes in db (utc); update movie metadata
* get movie cards from new table
* copy show/episode metadata
* remove old movie metadata type
* rename new movie metadata type
* code cleanup
* start to remove old television classes
* remove old television tables from database
* fix playout building
* fix collection views
* fix show/season views
* clean up movie metadata table
* fix scanner tests
* add libraries ui
* code cleanup
* fix movie scanning/metadata
* add library scan button to ui
* delete library path from ui
* temp disable movie scanning
* remove orphan media items and prevent duplicate paths
* attach artwork to metadata
* fix split show/season display
* fix television artwork
* store year distinct from release date
* fix collections ui
* code cleanup
* add library paths from ui
* fix adding to collections from ui
* fix schedule items loading
* schedule editing works again
* remove some todos
* more cleanup
* fix unit tests
* fix episode sorting
* fix deleting show library paths
* remove unused class
* fix playout list in ui
* fix log viewer
* start to use version/file instead of statistics
* clean up old columns
* fix playout display (time zone)
* fix playback
* fix channel guide time zone
* cascade more deletes
* fix compiler warnings
* fix adding new seasons
* use artwork for channel logo
* clean cache folder on startup (move channel logos, delete everything else)
* log database migration
* update homepage docs for libraries
* fix adding new channel with logo
* fix episode numbers in epg