From b904730b9c43d770319e0b501f63a0c125054277 Mon Sep 17 00:00:00 2001 From: enoch85 Date: Sun, 9 Aug 2026 07:25:03 +0000 Subject: [PATCH] docs: cover the bulk media actions rework and Tracearr server binding Maintainerr#3453 reworked the Add / Remove Media modal, and Maintainerr#3446 plus Maintainerr#3450 made the Tracearr server choice automatic and verified. Also adds AGENTS.md with the standing rules for docs drift PRs, and a thin Copilot router pointing at it. --- .github/copilot-instructions.md | 11 ++++ AGENTS.md | 54 +++++++++++++++++++ docs/API.md | 8 ++- docs/Collections.md | 40 +++++++------- docs/Configuration.md | 18 +++++-- .../openapi-spec/maintainerr_api_specs.yaml | 13 ++++- 6 files changed, 116 insertions(+), 28 deletions(-) create mode 100644 .github/copilot-instructions.md create mode 100644 AGENTS.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..5aede6665 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,11 @@ +# Copilot instructions + +Read [`AGENTS.md`](../AGENTS.md) at the repo root before making any change. It +holds the repo layout, the writing style, and the hard rules for opening a docs +drift PR. + +Those drift rules are not optional: work from local clones, read files and +commit diffs end to end, confirm every claim against the upstream commit, skip +what is already documented, keep the edits minimal and restricted to `docs/` and +`static/openapi-spec/`, and structure the PR description as "What was added" +plus "Already covered by prior PRs". diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f5672e41c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,54 @@ +# Agent instructions + +This repo is the Docusaurus site for Maintainerr. The code it documents lives in +[Maintainerr/Maintainerr](https://github.com/Maintainerr/Maintainerr). + +## Repo layout + +| Path | What it holds | +| ---------------------- | ------------------------------------------------------------------ | +| `docs/` | The Next (unreleased) docs. Edits for an unreleased change go here | +| `versioned_docs/` | Frozen snapshots of released versions. Do not edit by hand | +| `static/openapi-spec/` | The bundled OpenAPI YAML | +| `blog/`, `src/` | Blog posts and site theme | + +Run `npm run format` before committing, and `npm run build` to check the site +still builds and no link broke. + +## Docs drift PRs + +A bot opens a "Docs drift report" issue that compares the last release to +Maintainerr's `HEAD`. These rules are hard rules when you open the PR for one. + +- **Work from local clones, never from github.com web views.** Web views can be + cached or truncated. Clone `Maintainerr/Maintainerr` and + `Maintainerr/Maintainerr_docs` before anything else. +- **Read complete files, not excerpts.** Coverage you would otherwise miss often + sits past the first screenful. Read each doc file and each commit diff end to + end. +- **The upstream commits and diffs are the source of truth.** Use the issue's + prose summary as guidance, but confirm every claim against the local clone + with `git show ` before editing. +- **Skip what is already documented.** Read the current `main` of this repo and + the most recent merged docs PRs first. If something is already covered, do not + document it again. +- **Minimal edits only.** Make only the doc updates still missing for the Next + release. No speculative additions and no broad rewrites. +- **Doc-only changes.** Restrict edits to `docs/` and `static/openapi-spec/`. Do + not touch sidebars, config, or unrelated assets unless a doc edit strictly + requires it. +- **Keep the PR tied to the code diff**, not to the issue summary alone. +- **Structure the PR description** in two parts: + 1. **What was added**, one short bullet per doc edit, citing the upstream + commit or PR. + 2. **Already covered by prior PRs**, listing anything in this drift that an + earlier merged docs PR already documented, so reviewers can confirm it was + skipped on purpose. + +## Writing style + +- Plain English, kept short. Describe how things work today, not what changed + since some version. +- Plain hyphens only. No em dashes or en dashes. +- Always "Seerr", never Overseerr or Jellyseerr. +- No real media titles in examples. diff --git a/docs/API.md b/docs/API.md index f32fe0f01..87b95c2dc 100644 --- a/docs/API.md +++ b/docs/API.md @@ -93,6 +93,8 @@ The 250 limits one request, not how much a user can select. The web UI sends 25 If some items fail, the rest still go through. Both endpoints answer `{ results: [{ mediaId, code, message? }] }`, where `code` is `1` for success and `0` for failure, with `message` explaining a failure. A request is rejected outright with `400` only when it is empty, holds more than 250 ids, or asks to add without naming a collection. +Adding an exclusion takes the collection and rule execution lock, on both `POST /api/rules/exclusions/bulk` and `POST /api/rules/exclusion`, so it cannot land while a run is acting on the same item. Both wait up to 30 seconds for a running job and answer `409` if it is still going. Removing an exclusion takes no lock. + ### Metadata | Endpoint | Purpose | @@ -144,12 +146,14 @@ If some items fail, the rest still go through. Both endpoints answer `{ results: | Endpoint | Purpose | | ------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| `GET /api/settings/tracearr` | Read the saved Tracearr base URL, API key, and selected Tracearr server | -| `POST /api/settings/test/tracearr` | Test a Tracearr URL, API key, and server selection before saving | +| `GET /api/settings/tracearr` | Read the saved Tracearr base URL, API key, and bound Tracearr server | +| `POST /api/settings/test/tracearr` | Test a Tracearr URL and API key before saving | | `POST /api/settings/tracearr/servers` | Discover the Tracearr servers available for a URL and API key so the settings UI can populate the selector | | `POST /api/settings/tracearr` | Save the Tracearr connection settings | | `DELETE /api/settings/tracearr` | Remove the saved Tracearr connection settings | +`server_id` is optional on `POST /api/settings/tracearr`. Leave it out and Maintainerr picks the Tracearr server that tracks your media server; send it only when Tracearr has several servers of that type. Either way the save is refused if no server matches, or if the one you sent tracks a different media server. + ### Overlays | Endpoint | Purpose | diff --git a/docs/Collections.md b/docs/Collections.md index c8437f28b..5977a3bd4 100644 --- a/docs/Collections.md +++ b/docs/Collections.md @@ -13,7 +13,7 @@ When the specified amount of days that media must live in the collection is pass Collection handling is a batch process that runs every 12 hours. You can manually trigger it with the `Handle Collections` button on the Collections page. This runs each collection's configured action (such as delete, unmonitor, or do nothing), but it does not remove items from collections on its own. - If a rule-managed item is still in the collection but its most recent rule evaluation failed, Maintainerr skips the automatic handling action for that item until the rule can be evaluated cleanly again. Manually added items are still eligible for handling. + If a rule-managed item is still in the collection but its most recent rule evaluation failed, Maintainerr skips the automatic handling action for that item until the rule can be evaluated cleanly again. Manually added items are still eligible for handling, unless an exclusion covers them. That means an exclusion for this collection or a global one, and excluding a show or season covers everything inside it. When a delete-style action removes files, Maintainerr also prunes that media from any other Maintainerr-managed collections that still list it. This prevents already-deleted items from being re-processed while Jellyfin or Emby are still catching up on their next library scan. If eligible media is actively being streamed, Maintainerr defers it to the next collection-handler run instead of acting on it mid-playback. This is a best-effort snapshot taken once per run, so playback that starts later is only protected on the following pass. @@ -83,29 +83,27 @@ The collection-poster endpoints live under `/api/collections/:id`. ### The `Add / Remove Media` modal {#add-remove-media-modal} -Every manual collection and exclusion change runs through one modal. Click `Select items` in the action bar to enter selection mode, check the media you want, then click `Add/Exclude selected`. It is available in three places: +Every manual collection and exclusion change runs through one modal. Click `Select items` in the action bar to enter selection mode, check the media you want, then click `Add/Exclude selected`. It is available on the `Overview`, on a collection's `Media` tab, and on a collection's `Exclusions` tab. -| Page | Scope | -| ------------------------------- | ---------------------------------------------------------------------- | -| `Overview` | The selection can target any collection, or every collection at once | -| A collection's `Media` tab | Locked to that collection, so `Add to collection` is not offered there | -| A collection's `Exclusions` tab | Locked to that collection, so `Add exclusion` is not offered there | +All three offer the same actions. On a collection page the `Collection` list starts on that collection, and you can switch it to another one or to `All collections`. Pick an `Action` in the modal: -| Action | Effect | -| ----------------------------- | ---------------------------------------------------------------------------- | -| `Add to collection` | Add the selection to the collection you pick | -| `Remove from collection` | Remove the selection from the collection you pick | -| `Remove from all collections` | Remove the selection from every collection it is in | -| `Add exclusion` | Exclude the selection from one collection, or from all of them | -| `Remove exclusion` | Drop the selection's exclusions for one collection, or all of its exclusions | - -- The `Collection` list only offers collections from the library you are looking at. A show selection can target a show, season, or episode collection, a season selection a season or episode one, and a movie selection a movie collection. +| Action | Effect | +| ------------------------ | ---------------------------------------------------------------------------- | +| `Add to collection` | Add the selection to the collection you pick | +| `Remove from collection` | Remove the selection from the collection you pick, or from every collection | +| `Add exclusion` | Exclude the selection from one collection, or from all of them | +| `Remove exclusion` | Drop the selection's exclusions for one collection, or all of its exclusions | + +- Every action except `Add to collection` can also target `All collections`. An add needs one collection to add to. +- A show selection can target a show, season, or episode collection, a season selection a season or episode one, and a movie selection a movie collection. +- On Plex, a collection only holds items from its own library, so the `Collection` list shows that library's collections and an add from another library is refused. Jellyfin and Emby collections take any library, so all of them are offered. +- You can add search results to a collection too. A search covers every library, so Maintainerr takes the library from the items you picked. On Plex, if they are not all from one library, only the exclusion actions are left. - If your selection mixes media types, no collection can take it, so only `Add exclusion` and `Remove exclusion` are offered. -- Select exactly one show and you can narrow the action to specific `Seasons`, and from there to specific `Episodes`. The show stays the entry point, so you can still undo the change through the show later. Narrowing is not offered on a show collection's own tab, where the action applies to the show itself. +- Select exactly one show and you can narrow the action to specific `Seasons`, and from there to specific `Episodes`. The show stays the entry point, so you can still undo the change through the show later. - There is no cap on how many items you can select. Maintainerr sends them to the server 25 at a time, so a large selection becomes several requests rather than one. -- Maintainerr reports a result for each item. If some fail, it tells you which ones and still handles the rest. +- Maintainerr reports a result for each item. If some fail, it tells you which ones and why, and still handles the rest. - Anything that affects every collection asks you to confirm first. :::warning @@ -118,7 +116,7 @@ Select the media on the `Overview` page and choose `Add to collection`. Manually ### Removing -Choose `Remove from collection` for one collection, or `Remove from all collections` to clear the selection out of every collection it is in. +Choose `Remove from collection`, then pick a collection, or `All collections` to take the selection out of every collection it is in. To remove a single item, open the collection from the `Collections` page and use the `Remove` button on its card. @@ -134,6 +132,10 @@ Use `Postpone` from an item's collection details to delay its scheduled action b Select the media and choose `Add exclusion`. Pick a single collection to scope the exclusion to that collection's rule group, or `All collections` to exclude it everywhere. `Remove exclusion` works the same way. Excluding a show or season also covers everything it contains. +Excluding also removes the selection from the collections it covers: the one you picked, or every collection when you exclude everywhere. The confirmation says so first. + +If a collection or rule run is busy, an exclusion waits up to 30 seconds for it to finish, so it cannot land while the run is acting on the same item. If the run is still going after that, Maintainerr asks you to try again. Removing an exclusion never waits. + An exclusion tied to one specific collection / rule group only applies there. Other rule groups can still add or act on the same item unless you exclude it globally. Adding a global exclusion removes that item's existing collection-specific exclusions, and the confirmation dialog lists which ones it will drop. If you later remove the global exclusion, those narrower exclusions are not restored automatically. diff --git a/docs/Configuration.md b/docs/Configuration.md index 4c323c4a7..0f39c3e02 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -271,8 +271,16 @@ Tracearr's configuration is required to use its watch history parameters in rule Tracearr does not backfill media-server history. Only watch events recorded by Tracearr after it was connected to your media server are available to rules. ::: -| Setting | Description | -| --------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -| URL | The base URL of your Tracearr instance, such as `http://localhost:3000` or `https://tracearr.example.com` | -| API key | The API key from Tracearr settings | -| Tracearr server | The Tracearr server to use. Enter a valid URL and API key first, then select from the servers discovered from that Tracearr instance. | +| Setting | Description | +| --------------- | --------------------------------------------------------------------------------------------------------------------- | +| URL | The base URL of your Tracearr instance, such as `http://localhost:3000` or `https://tracearr.example.com` | +| API key | The API key from Tracearr, under `Settings`, `General`, `API Key` | +| Tracearr server | Only shown when Tracearr has more than one server of your media server's type. Otherwise Maintainerr picks it for you | + +Maintainerr uses the Tracearr server that tracks your media server. Watch history belongs to one media server, so the wrong Tracearr server matches nothing and every Tracearr rule reads as unwatched. + +- Saving is refused if no Tracearr server matches, or if the one you picked tracks a different media server. Add your media server in Tracearr and let it sync a library first. +- Point Maintainerr at a different media server, even another one of the same type, and the choice is checked again and cleared if it no longer fits. +- Every run checks the server before reading its history. If that fails, Tracearr rule values are unavailable for that run and the log says why, instead of the rules reading as watched by nobody. + +`Test Connection` checks the URL and the API key. The server is checked when you save. diff --git a/static/openapi-spec/maintainerr_api_specs.yaml b/static/openapi-spec/maintainerr_api_specs.yaml index 17cdab517..18193d637 100644 --- a/static/openapi-spec/maintainerr_api_specs.yaml +++ b/static/openapi-spec/maintainerr_api_specs.yaml @@ -519,7 +519,7 @@ paths: $ref: '#/components/schemas/TracearrSetting' responses: '201': - description: Tests Tracearr connectivity with a selected server. + description: Tests Tracearr connectivity with the supplied URL and API key. tags: - /settings /api/settings/tracearr/servers: @@ -1022,6 +1022,9 @@ paths: responses: '201': description: '' + '409': + description: >- + A collection or rule run held the execution lock for too long. tags: - /rules /api/rules/{id}: @@ -1140,6 +1143,9 @@ paths: description: >- Rejected without processing: empty, more than 250 media ids, or a context sent with more than one id. + '409': + description: >- + A collection or rule run held the execution lock for too long. tags: - /rules /api/rules/exclusions/{plexId}: @@ -2118,12 +2124,15 @@ components: api_key: type: string server_id: + description: >- + Omit it to let Maintainerr pick the Tracearr server that tracks the + configured media server. Send it only when Tracearr has several + servers of that type. type: string format: uuid required: - url - api_key - - server_id TracearrSettingForm: type: object description: >-