# Changelog ## v1.0.0 - 2026-11-10 - A short README; reference material moved to `docs/` (models, configuration, architecture, API and command line, upgrading). SECURITY.md describes Sign in with Plex and the generated keys. - Sign in with Plex always shows an "Open Plex sign-in" link, so a blocked pop-up no longer leaves you with nothing to click. - The Unraid template uses the `:latest` image, so Unraid offers updates after each release. Existing installs: set Repository to `ghcr.io/finechainesium/plexmind:latest` once. ## v0.9.15 - 2026-11-10 - Simpler install, on the way to 1.0: - Sign in with Plex: the dashboard and `setup.sh` use Plex's own sign-in link, so there is no token to find and paste. On first run it connects PlexMind to the server the account owns (asking which, if there are several); after that it is the dashboard login, for the server owner only. An API key still works for scripts. - The API key and webhook secret are generated on first start and kept in `data/secrets.json`; setting them yourself still works and wins. - `setup.sh` finds your Movies and TV folders from your Plex libraries (translating paths when Plex runs in Docker), picks a model for your GPU and downloads it with a checksum check, and writes a `.env` of about ten lines instead of a hundred. `setup.sh --model DIR` only fetches the model, for Unraid. - `.env.example` has the 7 settings people change; the full list moved to `docs/CONFIGURATION.md`. The Unraid template drops the Plex token, URL, keys and other settings that have defaults. - The Docker broker's allowed containers now follow `WHISPER_CONTAINER_NAME` and `LLAMA_CPP_CONTAINER_NAME`. - Model Trial: the Model Advisor card can try any model in the models folder on this GPU before you switch. It runs the subtitle batches translation really sends (same prompts and checks) in up to two of your languages, plus a recommendation request, next to the model in use, and shows GPU memory, speed, unusable batches, rejected lines, word-for-word idioms, Simplified characters in Traditional Chinese, and invented or malformed picks. Translation pauses during a trial and resumes after. A model that fails (too much memory, too slow, or broken replies) cannot be selected. ## v0.9.14 - 2026-11-10 - Recommendations no longer stay stuck when someone ignores them. A set nobody watched from was kept forever, so 23 of 25 users were skipped in a recent run, some with picks from April; now a set older than `REC_REFRESH_DAYS` (25) is replaced either way. - Dashboard fixes from an audit of every control: - Per-user Generate now also sends the recommendations to Plex, and says so. - Start uses the max runtime shown under Schedule (0 runs until done) instead of a hidden 60-minute limit. - Starting a job that is already running attaches to it instead of showing an error, and the scheduler no longer logs that as a failed launch. - Dates labelled UTC are shown in UTC. - The Plex card checks Plex itself; the header says Offline rather than Demo Mode; a Plex error no longer marks the API offline. - Maintenance cards say quarantine instead of delete, and the latest audit report is shown on the Maintenance page. - Settings: the API key is marked required, Test Connection checks the key, and saving handles errors. - Clearer wording for schedules, target languages, Whisper standby, the GPU card, Generate All waits and the Model Advisor (GiB, Refresh with a time, fixed list explained); shared Plex users are labelled Shared. ## v0.9.13 - 2026-11-09 - New logo: a P with a brain inside. - Choose the model from the dashboard's Model Advisor card. It lists every GGUF in `LLAMA_CPP_MODELS_DIR`, remembers the last one used, and applies to recommendations at once and to translation at its next file. Compose now runs llama.cpp in router mode; `LLAMA_CPP_MODELS_DIR` replaces `LLAMA_CPP_MODEL_PATH`, and a model's name is its file name without `.gguf`. - Default model is now Gemma 4 12B (`gemma-4-12b-it-Q5_K_M`), for translation and recommendations. PlexMind stays model-agnostic: any GGUF llama.cpp can load works. Installs relying on the Compose default must download the new file (see the README) or set `LLAMA_CPP_MODEL_ALIAS` to keep Qwen 3.5 9B. ## v0.9.12 - 2026-11-09 - Only call a recommendation a hidden gem if it really is little known: few TMDB votes for its type, low current popularity, not part of a film series, not a long-running show, and not trending. ## v0.9.11 - 2026-11-09 - Translate what people are watching first: the next episodes after each active viewer, then shows being watched, recently added titles, anything watched before, and the rest. Videos using an embedded or Plex subtitle share the same queue. - Give the translation model the title, genres, synopsis and character names from Plex, plus the last few finished lines, and end batches at sentence breaks. - Tell the model to translate slang and innuendo by meaning and to keep each line's words in its own line. A batch containing junk or a line far too long or short for its English is redone one line at a time. ## v0.9.10 - 2026-11-09 - Recommendations now score each candidate against the individual watched titles it most resembles, instead of one averaged taste profile. Candidates also come from TMDB's similar and recommended lists, watched titles count by how much of them was watched, rewatches, star ratings and thumbs up or down, the top picks keep some variety, and each reason names the titles a pick actually matched. ## v0.9.9 - 2026-11-08 - Add a Throttle page to the dashboard: switch each sensor on or off, set its limit and see its current reading. Saved limits replace the `THROTTLE_*` settings and reach running jobs, including one already held. ## v0.9.8 - 2026-11-08 - Translate videos that have no English `.srt` file from an English text subtitle inside the video or held by Plex. The source stays in PlexMind's data folder, so Plex does not list a second English subtitle. Picture subtitles (PGS, VobSub) cannot be used. ## v0.9.7 - 2026-11-08 - Hold transcription, translation, maintenance and recommendation jobs while the server is too hot or short of memory, with a separate limit for each of GPU, CPU, NVMe, hard drive temperature and RAM (`THROTTLE_*`; blank turns a sensor off, and all are off by default). Jobs resume once every enabled sensor has cooled by `THROTTLE_RESUME_MARGIN`. ## v0.9.6 - 2026-11-07 - Skip transcribing or translating a video that already has a full subtitle in that language anywhere Plex can show it: a track embedded in the file, or any subtitle Plex lists for it, including ones Plex downloaded itself. Before, only `.srt` files beside the video counted. Forced tracks never count, and languages match on their base (any Spanish covers `es-MX`, any Chinese `zh`). Answers are cached per file for `SUBTITLE_INVENTORY_TTL_HOURS` (24). On the author's library this skips about half of the queued translations. ## v0.9.5 - 2026-11-07 - Upgrade the base image's Alpine packages at build time: zlib 1.3.2 (CVE-2026-4738, critical) failed the v0.9.4 release scan. - Watermark: put the cue after a UTF-8 byte-order mark, and leave UTF-16 and other non-UTF-8 subtitles untouched (logged as skipped) instead of corrupting them. - Dedup: never weigh a job's in-progress `Title.ai.srt`, `.ai-.srt` or `translate_tmp` file (a crashed transcription's partial could get a user's untagged `Title.srt` quarantined); count UTF-16 cues; read `Title.hi.srt` as Hindi. - PGS cleanup: PlexMind's own Whisper/LLM subtitles no longer replace authored image subtitles unless `PGS_CLEANUP_ALLOW_GENERATED=1`, and a regular or SDH replacement needs at least 20 cues. - Job control: the control server sees a job started with `docker exec` and no longer deletes its PID file; `stop-job.sh` signals only a process running the job's script, never whatever now owns a stale PID. - Ordering fixer: treat whitespace-only lines as cue separators and keep position tags after the end time. - `repair_srt_timestamps.py`: skip non-UTF-8 files instead of mangling them, require the real media lock, keep file ownership, and continue past unreadable files. - Audit and dashboard: count subtitles the way transcription does, read translation targets from `TARGET_LANGUAGES`, and label the quarantine count as videos. - A translation run still going when its next window opens now counts for that window, instead of deferring on its own GPU load all night. ## v0.9.4 - 2026-11-07 - Tagged but not published: the release image vulnerability scan failed on zlib in the base image. Its changes ship in v0.9.5. ## v0.9.3 - 2026-11-03 - Never recommend a title Plex marks as watched for the user (any view of a movie, any watched episode of a show). Watched titles were only taken from the last `HISTORY_LIMIT` history entries, where every episode is one entry, so a title watched long ago was recommended again. ## v0.9.2 - 2026-11-03 - Remove an old watchlist pick that has left the library (watched and deleted, say) by finding it on the watchlist itself; before, it could not be resolved and stayed on the watchlist for good. New picks are tracked by their Plex GUID, which still identifies them after they leave the library. ## v0.9.1 - 2026-11-03 - Pause a running translation for the transcription window and the monthly recommendation batch instead of letting its backlog block both; it resumes from its checkpoint afterwards. A stop from the dashboard is not resumed. - Stop an idle on-demand llama.cpp before transcription so Whisper gets the GPU memory. - Retry a skipped or failed monthly recommendation batch hourly (`MONTHLY_RETRY_HOURS`, `MONTHLY_RETRY_LIMIT`) instead of waiting a month. - Transcribe the English audio track when a file has one, even when it is not the first track. Files with a Japanese track first and an English dub (Cyberpunk: Edgerunners) were transcribed from the Japanese track: the ffmpeg track specifier the script relied on does not parse on ffmpeg 8. The profiler, extraction and confidence check now all use the same track. - Trust an English audio tag instead of profiling, and decide a file's language by a majority of the profiler's samples rather than the first non-English one; a sample Whisper could not answer no longer counts as English. - Retry a permanently failed transcription after `TRANSCRIBE_FAILED_RETRY_DAYS` (default 30). - Keep the first cue's timing when collapsing a repeated-line hallucination instead of stretching one cue over the whole run. - Accept an audio chunk with no speech in a long video instead of failing the file; stop, without marking the video failed, when Whisper itself is down; check ffmpeg's exit status before transcribing extracted audio. - Count only a full subtitle as "already subtitled": a forced-only track, an unfinished `.ai.srt` or a sibling video's file no longer skip a video, and an untagged `Title.srt` now does. - Remove a stale recommendation sentinel instead of waiting on it forever. - Recommendations: match LLM titles that carry a year or type suffix and top up a short reply from the best candidates; repair a cut-off reply at the last complete pick; never recommend titles marked watched or disliked; apply `SUPPRESSION_DAYS` when reading shown titles; keep TV candidates in the prompt for movie-heavy users; report a batch where every user failed as failed; refuse an unknown user ID instead of using the admin's history; keep a friend's new playlists when deleting old ones fails. ## v0.9.0 - 2026-11-01 - Run PlexMind as one container: the dashboard/API, recommendation worker, scripts controller and Docker broker now share one `plexmind` image, started and restarted by `supervisor.py`. The `plexmind-scripts` and `plexmind-docker-broker` images are no longer published. - The scripts controller and Docker broker listen on 127.0.0.1 only; the API and recommendation worker still run as uid 1000. - `PLEXMIND_CONTROL_TOKEN` and `PLEXMIND_BROKER_TOKEN` are generated at each start when blank; `SCRIPTS_API_URL` and `DOCKER_BROKER_URL` are set internally. - The Unraid template is now the whole PlexMind: it mounts media and the Docker socket and sets `--stop-timeout=40`. Keep port 8000 off the internet (see SECURITY.md). ## v0.8.23 - 2026-10-29 - Size each translation request's token budget to its cues instead of a flat 256 tokens per batched cue (768 per single cue), growing it on each retry. A reply that loops on sung lyrics now stops within seconds instead of running for about 40 s. - Retry a single-cue reply cut off at the token limit instead of publishing it; the last attempt gets all of `LLAMA_CPP_MAX_TOKENS`, so no cue has less room than before. ## v0.8.22 - 2026-10-29 - Rebase the API, scripts and broker images on `python:3.13.18-alpine`; the August base image failed the release vulnerability scan. - Translate up to `TRANSLATION_BATCH_CUES` (default 8) cues per llama.cpp request, falling back to per-cue requests for any cue the batch does not deliver. - Keep a cue in the source language instead of failing the whole file when it cannot be translated; fail only past `TRANSLATION_MAX_FALLBACK_PCT`. Skip the watermark cue and restate the target language for zh. - Retry llama.cpp "peg-native" output errors with a new seed instead of stopping the file. - Start llama.cpp on demand and stop it after `LLAMA_IDLE_STOP_MINUTES` idle; `/health` reports it as standby; default `--parallel 1`. - Quarantine instead of deleting for PGS cleanup, leftover partial transcripts and encoding repair; stop forced/SDH SRTs counting as full replacements. - Fix watermarking and encoding detection under BusyBox, the transient-failure retry counter, and the SRT ordering fixer. - Redact secrets from access logs and TMDB/OMDb/TVDB errors; compare tokens as bytes. - Sample GPU load several times so batch runs do not back off on their own llama.cpp load; a held script lock no longer uses up the day's window. - Stop the whole job process group on stop, and stop Whisper before the cleanup report. - Bind llama.cpp and Whisper host ports to 127.0.0.1 by default (`LLAMA_CPP_BIND_ADDRESS`, `WHISPER_BIND_ADDRESS`); give Whisper its own tmp volume; add `no-new-privileges` to the scripts and broker services. - Fix watchlist ownership races, friend-account collection removal, session revocation, blocking Plex calls on the event loop, and permanent OMDb error caching. ## v0.8.21 - 2026-10-29 - Tagged but not published: the release image vulnerability scan failed on the old base image. Its changes ship in v0.8.22. ## v0.8.20 - 2026-08-15 - Route scheduled media work through the authenticated worker instead of the API-local runner. - Add narrow broker-backed NVIDIA utilization telemetry and fail closed when telemetry is unavailable. - Move recommendation batches to a durable singleton queue worker and remove implicit duplicate Plex sync. - Persist worker idempotency and validate worker process identity across restarts. - Repair API/worker response parity, state ownership checks, stable Plex identities, and transactional playlist rollback. - Pin build/runtime images and GitHub Actions; publish API, scripts worker, and broker as one release set. - Add post-remediation blast-radius integration regression coverage. ## v0.8.19 - 2026-08-15 - Closed the dashboard API-key disclosure and introduced short-lived authenticated sessions. - Added authenticated/idempotent worker control and an allowlisted Docker lifecycle broker. - Moved media writes out of the API container and made worker/GPU locking cross-container. - Persisted scheduler receipts, recommendation jobs, and verified local process identity. - Removed implicit PGS deletion and exposed per-file partial failures. - Made Plex replacement transactional and LLM recommendations candidate-allowlisted. - Added collision-safe media resolution, atomic state writes, HTTPS metadata traffic, pinned direct dependencies/images, release verification, SBOM, and provenance. - Corrected browser-only schedule wording, endpoint fallback, Whisper artifact/state/model truth. ## v0.9.1 - 2026-11-03 - Complete the production-hardening audit and deploy the API, recommendation worker, scripts worker, and authenticated Docker broker with end-to-end health verification. - Discover translation targets from the worker's configured language list instead of hard-coding Chinese and Mexican Spanish in the dashboard. - Read the displayed llama.cpp model, translation window, timezone, and zero-valued cron fields from runtime scheduler configuration instead of stale UI assumptions. - Make storage reporting tolerate media-filesystem latency, validate returned capacity data, and retain the last successful reading across transient failures. - Parse shared statistics as a strict numeric data format, preserve safe subtitle modes, and support configured media ownership. - Make PGS cleanup and its audit language-aware so an unrelated text subtitle cannot authorize deletion. - Serialize and validate worker controls, expose broker-backed health, and rate-limit bounded browser sessions. - Lock standalone subtitle mutation tools, use atomic timestamp/order repairs, and archive originals safely. - Add read-only setup preflight, wire documented settings, and expand regression coverage across the hardened suite. - Keep PGS cleanup logs separate from the numeric deletion count so standalone and full-maintenance results remain accurate. - Align packaging regression coverage with the HttpOnly-session security contract that forbids persisting raw API keys in browser storage. - Accept short invariant translations while rejecting substantial unchanged dialogue, and record cue-level rejection diagnostics without duplicate log lines. - Validate library SRT files with a strict timestamp parser, distinguish structural audits from generated-output quality policy, and add an atomic timestamp repair utility with original-file archives. - Preserve intentional job stops as interrupted runs and use durable absolute paths for recommendation-worker state. - Keep the PlexMind API card online during jobs by separating `/health/live` reachability from slower dependency health. - Populate the Whisper model dropdown from models actually present in the mounted local cache, and disable transcription when the configured model is unavailable. - Remove unwired language-detection and re-translate toggles, and clarify the destructive duplicate-subtitle action label. - Separate host media bind paths from fixed in-container scan roots and fail closed on missing, unreadable, identical, or unexpectedly empty roots. - Add Movies and TV mappings to the Unraid template and expose media inventory through scripts health. - Wait for cold llama.cpp startup, preserve failed job results, and stop only sidecars started by the current job. - Use one canonical llama.cpp base URL, keep the shared recommendation service externally managed by default, and remove brittle Docker bridge rewriting. - Consolidate application and scripts-sidecar builds on the root `scripts/` tree and add packaging, path, sidecar, readiness, and Plex-section regression tests. - Discover Plex video sections by media type instead of assuming the titles `Movies` and `TV Shows`. - Repair 16 affected live subtitle files to media ownership and mode `0644`; verify a new 1,661-cue transcription finalizes as UID/GID `99:100` with no root-owned mode-`0600` SRT files remaining. - Verify all shell and Python regressions plus a full 8,003-video/17,522-SRT library audit with zero invalid SRTs, zero language-safe PGS cleanup candidates, and zero sampled encoding issues. ## v0.8.18 - 2026-06-07 - Replace uncompressed Whisper WAV uploads with compressed 16 kHz mono MP3 extraction and segmented uploads above 50 MB to avoid large-audio ASR crashes. - Add a 12 GB Whisper sidecar memory limit and expose transcription upload controls in Compose and `.env.example`. - Move bundled PlexMind sidecar host ports away from common conflicts: llama.cpp defaults to `11435`, Whisper defaults to `9001`, while internal container ports remain unchanged. - Finish llama.cpp drift cleanup in setup, Unraid template, security docs, scheduler GPU fallback defaults, and app fallback URLs. - Rebuild and recreate the live PlexMind containers with the updated images, port map, and live `.env` values. - Add subtitle readability formatting for future transcription output: two-line visual reflow, conservative oversized-cue splitting, and high-CPS protection. ## v0.8.17 - 2026-04-17 - Move transcription and translation launch windows into the PlexMind scheduler so subtitle scripts run once per configured daily window. - Add script lifetime stats to the dashboard from persisted transcription and translation counters. - Improve Plex user discovery with a persistent cache, local account fallback, and optional admin username override. - Align bundled Ollama and Whisper sidecar container defaults, persistent cache paths, health probing, and UTC scheduling defaults. ## v0.8.16 - 2026-04-14 - Add a current-session recommendation batch log alongside transcription, translation, and maintenance logs. - Expose recommendation log status and log endpoints for the dashboard Logs page. - Include Recommendations in the Logs job picker and record manual or scheduled recommendation batch progress to `/app/data/recommendations.log`. ## v0.8.15 - 2026-04-14 - Rewrite the README with clearer product positioning, setup flow, architecture, API, security, and dashboard documentation. - Document local AI dependencies including Ollama models, Whisper ASR webservice, Whisper model/device settings, and the Compose Whisper profile. ## v0.8.14 - 2026-04-14 - Limit dashboard script progress logs to the latest session for each selected job instead of showing retained historical log output. - Keep full retained log files on disk while marking API log responses as current-session views. ## v0.8.13 - 2026-04-14 - Sync the transcription language fix into the API image scripts directory so GUI-launched transcription uses the same logic packaged for sidecar scripts. ## v0.8.12 - 2026-04-14 - Use primary audio stream language metadata before falling back to Whisper profiling so Japanese audio is not forced through English transcription. - Infer profiler language from Whisper JSON text when the API omits an explicit language field. - Score transcription confidence with the detected language instead of always forcing English. ## v0.8.11 - 2026-04-14 - Serve the existing PlexMind icon at `/favicon.ico` so browser favicon requests no longer return 404. ## v0.8.10 - 2026-04-13 - Replace the Logs grid with a job dropdown so only the selected script log is loaded. - Default the Logs page to a running job or the most recently updated log session. - Cap dashboard and API log reads at 500 lines to avoid large log payloads. ## v0.8.9 - 2026-04-13 - Start the Whisper sidecar container before transcription and stop it when transcription exits or is terminated. - Start the Ollama sidecar container before translation and stop it when translation exits or is terminated. - Add Docker socket access for PlexMind-managed scripts so sidecar lifecycle control works from containerized runs. ## v0.8.8 - 2026-04-13 - Use the PlexMind logo asset in the dashboard sidebar brand mark. ## v0.8.7 - 2026-04-13 - Add the PlexMind Unraid icon asset used by the template. - Add Unraid Docker labels for WebUI and icon discovery so existing containers can show the WebUI dropdown entry after recreation. ## v0.8.6 - 2026-04-13 - Replace the redundant Jobs sidebar page with a Logs page that tails PlexMind-managed script logs from transcription, translation, and maintenance jobs. - Keep old `#/jobs` links routed to the new Logs page. ## v0.8.5 - 2026-04-13 - Fix the Unraid Docker template WebUI target so the Docker tab opens the PlexMind dashboard at `/` instead of the API docs path. - Update the PlexMind template release metadata to the `v0.8.5` release line. ## v0.8.4 - 2026-04-13 - Add hash-routed dashboard pages so sidebar pages reload back to their current page instead of resetting to Dashboard. - Add a Jobs sidebar page backed by `/api/scripts/jobs` for PlexMind-controlled script job status. - Add job catalog metadata in the local PlexMind runner for titles, groups, descriptions, page targets, destructive flags, log presence, and running state. ## v0.8.3 - 2026-04-13 - Route generated Unraid schedule helper commands through the PlexMind `/api/scripts/{job}/start` API so scheduled script runs share PlexMind status, logs, limits, and stop controls. - Fix transcription confidence parsing so timestamped log output from `score_confidence` cannot be captured as the numeric score. - Align script headers and runtime log banners on the `v0.8.3` PlexMind release line. ## v0.8.2 - 2026-04-13 - Protect the one-time playlist migration endpoint with the configured PlexMind API key. - Render dashboard toast messages as text content to avoid HTML injection from dynamic error strings. - Serialize JSON persistence updates with a shared lock and atomic temp-file replacement. - Raise the GUI script-start rate-limit default from `5/hour` to configurable `SCRIPT_START_RATE_LIMIT`, default `60/hour`, to avoid normal Unraid retries hitting HTTP 429. - Fall back to the Docker bridge gateway for Whisper when legacy bridge DNS cannot resolve `whisper` or `whisper-asr-webservice`. ## v0.8.1 - 2026-04-13 - Align the dashboard, FastAPI metadata, package metadata, and script runtime banners on the `v0.8.1` release line. - Replace the old standalone script `2.0` labels with PlexMind release-line versions so script logs and docs no longer imply a separate production-ready major version. ## v0.8.0 - 2026-04-12 - Add a live Whisper ASR health probe to `/health` using the configured `WHISPER_API_URL`. - Replace the static Whisper dashboard placeholder with real Ready/Offline/Not checked state. - Show an explicit pending-restart state when the dashboard is newer than the running API process. ## v0.7.1 - 2026-04-12 - Make script job log polling resilient to transient API/proxy disconnects by backing off and resuming instead of appending repeated `Failed to fetch` lines every 3 seconds. - Keep already-running transcribe/translate jobs attached to the existing log poller without raw 409 JSON or duplicate polling intervals. - Fix maintenance already-running handling so maintenance jobs show a toast instead of referencing the transcribe/translate log element. ## v0.7.0 - 2026-04-12 - Run transcription, translation, and maintenance jobs directly from the PlexMind API container by default, with the optional scripts sidecar still supported via `PLEXMIND_SCRIPT_MODE=sidecar`. - Package `/app/scripts` plus `curl`, `jq`, and `ffmpeg` into the API image, and mount Movies/TV paths into the API service for GUI-launched script jobs. - Fix recommendation generation in the dashboard so long runs do not trip a 60-second timeout and incorrectly flip the UI into demo mode. - Persist generated recommendation history and load the Recent Recommendations section from real generated results instead of mock/demo data. - Add maintenance job execution through the GUI with confirmation prompts for destructive dedupe, PGS cleanup, and all-maintenance runs. - Update the live container to `mode=local` script execution with `/app/scripts`, `/media/movies`, `/media/tv`, `curl`, `jq`, and `ffmpeg` available. - Harden the default browser surface by disabling wildcard CORS unless `CORS_ORIGINS` is explicitly configured. - Fall back from run-all SSE to job-status polling when the browser/proxy drops the job stream. - Treat already-running transcribe/translate jobs as attach-to-log states in the dashboard instead of showing raw 409 JSON. - Stop duplicating script log lines on future local/sidecar script starts by letting scripts write `LOG_FILE` once and sending stderr to the log.