Skip to content

fix(server): Use shared watch state for isWatched and Plex viewCount#2570

Merged
enoch85 merged 2 commits into
developmentfrom
fix-plex-viewcount-use-native-field
Mar 30, 2026
Merged

fix(server): Use shared watch state for isWatched and Plex viewCount#2570
enoch85 merged 2 commits into
developmentfrom
fix-plex-viewcount-use-native-field

Conversation

@enoch85

@enoch85 enoch85 commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

All credit to @wortmanb which is the one who made this PR. Due to that we couldn't make a rebase, I made this PR instead.

Summary

Addresses reviewer feedback from the original PR. The previous approach replaced the watch history API entirely with libItem.viewCount, which is per-user (admin token only) and not suitable for server-wide view counts.

viewCount (revised approach)

  • Primary: watch history API (/status/sessions/history/all) — server-wide, counts all users
  • Fallback: libItem.viewCount when history returns 0 or the API fails — per-user (admin), but better than returning 0 when Plex has purged watch history
  • Debug logging when fallback is used, noting the per-user limitation
Scenario Before (original code) After (this PR)
History available Uses history count Uses history count (same)
History purged Returns 0 (wrong) Falls back to admin viewCount with warning
API failure Uncaught error Falls back to admin viewCount with warning

isWatched (new property)

New boolean rule option: Plex - Is Watched (id: 43, movie type).

Returns true if the item has been watched by anyone:

  1. Checks watch history first (server-wide)
  2. Falls back to libItem.viewCount > 0 if history is empty/fails

This provides a simple "has this been watched?" check that is resilient to Plex history purging.

Re: Jellyfin parity (enoch85)

Noted that Jellyfin viewCount is derived from aggregated watch history, not a native counter. Aligning Plex and Jellyfin semantics is worthwhile but orthogonal to this fix — suggest addressing in a follow-up.

Re: remove/re-add behavior (ydkmlt84)

When an item is removed from Plex and re-added, the watch history is lost and viewCount resets to 0. Neither the history API nor the native counter survives this. The isWatched boolean has the same limitation. This is a Plex-level constraint.

Test plan

  • 10 unit tests covering viewCount (5 cases) and isWatched (5 cases): history available, history empty with fallback, history empty without fallback, API failure with fallback, API failure without fallback
  • All 510 existing tests pass (0 regressions)
  • Formatted with Prettier

Rebased from #2453 (by @wortmanb) onto latest development to resolve merge conflicts.

@enoch85 enoch85 self-assigned this Mar 30, 2026
@enoch85 enoch85 force-pushed the fix-plex-viewcount-use-native-field branch from 1af9711 to a29ea2f Compare March 30, 2026 21:26
Co-authored-by: mayor <bret@thewortmans.org>
@enoch85 enoch85 force-pushed the fix-plex-viewcount-use-native-field branch from a29ea2f to b1ea3c9 Compare March 30, 2026 21:27
@enoch85 enoch85 merged commit 84187b8 into development Mar 30, 2026
13 checks passed
@enoch85 enoch85 deleted the fix-plex-viewcount-use-native-field branch March 30, 2026 21:53
@enoch85

enoch85 commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator Author

@wortmanb this is now in the development branch.

Please test it thoroughly.

enoch85 pushed a commit that referenced this pull request Mar 30, 2026
maintainerr-automation Bot added a commit that referenced this pull request Apr 3, 2026
* fix(server): Use shared watch state for isWatched and Plex viewCount (#2570)

* Metadata provider abstraction layer with TVDB support (#2573)

* fix: register tvdb cache in CacheManager

* build(deps-dev): bump the nestjs group with 2 updates (#2575)

Bumps the nestjs group with 2 updates: [@nestjs/cli](https://github.com/nestjs/nest-cli) and [@nestjs/schematics](https://github.com/nestjs/schematics).


Updates `@nestjs/cli` from 11.0.16 to 11.0.17
- [Release notes](https://github.com/nestjs/nest-cli/releases)
- [Commits](nestjs/nest-cli@11.0.16...11.0.17)

Updates `@nestjs/schematics` from 11.0.9 to 11.0.10
- [Release notes](https://github.com/nestjs/schematics/releases)
- [Commits](nestjs/schematics@11.0.9...11.0.10)

---
updated-dependencies:
- dependency-name: "@nestjs/cli"
  dependency-version: 11.0.17
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/schematics"
  dependency-version: 11.0.10
  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 axios from 1.13.6 to 1.14.0 (#2576)

Bumps [axios](https://github.com/axios/axios) from 1.13.6 to 1.14.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](axios/axios@v1.13.6...v1.14.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.14.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.0.6 to 6.1.1 (#2578)

Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.0.6 to 6.1.1.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.1.1/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 6.1.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 @tanstack/eslint-plugin-query (#2579)

Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.91.4 to 5.96.0.
- [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.96.0/packages/eslint-plugin-query)

---
updated-dependencies:
- dependency-name: "@tanstack/eslint-plugin-query"
  dependency-version: 5.96.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>

* fix(ui): smooth settings refresh and stream warnings + tune flickering and loadingspinner (#2574)

* build(deps): bump react-router-dom from 7.13.1 to 7.13.2 (#2584)

Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 7.13.1 to 7.13.2.
- [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.13.2/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-version: 7.13.2
  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 ts-jest from 29.4.6 to 29.4.9 (#2583)

Bumps [ts-jest](https://github.com/kulshekhar/ts-jest) from 29.4.6 to 29.4.9.
- [Release notes](https://github.com/kulshekhar/ts-jest/releases)
- [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md)
- [Commits](kulshekhar/ts-jest@v29.4.6...v29.4.9)

---
updated-dependencies:
- dependency-name: ts-jest
  dependency-version: 29.4.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/eslint-plugin (#2585)

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

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.58.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>

* fix(ui): stabilize library switching and add media sorting (#2586)

* build(deps): bump lodash-es from 4.17.23 to 4.18.1 (#2587)

Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](lodash/lodash@4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(metadata): add per-provider metadata refresh action

Adds a "Refresh metadata" button to each provider card in Metadata
Settings. Clicking it:
1. Tests the provider connection as a safety net before touching cache
2. Flushes only that provider's local NodeCache
3. Fires batched refreshItemMetadata calls to Plex/Jellyfin for all
   collection items linked to that provider (fire-and-forget)

Button label is context-aware ("Save to refresh", "Configure to
refresh") and disabled while unsaved changes are pending. Inline
feedback reuses the shared useSettingsFeedback pattern. A server-side
in-progress guard prevents concurrent refreshes of the same provider.

* fix(ui): make overview title sort explicit (#2589)

* build(deps): bump @tanstack/react-query from 5.91.3 to 5.96.1 (#2590)

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

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.96.1
  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 @tanstack/eslint-plugin-query (#2591)

Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.96.0 to 5.96.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.96.1/packages/eslint-plugin-query)

---
updated-dependencies:
- dependency-name: "@tanstack/eslint-plugin-query"
  dependency-version: 5.96.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 lodash from 4.17.23 to 4.18.1 (#2592)

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](lodash/lodash@4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(metadata): add ID validation and retry path for metadata refresh (#2594)

Introduces media-server-id.utils with heuristic ID classification to
skip foreign-server IDs (e.g. Jellyfin UUIDs stored against a Plex
instance) before queuing metadata refresh requests. Adds a GET-on-failure
retry path in MetadataSettingsService for manual refreshes: on first
failure, looks up the item to get a verified (possibly corrected) ID and
retries once, covering Jellyfin 'Guid can't be empty' corruption scenarios.

* fix: stabilize metadata sourcing and settings feedback flows (#2595)

* fix(ui): use active metadata provider in media modal

* chore(server): clean metadata lint warnings

* change tvdb link

* fix: align settings feedback and persist github cache

* fix(ui): align metadata test button text

* Update API key link in Metadata component

* fix: delete Tautulli config on media server switch to Jellyfin (#2597)

* build(deps): bump the nestjs group with 5 updates (#2598)

Bumps the nestjs group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.1.17` | `11.1.18` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.1.17` | `11.1.18` |
| [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) | `11.1.17` | `11.1.18` |
| [@nestjs/typeorm](https://github.com/nestjs/typeorm) | `11.0.0` | `11.0.1` |
| [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing) | `11.1.17` | `11.1.18` |


Updates `@nestjs/common` from 11.1.17 to 11.1.18
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.18/packages/common)

Updates `@nestjs/core` from 11.1.17 to 11.1.18
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.18/packages/core)

Updates `@nestjs/platform-express` from 11.1.17 to 11.1.18
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.18/packages/platform-express)

Updates `@nestjs/typeorm` from 11.0.0 to 11.0.1
- [Release notes](https://github.com/nestjs/typeorm/releases)
- [Commits](nestjs/typeorm@11.0.0...11.0.1)

Updates `@nestjs/testing` from 11.1.17 to 11.1.18
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.18/packages/testing)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.1.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/typeorm"
  dependency-version: 11.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.18
  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 react-hook-form from 7.72.0 to 7.72.1 (#2599)

Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.72.0 to 7.72.1.
- [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.72.0...v7.72.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.72.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): bump @tanstack/react-query from 5.96.1 to 5.96.2 (#2600)

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

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.96.2
  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 @tanstack/eslint-plugin-query (#2601)

Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.96.1 to 5.96.2.
- [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.96.2/packages/eslint-plugin-query)

---
updated-dependencies:
- dependency-name: "@tanstack/eslint-plugin-query"
  dependency-version: 5.96.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>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: wortmanb <744697+wortmanb@users.noreply.github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Javiink <43996484+Javiink@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant