Skip to content

feat: defer deletion of currently-playing media#3027

Merged
enoch85 merged 4 commits into
developmentfrom
feat/defer-currently-playing-media
Jun 4, 2026
Merged

feat: defer deletion of currently-playing media#3027
enoch85 merged 4 commits into
developmentfrom
feat/defer-currently-playing-media

Conversation

@enoch85

@enoch85 enoch85 commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Skips media that's currently being streamed so the collection handler doesn't act on it — chiefly delete it — out from under an active viewer. Implements feature #195.

At the start of each run the worker fetches the active-session set once and skips any eligible item that's playing. Skipped items stay eligible and are handled on the next run.

  • Plex: GET /status/sessions; Jellyfin/Emby: GET /Sessions (NowPlayingItem).
  • A playing item protects its episode, season, and show, so a collection tracking any of those levels is deferred.
  • Best-effort: the snapshot is read once per run (media that starts playing mid-run isn't protected until the next run), and an empty or failed lookup degrades to normal handling rather than blocking the run.
  • Defers all handling for in-use media, not only deletion — deferring the occasional non-destructive action (unmonitor, quality-profile change) is a deliberate simplification.

Also consolidates the axios retry config into a shared applyHttpRetry helper, adding retry parity to the Emby/Jellyfin clients.

Comment thread apps/server/src/modules/api/media-server/emby/emby-adapter.service.ts Dismissed
@enoch85

enoch85 commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator Author

/release-pr

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Released to maintainerr/maintainerr:pr-3027 🚀

Before the collection handler acts on eligible media, skip anything that is
currently being streamed so it isn't removed out from under an active viewer.
Skipped media stays eligible and is handled on the next run.

- Plex: GET /status/sessions
- Jellyfin/Emby: GET /Sessions (NowPlayingItem)

A playing item protects its episode, season, and show, so a collection
tracking any of those levels is deferred. Best-effort: the session snapshot is
read once per run and an empty or failed lookup just means "handle as usual".

Also consolidate the axios retry config into a shared applyHttpRetry helper,
adding retry parity to the Emby and Jellyfin clients.
@enoch85 enoch85 force-pushed the feat/defer-currently-playing-media branch from 5061390 to 6a0441b Compare June 3, 2026 17:56
@enoch85

enoch85 commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator Author

/release-pr

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Released to maintainerr/maintainerr:pr-3027 🚀

@gianfelicevincenzo

gianfelicevincenzo commented Jun 4, 2026

Copy link
Copy Markdown

I ran some tests. It seems to work. The movie in question didn't get erased while I was watching it. Obviously, I need to test it in a "real-world" environment to see even better. But for now, I think it's working fine! Great!

image

But I noticed something strange. To create the test, I had to set the value "Take action after days" to 0 in the rules settings. But when I go to Collections, I have see this value to "-8." Bug?

image

@enoch85

enoch85 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

@gianfelicevincenzo Can you please provide debug logs when you test?

Would help a alot! 🙏🏼

@gianfelicevincenzo

Copy link
Copy Markdown

maintainerr-2026-06-04.log

@enoch85

enoch85 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

Perfect, here's the evidence:

[maintainerr]  |  04/06/2026 16:33:11  [INFO] [CollectionWorkerService] Deferring 1 currently-playing media item(s) in collection 'Film presto non disponibili' to the next run

[maintainerr]  |  04/06/2026 16:33:11  [INFO] [CollectionWorkerService] Deferring 1 currently-playing media item(s) in collection 'Film presto non disponibili' to the next run

I will investigate further before merge, but thanks for providing that info!

@gianfelicevincenzo

Copy link
Copy Markdown

I think I found some more bugs...

image

How is it possible to have two default overlays? I also can't remove this property. But if you want, I can open another ticket for this.

@enoch85

enoch85 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

I think I found some more bugs...
image

How is it possible to have two default overlays? I also can't remove this property. But if you want, I can open another ticket for this.

Not a bug. They are for different things. https://docs.maintainerr.info/overlays/#templates

@gianfelicevincenzo

Copy link
Copy Markdown

Okay, no, because I'm way behind on the versions, so these are new to me. I said this because I can choose the default in the rules, and if I create one, I won't see it here.

image

@enoch85

enoch85 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

Okay, no, because I'm way behind on the versions, so these are new to me. I said this because I can choose the default in the rules, and if I create one, I won't see it here.
image

It depends one the rule. So different rules can have different posters. :) I suggests you ask on Discord, or read documentation. 👍

@gianfelicevincenzo

gianfelicevincenzo commented Jun 4, 2026

Copy link
Copy Markdown

Yes, you're right. Maybe I'm speaking too quickly. Maybe I'll study these new versions more closely. I hope I don't find anything wrong 😂. Anyway, I'll let you investigate that strange "-8" value further. Maybe I'll let you know if I find anything. Maybe, for now I'll stick with the old version.

PS: maybe if you can add a flag for checking like "Check if the movie is currently playing on plex" it would be better also for those people who don't care about this feature

Thanks!

@enoch85

enoch85 commented Jun 4, 2026

Copy link
Copy Markdown
Collaborator Author

Regarding the countdown. It's expected.

  • scheduledDeletion = addDate + deleteAfterDays. With deleteAfterDays = 0, the scheduled deletion is the add-date.
  • daysLeft = −8 ⟹ the scheduled deletion (= the add-date) is 8 days in the past ⟹ the item was added to the collection ~8 days ago and when changing delete to 0, it became -8 since it's then based on the add-date.

@enoch85 enoch85 merged commit 6135757 into development Jun 4, 2026
10 of 12 checks passed
@enoch85 enoch85 deleted the feat/defer-currently-playing-media branch June 4, 2026 17:28
maintainerr-automation Bot added a commit that referenced this pull request Jun 4, 2026
* docs: sync v3.13.0 changelog with release notes

* build(deps-dev): bump @typescript-eslint/eslint-plugin (#3032)

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.60.0 to 8.60.1.
- [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.60.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.60.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 axios from 1.16.1 to 1.17.0 (#3033)

Bumps [axios](https://github.com/axios/axios) from 1.16.1 to 1.17.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.16.1...v1.17.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.17.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): bump react-hook-form from 7.76.1 to 7.77.0 (#3035)

Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.76.1 to 7.77.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.76.1...v7.77.0)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.77.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 typescript-eslint from 8.60.0 to 8.60.1 (#3034)

Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.60.0 to 8.60.1.
- [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.60.1/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.60.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 @tanstack/react-query from 5.100.14 to 5.101.0 (#3036)

Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.100.14 to 5.101.0.
- [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.0/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.101.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): bump rolldown from 1.0.3 to 1.1.0 (#3037)

Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.0.3 to 1.1.0.
- [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.0/packages/rolldown)

---
updated-dependencies:
- dependency-name: rolldown
  dependency-version: 1.1.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): bump react-router-dom from 7.16.0 to 7.17.0 (#3038)

Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 7.16.0 to 7.17.0.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@7.17.0/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-version: 7.17.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): bump openpgp from 6.3.0 to 6.3.1 (#3039)

Bumps [openpgp](https://github.com/openpgpjs/openpgpjs) from 6.3.0 to 6.3.1.
- [Release notes](https://github.com/openpgpjs/openpgpjs/releases)
- [Commits](openpgpjs/openpgpjs@v6.3.0...v6.3.1)

---
updated-dependencies:
- dependency-name: openpgp
  dependency-version: 6.3.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>

* fix(collections): prune media that no longer exists on the media server (#3023) (#3040)

When a collection action can't run because the item is already gone from
the media server, handle it instead of failing: confirm absence with the
transient-safe itemExists primitive, then drop the stale rows from the
collection (and any siblings) so it stops being re-processed every run.

Also promote itemExists onto the shared IMediaServerService interface as
the single existence-check primitive (consumed by the collection handler,
removeStaleCollectionMedia, and the overlay processor), replacing the
per-overlay-provider copies and the getMetadata-falsiness staleness check
that could drop live rows on a transient error.

Follow-up to #3024.

* feat: skip currently-playing media in the collection handler (#3027)

Before the collection handler acts on eligible media, skip anything that is
currently being streamed so it isn't removed out from under an active viewer.
Skipped media stays eligible and is handled on the next run.

- Plex: GET /status/sessions
- Jellyfin/Emby: GET /Sessions (NowPlayingItem)

A playing item protects its episode, season, and show, so a collection
tracking any of those levels is deferred. Best-effort: the session snapshot is
read once per run and an empty or failed lookup just means "handle as usual".

Also consolidate the axios retry config into a shared applyHttpRetry helper,
adding retry parity to the Emby and Jellyfin clients.

* fix(collections): find manual collections across libraries on Jellyfin/Emby (#3026) (#3042)

Jellyfin/Emby BoxSets are server-global and only reported under libraries
whose content they currently hold, so a manual collection shared between a
movie rule and a show rule was invisible to the show-library lookup. Add a
capability-gated cross-library fallback for manual collections, and cache
Emby getCollections with invalidation to mirror the Jellyfin adapter.

Make Emby getCollections/getCollectionChildren user-scoped; Emby resolves
BoxSet queries against a user's library view, so an unscoped read can miss or
404 and break the manual-collection bootstrap. Maintainerr only ever operates
as an admin, so when no emby_user_id is configured, resolve and cache the
first admin user. The user is passed via the UserId query param on the literal
/Items path (not interpolated into the request path), which keeps the read
user-scoped without tripping CodeQL's request-forgery sink.

Add dev tooling to reproduce this end-to-end: a fake-emby mock plus
MEDIA_SERVER=emby support in seed-db, and teach fake-jellyfin to model a
server-global BoxSet reported under the movie library only.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@maintainerr-automation

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.14.0 🎉

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 6, 2026
… ➔ 3.14.0) (#207)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | minor | `3.13.0` → `3.14.0` |

---

### Release Notes

<details>
<summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary>

### [`v3.14.0`](https://github.com/Maintainerr/Maintainerr/blob/HEAD/CHANGELOG.md#3140-2026-06-05)

[Compare Source](Maintainerr/Maintainerr@v3.13.0...v3.14.0)

#### Highlights

- Added `/api/health` endpoints with liveness and readiness checks for monitoring and integration with tools like Kubernetes and Docker Compose ([#&#8203;3029](Maintainerr/Maintainerr#3029)).
- Collection handler now skips media currently being streamed to avoid disrupting active viewers ([#&#8203;3027](Maintainerr/Maintainerr#3027)).
- Fixed issue where saving log settings would overwrite an active `LOG_LEVEL` environment variable override ([#&#8203;3053](Maintainerr/Maintainerr#3053)).

#### Features

- Added `/api/health` endpoints with liveness and readiness checks ([#&#8203;3029](Maintainerr/Maintainerr#3029)).
- Collection handler now skips media currently being streamed ([#&#8203;3027](Maintainerr/Maintainerr#3027)).
- Logging system now honors the `LOG_LEVEL` environment variable on startup ([#&#8203;3030](Maintainerr/Maintainerr#3030)).

#### Fixes

- Fixed issue where saving log settings would overwrite an active `LOG_LEVEL` environment variable override ([#&#8203;3053](Maintainerr/Maintainerr#3053)).
- Validated webhook URL schemes to prevent invalid or potentially harmful requests ([#&#8203;3031](Maintainerr/Maintainerr#3031)).
- Fixed issue where rule groups lost collection links and visibility on partial updates ([#&#8203;3045](Maintainerr/Maintainerr#3045), [#&#8203;3046](Maintainerr/Maintainerr#3046)).
- Fixed issue with manual collections not being found across libraries on Jellyfin/Emby ([#&#8203;3026](Maintainerr/Maintainerr#3026), [#&#8203;3042](Maintainerr/Maintainerr#3042)).
- Resolved issue where deleted media remained stuck in Jellyfin/Emby collections and caused repeated processing errors ([#&#8203;3023](Maintainerr/Maintainerr#3023), [#&#8203;3024](Maintainerr/Maintainerr#3024), [#&#8203;3040](Maintainerr/Maintainerr#3040)).
- Fixed issue where Seerr requests for episode rules incorrectly deleted entire season requests ([#&#8203;3015](Maintainerr/Maintainerr#3015)).
- Improved error notifications for collection handling failures to include the name of the failing collection ([#&#8203;3013](Maintainerr/Maintainerr#3013)).
- Used Radarr bulk exclusions endpoint to avoid duplicate 400 errors when adding exclusions ([#&#8203;3012](Maintainerr/Maintainerr#3012)).

#### Performance

- Pruned media that no longer exists on the media server to improve collection handling efficiency ([#&#8203;3023](Maintainerr/Maintainerr#3023), [#&#8203;3040](Maintainerr/Maintainerr#3040)).

#### Internal

- Refreshed README with updated features, deployment examples, and credits ([#&#8203;3048](Maintainerr/Maintainerr#3048)).
- Clarified that a missing `yarn` command indicates a stale `node_modules` directory.

#### Dependencies

- Updated 20 dependencies, including `@typescript-eslint/parser`, `react-router-dom`, `axios`, and `vite`.

#### New Contributors

- [@&#8203;Arvuno](https://github.com/Arvuno) made their first contribution in [#&#8203;3029](Maintainerr/Maintainerr#3029)

</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:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDYuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwNi4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL21pbm9yIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/207
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants