fix(metadata): validate direct provider IDs by year with cross-provider fallback#2643
Merged
Conversation
Collaborator
Author
|
For some context:
|
Collaborator
Author
|
/release-pr |
Contributor
|
Released to |
maintainerr-automation Bot
added a commit
that referenced
this pull request
Apr 9, 2026
* build(deps-dev): bump @types/node from 22.19.15 to 22.19.17 (#2630) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.15 to 22.19.17. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.19.17 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump postcss from 8.5.8 to 8.5.9 (#2629) Bumps [postcss](https://github.com/postcss/postcss) from 8.5.8 to 8.5.9. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](postcss/postcss@8.5.8...8.5.9) --- updated-dependencies: - dependency-name: postcss dependency-version: 8.5.9 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @typescript-eslint/parser from 8.57.2 to 8.58.1 (#2631) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.57.2 to 8.58.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.58.1/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.58.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump vitest from 4.1.2 to 4.1.3 (#2632) Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.2 to 4.1.3. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.3/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(jellyfin): clear stale collection link when media server collection is deleted Re-throw 400/404 errors from Jellyfin getCollectionChildren instead of swallowing them, so callers can detect when a collection no longer exists. In getCollectionMediaMetadata, verify the collection is truly gone via getCollection before clearing the mediaServerId link. This double-check prevents false positives from resetting collection counters. If the verification call itself fails transiently, the link is preserved. * refactor(metadata): generic metadata-lookup utility (#2633) * fix(jellyfin): exclude virtual episodes from child queries (#2624) Virtual episodes in Jellyfin are placeholders for unaired content. Including them caused ended shows to appear as having episodes, preventing proper cleanup. Closes #2558 * feat: clean up empty Sonarr shows after season actions (#2618) * fix(server): import SeerrApiModule into ActionsModule * fix(jellyfin): lower collection mutation batch size * build(deps): bump nodemailer from 8.0.4 to 8.0.5 (#2635) Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.4 to 8.0.5. - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](nodemailer/nodemailer@v8.0.4...v8.0.5) --- updated-dependencies: - dependency-name: nodemailer dependency-version: 8.0.5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump the nestjs group with 3 updates (#2639) Bumps the nestjs group with 3 updates: [@nestjs/serve-static](https://github.com/nestjs/serve-static), [@nestjs/swagger](https://github.com/nestjs/swagger) and [@nestjs/cli](https://github.com/nestjs/nest-cli). Updates `@nestjs/serve-static` from 5.0.4 to 5.0.5 - [Release notes](https://github.com/nestjs/serve-static/releases) - [Commits](nestjs/serve-static@5.0.4...5.0.5) Updates `@nestjs/swagger` from 11.2.6 to 11.2.7 - [Release notes](https://github.com/nestjs/swagger/releases) - [Commits](nestjs/swagger@11.2.6...11.2.7) Updates `@nestjs/cli` from 11.0.18 to 11.0.19 - [Release notes](https://github.com/nestjs/nest-cli/releases) - [Commits](nestjs/nest-cli@11.0.18...11.0.19) --- updated-dependencies: - dependency-name: "@nestjs/serve-static" dependency-version: 5.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nestjs - dependency-name: "@nestjs/swagger" dependency-version: 11.2.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: nestjs - dependency-name: "@nestjs/cli" dependency-version: 11.0.19 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: nestjs ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump the react group with 2 updates (#2640) Bumps the react group with 2 updates: [react](https://github.com/facebook/react/tree/HEAD/packages/react) and [react-dom](https://github.com/facebook/react/tree/HEAD/packages/react-dom). Updates `react` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react) Updates `react-dom` from 19.2.4 to 19.2.5 - [Release notes](https://github.com/facebook/react/releases) - [Changelog](https://github.com/facebook/react/blob/main/CHANGELOG.md) - [Commits](https://github.com/facebook/react/commits/v19.2.5/packages/react-dom) --- updated-dependencies: - dependency-name: react dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react - dependency-name: react-dom dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: react ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump vitest from 4.1.3 to 4.1.4 (#2641) Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 4.1.3 to 4.1.4. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-version: 4.1.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump axios from 1.14.0 to 1.15.0 (#2642) Bumps [axios](https://github.com/axios/axios) from 1.14.0 to 1.15.0. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.14.0...v1.15.0) --- updated-dependencies: - dependency-name: axios dependency-version: 1.15.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(collections): make Delete Latest send a real sort param (#2634) Delete Latest was defined as the empty-string "no sort" fallback with sortParams: undefined. In CollectionMediaPage.fetchCollectionMediaPage, passing undefined as the override triggered the `= sortParams` default parameter fallback, so the request sent for Delete Latest was identical to Delete Soonest. Introduce a dedicated deleteSoonest.desc option and filter the empty fallback out when delete sorts are present. Both options still hit the fast addDate branch in getCollectionMediaWithServerDataAndPaging, so performance is unchanged. Fixes #2634 * fix(collections): auto-load next page when viewport already filled The infinite-scroll hook only fetched more pages in response to a scroll event. On large viewports that could display all 30 initial items without producing a scrollbar, no scroll event ever fired and the list got stuck at 30 until the window was resized. Re-check isNearBottom() after each append and also listen for resize, so additional pages cascade in until the document is actually scrollable (or totalSize is reached). Fixes #2637 * fix: carry over valid PR 2534 hardening fixes (#2617) * fix(metadata): validate direct provider IDs by year with cross-provider fallback (#2643) --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: enoch85 <mailto@danielhansson.nu>
Contributor
|
🎉 This PR is included in version 3.5.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
maintainerr-automation Bot
added a commit
that referenced
this pull request
May 28, 2026
* chore(metadata): log resolved id and title on year-drift accept Bumps the silent ±1 year-drift accept from debug to info and includes the resolved provider id and title in the message, so a human reading the log can verify the match (the underlying id-primary / year-sanity design from #2643 still applies — this only adds visibility). * chore(deps): drop vulnerable tmp@0.0.33 by deduping @inquirer/editor Resolves GHSA path-traversal advisory #213 (tmp < 0.2.6). The vulnerable copy was pulled transitively via @nestjs/cli (dev) -> @angular-devkit/schematics-cli@19.2.24 -> @inquirer/prompts@7.3.2 -> @inquirer/editor@4.2.7 -> external-editor@3.1.0 -> tmp@0.0.33. Deduping @inquirer/editor to 4.2.23 (already in the tree via @inquirer/prompts@7.10.1) swaps to @inquirer/external-editor, which has no tmp dependency. Removes external-editor, tmp, os-tmpdir, chardet, iconv-lite, and the old @inquirer/editor from the lockfile. --------- Co-authored-by: enoch85 <mailto@danielhansson.nu>
fragglexarmy
pushed a commit
to fragglexarmy/Maintainerr
that referenced
this pull request
May 29, 2026
Bumps the silent ±1 year-drift accept from debug to info and includes the resolved provider id and title in the message, so a human reading the log can verify the match (the underlying id-primary / year-sanity design from Maintainerr#2643 still applies — this only adds visibility).
enoch85
added a commit
that referenced
this pull request
Jun 3, 2026
…as alternate fallback When TMDB's external_ids reports a different (sometimes wrong or dead) id for another provider, applyIdCorrections used to overwrite the media-server id with it — issue #3010 verified TMDB returning a 404 TVDB id for a show the media server had correct, breaking the Sonarr lookup. Now the media-server primary stays authoritative; the cross-reference id is recorded as a per-(ids,provider) alternate in a WeakMap side channel. buildLookupCandidates emits primary first, alternate second; the Sonarr/Radarr/Seerr getters and action handlers all reach the alternate via their existing candidate flows. findMetadataLookupMatch advances past a confirmed-miss null (preserved as a remembered fallback) so the new candidate iteration actually reaches the alternate when the primary is not in Sonarr. The #2643 redirect intent is preserved: stale primaries still work, just via the fallback instead of an overwrite.
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.
Summary
Swaps the title-based check for direct provider IDs with an ID-first, year-as-sanity-check approach.
When an item has a direct TMDB/TVDB/IMDB id, the matching provider is asked for the entry's year. If it agrees with the media server, the id is accepted. If it disagrees, the next configured provider gets a chance to vouch — and if that secondary doesn't have an id on the item itself, Maintainerr bridges to it through an existing external reference (e.g. IMDB → TVDB). If nothing confirms the year, the direct ids are dropped so rules never run against the wrong entry.
Stale ids that a provider has since redirected (e.g.
tmdb: 111→222) are still rewritten to the canonical value, same as before.Why
The old title check was too strict for normal libraries. Cosmetic drift — localization, Roman vs Arabic numerals, word-vs-digit, edition suffixes, punctuation — was enough to reject correct ids and silently drop items from rule evaluation.
Titles are also the wrong signal to lean on: they're too permissive when identical (re-scan mixups slip through) and too strict when they're not. Year is a much stronger discriminator — it's a single integer, immune to all the cosmetic drift, and genuine disagreement is a real red flag worth surfacing.
Fail-closed on unresolved year disagreement is the safer default for a deletion tool. Skipping a deletion is recoverable; deleting the wrong thing isn't.
Known gap: a wrong id that coincidentally shares the correct entry's year still slips through. Catching that would need an external title search the provider interface doesn't expose. Same as 3.3.0.
Fixes