fix(media-sources): mark missing libraries unavailable instead of deleting them - #2086
Merged
Merged
Conversation
6 tasks
…eting them The hourly library refresh deleted any stored library absent from a media server response. Library foreign keys cascade, so one incomplete response (restricted token, server still starting) silently removed the library's programs and channel schedule entries. - Share one reconcile function across Plex, Jellyfin and Emby - Mark missing libraries with unavailable_since; clear it when they return - Skip reconciliation when the server reports no supported libraries - Move programs and groupings to the kept row before deleting duplicate library rows - Refuse scans of unavailable libraries in MediaSourceScanCoordinator - Offset the library scan cron from the hourly refresh - Show an Unavailable badge in the library table and libraries dialog Fixes #1973 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chrisbenincasa
force-pushed
the
fix/library-reconciliation-1973
branch
from
September 22, 2026 00:03
4892916 to
830e636
Compare
chrisbenincasa
changed the base branch from
main
to
fix/external-subtitles-jellyfin-emby
September 22, 2026 00:03
Base automatically changed from
fix/external-subtitles-jellyfin-emby
to
main
September 22, 2026 15:54
The pseudo-LOCALE, es and en catalogs each had `<<<<<<< HEAD` baked into 36 translation strings, so the marker rendered in the UI wherever those messages were used. In en it was appended to real strings; in es and pseudo-LOCALE it replaced the empty msgstr. `tunarr-v1.1.0-dev.1-openapi.json` was committed with an unresolved conflict holding both copies of the spec. Restored the HEAD side, which is byte identical to the file as added in 580091a and matches the current /api/programs/{id}/scan route. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ejected A revoked or mistyped access token left stale libraries looking healthy forever. Reaching that case first meant repairing the client error handling it depended on. `doRequest` throws on every axios error and `doTypeCheckedGet` never caught it, so its error classification block was unreachable and every isFailure() branch in the Plex, Jellyfin and Emby clients was dead for HTTP errors. The 404 -> not_found mapping had never worked either. It now returns failure results as its signature always promised, with a new auth_error code for 401 and 403. - Mark a source's libraries unavailable after 3 consecutive rejected refreshes, tracked in media_source.consecutive_auth_failures and cleared by any successful fetch. A server rejecting tokens while it restarts recovers well inside that; a revoked token does not. - Leave libraries alone for every other failure, which carries no information about them - Never delete duplicates on the auth path, since a rejected credential says nothing about which rows are redundant - Guard the refreshAll loop so one unreachable source no longer strands the sources behind it - Fix deleteCachedClient, which built its key as type|uri|token while the cache is written as type|id, so it never deleted and credential edits were ignored for up to the 1 hour TTL - Show a warning icon rather than a chip in the library table, where the chip wrapped onto its own line Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
|
This is included in v2026.9.0. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1973.
Problem
MediaSourceLibraryRefresherruns at startup and every hour. It deleted any stored library that was absent from the media server's library list.program.library_idandprogram_grouping.library_idcascade on delete, andchannel_programscascades fromprogram. One incomplete response, such as a restricted token or a server that is still starting, silently removed the library's programs and channel schedule entries. The three backend handlers had also drifted apart. Only Emby guarded against an empty response, and only before filtering out unsupported types. Jellyfin and Emby never synced library renames.Changes
{ externalKey, name, mediaType }and call one pure function,reconcileLibraries.media_source_library.unavailable_sincecolumn marks them, and the mark clears when the library returns. The user'senabledflag is never touched.MediaSourceScanCoordinator.add()refuses unavailable libraries, which covers the scheduled task and the manual scan endpoint.ScanLibrariesTasknow runs at :30 so it never fires in the same second as the hourly refresh.unavailableSinceis exposed on library responses, and an "Unavailable" badge appears in the library table and the Manage Libraries dialog.Not included
DELETE /trash. It still skips lineup invalidation and a guide rebuild. It overlaps with the incremental empty-trash work and is left for that change.mainis stale against the server. This PR adds only the 15unavailableSincelines toweb/src/generated/types.gen.ts, because a full regen pulls in unrelated changes that breakShowSearchSlotProgrammingForm.tsx.docs/generatedis unchanged.Test plan
reconcileLibraries.test.ts: missing → unavailable, returning → available withenabledkept, empty-response guard, new libraries added disabled, no re-marking, renames, duplicates keep the enabled rowMediaSourceLibraryRefresher.test.ts: per-backend wiring, including unsupported-only responses and request failureMediaSourceDB.test.ts: duplicate merge moves programs, unavailable/available round trip keeps programs andenabledpnpm turbo test(server), server and web typecheck,pnpm lint-changed🤖 Generated with Claude Code