Skip to content

feat: add Plex deep scan mode - #12

Merged
cbulock merged 4 commits into
cbulock:mainfrom
zeno-codebot:feat/deep-video-scan
Jul 16, 2026
Merged

feat: add Plex deep scan mode#12
cbulock merged 4 commits into
cbulock:mainfrom
zeno-codebot:feat/deep-video-scan

Conversation

@zeno-codebot

@zeno-codebot zeno-codebot commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add an optional Plex deep-scan mode that runs a full ffmpeg decode pass for movie quality and episode duration checks
  • persist the default deep-scan preference in Settings and expose guarded per-run toggles in both scan views
  • harden the backend deep-scan flow with metadata fallback handling, timeout escalation, token redaction, backend tests, and Docker ffmpeg support

Verification

  • npm test in backend/
  • node --check src/api/plex.js src/routes/quality.js src/routes/settings.js src/routes/episodeDuration.js src/db/index.js in backend/
  • npm run build in frontend/

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional “deep scan” mode for Plex media checks that performs a real ffmpeg full-decode pass to derive more reliable stream metadata/durations, wires that toggle through settings → API → UI, and introduces backend tests plus container support for ffmpeg.

Changes:

  • Implement ffmpeg-based deep-scan batching (with concurrency + timeout/termination handling) for Plex movies and TV episodes in the backend Plex client.
  • Expose a persisted deep-scan setting and per-run deep-scan toggle + stats in the Movie Quality and Episode Duration views.
  • Add backend test coverage for mixed movie/episode deep-scan behavior and timeout SIGTERM/SIGKILL escalation; install ffmpeg in the Docker image.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
README.md Documents the new deep-scan option and its performance tradeoffs in both check workflows.
frontend/src/views/SettingsView.vue Adds a persisted “deep scan for Plex media checks” toggle in Settings and saves it to the backend.
frontend/src/views/MovieQualityView.vue Adds a per-run deep-scan toggle, loads the default from settings, and shows deep-scan stats in the UI.
frontend/src/views/EpisodeDurationView.vue Adds a per-run deep-scan toggle, loads the default from settings, and shows deep-scan stats in the UI.
frontend/src/stores/settings.js Adds quality_deep_scan to the settings default shape.
frontend/src/stores/movieQuality.js Extends runCheck to optionally pass deep=0/1 to the backend.
frontend/src/stores/episodeDuration.js Extends runCheck to optionally pass deep=0/1 to the backend.
Dockerfile Installs ffmpeg in the backend runtime image to support deep scan in Docker.
backend/test/api/plex.test.js Adds node:test coverage for deep-scan batching + timeout escalation behavior via injected process/timer harnesses.
backend/src/routes/settings.js Persists/validates the new deep-scan setting key in the settings API.
backend/src/routes/quality.js Reads the deep-scan setting/query param and returns deep-scan stats in the response summary.
backend/src/routes/episodeDuration.js Reads the deep-scan setting/query param and returns deep-scan stats in the response summary.
backend/src/db/index.js Seeds the new deep-scan setting default in SQLite.
backend/src/api/plex.js Implements ffmpeg deep scan helpers and integrates them into Plex movie/episode fetching.
backend/package.json Adds a test script to run backend tests via node --test.

Comment thread backend/src/api/plex.js Outdated
Comment on lines +262 to +263
const detail = stderr.trim().split(/\r?\n/).slice(-3).join(' | ') || `exit=${code} signal=${signal || 'none'}`;
reject(new Error(`ffmpeg deep scan failed: ${detail}`));

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7f89920. ffmpeg stderr tails are now redacted before they are included in deep-scan error messages, so X-Plex-Token values do not leak into logs or API errors.

Comment thread backend/src/api/plex.js
Comment on lines +251 to +254
if (timedOut) {
const killDetail = forcedKill ? ` after SIGTERM+SIGKILL (${termGraceMs}ms grace)` : ' after SIGTERM';
reject(new Error(`ffmpeg deep scan timed out after ${Math.round(timeoutMs / 1000)}s${killDetail}`));
return;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7f89920. Timeout text now reports sub-second values in milliseconds and rounds second-based values up, so short test timeouts no longer render as 0s.

Comment thread backend/src/routes/settings.js Outdated
const { getAllSettings, setSetting } = require('../db');

const ALLOWED_KEYS = ['plex_url', 'plex_token', 'radarr_url', 'radarr_api_key', 'sonarr_url', 'sonarr_api_key', 'leeway_percent', 'movie_min_diff_min', 'episode_min_diff_min', 'plex_library_ids', 'quality_thresholds'];
const ALLOWED_KEYS = ['plex_url', 'plex_token', 'radarr_url', 'radarr_api_key', 'sonarr_url', 'sonarr_api_key', 'leeway_percent', 'movie_min_diff_min', 'episode_min_diff_min', 'plex_library_ids', 'quality_thresholds', 'quality_deep_scan'];

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7f89920. The persisted setting is now plex_deep_scan, and the backend migrates the legacy quality_deep_scan key forward so existing installs keep their saved preference.

Comment on lines +259 to +262
onMounted(async () => {
await settingsStore.fetchSettings()
deepScan.value = settingsStore.settings.quality_deep_scan === '1'
})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7f89920. MovieQualityView now only hydrates the deep-scan toggle from settings if the user has not already changed it while the async fetch was in flight.

Comment on lines +267 to +270
onMounted(async () => {
await settingsStore.fetchSettings()
deepScan.value = settingsStore.settings.quality_deep_scan === '1'
})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7f89920. EpisodeDurationView uses the same guarded hydration path now, so an in-flight settings fetch no longer reverts a user toggle.

@cbulock

cbulock commented Jul 15, 2026

Copy link
Copy Markdown
Owner

@copilot fix all the commented issues

@zeno-codebot zeno-codebot changed the title feat: add deep video scan option feat: add Plex deep scan mode Jul 15, 2026
@cbulock
cbulock merged commit 3c44767 into cbulock:main Jul 16, 2026
1 of 2 checks passed
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.

3 participants