fix(metadata): resolve movie/show ids from the item, not its parent#3100
Merged
Conversation
…3065) getHierarchyResolutionItem walked up to item.grandparentId ?? item.parentId for any item with a parent. That's right for episodes/seasons (resolve up to the show) but wrong for movies: on Emby/Jellyfin a movie's parentId points at an id-less library/container folder, so the movie's own provider ids were discarded → 0 lookup candidates → every Radarr movie action failed with "Couldn't resolve any supported external IDs". The matching path was unaffected, so collections filled but no action could run. Switch on item.type (never parentId presence), matching resolveShowIdsForImage: only episodes/seasons resolve from a parent; movies and shows use their own ids. Plex is unchanged (its top-level movies already had no parent).
maintainerr-automation Bot
added a commit
that referenced
this pull request
Jun 13, 2026
* build(deps-dev): bump semantic-release from 25.0.3 to 25.0.5 (#3078) Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 25.0.3 to 25.0.5. - [Release notes](https://github.com/semantic-release/semantic-release/releases) - [Commits](semantic-release/semantic-release@v25.0.3...v25.0.5) --- updated-dependencies: - dependency-name: semantic-release dependency-version: 25.0.5 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @tailwindcss/typography from 0.5.19 to 0.5.20 (#3079) Bumps [@tailwindcss/typography](https://github.com/tailwindlabs/tailwindcss-typography) from 0.5.19 to 0.5.20. - [Release notes](https://github.com/tailwindlabs/tailwindcss-typography/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss-typography/blob/main/CHANGELOG.md) - [Commits](tailwindlabs/tailwindcss-typography@v0.5.19...v0.5.20) --- updated-dependencies: - dependency-name: "@tailwindcss/typography" dependency-version: 0.5.20 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump react-konva from 19.2.4 to 19.2.5 (#3080) Bumps [react-konva](https://github.com/konvajs/react-konva) from 19.2.4 to 19.2.5. - [Release notes](https://github.com/konvajs/react-konva/releases) - [Commits](https://github.com/konvajs/react-konva/commits) --- updated-dependencies: - dependency-name: react-konva dependency-version: 19.2.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @swc/core from 1.15.40 to 1.15.41 (#3081) Bumps [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) from 1.15.40 to 1.15.41. - [Release notes](https://github.com/swc-project/swc/releases) - [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md) - [Commits](https://github.com/swc-project/swc/commits/v1.15.41/packages/core) --- updated-dependencies: - dependency-name: "@swc/core" dependency-version: 1.15.41 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump nodemailer from 8.0.10 to 8.0.11 (#3086) Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.10 to 8.0.11. - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](nodemailer/nodemailer@v8.0.10...v8.0.11) --- updated-dependencies: - dependency-name: nodemailer dependency-version: 8.0.11 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump rolldown from 1.1.0 to 1.1.1 (#3087) Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.1.0 to 1.1.1. - [Release notes](https://github.com/rolldown/rolldown/releases) - [Changelog](https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md) - [Commits](https://github.com/rolldown/rolldown/commits/v1.1.1/packages/rolldown) --- updated-dependencies: - dependency-name: rolldown dependency-version: 1.1.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump prettier from 3.8.3 to 3.8.4 (#3088) Bumps [prettier](https://github.com/prettier/prettier) from 3.8.3 to 3.8.4. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](prettier/prettier@3.8.3...3.8.4) --- updated-dependencies: - dependency-name: prettier dependency-version: 3.8.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @types/node from 22.19.20 to 22.19.21 (#3089) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.20 to 22.19.21. - [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.21 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @typescript-eslint/parser from 8.60.1 to 8.61.0 (#3090) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.60.1 to 8.61.0. - [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.61.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.61.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump turbo from 2.9.16 to 2.9.18 (#3092) Bumps [turbo](https://github.com/vercel/turborepo) from 2.9.16 to 2.9.18. - [Release notes](https://github.com/vercel/turborepo/releases) - [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md) - [Commits](vercel/turborepo@v2.9.16...v2.9.18) --- updated-dependencies: - dependency-name: turbo dependency-version: 2.9.18 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump sharp from 0.34.5 to 0.35.1 (#3093) Bumps [sharp](https://github.com/lovell/sharp) from 0.34.5 to 0.35.1. - [Release notes](https://github.com/lovell/sharp/releases) - [Commits](lovell/sharp@v0.34.5...v0.35.1) --- updated-dependencies: - dependency-name: sharp dependency-version: 0.35.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps-dev): bump @typescript-eslint/eslint-plugin (#3091) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.60.1 to 8.61.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.61.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix(collections): self-heal empty Plex collections that reject every add (#3094) * fix(collections): self-heal empty Plex collections that reject every add When a Plex collection record goes stale/corrupt it rejects all item adds with 400 while reads still succeed. Since #3001 collections are created empty, so such a record sticks forever: shared collections were never deleted (#2766), every run retried the same adds, and the empty collection stayed invisible in Plex while 'added' notifications kept firing. - Restore the delete-and-recreate heal (f0dcea7) for this case: when a media server add attempt is rejected for every item and a live read confirms the automatic collection is still empty, delete it and clear the link so the next pass recreates it fresh. Plex-only, matching the existing empty-collection cleanup gate. Shared collections heal too: an empty collection holds nothing a sibling rule group could lose. - Guard against churn: a collection heals once per process; a second total rejection without an accepted add in between logs an error instead of looping delete/recreate every run. - Emit CollectionMedia_Added only for items the media server accepted, so failed adds no longer notify as successes. - Unwrap the lib/plexApi error wrapper in buildCollectionMutationFailure so Plex's 400 response body (the rejection reason) reaches the logs. - Drop a duplicated reconcileSharedManualCollectionState call. * fix(collections): only notify added media whose local membership was persisted * test(collections): pin stale-link clearing that sibling heal recovery relies on * fix(radarr): treat already-excluded 400 as success when adding import-list exclusions (#3084) (#3096) Since Radarr v5.26.2, RestController.OnActionExecuting unpacks and validates IEnumerable bodies, so POST /exclusions/bulk now runs the uniqueness validator and returns HTTP 400 ("This exclusion has already been added") on a re-add — before the request reaches Radarr's server-side de-dup. That failed the whole UNMONITOR/UNMONITOR_DELETE_ALL action on every re-run, so items never left the collection (the singular endpoint always validated, so neither avoids it). - Adding the exclusion is best-effort; an already-excluded 400 means the goal is already met, so treat it as success instead of failing the action whose unmonitor/delete already ran. Other failures still fail. - Keep the bulk endpoint (server de-dup where reachable, single-movie array). - Route through the shared post() client via a new opt-in { rethrow } so the caller can read the HTTP status, keeping the request on the one HTTP client the rest of servarr uses. - Make the fake-radarr dev mock 400 on a duplicate for both endpoints, matching Radarr v5.26.2+. * fix(emby): create collections with an initial item to avoid HTTP 500 (#3075) (#3097) Emby's create-collection endpoint throws HTTP 500 ("Sequence contains no elements" in CollectionManager) when asked to create an empty collection under a library folder. Since #3001 (v3.13) switched all servers to create-empty then batch-add (to avoid a 414 from seeding every id), Emby rejected every create, so no rule action could complete and nothing was deleted. - Add optional initialItemId to CreateCollectionParams; the Emby adapter sends it as Ids on create so the collection is created with one member. Plex and Jellyfin create empty and ignore it. - One id keeps the create request well under the URL length limit that the all-ids create hit (#3001); the rest are added via addBatchToCollection (re-adding the seed there is an idempotent no-op). - fake-emby mock now 500s on an empty create, matching the real server. * fix(collections): skip remote create for empty collections (Emby) (#3075) (#3098) createCollectionWithChildren is reachable via POST /api/collections with no media (the body's media is optional), and it always created the remote collection. With no items that means an empty remote create — pointless on every server and a hard HTTP 500 on Emby, the same failure #3075 fixed for the item-bearing paths, just through a narrower route. - Create the DB row only when there are no items; the remote collection is created lazily on the first add, which seeds it with an item. Server-agnostic (no per-server branching in the shared layer). - Tests: assert the singular initialItemId seed is passed on create (the prior assertions guarded the removed plural initialItemIds, so they passed vacuously), refresh the stale "create empty" comments, and add an empty-media case asserting no remote create. * fix(radarr): only swallow the "already excluded" exclusion 400 (#3084) (#3099) The exclusion add treated every HTTP 400 from /exclusions/bulk as "already excluded". Radarr's validator also enforces non-empty tmdbId/title and a non-negative year, so a non-duplicate validation 400 was misreported as success — leaving the movie unmonitored/deleted but not actually import-excluded. Inspect the 400 body and only treat the uniqueness failure ("This exclusion has already been added") as success; any other validation 400 stays a failure and is surfaced. * fix(metadata): resolve movie/show ids from the item, not its parent (#3065) (#3100) getHierarchyResolutionItem walked up to item.grandparentId ?? item.parentId for any item with a parent. That's right for episodes/seasons (resolve up to the show) but wrong for movies: on Emby/Jellyfin a movie's parentId points at an id-less library/container folder, so the movie's own provider ids were discarded → 0 lookup candidates → every Radarr movie action failed with "Couldn't resolve any supported external IDs". The matching path was unaffected, so collections filled but no action could run. Switch on item.type (never parentId presence), matching resolveShowIdsForImage: only episodes/seasons resolve from a parent; movies and shows use their own ids. Plex is unchanged (its top-level movies already had no parent). * test(overlays): raise timeout for sharp-based render tests to avoid CI flake (#3101) The OverlayRenderService render tests do real sharp image processing and TrueType font loading; the native cold-start can exceed Jest's 5s default on a slow/cold CI runner (the whole suite is ~1.5s locally), intermittently failing with "Exceeded timeout of 5000 ms". Raise the suite timeout to 15s. * fix(rules): sw_lastWatched returns null for never-watched shows on Plex (#3083) (#3102) The Plex sw_lastWatched getter ("Newest episode view date") guarded the date branch with a truthy check on watchHistory. getWatchHistory returns [] for a confirmed-empty history (it throws on a real outage), and [] is truthy — so for a never-watched show it read viewedAt off watchHistory[0] (undefined) and threw TypeError. The outer catch surfaced that as undefined, which the comparator treats as a transient error, so the executor preserved the item — never-watched shows got stuck in collections across runs. Guard on length so an empty history takes the existing null branch ("never watched", confirmed absent), per the getter contract (null = absent, undefined = error). Emby/Jellyfin already length-guard this. Adds Plex coverage for empty → null, populated → newest date, and outage → undefined. * docs: update README feature wording --------- Signed-off-by: dependabot[bot] <[email protected]> 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 <[email protected]>
Contributor
|
🎉 This PR is included in version 3.15.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
doonga
pushed a commit
to greyrock-labs/home-ops
that referenced
this pull request
Jun 17, 2026
…➔ 3.15.1) (#316) This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | patch | `3.15.0` → `3.15.1` | --- ### Release Notes <details> <summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary> ### [`v3.15.1`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3151-2026-06-17) [Compare Source](Maintainerr/Maintainerr@v3.15.0...v3.15.1) #### Highlights - Fixed Plex `sw_lastWatched` getter to correctly return `null` for shows never watched, resolving a `TypeError` when accessing empty watch history ([#​3102](Maintainerr/Maintainerr#3102)). - Fixed Emby collection creation to include an initial item, avoiding HTTP 500 errors for empty collections ([#​3097](Maintainerr/Maintainerr#3097)). - Fixed Radarr import-list exclusions to correctly handle "already excluded" HTTP 400 responses as success ([#​3096](Maintainerr/Maintainerr#3096), [#​3099](Maintainerr/Maintainerr#3099)). #### Fixes - Fixed Plex `sw_lastWatched` getter to correctly return `null` for shows never watched, resolving a `TypeError` when accessing empty watch history ([#​3102](Maintainerr/Maintainerr#3102)). - Fixed Emby collection creation to include an initial item, avoiding HTTP 500 errors for empty collections ([#​3097](Maintainerr/Maintainerr#3097)). - Fixed Emby collection creation to skip remote creation for empty collections, preventing HTTP 500 errors ([#​3098](Maintainerr/Maintainerr#3098)). - Fixed Radarr import-list exclusions to correctly handle "already excluded" HTTP 400 responses as success ([#​3096](Maintainerr/Maintainerr#3096), [#​3099](Maintainerr/Maintainerr#3099)). - Fixed Radarr exclusion handling to avoid misreporting non-duplicate validation errors as success ([#​3099](Maintainerr/Maintainerr#3099)). - Fixed Plex collections to self-heal when stale/corrupt records reject every item add ([#​3094](Maintainerr/Maintainerr#3094)). - Fixed metadata resolution for movie/show IDs to correctly resolve from the item itself instead of its parent ([#​3100](Maintainerr/Maintainerr#3100)). #### Performance - Increased timeout for `OverlayRenderService` render tests to avoid CI flakiness during sharp-based image processing ([#​3101](Maintainerr/Maintainerr#3101)). #### Internal - Updated README feature wording. #### Dependencies - 23 dependency updates, including nodemailer, [@​typescript-eslint/parser](https://github.com/typescript-eslint/parser), axios, [@​nestjs/typeorm](https://github.com/nestjs/typeorm), sharp, and semantic-release. </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMjQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIyNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19--> Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/316
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 #3065.
On Emby, every movie rule action failed with
Couldn't resolve any supported external IDs for movie with media server ID <id>, even though the Emby item has TMDB/IMDb ids. Matching worked (collections filled), but the action could never resolve a TMDB id, so nothing could be unmonitored/deleted.getHierarchyResolutionItemresolved ids fromitem.grandparentId ?? item.parentIdfor any item with a parent. That's correct for episodes/seasons (resolve up to the show), but on Emby/Jellyfin a movie'sparentIdpoints at an id-less library/container folder — so the movie's own provider ids were discarded → 0 lookup candidates → theRadarrActionHandlerempty-candidates branch.item.type(neverparentIdpresence), matching the existing correct pattern inresolveShowIdsForImage: only episodes/seasons resolve from a parent; movies and shows use their own ids.Verified end-to-end against a mock Emby reproducing the reporter's exact shape (movie with tmdb + id-less parent container): the Radarr UNMONITOR action now resolves the tmdb id from the movie and completes, instead of "Couldn't resolve…".