feat: write Sonarr/Radarr tags from Maintainerr collections & exclusions#3162
Merged
Conversation
Adds the write side of three feature requests — Maintainerr now applies *arr tags as a side effect of its own state, via a shared best-effort `ServarrTagService`: - https://features.maintainerr.info/posts/8 - https://features.maintainerr.info/posts/125 - https://features.maintainerr.info/posts/81 Two triggers share the plumbing: - Membership (A): while a `tagInArr` rule group's collection holds an item, the matching Radarr movie / Sonarr series carries a tag named after the group. Driven from the executor's per-run membership deltas, and reconciled on the enable/disable toggle and on rule-group deletion. Off the per-item eval loop. - Exclusion (B): excluding an item applies a protective tag (default "dnd") to the matching *arr entity — covering both collection-scoped and global exclusions, across every exclude/un-exclude path. Radarr and Sonarr are configured independently (enable + label + opt-in removal-on-unexclude). Removal is conservative: opt-in, only the configured label, and never while another exclusion still protects the item. Details: - Shared helpers `ensureTag` (race-tolerant), `setMovieTags`/`setSeriesTags` (`movie|series/editor`, applyTags add/remove only — never replace). - Tag labels normalized to the *arr charset `^[a-z0-9-]+$` — a single source of truth in @maintainerr/contracts, validated up front in the UI so the saved label is exactly the tag the instance receives (Radarr/Sonarr 400 otherwise). - `Collection.tagInArr` + 6 per-service settings columns via a generated, end-to-end-verified migration; item→*arr resolution reuses the action-handler path and honours the #3125 transient/null contract. - UI: a conditional "Tag this content" checkbox in the rule modal and per-service exclusion-tag settings on the Radarr/Sonarr pages. - Dev mocks: fake-radarr gains tag + movie/editor (+ charset 400); new fake-sonarr mirrors it.
609c0f6 to
f43e1d0
Compare
Adds the write side of three feature requests — Maintainerr now applies *arr tags as a side effect of its own state, via a shared best-effort `ServarrTagService`: - https://features.maintainerr.info/posts/8 - https://features.maintainerr.info/posts/125 - https://features.maintainerr.info/posts/81 Two triggers share the plumbing: - Membership (A): while a `tagInArr` rule group's collection holds an item, the matching Radarr movie / Sonarr series carries a tag named after the group. Driven from the executor's per-run membership deltas, and reconciled on the enable/disable toggle and on rule-group deletion. Off the per-item eval loop. - Exclusion (B): excluding an item applies a protective tag (default "dnd") to the matching *arr entity — covering both collection-scoped and global exclusions, across every exclude/un-exclude path. Radarr and Sonarr are configured independently (enable + label + opt-in removal-on-unexclude). Removal is conservative: opt-in, only the configured label, and never while another exclusion still protects the item. Details: - Shared helpers `ensureTag` (race-tolerant), `setMovieTags`/`setSeriesTags` (`movie|series/editor`, applyTags add/remove only — never replace). - Tag labels normalized to the *arr charset `^[a-z0-9-]+$` — a single source of truth in @maintainerr/contracts, validated up front in the UI so the saved label is exactly the tag the instance receives (Radarr/Sonarr 400 otherwise). - `Collection.tagInArr` + 6 per-service settings columns via a generated, end-to-end-verified migration; item→*arr resolution reuses the action-handler path and honours the #3125 transient/null contract. - UI: a conditional "Tag this content" checkbox in the rule modal and per-service exclusion-tag settings on the Radarr/Sonarr pages. - Dev mocks: `fake-radarr.mjs` gains `/tag` + `PUT /movie/editor` (+ the charset 400).
4142f49 to
2c5bcb1
Compare
- await the tagInArr toggle reconcile so the backfill completes before save returns - clarify invalid exclusion-tag message (no leading/trailing/repeated hyphens) - document multi-instance untag limitation and the strict 'show' AddModal gate
Matching columns can be reproduced by a hand-written ALTER TABLE ADD COLUMN; only migration:generate emits the create-temporary-table rebuild, so assert its presence as a stronger hand-waived-migration guard.
maintainerr-automation Bot
added a commit
that referenced
this pull request
Jun 25, 2026
* build(deps-dev): bump @eslint-react/eslint-plugin from 5.9.0 to 5.9.1 (#3148) Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) from 5.9.0 to 5.9.1. - [Release notes](https://github.com/Rel1cx/eslint-react/releases) - [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md) - [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.9.1/plugins/eslint-plugin) --- updated-dependencies: - dependency-name: "@eslint-react/eslint-plugin" dependency-version: 5.9.1 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 react-hook-form from 7.79.0 to 7.80.0 (#3149) Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.79.0 to 7.80.0. - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](react-hook-form/react-hook-form@v7.79.0...v7.80.0) --- updated-dependencies: - dependency-name: react-hook-form dependency-version: 7.80.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> * build(deps-dev): bump knip from 6.17.1 to 6.17.2 (#3151) Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.17.1 to 6.17.2. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.17.2/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.17.2 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 @eslint-react/eslint-plugin from 5.9.1 to 5.9.2 (#3156) Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) from 5.9.1 to 5.9.2. - [Release notes](https://github.com/Rel1cx/eslint-react/releases) - [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md) - [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.9.2/plugins/eslint-plugin) --- updated-dependencies: - dependency-name: "@eslint-react/eslint-plugin" dependency-version: 5.9.2 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 @vitejs/plugin-react from 6.0.2 to 6.0.3 (#3157) Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 6.0.2 to 6.0.3. - [Release notes](https://github.com/vitejs/vite-plugin-react/releases) - [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.0.3/packages/plugin-react) --- updated-dependencies: - dependency-name: "@vitejs/plugin-react" dependency-version: 6.0.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> * build(deps-dev): bump vite from 8.0.16 to 8.1.0 (#3158) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.16 to 8.1.0. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/create-vite@8.1.0/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.1.0 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 @typescript-eslint/eslint-plugin (#3159) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.61.1 to 8.62.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.62.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.62.0 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 @typescript-eslint/parser from 8.61.1 to 8.62.0 (#3160) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.61.1 to 8.62.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.62.0/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.62.0 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 globals from 17.6.0 to 17.7.0 (#3161) Bumps [globals](https://github.com/sindresorhus/globals) from 17.6.0 to 17.7.0. - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](sindresorhus/globals@v17.6.0...v17.7.0) --- updated-dependencies: - dependency-name: globals dependency-version: 17.7.0 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 knip from 6.17.2 to 6.18.0 (#3155) Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.17.2 to 6.18.0. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.18.0/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.18.0 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): bump actions/checkout from 6 to 7 (#3150) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](actions/checkout@v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: enoch85 <mailto@danielhansson.nu> * build(deps): bump @tanstack/react-query from 5.101.0 to 5.101.1 (#3163) Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.101.0 to 5.101.1. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.101.1/packages/react-query) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.101.1 dependency-type: direct:production 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 from 8.61.1 to 8.62.0 (#3164) Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.61.1 to 8.62.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-version: 8.62.0 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): bump axios from 1.18.0 to 1.18.1 (#3165) Bumps [axios](https://github.com/axios/axios) from 1.18.0 to 1.18.1. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](axios/axios@v1.18.0...v1.18.1) --- updated-dependencies: - dependency-name: axios dependency-version: 1.18.1 dependency-type: direct:production 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 knip from 6.18.0 to 6.20.0 (#3166) Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.18.0 to 6.20.0. - [Release notes](https://github.com/webpro-nl/knip/releases) - [Commits](https://github.com/webpro-nl/knip/commits/knip@6.20.0/packages/knip) --- updated-dependencies: - dependency-name: knip dependency-version: 6.20.0 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 @tanstack/eslint-plugin-query (#3167) Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.101.0 to 5.101.1. - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/eslint-plugin-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/eslint-plugin-query@5.101.1/packages/eslint-plugin-query) --- updated-dependencies: - dependency-name: "@tanstack/eslint-plugin-query" dependency-version: 5.101.1 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> * feat: write Sonarr/Radarr tags from Maintainerr collections & exclusions (#3162) * feat: write Sonarr/Radarr tags from Maintainerr collections & exclusions Adds the write side of three feature requests — Maintainerr now applies *arr tags as a side effect of its own state, via a shared best-effort `ServarrTagService`: - https://features.maintainerr.info/posts/8 - https://features.maintainerr.info/posts/125 - https://features.maintainerr.info/posts/81 Two triggers share the plumbing: - Membership (A): while a `tagInArr` rule group's collection holds an item, the matching Radarr movie / Sonarr series carries a tag named after the group. Driven from the executor's per-run membership deltas, and reconciled on the enable/disable toggle and on rule-group deletion. Off the per-item eval loop. - Exclusion (B): excluding an item applies a protective tag (default "dnd") to the matching *arr entity — covering both collection-scoped and global exclusions, across every exclude/un-exclude path. Radarr and Sonarr are configured independently (enable + label + opt-in removal-on-unexclude). Removal is conservative: opt-in, only the configured label, and never while another exclusion still protects the item. Details: - Shared helpers `ensureTag` (race-tolerant), `setMovieTags`/`setSeriesTags` (`movie|series/editor`, applyTags add/remove only — never replace). - Tag labels normalized to the *arr charset `^[a-z0-9-]+$` — a single source of truth in @maintainerr/contracts, validated up front in the UI so the saved label is exactly the tag the instance receives (Radarr/Sonarr 400 otherwise). - `Collection.tagInArr` + 6 per-service settings columns via a generated, end-to-end-verified migration; item→*arr resolution reuses the action-handler path and honours the #3125 transient/null contract. - UI: a conditional "Tag this content" checkbox in the rule modal and per-service exclusion-tag settings on the Radarr/Sonarr pages. - Dev mocks: fake-radarr gains tag + movie/editor (+ charset 400); new fake-sonarr mirrors it. * refactor(arr-tagging): address review feedback - await the tagInArr toggle reconcile so the backfill completes before save returns - clarify invalid exclusion-tag message (no leading/trailing/repeated hyphens) - document multi-instance untag limitation and the strict 'show' AddModal gate * test(migrations): assert generated SQLite temporary-table rebuild Matching columns can be reproduced by a hand-written ALTER TABLE ADD COLUMN; only migration:generate emits the create-temporary-table rebuild, so assert its presence as a stronger hand-waived-migration guard. * fix: clean collection logs without TypeORMError on undefined ruleGroup (#3147) (#3168) * fix: clean collection logs without TypeORMError on undefined ruleGroup (#3147) * chore: format * fix: don't mutate shared Sonarr season array in part_of_latest_season (#3153) (#3169) getLastAiredOrCurrentlyAiringSeason reversed showResponse.seasons in place. The series is memoized per run via ArrLookupCache, so every season of a show shares one seasons array; a full run resolves them concurrently (RULE_EVALUATION_CONCURRENCY) and the in-place reverse corrupted season order mid-iteration, collapsing the latest aired season to an earlier one. part_of_latest_season then returned false for the latest season. Test Media evaluates a single item with no shared cache, so it stayed correct and the two paths disagreed. Reverse a copy instead, and correct the return type to SonarrSeason | undefined. Also adds dev tooling to exercise the Sonarr season path end-to-end: tools/dev/fake-sonarr.mjs, a four-season show in fake-plex.mjs, and a seeded part_of_latest_season == False season group. * build(deps-dev): bump @swc/core from 1.15.41 to 1.15.43 (#3171) Bumps [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) from 1.15.41 to 1.15.43. - [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.43/packages/core) --- updated-dependencies: - dependency-name: "@swc/core" dependency-version: 1.15.43 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 turbo from 2.9.18 to 2.10.0 (#3172) Bumps [turbo](https://github.com/vercel/turborepo) from 2.9.18 to 2.10.0. - [Release notes](https://github.com/vercel/turborepo/releases) - [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md) - [Commits](vercel/turborepo@v2.9.18...v2.10.0) --- updated-dependencies: - dependency-name: turbo dependency-version: 2.10.0 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 @eslint-react/eslint-plugin from 5.9.2 to 5.9.3 (#3173) Bumps [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react/tree/HEAD/plugins/eslint-plugin) from 5.9.2 to 5.9.3. - [Release notes](https://github.com/Rel1cx/eslint-react/releases) - [Changelog](https://github.com/Rel1cx/eslint-react/blob/main/CHANGELOG.md) - [Commits](https://github.com/Rel1cx/eslint-react/commits/v5.9.3/plugins/eslint-plugin) --- updated-dependencies: - dependency-name: "@eslint-react/eslint-plugin" dependency-version: 5.9.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> * build(deps): bump rolldown from 1.1.2 to 1.1.3 (#3174) Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.1.2 to 1.1.3. - [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.3/packages/rolldown) --- updated-dependencies: - dependency-name: rolldown dependency-version: 1.1.3 dependency-type: direct:production 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: bulk-prefetch Seerr requests for rule evaluation (#3152) (#3170) * fix: bulk-prefetch Seerr requests for rule evaluation (#3152) Rules seeded on a Seerr property (e.g. "Seerr - Requested") matched almost nothing on large libraries. The getter made a per-item GET /movie|/tv call per item; under a whole-library run those calls rate-limited, the getter returned a value the comparator skipped, and Seerr-seeded rules silently degraded to near-zero matches. - SeerrApiService now fetches all requests in one paginated /request sweep and builds a run-scoped index grouped by media.tmdbId, with in-flight dedup, cached in a dedicated non-persistent cache that is flushed (and rebuilt) per rule-group run. Page size is 100 (Seerr imposes no take cap and OFFSET re-scans), reducing round-trips. - SeerrGetterService reads that index for every request-derived property (isRequested, amountRequested, requestDate, addUser, approvalDate, mediaAddedAt) at movie and season/episode level. - A failed sweep yields undefined (transient — the comparator protects the item), never null; releaseDate keeps its per-item fallback. - Add tools/dev/fake-seerr.mjs and a shared scale-library fixture, and seed Seerr rule groups, for end-to-end testing. - Consolidate request-service naming to "Seerr" across code comments, UI strings and docs (the legacy /overseerr and /jellyseerr route aliases stay for backward compatibility). * fix: order Seerr requestDate oldest-first and drop no-op sort param (#3152) Review follow-up to #3152: - buildRequestIndex sorts each title's requests createdAt-ascending, so requestDate (and addUser/season order) returns the first request, matching the pre-#3152 getMovie ordering rather than the newest re-request. - Drop the `sort=added` query param: Seerr has no `added` sort (it falls back to request.id DESC); the index now normalises order itself. - getRequestsForMedia returns a deep copy (lodash cloneDeep) so callers cannot mutate the cached run-scoped index (useClones is off); cloneDeep over structuredClone so an unexpected non-cloneable value can't throw. - Seed the remaining Seerr property rule groups and add focused tests. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com> Co-authored-by: enoch85 <mailto@danielhansson.nu>
Contributor
|
🎉 This PR is included in version 3.16.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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.
Adds the write side of three feature requests — custom *arr tag for content, its duplicate tag based on collections/rules, and a
dndtag when excluded (the read side — matching rules on existing *arr tags — already shipped). Maintainerr now applies Radarr/Sonarr tags as a side effect of its own state, via a shared best-effortServarrTagService.What
tagInArrrule group's collection holds an item, the matching Radarr movie / Sonarr series carries a tag named after the group. Maintained off the executor's per-run membership deltas (not the per-item eval loop), and reconciled on the enable/disable toggle and on rule-group deletion — so existing members are backfilled on enable and stripped on disable/delete.dnd) to the matching *arr entity, across every exclude/un-exclude path (collection-scoped + global,POST+DELETE). A global exclusion resolves the single configured instance of the item's type. Radarr and Sonarr are configured independently (enable + label + opt-in removal-on-unexclude). Removal is conservative — opt-in, only ever the configured label, and never while another exclusion still protects the item.ensureTag(race-tolerant),setMovieTags/setSeriesTags(movie|series/editor,applyTagsadd/remove only — neverreplace).^[a-z0-9-]+$(single source of truth in@maintainerr/contracts, validated up front in the UI so the saved label is exactly the tag the instance receives). A rule group "Stale Movies" → tagstale-movies; Radarr/Sonarr 400 on anything else.Collection.tagInArr+ 6 per-service settings columns via a generated, end-to-end-verified migration. Item→*arr resolution reuses the action-handler path and honours the #3125 transient(undefined)/not-tracked(null) contract.fake-radarr.mjsgains/tag+PUT /movie/editor(+ the charset 400).Gated to movie+show (Sonarr has no per-season tag); season/episode collections are skipped with a debug log. No new
ServarrAction.Verification
yarn lint/check-types/test/buildgreen. 1796 server specs incl.ServarrTagService(every branch),ensureTag/setMovieTags/setSeriesTags, the exclusion hooks across all paths (scoped + global, conservative removal + shared-tag guard + null-type→collection-type fallback), the membership toggle reconcile (enable backfill, disable + crucial-change snapshot), delete-group cleanup, a 100k-item bounded-batch stress test, a generic in-memory migration matrix (whole chain applies + the new migration reproduces its entity columns exactly — a hand-waived/drifted migration fails — + the newest migration reverts), and Vitest+RTL UI (per-service toggle gating, payload, charset + normalize-stability validation). Migration verified end-to-end (clean DB →migration:run→migration:generate= "No changes").tagInArron a populated collection backfilled all 5 members indev-radarr(no rule re-run); disabling removed them. Confirmed in Radarr's own Settings → Tags (the group tag on 5 movies). Behavior B via the media-modal path: a global exclude applieddndto the resolved single instance; thePOSTremove un-exclude path stripped it. The rule-modal checkbox renders only for the matching *arr; the settings label input rejects hyphen-edge labels up front (no save).dev-radarr/dev-sonarr; confirms tmdb/tvdb resolution and the Emby/JellyfinparentIdmovie-classification gotcha (resolved byitem.type).parentIdbehaviour), covered by the cross-server resolution unit tests. The transient *arr-down case is unit-tested (the dev Docker socket is read-only). A delete action removes the tag with the *arr object (resolveArrId→null, no run error). Streamystats/Seerr/plex.tv enrichment degrade gracefully and can't be fully mocked.