# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [v1.6.6] - 2026-10-31 ### Added - Show opt-in same-source R18 evidence in scrape review (#474). - Add opt-in read-only catalog name output to scrapes (#427). - Add confirmed offline actress name sync with preview, batch jobs, API worker and catalog UI (#452, #453, #454, #457, #472). - Preserve target scalars with an explicit merge policy and opt in to transactional imported name updates (#396, #397). - Add verified catalog filters while preserving UI state (#389). - Add guarded per-performer name overrides and guarded source-name corrections with provenance UI (#401, #441). - Add an isolated metadata provider resolver boundary (#456). - Add unwired bounded target-safe metadata acquisition (#473). - Add bounded cache groundwork for image input validation, evidence snapshots, an offline r18.dev dump adapter and POSIX artifact publication (#444, #446, #447, #448). ### Fixed - Add explicit video collision decisions and guide credit-blocked files to current collision details (#378, #379). - Keep recursive metadata and artwork beside each movie (#387). - Retain native cast names in JapaneseName and decode cast labels once before routing (#386, #440). - Decode Jav321 actress entities before splitting names (#477). - Keep JavLibrary metadata fields within their own sections and preserve fallback when the HTML parser rejects a document (#418, #537). - Recognize EBML unknown-size VINT values (#405). - Create migration metadata only after acquiring the startup lock, bound malformed stored migration hash diagnostics, and retain preview rollback ownership after cancellation (#433, #434, #436). - Preserve bulk outcomes and sanitize boundary failures (#420). - Join the startup sweep during shutdown (#439). - Fence scrape admission on server cancellation and stop preview collision planning after cancellation (#471, #484). - Tolerate refused chmod on ACL-managed staging files and use exclusive rename on Darwin (#390, #424). - Harden artifact staging seal key loading (#392). - Preserve explicit numeric scraper overrides (#398). - Restore the last browse location after scrape navigation, restore the Jobs review source after automatic completion, and restore document titles across navigation (#426, #463, #553). - Redact paid Google request construction errors (#432). - Pin validated endpoints within dial budgets (#445). - Honor opt-in Undo backup retention (#464). - Update golang.org/x/net for HTML security fixes (#493). - Close DMM direct probe response bodies and reject empty DMM results after streaming redirects (#500, #546). - Release owned FlareSolverr sessions on graceful teardown (#545). - Resolve dot autocomplete in the current directory (#532). - Preserve source credit identity before enrichment (#556). ### Changed - Reduce positive-DMM import scans, index canonical import matching transactionally, and skip unchanged candidate quarantine refreshes (#388, #399, #402). - Remove unreachable version handler fallbacks, unreachable scraper fallback branches, redundant actress template handlers and impossible name and encoding branches (#523, #526, #531, #550). - Consolidate scraper charset fallback handling and simplify concrete update status callers (#528, #534). - Share event filter query predicates, apply progress callback wiring, FileBrowser stale-selection pruning, folder-name budgeting and translation model picker presentation (#375, #376, #377, #381, #382). - Limit NFO pointer metadata to emptiness checks (#541). - Extend offline and boundary coverage contracts across CLI, API, scraper, database, storage, worker, publication and native filesystem paths (#394, #400, #403, #466, #524, #535). - Raise the Windows test job timeout to 40 minutes, isolate database-heavy Windows unit suites and allow frontend browser setup within the overall job budget (#393, #438, #513). - Clarify imported alias metadata boundaries and the durable name correction workflow (#395, #451). - Accept either fail-closed path error when the working directory is removed, keeping the API suite green on macOS. ## [v1.6.5] - 2026-10-21 ### Added - Add optional center and fit poster fallbacks (#373). ### Fixed - Isolate batch test working directories to prevent accumulated fixture timeouts. - Preserve indexed lookups for legacy R18 dump sidecars (#374). - Preserve upstream service variants and Unicode DVD IDs (#372). - Clarify strict-upload row rejection diagnostics (#371). - Avoid redundant chmod on exact-mode staging files (#370). - Preserve mixed-case source content IDs (#369). ### Changed - Share poster success reconciliation (#365). - Extract production review selection model (#364). - Share selected operation revert runner (#363). - Centralize output rendering options (#362). - Share authoritative progress count selection (#361). - Remove unused genre replacement editor (#360). - Unify structural validation (#359). ## [v1.6.4] - 2026-10-20 ### Added - Add manual dump uploads (#273). - Activate ASCII remaster identity with guarded sources (#346). ### Fixed - Honor umask for published directories (#358). - Omit empty FlareSolverr session IDs (#355). - Exercise concurrent sweeps on native filesystem (#356). - Discharge legacy credit errors after successful retry (#352). - Keep new collision-only attempts retryable (#351). - Preserve selection when movie groups change (#350). - Report retained staging proofs and preserve recovery state (#348). - Recognize persisted legacy credit gate failures (#349). - Preserve channel prefix on selected raw CID (#347). - Bind dump lookup and remaster source identities atomically (#344). - Resolve remaster queries with end-to-end identity guards (#345). - Guard raw CID and parsed page identity (#341). - Validate remaster selection and final detail identity (#342). - Add guarded marker-aware dump candidates (#340). - Expand exponent settings input before integer parsing (#338). - Compose history filters and bound replacement pagination (#337). - Cancel and join dump workers during runtime shutdown (#336). - Preserve word drafts and show current settings errors (#335). - Honor translated metadata priorities and source attribution (#334). ### Changed - Isolate Windows workflow offline test budget (#353). - Remove superseded timing-based rollback assertion (#343). - Share LLM HTTP execution (#339). ## [v1.6.3] - 2026-10-18 ### Fixed - Retain selected raw actor evidence across alias conversion (#333). - Preserve legacy actor thumbnail edits and omit blank separators (#332). - Keep actor selection, save receipts and credited fallback consistent (#331). - Preserve actor-credit choices during forced rescrape (#329). - Reject destructive empty actor renames in movie edits (#330). - Refresh inferred legacy single-part actor names safely (#328). - Handle pinned preview connection cleanup errors. - Retain NFO cast projection across rescrape ID correction. - Preserve actor credit choices across NFO merges (#325). - Reconcile actor identity lifecycle during import (#323). - Stage timeout outcomes until the database fence commits. - Preserve credit review recovery and mutation ordering. - Derive display titles from authoritative credits. - Retain credit-fenced Apply timeout outcomes. - Align the organize credit gate with live credits (#320). - Truncate multiline Discord release notes correctly (#319). ### Changed - Avoid intercepting Vite imports in retry browser mocks. - Make watchdog and idle-reclaim timing deterministic (#326). - Use native paths for rescraped actor images. - Synchronize expired writeback with completed SQLite rollback. ## [v1.6.2] - 2026-10-16 ### Fixed - Keep completed dump imports from stalling on optional EOF probes while retaining resumed-payload stall detection (#316). - Isolate review refresh state between jobs and prevent Apply or retry after failed saves or non-authoritative refreshes (#317). - Fix native BSD lint validation while preserving primary Undo timestamp precision. - Avoid redundant staging copies for organize and in-place video moves; allocate staging on the planned destination filesystem, including writable destinations with read-only parents (#276, #282, #285). - Preserve configured subtitle policy and source ownership across copy, link, multipart and case-insensitive publication; keep internal busy markers out of published artifacts and allow safe cleanup retries (#281, #284, #290). - Preserve existing resident videos, aliases and unowned artwork during Apply and Undo; recover owned directory moves and retain unresolved recovery state (#291, #292, #295, #296). - Preserve restored replacement files during cleanup; retain native Windows volumes when restoring primary files and NFO snapshots (#294, #303, #304). - Add journaled, copy-first cross-volume primary Undo on supported POSIX platforms, with identity checks, retry retention and cancellation checks before further mutations (#303, #306). - Show failed or skipped Undo outcomes accurately and keep eligible retries available (#300). - Keep failed-Apply recovery safe after cancellation; prevent late Apply updates from racing Undo, and commit recovery records together with their paths and status (#315). - Stabilize title previews across metadata refreshes and catalog-identity choices; preserve movie-specific actress-name drafts through refreshes and asynchronous saves (#276, #282, #283, #286). - Resume interrupted r18.dev dump downloads using validated HTTP Range requests; exclude local importer work from the network-stall budget and finish cancelled SQLite rollback before temporary-file cleanup (#272, #301, #309). - Honor cancellation during DMM direct URL probes (#299). - Omit unsupported temperature parameters for explicitly recognized OpenAI reasoning models while preserving other provider/model behavior (#288). - Preserve committed configuration during failed publication and concurrent initialization (#289). - Preflight no-clobber capability before verified payload streaming on unsupported destination volumes (#308). - Stop and join the API-owned token cleanup worker during shutdown and explicitly validate nil dependency receivers (#310, #311). ### Changed - Replace live-network short-test dependencies with synthetic/offline fixtures and add guarded Linux/Windows CI; isolate the synthetic UNC fixture from host filesystem access (#298, #307, #312). - Expand native Windows, isolated SMB, FUSE, browser and recovery regression coverage; stabilize native identity and timing fixtures (#274, #287, #293, #297, #302, #305, #308). - Update the pinned linter for Go 1.27 export data (#271). ### Known limitations - Existing distinct video files are still protected against overwrite. Issue #277 remains open; related resident-file fixes do not establish resolution of the original report. - Volumes lacking both supported atomic no-replace publication primitives remain unsupported. FUSE fixtures do not establish compatibility with a particular Unraid/shfs deployment; actual Unraid validation is deferred (#252). - Windows cross-volume primary Undo remains unsupported and fails closed. Ambiguous historical recovery records are not migrated; interrupted cleanup claims may require manual recovery. Legacy unowned staging directories are retained. - Unresolved failed-Apply reconciliation can leave Undo blocked and require manual recovery; missing recovery proofs are not reconstructed automatically. Do not clear recovery markers or use older binaries to Undo affected operations (#315). - AI-remastered title sorting (#254 / #257) is not included. - The historical Windows Go GC crash and subsequent test hang remain unexplained. The lifecycle/nil-validation fixes and successful bounded tests do not prove their cause or resolution. Diagnostic-only PR #313 is not included. ## [v1.6.1] - 2026-09-27 ### Fixed - Stop title preview flap from volatile actress metadata (#269) - Resolve pre-commit golangci-lint robustly (#267) - Replace 30m r18dev dump download deadline with stall watchdog (#266) - Render unquarantined scrape candidates (#265) ## [v1.6.0] - 2026-09-24 ### Added - Separate actress identity from per-movie credits: the actress database is the source of truth, scrape results record reported attribution per movie without mutating identities, ambiguous names surface as review candidates, and per-movie credit overrides persist independently of the identity - Gate actress image URLs in NFO actors ### Fixed - Review thumbnails, name matching, and per-movie overrides: identity-snapshot credits refresh on every rename path (catalog edit, review rename, collision adoption), scraper-reported and user-touched credits are never rewritten as snapshots, explicit thumbnail clears are honored with stale-payload drift protection, and persisted credit overrides update the authoritative review state immediately (#264) - Exclude male co-stars from javdb casts and keep actress thumbnails - Test unsaved proxy profiles during initial setup (#259) - Probe no-clobber capability before staging streams (#255) - Restore background job indicator after page reload (#253) ## [v1.5.2] - 2026-09-08 ### Added - Detect cd/disc/disk multipart suffixes (closes #224 phase matcher) (#242) - Per-destination lock registry unification for organizer (#239) - Dry-run duplicate preflight + subtitle modes (#241) - Audit crumb for force-overwrite of occupied destinations (#249) - Persist batch skip warnings to history (#248) - Require proven source identity for manual poster crops (#251) - Expose typed warning codes to API and UI (#236) - Wildcard match mode for word replacements (#228) - Recover review loads and apply state (#222) ### Fixed - Codex review iteration on crop-identity enforcement: bounded poster verify body (64 MiB), poster-verified marker clear semantics, wrapped recrop refusal recognition (#251 rounds 1–11) - Carry multipart part-suffix into sort planning (#243) - Refuse silent overwrite of same-named multipart destinations (#225) - Atomic no-clobber terminal ops via fsutil composites (#234) - Kind-typed organize conflicts; authorization applies to files only (#235) - Joinable background-check shutdown (Windows TempDir race) (#250) - Skip revert journaling in dry-run and dedupe duplicate-path message (#247) - Match censored-token word replacements in CJK text (#231) - Honor rename_file=false for rename-in-place operation (#230) - Reflect rename_file setting in rename-in-place description (#233) - Image crop/reader zero-dimension guard coverage - Windows CI tolerance for transient bridge path walk entries ### Changed - Resolved stepDownload's PosterVerified flag from aggregate Downloaded to per-media-type verification (#251 round 9) ## [v1.5.1] - 2026-08-12 ### Added - Resolve null-dvd_id titles via dump content-id candidates (#205) ### Fixed - Use canonical movie ID for poster crop source URL (#206) - Eliminate import heartbeat data race in r18devdump (#207) ## [v1.5.0] - 2026-08-09 ### Added - Add server-side image cache for review preview images (#197) - Write in tiered Kodi 19+ collection format (#196) - Surface tag in folder/file naming UI (#194) - Add overwrite-media option with SSR-stable browse plan UI (#177) ### Fixed - Render statically served SPA on hard navigation (#204) - Expand template tags in tagline and custom NFO tags (#202) - Page-derived identity for JavLibrary + JavStash, URL redaction hardening (#198) - Detect letter+quality-tag multipart suffixes (#193) - Persist manual poster crop through organize (#187) - Harden release version flow and add artifact assertions (#185) - Native export save + in-app import confirms (webview gaps) (#183) - Local project coverage matches Codecov + fail-loud test run (#181) ### Changed - Skip live-network tryDirectURLs tests in short mode (#203) ## [v1.4.0] - 2026-07-29 ### Added - Wire history record creation during scrape/organize (#171) - Redesign review page with sidebar layout and UX improvements (#170) ### Fixed - Correct actress panel spacing, dynamic grids, and single-column layout (#175) - Honor unknown_actress_mode=skip in GroupActress @Unknown substitution (#174) - Clear error when path overhead exceeds max_path_length + CJK rune truncation (#173) - Match plain numeric IDs in itemIDRegex (#172) ## [v1.3.3] - 2026-07-24 ### Added - Add bidirectional favorite-genre shuttle in movie editor (#169) - Add favorite genre quick-apply palette (#168) - Show existing NFO values alongside scraped data (#160) ### Fixed - Sync scraper ProxyEnabled with resolved proxy mode (#167) - Use CREATE_NO_WINDOW for silent Windows upgrade helper (#166) ## [v1.3.2] - 2026-07-21 ### Added - Group actress min threshold config option (#163) - Language selector on pre-auth (setup/login) screens (#165) ### Fixed - Infinite page-refresh loop for Simplified/Traditional Chinese browser locales (#164, #165) ## [v1.3.1] - 2026-07-19 ### Added - Minimal config persistence with user-only scraper overrides and sparse saves (#161) - Surface localized, typed scrape-error messages (#157) ### Fixed - Separate title and display_title fields to prevent code doubling (#159) - Raise request_timeout_seconds default to 180s and keep environment overrides out of config.yaml ## [v1.3.0] - 2026-07-17 ### Added - Add --output json mode to scrape for machine-readable per-scraper results (#155) - Full internationalization support across Web UI, TUI, and API (#144) ### Fixed - Enforce configurable timeouts and guard against hard-coded literals (#154) - Join background check goroutine to fix flaky TempDir cleanup - Prune phantom file selections on directory refresh (#147) - Preserve matcher MovieID when scraped ID differs, preventing wrong grouping (#146) - Check for duplicate DMM ID before actress update - Reuse configured port across desktop restarts instead of random ### Changed - Guard web-build deps and add build-app-local Makefile target - Replace ProgressFunc with context-scoped ProgressReporter (#151) - Extract job persistence codec into jobpersist package (#150) - Extract fs utilities into fscase and fanout packages (#149) - Extract result tracker into resultstore package (#148) - Remove openspec from tracked .gitignore - Remove tracked unused.yaml fixture, use temp dirs in tests - Ignore node_modules at repo root, not just web/frontend ## [v1.2.3] - 2026-07-15 ### Fixed - Preserve matcher MovieID when scraped ID differs, preventing wrong grouping (#146) - Check for duplicate DMM ID before actress update - Reuse configured port across desktop restarts instead of random ### Changed - Remove tracked unused.yaml fixture, use temp dirs in tests - Ignore node_modules at repo root, not just web/frontend ## [v1.2.2] - 2026-07-13 ### Added - Expose r18.dev dump management via API + WebUI (#136) - Validate disabled-scraper priority overrides (#138) ### Fixed - Manual inputs override multi-part grouping key so files with different manual IDs split into separate movies (#143, closes #142) - Seamless progress bar with monotonic phase transitions + clear file selection after batch scrape completion (#141) - Retry lock release on Windows to handle AV/sharing violations (#137, #140) - Resolve ABF-030 misresolution via content-id variations (#135) - Use high-res cover as poster in r18dev dump path (#134) - Use javinizer icon for favicon instead of generic J logo (#130) - Lower MinPoster threshold to accept real DMM posters (#132) ### Changed - Codex label sync tracks latest review, not unresolved threads (#139) - Block npx/npm drive-by suggestions from sleeper accounts - Disable auto-triggering @codex review from label sync - Grant pull-requests: write for codex label sync - Use PowerShell syntax for APPDATA in migration snippets (#133) - Cover missing readFileWithRetry not-exist path ## [v1.2.1] - 2026-07-11 ### Added - Display actress names in first-name order when config enabled (#127) ### Fixed - Respect actress_language_ja preference in name display (#129) - Run organize step for in-place rename modes (#125) - Upgrade DMM covers/posters to high-res awsimgsrc CDN (#124) ### Changed - Add Codex review label sync workflow (#126) ## [v1.2.0] - 2026-07-10 ### Added - Single-line progress bar to r18dev dump download - First-run setup as a 3-step wizard - Ignore-list and favorites UI to genre management ### Fixed - Recover orphaned running/pending jobs on startup - Deny root-resolving allowed_directories entries - Pin actions/github-script to v7 tag - Make TestAcquireConfigFileLock_Contention deterministic - CORS preflight, default port, `` auth in dev environment - Browse box focus ring + allowed_directories persistence - Windows CI flake: SQLite file handle race in batch test teardown - Boundary-aware word replacement for censored tokens - Ship metadata.priority.* as inherited by default ### Changed - Add CLI-to-Desktop migration guide with Windows paths - Harden path picker: autocomplete stays alive + browse respects allowed_directories - Bump Go to 1.26.5 to clear govulncheck GO-2026-5856 - Clarify desktop/CLI relationship, document r18.dev dump - Add anti-spam filter for unauthorized patch/attachment drops on issue comments ## [v1.1.1] - 2026-07-07 ### Added - Desktop app one-liner installers ### Fixed - Poster preview rendering across desktop, r18dev, and web subcommand - Group Update & restart with Check for Updates action - Docker `make docker-run` now works end-to-end (CMD api→web, writable media, trusted CIDRs, quoted paths) - WebSocket hub goroutine shut down before DB close in CreateTestDeps (Windows/Linux temp-dir cleanup flake) - Docker entrypoint chown non-fatal on read-only mounts - Update popover redesign: version flow, copy-to-clipboard command block, docker instructions hidden - Discord release notification embed enriched (thumbnail, author, fields, truncated release notes) - Docker Quick Start admin setup unblocked, migrated to docker compose v2 ### Changed - Docker host port default 8080 → 8765 (less collision-prone) - Bump quic-go v0.57.0 → v0.59.1 (GO-2026-5676) - Lead README Quick Start with desktop one-liner, demote CLI to alternative - Fix cli-release.yml YAML syntax error in Discord notify job ## [v1.1.0] - 2026-07-07 ### Added - Package as clickable desktop app for macOS, Windows, and Linux (#84) - Add desktop bundle self-upgrade (#89) - Add environment-aware upgrade UX (docker/desktop/cli) - Add UI and API for managing allowed_directories (#93) - Add allowed_directories to first-run setup (#94) - Embed Windows app icon via genicon .syso resource - Extract UpgradeAction and force same-origin API in desktop webview - Add per-field source override with source-merge modal (#80) - Add local dump lookup with zero-HTTP fast path for r18dev (#76) ### Fixed - Fix desktop Windows launch, WebSocket progress, and browse security model (#99) - Redesign allowed directories editor and fix PathInput Enter behavior (#98) - Reverse-proxy through Wails asset server instead of redirecting (#96) - Add desktop app icon and fix web favicon (#95) - Resolve NTFS volume mount points in canonicalizePath (#92) - Make hot_reload atomic across registry/config/factories (#82) - Fail-closed on nil auth manager (#81) - Give "Remember me" sessions a longer TTL than ephemeral ones - Detect podman/nerdctl containers and document cgroup v2 limit - Persist window geometry across launches - Point JAVINIZER_DATA_DIR at the portable data dir - Use OS-correct separator in organize preview folder names - Use 'web' subcommand in CLI flag e2e test - Skip window-state perm test on Windows ### Changed - Rename 'api' subcommand to 'web' - Introduce DTOs for MovieView nested collections (#83) - Add make config-sync to mirror the embedded config master - Move Discord notify into cli-release workflow - Add brew trust step for Homebrew 6.0+ tap-trust requirement - Document environment-aware upgrade behavior - Post-v1.0.0 housekeeping - Split e2e suites into frontend/fullstack/live and add upgrade-CTA coverage - Expand fullstack e2e coverage to scrape pipeline and APIs - Cover PersistentSessionTTL getter, NoOpAuth mock, and clamp guard - Cover container-detected branch in fetchWithBrowser - Rename pendingCount to revertEligibleCount to match semantics ## [v1.0.0] - 2026-07-03 ### Added - Add per-scraper output invariants and update-checker live e2e tests - Add PR template with issue auto-close reminder - Add stale issues workflow - Add real-live scraper e2e suite (local only, not CI) - Add live CLI end-to-end suite driving the real javinizer binary ### Fixed - Respect download_extrafanart config when --extrafanart is omitted (#79) ### Changed - Notify Discord webhook on release publish - Rewrite README future-tense framing to present tense for v1.0.0 stable - Ignore build-tagged e2e and generated/vendored paths in codecov - Pin all CLI flags and flag-supersedes-config behavior in e2e - Drop issue #72 refs, SignPath-pending note, and broken code-signing link in README ## [v1.0.0-rc4] - 2026-07-02 ### Added - Add self-upgrade (`javinizer upgrade`), Homebrew/Scoop taps, and one-shot installers for v1.0.0 (#78) - Add alias-aware actress dedup and per-movie NFO name choice (#74) ### Fixed - Harden actress rename path and guard review save-flow (rc3 regressions) (#73) - Address CodeRabbit review: collapse redundant Homebrew darwin branches and restore BackgroundCheck error logging ### Changed - Close codecov patch gaps and enforce the 80% patch gate over informational hide - Cover defensive branches in the update/version commands via DI seams rather than filesystem tricks ## [v1.0.0-rc3] - 2026-06-30 ### Fixed - Persist actress name edits from review page to DB and NFO (#71) - Restore movie.OriginalFileName so `` NFO tag resolves ### Changed - Drop version from release asset names + link to releases page - Add doc comments to exported identifiers + enable exported lint (#47) - Remove 146 stale ADR citations referencing deleted ADRs (#46) ## [v1.0.0-rc2] - 2026-06-30 ### Added - Manual scrape: per-file ID/URL override + /manual review route (#68) ### Fixed - WebUI Output/NFO settings broken by nested JSON (#70) — OutputConfig and NFOConfig serialized as nested objects while the frontend read a flat shape, leaving download checkboxes unchecked and dropping saves; also fixed the delimiter ↔ actress_delimiter mismatch and guarded array .join() against undefined. Closes #69. - Rescrape: refresh images and reset baseline on content-id change (#67) - Poster: preserve original cover URL across resets ### Changed - Docs: fix accuracy across all docs per audit (18 files) - CI(docker): bump login-action to v4.2.0 (Node 24 runtime) - Test: reach 100% coverage on Output/NFO UnmarshalJSON probe branch ## [v1.0.0-rc1] - 2026-06-28 ### Changed - **Workflow seam**: replaced ad-hoc task types (ScrapeTask/DownloadTask/OrganizeTask/NFOTask) with a unified `Workflow` abstraction. Jobs execute via phase-based `BatchJobInterface` (scrape / apply / rescrape), consolidating the worker pool onto a single seam. - **API projection layer**: movie/actress/genre views lifted into `internal/api/contracts`, decoupling the wire format from persistence models (content_id → code rename, PosterState fields lifted to top-level). - **Scraper registry**: replaced the `init()`-based plugin system with a centralized registry (`internal/scraper/registry.go` + `scraperutil` registration catalog). - **Job reconstruction**: `SetReconstructionDeps` re-hydrates infrastructure dependencies on jobs loaded from the database at startup, so post-restart apply/rescrape and movie-edit persistence work. ### Removed - **Dead code**: ~138 intentional deletions consolidated during the refactor — `version.txt` → `version.go` (VCS-embedded via `debug.ReadBuildInfo`), `internal/api/*/aliases.go` re-exports folded in, worker pool refactored onto the Workflow seam. 5 empty/comment-only files removed (zero declarations, zero references). ### Added - **Word replacement toggle + opt-in default**: added `metadata.word_replacement` (opt-in, `enabled: false`) to the example configs and `defaultMetadataConfig()`, and moved the genre/word replacement enable toggles into the dedicated `/settings` sections with null-safe handlers. - **Update-available UI**: surface update availability in the web UI and harden the checker against GitHub rate limits. - **JavDB actress avatars**: construct actress thumbnail URLs from the actor ID in the movie-page link (`https://c0.jdbstatic.com/avatars//.jpg`), populating `ActressInfo.ThumbURL` without extra HTTP fetches. - **CI release hardening**: explicit `make_latest` on the GitHub release step so stable releases deterministically become "Latest" and prereleases never steal it. - **Docker CI**: build arm64 natively via a per-arch matrix (free `ubuntu-24.04-arm` runner) + `merge-manifest` job instead of QEMU emulation, cutting the docker job from ~15min to ~5min. - **Web frontend (SvelteKit SPA)**: review flow with jobId-scoped state and monotonic WebSocket-driven progress bars; fullstack E2E suite + vitest unit tests. - **Mockery freshness gate**: pinned mockery v3.7.1; added a `make check-mocks` target + `test.yml` step that fails CI if mocks drift from their interfaces (caught a real portability bug — `sed -i` macOS-only — on its first run). - **`.gitignore` hardening**: `tmp/`, `web/frontend/playwright-report/`, `blob-report/`, `playwright/.cache/`, `*.db-shm`, `*.db-wal`, `*.log`. ### Fixed - **Per-field metadata.priority exclusivity (consistency + `--scrapers`)**: `AggregateWithPriority` (the selected-scrapers / `--scrapers` path) now honors a per-field override exclusively, matching `resolvePriorities` (the default path). Previously it applied `cmd.SelectedScrapers` as a flat priority for every field, ignoring per-field overrides — so `series: [tokyohot]` (skip Series via exclusivity) still populated Series from DMM when scraping with `--scrapers dmm` (BMD-284). Both paths now use pure v1 exclusivity consistently: a per-field list means "only these scrapers, no global fallback." No skip sentinel — a field is left empty by pointing it at a scraper that didn't run or lacks it (e.g. `series: [tokyohot]`). **Backward-incompatible (config semantics):** an empty list `metadata.priority.: []` previously meant "inherit global"; it now means "consult no scrapers — the field is left empty" (key *absent* is the inherit-global state). If you hand-wrote `: []` in `config.yaml` expecting the field to inherit the global priority, **delete the key** so it inherits; otherwise the field will now be left empty after upgrading. "Remove all" + Save in the priority editor intentionally produces this empty-list state. - **Rescrape merge strategies**: the API previously accepted + validated `preset` / `scalar_strategy` / `array_strategy` then silently dropped them; `RescrapeCmd` now carries `MergeOptions` and `CompleteRescrape` applies the merge (reusing the apply-path logic). - **Theme toggle**: rendered as a component, fixing an unknown-element regression in the web UI. - **Test coverage** raised to meet the 75% CI threshold; race-clean. - **DMM director + rating extraction** restored after the upstream DMM markup migration. - **Image previews** refetch on URL change via a reactive error state, eliminating stale thumbnails. - **Per-field metadata.priority (consolidated)**: `[]` now inherits global; the `["__skip__"]` skip sentinel is honored, with YAML persistence fixed across the priority editor. - **Aggregator per-field priority exclusivity**: per-field `metadata.priority` is now exclusive (not merged with global) consistently across both the default and `--scrapers` paths. - **Update checker**: background update checker + startup check wired up. - **History pagination**: filtered history API endpoints now paginate correctly. - **Poster crop**: preserve `ShouldCropPoster` through `GeneratePoster` so the apply-phase crop runs after rescrape. - **Web rescrape modal**: removed stray `}` in the title; pass `movie_id` (not `result_id`) to `openRescrapeModal`; show feedback when "Reload Config" is pressed. - **Filename template**: strip the extension from the `` token and add ``. - **Vulnerability scan**: bump `x/image` + Go toolchain to clear 10 govulncheck findings; gate the Vulnerability Scan job on token presence. Codecov upload now supplies a token and fails loud on upload error. ### Tooling - **`.coderabbit.yaml`** rewritten as a durable repo-level review policy (dropped the transient review-history header and invalid `review_status` sub-keys; added `auto_review`, `high_level_summary`, `poem`, `review_status: true`). ## [v0.3.15-alpha] - 2026-06-23 ### Fixed - Persist edited poster URL before manual crop so the crop endpoint operates on the edited image, not the stale server-side poster (issue #37) - Persist Move Files setting across restarts via a real Output.MoveFiles config field; --move flag remains a session-only override (issue #36) - Prevent config leakage and harden move-files persistence: saveConfig reloads from disk so session-only CLI/env overrides are not written to config.yaml - Use atomic config.Update (read-modify-write under the file lock) for move-files persistence, eliminating a TOCTOU race and lost updates - Strip stdout/stderr from the TUI logger so logs don't leak into the terminal (the default dual-output "stdout,file" config was leaking) - Harden the TUI log-leak fix: InitLogger errors on zero valid outputs instead of silently falling back to stdout; fix the pre-alt-screen startup leak via isTUICommand; extract a pure configureTUILogging helper - Resolve JAVINIZER_CONFIG in the TUI so LoadOrCreate and SetConfigPath use the same path (config persistence broke when the env var was set) - Guard the runtime move-files toggle against the move+link combo rejected at startup (ValidateMoveLinkMode) - Honor JAVINIZER_LOG_DIR in the TUI fallback log path and report the actual relocated log path - Nil-guard config.Update against a nil mutate callback ### Changed - Add end-to-end stdout-leak tests (os.Pipe capture) plus a non-vacuous contrast test - Add tests for resolveConfigPath, canEnableMoveMode/runtime guard, JAVINIZER_LOG_DIR fallback, and config.Update nil-guard - Prove config.Update atomicity with a 100-writer concurrency test (no lost updates) - Cover persist-before-crop in the poster crop controller - Use the real TUI command in the isTUICommand detection test; use io.ReadAll for stdout pipe capture consistency ## [v0.3.14-alpha] - 2026-06-22 ### Fixed - Resolve poster/cover reactivity and edit persistence bugs - Add id DESC tiebreaker to job-list ordering ## [v0.3.13-alpha] - 2026-06-21 ### Added - Configurable max_poster_height with frontend override ## [v0.3.12-alpha] - 2026-06-20 ### Added - Implement User-Agent priority chain for scrapers - Support actress Japanese name in folder/file templates - Add @Unknown substitution for grouped unknown actresses - Support tag-level first-name-order modifier on actress tags - Rename output.delimiter -> actress_delimiter; DELIM= modifier; hard break ### Fixed - Disable proxy by default since r18.dev doesn't need it - Capture PPV underscore prefixes in content_id lookup - Apply word replacement to genres - Stop upgrading poster URL ps.jpg -> pl.jpg - Set shouldCrop=true when falling back to cover poster - Address sub-agent code review findings ### Changed - Regenerate swagger docs - Update template system docs for new actress tag features ## [v0.3.11-alpha] - 2026-06-07 ### Fixed - Generate 3-digit padded content_id variations for lookup prefixes (fixes ABF-346 and similar titles) ### Changed - Fix stale doc comment on generateContentIDVariations ## [v0.3.10-alpha] - 2026-06-07 ### Added - Content-ID variation fallback for digital-only r18.dev titles that return 404 on the dvd_id endpoint - Awsimgsrc poster resolution using prefix lookup table for digital/video content - Screenshot discovery fallback that probes pics.dmm.co.jp when the API returns no screenshots or placeholder filter removes all - DepadContentID to handle zero-padded content_ids (e.g. 118gets00081 → 118gets081) - Configurable RespectRetryAfter setting for r18dev scraper with tri-state *bool (nil = default true) - 274KB content_id_prefixes lookup table generated from r18.dev database dump ### Changed - Retry logic uses max(Retry-After, exponential backoff) when RespectRetryAfter is enabled - Export GetImageDimensions from imageutil for reuse - Move DiscoverScreenshots fallback after placeholder filter so it triggers when all screenshots are filtered ## [v0.3.9-alpha] - 2026-06-06 ### Added - Replace mutable movieId with stable ResultID for batch job API lookups ## [v0.3.8] - 2026-06-05 ### Added - PathInput component with whitelist autocomplete for source and destination path inputs ### Changed - Improve multipart detection with TrailingPrefix and prefix-based validation ## [v0.3.7-alpha] - 2026-06-02 ### Added - Trailer preview, review tabs, and View Failed button on jobs page ### Fixed - Redirect View Failed to Failed tab and sync tab URL bidirectionally - Extract javlibrary rating from `var $rating` JS var and add aggregator-side range check that surfaces corrupt scores as warnings ## [v0.3.6-alpha] - 2026-05-31 ### Added - Expand LibreDMM scope to support FC2/MGStage/SOD sources - Surface unidentified files and allow manual rescrape from review page - Add first_name_order config for actress name formatting ### Fixed - Fix caribbeancom English URL construction and reject soft-404 responses - Detect caribbeancom English blank-entry soft-404 (var Movie = null) - Fix docker frontend-builder stage ordering and limit Rollup parallel file ops - Remove content ID zero-stripping canonicalization that broke r18.dev image URLs - Refresh aggregator replacement caches after CRUD operations - Detect Windows cross-device move error, add customizable GroupActressName ### Changed - Embed swagger.json in binary via go:embed - Clarify --scrapers flag only selects from enabled scrapers - Add .omo/ to .gitignore ## [v0.3.5-alpha] - 2026-05-25 ### Fixed - Extract DMM screenshot URL normalization into shared imageutil package, fixing R18Dev screenshots missing `jp` suffix which caused smaller thumbnails to be pulled (issue #23) - Fix DMM new-site extractScreenshotsNewSite missing NormalizeDMMScreenshotURL - Fix JavLibrary incorrect jp- screenshot filter that was filtering out higher-resolution images - Remove LibreDMM dead ThumbnailImageURL-derived poster code that was always overwritten ### Changed - Consolidate duplicated DMM screenshot URL normalization logic from 6 scrapers into shared `internal/imageutil/` package (NormalizeDMMScreenshotURL, UpgradeCoverResolution, GetOptimalPosterURL) - All 6 scrapers (DMM, R18Dev, JavBus, LibreDMM, JavLibrary, DMM actress) now use shared normalization pipeline for consistent CDN rewrite, host lowercase, and cover resolution upgrade - Actress thumbnail normalization in DMM actress.go delegates to shared imageutil.NormalizeDMMScreenshotURL instead of inline CDN rewrite ## [v0.3.4-alpha] - 2026-05-02 ### Fixed - Replace contentIDMatchesExpected with core series+number validation (contentIDCoreMatch) to reach 97.7% r18.dev match rate, ignoring suffix mismatches that caused 84% of old failures - Remove generateAlternateContentIDs fallback which matched 0% of misses due to only trying 4 hardcoded maker codes out of 7,704 - Prevent ONED-025 false positive resolving to ONED-205 by validating core number match ## [v0.3.3-alpha] - 2026-05-02 ### Added - Completeness scoring system with configurable essential/important/nice-to-have tiers and weighted percentage calculation - CompletenessDial SVG component with tier-appropriate colors (red/yellow/green) and hover breakdown tooltip - Completeness filter buttons on review page to filter movies by Incomplete/Partial/Complete - Grid card selection mode with shift-click range selection, select all/deselect all, and bulk exclude/rescrape actions - Review page grid view with poster and cover display modes (3-column layout for cover art) - WebUI config section with `default_review_view` setting (detail, grid-poster, grid-cover) and settings UI - Runes-based global background-job store (`$state`) replacing local component state for cross-page persistence - BackgroundJobIndicator and ProgressModal moved to authenticated layout so they persist across page navigation - BackgroundJobIndicator redesigned as theme-aware card (`bg-card`) with status-tinted rings and lucide icons - All Go backend job statuses handled in indicator and modal (completed, failed, cancelled, organized, reverted, pending, running) - Status-specific success messages in ProgressModal (scraping completed, organization complete, revert complete) - Real-time job progress synchronization on /jobs page via WebSocket-derived `computeJobProgress` utility - Auto-polling (5s interval) for running jobs on /jobs page, stopping when no jobs are running - `liveProgress` in ProgressModal derived from WebSocket data instead of REST API polling - Shared `$lib/utils/job-progress.ts` utility with `TERMINAL_STATUSES`, `isTerminalStatus()`, and `computeJobProgress()` - `clearJobMessages()` method on websocket store for evicting stale per-job data - WebSocket store deep-copy for `messagesByFile` per-job records (store immutability) - Capped WebSocket `messages` array at 200 entries to prevent unbounded growth - Playwright E2E test suite (98 tests) for review page: completeness dial, selection mode, bulk actions, view toggle, keyboard navigation, filter persistence, and edge cases - Review state unit tests for completeness computation and view mode handling - Completeness utility tests for tier scoring, edge cases, and custom weight validation - Batch rescrape endpoint with scraper selection, NFO merge strategy presets, and progress indicator - Batch exclude endpoint with per-movie and bulk exclusion support - Movie edit PATCH endpoint for inline field editing on review page - `Update` field persisted on `BatchJob` and `BatchJobSlim` for update mode tracking - Database migration `000008_jobs_update_column.sql` for jobs update column - Comprehensive backend test coverage: batch rescrape (unit + integration), batch exclude integration, API handlers (actress, auth, genre, history, events, version, system, jobs), config validation/redact, NFO merger DMMID preservation, database helpers/race branches, WebSocket hub nil guards, fsutil move, httpclient builder, DMM scraper helpers, worker single_scrape branches ### Changed - Rename `metadata-only` operation mode to `metadata-artwork` across codebase (Go types, organizer strategy, config, tests) - Derive version from git tags/commit hash instead of embedded `version.txt` file (removed `version.txt`) - Review page view mode is now 3-state: `detail | grid-poster | grid-cover` (legacy `grid` maps to `grid-poster`) - ProgressModal uses two separate `$effect` blocks with `untrack()` to avoid `cancelRedirect` circular dependency - ProgressModal auto-redirect countdown now guarded by `hasNavigated` flag preventing duplicate navigation - ProgressModal `latestMessage` derived from `messagesByFile` instead of capped `messages` array - `TERMINAL_STATUSES` aligned with Go backend: `completed`, `failed`, `cancelled`, `organized`, `reverted` (removed non-existent `done` and `skipped`) - `createBatchJobPollingQuery` uses `isTerminalStatus()` to stop polling for all terminal statuses - Homepage `activeJobCount` uses shared `isTerminalStatus` utility instead of inline Set - E2E test selectors updated from `/^grid$/i` to `/^poster$/i` for view mode toggle buttons ### Fixed - Scraped movies now always persisted to database regardless of custom scraper selection - NFO actress merge preserves DMMIDs from scraped data when matching by JapaneseName or romanized name - Omit `Translations` in `Upsert` to prevent transaction rollback on orphaned rows - Frontend review page reads destination from job data instead of query param - Actress search switched to server-side to prevent client-side performance issues - svelte-check added to pre-commit hook to catch TypeScript errors early - BackgroundJobIndicator auto-dismiss timer re-arms when user closes modal after terminal status (tracks `showModal` as dependency) - Auto-dismiss timer callback guards against null `jobId` and verifies prop matches store's current jobId - ProgressModal dismisses job store state after successful `goto()` navigation (handles rejection too) - `reopenModal()` guards against null `jobId` to prevent inconsistent store state - `Math.max(0, ...)` for remaining files count when `completed + failed > total_files` (retry scenarios) - Windows path separators handled in browse page file display (`split(/[\\/]/)`) - Unnecessary `Record` type assertion removed from browse page config access ## [v0.3.2-alpha] - 2026-05-01 ### Added - API token authentication with bearer tokens (jv_ prefix, SHA-256 hash storage, one-time display) - Token CRUD REST endpoints: POST/GET/DELETE /api/v1/tokens, POST /api/v1/tokens/:id/regenerate - `requireTokenOrSession` middleware coexisting with existing session/cookie auth - `javinizer token create/revoke/list` CLI subcommands with `--json` output - API Tokens section in web UI settings with create, revoke, and regenerate flows - TokenDisplayModal with security warning, copy-to-clipboard, and one-time display pattern - Rate limiting on token write endpoints (create, revoke, regenerate) ### Fixed - MediaInfo OOM issues — use mp4.DecModeLazyMdat to skip mdat data in MP4/MOV parsing, add 16MB element size cap in EBML reader, fix EBML readVintSize mask bug for lengths 3-6 - Replace ebml-go with manual EBML parser and add mp4_fallback.go for manual ISOBMFF parsing when mp4ff fails - Remove FLV support (flv.go, flv_test.go) - TokenService.Validate() now uses synchronous UpdateLastUsed matching middleware behavior - Token CRUD endpoints moved to writeProtected group with IP rate limiting ## [v0.3.1-alpha] - 2026-04-30 ### Added - Word replacement (uncensor) system with database-backed replacements and API endpoints - Import/export API endpoints for genres, actresses, and word replacements - Frontend types and API client methods for import/export - Import/export UI to genres, words, and actresses pages - CLI word command with list/add/remove/export/import subcommands - Import/export subcommands to genre and actress CLI commands - Playwright e2e test infrastructure - E2e tests for import/export with fixtures - E2e auth mode with rate limit bypass - E2e tests for import/export across all three features - Support both id and original query params for genre/word deletion endpoints - Swagger annotations added to all API handlers - golangci-lint config expanded with custom rules - govulncheck added to CI pipeline - Frontend tests included in CI pipeline ### Fixed - R18.dev scraper reliability — rental ID handling, direct URL fallback, alternate content IDs, nil guards, regex dedup - DMM scraper hardening — priority scaling, cover extraction, proxy auth, JSON parsing, nil guards - Replace remaining native confirm() with confirmDialog - Hide Review & Organize button when no files completed successfully - Address code review findings on import/export and Upsert paths - TypeScript type errors in e2e tests - SQLite lock detection — add string-based fallback for database is locked errors in retryOnLocked - E2e runner port check to only match LISTEN state - Export endpoints to use GET instead of POST - defer fileCancel() in for-loop context leak - initBatchDependencies error handling - Debug log artifact removed from production logging - TypeScript type for original_should_crop_poster uses `boolean | null` matching Go `*bool` - context.Background() replaced with caller context in scraper GetURL wrappers - Scraper GetURL method naming normalized - Decomposed batch functions use consistent error wrapping - saveScrapedResult returns Upsert errors to callers - Frontend store errors surface to users via toast/notification - Retry loops use context-aware sleep - applyDisplayTitle exists in single shared location - NFO discovery logic extracted into shared helper - Duplicate normalize* functions consolidated into shared scraperutil helpers - Duplicate FlareSolverrConfig unified into single definition - Unused `_ = db` parameter removed from 11 scraper module.go Config() methods ### Changed - Switch genre/word replacement deletion from original-key to ID-based (both id and original params supported) - Gitignore Playwright test artifacts (test-results/, auth-state.json) - All `any` types replaced with proper typed interfaces - settings/+page.svelte split into focused sub-components - review/[jobId]/+page.svelte split into focused sub-components - actresses/+page.svelte split into focused sub-components - Debug console.log statements removed from production code - Large files (dmm.go, translation/service.go, aggregator.go) split into focused files - RunBatchScrapeOnce, processUpdateMode, processOrganizeJob, processBatchJob decomposed into focused sub-functions ## [v0.3.0-alpha] - 2026-04-29 ### Added - StandardModule base struct and BaseScraperConfig eliminating ~2,150 lines of module.go boilerplate across 14 scrapers - ValidateCommonSettings shared validation function replacing 14 identical validation blocks - NewScraperHTTPClient and InitScraperClient helpers replacing 14 per-scraper httpclient.go files - BaseRepository[T, ID] generic struct with CRUD methods for 12 database repositories - ResolveNFOPath/FindNFOFile shared helpers replacing triplicated NFO path discovery logic - FormatActressName package-level function consolidating duplicated implementations - core.ParsePagination helper replacing 4 duplicated limit/offset parsing implementations - toHistoryRecord and paginateAndConvert helpers eliminating 4x copy-pasted conversion blocks - Svelte-query QueryClientProvider with SSR-safe lazy client module - Poster-from-url, exclude-movie, and save-edits mutations using createMutation - Shared query helpers for config and scrapers with cache deduplication - createBatchJobPollingQuery helper with refetchInterval for polling components - Reactive actress list query with CRUD mutations and merge flow - Dark mode with class-based toggle and styling across all pages - Grid view for review page with ReviewGridCard and viewMode toggle - Use-as-poster button for screenshots in review page - Favicon for web frontend - unknown_actress_mode config (skip by default, fallback for placeholder) - Use-as-poster backend with poster generation, reset, and cache-busting - Shift-click range selection for files and persistent recursive checkbox - Translation warnings propagated to frontend UI - BaseFileName in OrganizePlan, MaxPathLength enabled in preview - Auto-switch operation mode to 'Rename file only' when destination matches source path with empty folder/subfolder format ### Fixed - Recursive chown on /javinizer for Unraid compatibility — fixes pre-existing root-owned files from prior versions - Convert javstash runtime from seconds to minutes - JavLibrary search result parsing: handle current HTML format with videothumblist divs, multiline HTML, attribute-order independence - JavLibrary double language segment in URL construction when legacy pattern returns /en/?v=... - JavLibrary over-broad cover-screenshot filter that removed all screenshots - FlareSolverr proxy bypass in direct mode — removed fallback that overrode empty proxyProfile - URL encoding for query parameters in GetURL/ScrapeURL - Missing template tags and YEAR/Translations data loss - Template structure preservation during MaxPathLength truncation - Truncated path names use ~ instead of ... with improved in-place preview path display - Umask respected in file/directory permissions for Docker group-write support - MGStage prefixed IDs (GANA-2850 → 200GANA-2850) and hyphenated ID format for search queries - Actresses matching unknown_actress_text filtered from scraper results - Stale actress/genre associations cleared when upserting with empty lists - Global scraper priority merged as fallback when per-field overrides exclude sources - Actress name keys normalized in aggregator and batch jobs cancelled when all movies excluded - DMM-resolved ContentID propagated as fallback with diagnostic logging - Operation_mode forwarded in organize request - Organizer regression: copyOnly path, subtitle handling, truncation guards - OOM issue #13: reduced memory in crop/placeholder/dimension reads, persist poster crop state - Output preview refreshed when movie fields are edited on review page - Genre replacement DELETE uses query param to handle special characters - Local state used instead of direct onUpdate for Use as Poster - Live R18.dev test skips on 403 instead of failing - Direct proxy mode does not leak global proxy to FlareSolverr - Inherit proxy mode passes global proxy to FlareSolverr ### Changed - All 14 scrapers migrated to shared infrastructure (StandardModule, BaseScraperConfig, shared HTTP client) - All frontend pages migrated from manual fetch + $state patterns to TanStack svelte-query - MoveToFolder/RenameFolderInPlace removed from OutputConfig, API contracts, and frontend - Deprecated merge_strategy removed from NFOComparisonRequest API contracts and Swagger - Deprecated MergeMovieMetadata() and ParseMergeStrategy() functions removed - No-op validateNoLegacyProxyDirectFields() stub removed, rejectUnknownProxyFields() wired - Stale artifacts deleted: coverage files, root binaries, orphaned testdata, stale docs - Preview generation refactored to use OperationStrategy.Plan() - 86 files changed, +3,795/-1,598 lines ## [v0.2.11-alpha] - 2026-04-28 ### Added - Persist file browser sort preference across navigation within session - Use '...' truncation marker with '~' fallback for trailing dots ### Fixed - Prevent blank file on Windows when src==dst and harden preview/organizer edge cases - Windows mixed slashes in preview and empty folder format creating unwanted subfolders ## [v0.2.10-alpha] - 2026-04-27 ### Added - Client-side pagination to FileBrowser - Pagination controls above file list in FileBrowser - Allow Docker bridge access to /auth/setup via configurable trusted CIDRs - GitHub issue templates for bug reports, feature requests, and scraper issues ### Fixed - Allow same-origin WebSocket and CORS when AllowedOrigins is configured - Remove user directive from docker-compose to enable entrypoint privilege bootstrap - Add Docker defaults for setup CIDRs and fix bind mount ownership - Resolve AP-288 404 from r18.dev by accepting blank dvd_id with validated content_id fallback - Add TokyoHot short-prefix ID matching and search query resolution ## [v0.2.9-alpha] - 2026-04-22 ### Fixed - Apply environment variable overrides (DEEPL_API_KEY, OPENAI_API_KEY, etc.) before config validation in `LoadOrCreate()`, preventing "api_key is required" errors when keys are set via env vars but empty in config file- Persist per-field scraper priorities to config file and add scrollbar to priority modal- Clean `web/dist` before `git checkout` to remove stale build artifacts- Update `with_embedded_web.sh` restore_placeholder to use `git checkout`- Add `ACTRESS` singular tag and fix `ACTORNAME` to resolve from actress data instead of movie title- Add `subfolder_path` to organize preview response and improve cross-platform compatibility- Add cross-device move fallback to file organizer ### Changed - Switch nightly releases to commit-hash tagging scheme- Remove duplicated `web/placeholder` dir, use `git checkout` in Makefile restore target ## [v0.2.8-alpha] - 2026-04-19 ### Added - Windows CI test runner for path-specific bug detection- `fsPath()` helper in `internal/history/reverter.go` that normalizes paths for afero MemMapFs (forward-slash + drive letter stripping)- `filepath.ToSlash()` normalization on `WillMove` path comparisons in organizer strategies to prevent false positives on Windows- `filepath.ToSlash()` normalization on `isDescendant()` path comparisons for cross-platform directory hierarchy checks- Windows volume root detection in `cleanupEmptyDir` termination conditions- `runtime.GOOS == "windows"` skip guards for Unix-only tests (file permissions, shell scripts, `/dev/null`, concurrent file locking)- `FormatActressName` shared helper in `internal/models/movie.go` deduplicating `Actress.FullName()` and `ActressInfo.FullName()` implementations- `ScraperOption` and `ScraperChoice` types moved from `internal/api/contracts` to `internal/models/` with backward-compatible aliases, eliminating reverse dependency where all 14 scrapers imported the API layer- `JobRepositoryInterface.Upsert()` method replacing read-then-write `FindByID` → `Create`/`Update` pattern with single atomic operation- `UpdateRequest` struct for `POST /api/v1/batch/{id}/update` with `force_overwrite`, `preserve_nfo`, `preset`, `scalar_strategy`, `array_strategy`, `skip_nfo`, `skip_download` fields- `skip_nfo` and `skip_download` fields on `OrganizeRequest` for `POST /api/v1/batch/{id}/organize`- Update options UI in review page header (force overwrite, preserve NFO, skip NFO, skip download toggles) with collapsible options panel- Organize options UI in destination settings card (skip NFO, skip download toggles)- Older-than-days cleanup picker on Jobs page with "Clean History" and "Clean Events" buttons and client-side validation- Version check section in ServerSettingsSection: current/latest version display, "Update available" badge, "Check for Updates" button, error state handling- `getVersionStatus()` and `checkVersion()` API client methods- `VersionStatusResponse` TypeScript interface with error field- Server-side validation rejecting `force_overwrite` + `preserve_nfo` as mutually exclusive (returns 400)- `preset` binding validation (`oneof=conservative gap-fill aggressive`)- 1MB request body size cap on update endpoint via `io.LimitReader` ### Fixed - Windows path separator mismatches in organizer plan tests and batch preview tests (hardcoded `/` vs `\` from `filepath.Join`)- Windows file handle locks preventing `t.TempDir()` cleanup — added `defer CloseLogger()` and `defer db.Close()` in test helpers- Windows `filepath.IsAbs` returning false for `\dest\...` paths (no drive letter) in multipart test- Windows tilde expansion failure in `validateNFOPath` — `filepath.Join("~", ...)` produces `~\...` which doesn't match the `~/` prefix check- Windows double-slash in database error messages — normalized with `strings.ReplaceAll("//", "/")` after `filepath.ToSlash`- Windows `WillMove` incorrectly `true` when source/target paths differ only in separator style (production bug in `organizer.go`, `strategy_organize.go`, `strategy_inplace.go`, `strategy_inplace_norenamefolder.go`)- Windows `isDescendant()` failing to detect descendant paths due to separator mismatch- Windows `cleanupEmptyDir` walking past drive root — added `filepath.VolumeName` root detection- Windows concurrent config writer test timing out due to different file locking semantics- Windows `fsPath()` drive-letter check order — check before `filepath.ToSlash()` to avoid dead code- Windows afero MemMapFs path lookup failures in `ReadNFOSnapshot` — use `fsPath()` to strip drive letter and normalize separators- Windows LogDir output using backslash separators — apply `filepath.ToSlash` in `overrides.go` ### Changed - Javstash scraper `module.go` proxy declaration changed from value type `config.ProxyConfig` to pointer type `*config.ProxyConfig`, matching the pattern used by all other 13 scrapers- `AggregatorOptions` and `Aggregator` struct fields changed from concrete `*database.GenreReplacementRepository`/`*database.ActressAliasRepository` to interface types `database.GenreReplacementRepositoryInterface`/`database.ActressAliasRepositoryInterface`- `processUpdateJob` and `processUpdateMode` now accept `*UpdateOptions` parameter for configurable update behavior- `processOrganizeJob` now accepts `skipNFO` and `skipDownload` parameters- `updateBatchJob` handler now parses optional `UpdateRequest` body (backward compatible with empty body), validates job existence/status before body parsing, and returns 400 for malformed JSON- `persistToDatabase` uses `Upsert` instead of `FindByID` + `Create`/`Update`, eliminating persistence race condition- All 14 scraper module.go files updated to import `models.ScraperOption`/`models.ScraperChoice` instead of `contracts.ScraperOption`/`contracts.ScraperChoice`- Downloader HTTP client creation deferred until actually needed — `skip_download=true` no longer triggers downloader setup, preventing proxy/registry errors on NFO-only updates- `preserve_nfo` now uses `PreserveExisting` merge strategy (not `PreferNFO`) to prevent blanking fields when existing NFO is incomplete- `preserve_nfo` takes final precedence after preset resolution, preventing silent override- DisplayTitle templating runs for ALL update paths (including `force_overwrite`), not just the NFO merge block- Preview `$effect` reactive to skip toggles via `void` references; retry path persists last update options and skip flags- Force Overwrite and Preserve NFO made mutually exclusive in UI via reactive `$effects`- Test databases switched from in-memory shared-cache SQLite to temp file-based SQLite with WAL mode, eliminating `database table is locked` flaky test failures under concurrency- Cross-platform path utility (`path.ts`) with `splitPath`, `buildPathUp`, `buildBreadcrumbPath`, `isRootPath` functions for Windows/Unix path handling- FileBrowser breadcrumb navigation, "go up" button, and path construction now use cross-platform path utility instead of Unix-only logic- OperationRow truncated paths now show 40/60 head-tail split with click-to-expand/collapse- 54 files changed, ~1,192 lines added, ~468 lines removed ## [v0.2.7-alpha] - 2026-04-16 ### Added - Per-IP rate limiting middleware (`internal/api/middleware/ratelimit.go`) for write endpoints using `golang.org/x/time/rate` token bucket- Sort-by whitelist validation returning 400 Bad Request on invalid column names per endpoint- Secure cookie flag support for reverse proxy via `X-Forwarded-Proto` header and `force_secure_cookies` config option- Bootstrap secret env var (`JAVINIZER_SETUP_SECRET`) for `/auth/setup` endpoint protection with local-only fallback- `DrainAndClose` HTTP utility (`internal/httpclient/drain.go`) replacing bare `resp.Body.Close()` on all error paths- JobQueue cleanup goroutine lifecycle with `stopCleanup` channel for graceful shutdown- BatchJob thread-safe accessors (`GetID`, `GetJobStatus`) for concurrent read access- `ErrNotFound` sentinel error (`internal/database/errors.go`) for cache miss distinction from actual database failures- Partial success response for NFO merge operations indicating which fields succeeded or failed- Per-file timeout for `processUpdateJob` with configurable duration bounds (30s–600s)- Job queue `PersistError` field for persistence failure visibility in job status- Database save error wrapping with operation context for debugging- `GetStatusSlim` for lightweight status polling without full movie data payloads- `MovieRepository.Upsert` returning populated `(*Movie, error)` with associations, eliminating extra database query- Template engine injection as shared dependency (`template.Engine`) across organizer, downloader, and job queue- `strings.Fields` replacing character-by-character `splitActressName` concatenation- O(1) scanner extension map lookup replacing linear scan- `DefaultFlattenConfig` and `DefaultFlattenConfigWithRaw` helpers (`internal/scraperutil/flatten.go`) with `FlattenOverrides` struct for all 14 scrapers- Rescrape handler decomposition into focused files: `rescrape_scrape.go`, `rescrape_update.go`, `rescrape_validate.go`, `rescrape_poster.go`- `BatchProcessOptions` and `BatchScrapeOptions` structs replacing positional parameters- `AggregateWithPriority` method on `AggregatorInterface` for priority-based aggregation- `MovieRepositoryInterface.Upsert` updated to return `(*Movie, error)`- Genre replacement CRUD API at `/api/v1/genres/replacements/` with list, create (idempotent), and delete endpoints- `GenreReplacementsSection` Svelte component in settings page with two-column add/remove table ### Changed - All 14 scrapers migrated to shared `DefaultFlattenConfig` or `DefaultFlattenConfigWithRaw` helpers, eliminating per-scraper `FlattenFunc` boilerplate- Rescrape handler decomposed from 666-line monolith to 110-line main handler + 8 focused helper functions- Batch job processing uses options structs instead of positional parameters- Mocks regenerated with updated `AggregatorInterface` and `MovieRepositoryInterface` signatures- 146 files changed, ~4,057 lines added, ~2,382 lines removed ### Fixed - Unauthenticated users cannot create admin accounts via `/auth/setup` without bootstrap secret or local access- Authenticated API endpoints reject excessive requests from a single IP with 429 response- Invalid `sort_by` query parameters rejected with 400 instead of being passed to the database- Session cookies have Secure flag set when request arrives via HTTPS reverse proxy- HTTP response bodies fully drained before close on all error paths — no leaked connections- Job cleanup goroutine stops cleanly when application shuts down- Concurrent access to BatchJob fields passes race detector with no warnings- Download errors for covers, posters, and trailers visible in logs and API responses- Cache miss errors distinguishable from actual database failures in code- Job queue persistence failures visible in job status- Review page explicitly requests full batch data (`include_data=true`) to prevent blank editor- Slim polling by default for `BackgroundJobIndicator` and `ProgressModal` — avoids unnecessary payload overhead- Genre replacement POST/DELETE routes under rate-limited `writeProtected` group- `GetStatusSlim` deep-copies `FieldSources` and `ActressSources` maps to prevent shared mutable state- Preview effect gated during organize polling to avoid redundant `/preview` API calls ### Removed - `normalizeJSONLDImageURL` dead code from `internal/scraper/dmm/jsonld.go`- Ineffective first loop from `extractDescriptionNewSite` in `internal/scraper/dmm/video_dmm.go` ### Security - Rate limiting protects write endpoints (scrape, batch, proxy, auth, genre mutations) from per-IP abuse- Sort-by whitelist prevents SQL injection via query parameters- Bootstrap secret prevents first-arriver takeover of `/auth/setup`- Secure cookie flag prevents session cookie leakage over HTTP behind reverse proxy ## [v0.2.6-alpha] - 2026-04-16 ### Added - SSRF protection package (`internal/ssrf`) with `NewSSRFSafeClient()`, `WrapTransportWithSSRFCheck()`, and `CheckRedirect()` validation blocking private/loopback/link-local IPs- Typed scraper error model (`models.ScraperError`) with categorized error kinds (network, parsing, not-found, rate-limit, auth, timeout, context-cancelled)- Config redaction utility (`internal/config/redact.go`) for safe logging of sensitive fields (API keys, passwords, tokens)- Panic recovery middleware for batch processing with structured error reporting- Job queue improvements: context-aware cancellation, improved state transitions, structured error aggregation- Batch query support for movie repository (`FindMoviesByIDs`, `FindMoviesByContentIDs`) reducing N+1 database queries- Translation service typed errors with retry classification- Panic recovery tests for batch execute pipeline ### Changed - Context propagation threaded through all 14 scrapers: `.SetContext(ctx)` on every resty request in ctx-aware methods- Context threaded through full DMM actress thumbnail chain: `parseHTML` → `extractActresses` → `extractActressFromLink` → `tryActressThumbURLs` → `extractRomajiVariantsFromActressPageCtx`- Context threaded through JavDB `Search` retry and `ScrapeURL` paths via `fetchPageDirectCtx`- Context threaded through DMM `FetchWithBrowser` as chromedp parent context- Context threaded through `DownloadMediaFiles` → `DownloadAll` chain- Aggregator simplified with typed scraper errors replacing ad-hoc error classification- Worker pool and scraper task pipeline refactored for structured error handling- Downloader retry logic improved with per-error-kind backoff strategies- Temp API handlers now use `ssrf.NewSSRFSafeClient()` instead of raw `http.Client`- Proxy test client uses `resty.NoRedirectPolicy()` to prevent open-redirect SSRF- Removed unused context-free wrapper functions (`fetchPageDirect`, `extractRomajiVariantsFromActressPage`, etc.)- 135 files changed, ~2,659 lines added, ~1,265 lines removed ### Fixed - SSRF redirect bypass: proxy test and temp API handlers now validate redirect destinations against internal IPs- Scraper context cancellation gaps: all scraper HTTP requests now respect caller context for proper timeout/cancel propagation- DMM actress thumbnail fallback now cancellable (previously used `context.Background()` for romaji lookup and HEAD probes)- JavDB ScrapeURL retry path now respects caller context instead of spawning untracked requests- Batch organize goroutine immediately cancelled due to deriving context from `c.Request.Context()` instead of `context.Background()` ### Security - SSRF hardening: `NewSSRFSafeClient()` blocks connections to loopback, private, and link-local IP ranges (prevents cloud metadata credential exfiltration via `169.254.169.254` and internal service access)- SSRF redirect validation: `CheckRedirect()` blocks HTTP redirects to internal IP addresses- Config redaction prevents API keys and tokens from leaking into logs ## [v0.2.5-alpha] - 2026-04-14 ### Added - Database repository layer extracted into focused repos: `movie_repo`, `actress_repo`, `actress_alias_repo`, `genre_repo`, `genre_replacement_repo`, `movie_tag_repo`, `movie_translation_repo`, `event_repo`, `batch_file_operation_repo`, `history_repo`- Database helpers package with `InTransaction()` wrapper and common query builders- Scraper config validation tests for all 12 configurable scrapers- Shared scraper utility helpers (`internal/scraperutil/helpers.go`) for common extraction patterns- Aggregator priority tests for field resolution ordering- Organizer strategy tests for all operation modes- NFO generator and merger unit tests- MediaInfo extended tests: AVI/RIFF parser, MKV, MP4 with edge cases- Worker pool error classification tests and poster cache tests- Batch revert check tests and lifecycle extra tests ### Changed - Monolithic `database.go` (~1,436 lines) decomposed into 10 focused repository files- All 14 scraper `Search`/`ScrapeURL` methods refactored for consistent error handling and config-driven behavior- Jav321 scraper restructured with improved HTML parsing reliability- Worker pool improved with structured error wrapping- Test coverage increased (67 files changed, ~4,829 lines added, ~2,153 lines removed) ### Fixed - Aventertainment, DLGetchu, Jav321, JavBus, JavDB, LibreDMM, MGStage, R18Dev, TokyoHot scraper config and edge-case bugs- DMM JSON-LD parsing and video.dmm.co.jp extraction robustness- FC2 and Caribbeancom scraper config handling- Worker pool error reporting for concurrent scrape failures ## [v0.2.4-alpha] - 2026-04-12 ### Added - 5-mode OperationMode enum (organize, in-place, in-place-norenamefolder, metadata-only, preview) with strategy pattern- Auto-migration from legacy `MoveToFolder`/`RenameFolderInPlace` boolean flags to OperationMode in config- OperationMode wired through full API stack with 4-mode frontend selector- `LooksLikeTemplatedTitle()` with UTF-8 safe rune-based detection for double-templating prevention- NFOTitle field to ParseResult for future NFO preservation logic- Regression tests for double-templating and display title edge cases- `internal/types/operation_mode.go` package with validation and parsing- Config pipeline system for structured migration paths- Operation mode tests across organizer, config, API, and worker packages ### Changed - Renamed `display_name` to `display_title` across Go backend and TypeScript frontend- DisplayTitle is now the canonical editable field with aggregator always setting it- DisplayTitle handling simplified: always regenerate from template with fallback to Title- Preview mode removed from frontend UI (kept in backend API)- Strategy pattern replaces monolithic Organizer with separate strategies per operation mode- Database migration 000003 for column rename (display_name → display_title)- 123 files changed, ~6,665 lines added, ~988 lines removed ### Fixed - NFO and media generation for in-place and metadata-only modes (ShouldGenerateMetadata)- History logging for metadata-only and in-place modes- Preview missing screenshots for metadata-only mode- Date clearing now emits undefined instead of empty string for `*time.Time` fields- Date formatting guards against invalid dates- DisplayTitle not regenerated when user edits Title — now always recomputed from template ## [v0.2.3-alpha] - 2026-04-10 ### Added - DMM placeholder detection with hash-based filtering for "now_printing.jpg" screenshots- Shared placeholder detection package (`internal/scraper/image/placeholder`) for multi-scraper reuse- Config-driven placeholder filtering opt-in via `ScraperSettings.Extra`- Default placeholder hashes for DMM CDN images- Collapsible info banner in Web UI explaining screenshot filtering behavior- Runtime config drift detection script (`scripts/validate-config-sync.sh`) with multiline struct support ### Changed - r18dev and libredmm scrapers now use shared placeholder detection package- Test coverage increased to 76.02% (from 75.97%) ### Fixed - DMM scraper config drift: hardcoded Timeout=30, RetryCount=3, RateLimit=0 now correctly use settings values- DMM scraper fallback HTTP client now preserves Proxy and DownloadProxy settings- Placeholder detection early return bug that skipped ALL filtering when hashes empty- Size-based placeholder detection now works independently of hash matching- Aggregator fallback to r18dev/libredmm with unfiltered placeholders resolved ### Security - Path validation TOCTOU vulnerability resolved- Rate limiter cancellation under contention fixed ## [v0.2.2-alpha] - 2026-04-09 ### Added - Unified scraper scaffolding across all 14 scrapers with 86% reduction in registration boilerplate- HTTP Client Builder pattern eliminating ~560 lines of duplicated code- Declarative scraper registration system (reduced from 98 to 14 registration calls) ### Changed - Consolidated scraper platform architecture for easier maintenance and extension- Test coverage increased to 75.97% (from 67.4%) ## [v0.2.1-alpha] - 2026-04-09 ### Added - JavStash scraper for Stash-Box GraphQL API integration- Clear All Jobs button with confirmation dialog on jobs page- Status filter and visual grouping on jobs page- Log rotation and improved logging configuration- DirectURLScraper interface for all scrapers supporting direct URL scraping ### Changed - Refactored rate limiting to shared thread-safe package for consistent throttling- Reorganized Browser Settings UI section with subsections for clarity ### Fixed - Security vulnerabilities from code review (rate limiter rollback bug, path validation TOCTOU, scanner TOCTOU, job queue deadlock)- Job state machine for organization retry workflow- Temp poster cleanup moved from organization to job dismissal- Chrome crashpad handler error in Docker container- Log file creation and permissions issues in Docker- Job poster persistence after rescrape- Frontend manual search rescrape using correct movie ID- Domain boundary checks in multiple scrapers (javbus, r18dev, javdb, caribbeancom)- Race conditions and edge cases in rescrape functionality ## [v0.2.0-alpha] - 2026-04-05 ### Added - Multi-language template tags support (e.g., ``, ``)- Language-specific fields for R18.dev API (EN/JA)- Job persistence to database for batch operations- Auto-archive cleanup goroutine in JobQueue- Persistent destination path in jobs- Jobs page redesign with job cards and temp poster persistence- OpenAI Compatible and Anthropic translation providers- Extended model discovery for new translation providers- Hash-based cache invalidation for translations (settings_hash)- Remember-me sessions for authentication- OpenAI-compatible thinking toggle for translations- Configurable temp directory for poster files- Scraper plugin system with unified config architecture- Configuration migration system- Browser automation settings UI- Letter-pattern multipart file discovery ### Changed - Database migrations squashed to single baseline with hash tracking- Renamed SystemConfig fields for clarity- Translation configuration provider value standardized to `openai-compatible`- Frontend scraper options disabled when global switches are off ### Fixed - R18.dev API translations populated for both EN and JA languages- Invalid language specs handled with base field fallback- Destination field included in GetStatus snapshot- Svelte 5 runes mode compatibility for dynamic components- Navigation to /jobs when all movies excluded in review- Job card layout and poster thumbnail centering- Preserve multipart metadata for letter-pattern files- Multipart move conflict for letter-pattern files- Preserve multipart metadata in rescrape endpoint- Translation JSON array parsing with conversational text handling- WebSocket origin validation hardened (removed wildcard support)- Poster path generation only when DownloadPoster enabled- Organize preview respects NFO/media download settings ## [v0.1.5-alpha] - 2026-03-30 ### Fixed - Config round-trip: YAML/JSON save/load now preserves all scraper-specific fields- FlareSolverr block preserved across config cycles- DeepCopy() prevents mutation leaks between DefaultConfig() and global registry- JavLibrary FlareSolverr client only initializes when enabled; nil proxy handled safely- Translation ordering: buildTranslations() called after field aggregation- Registry validation: fail-fast on malformed scraper config blocks, unknown fields disallowed- API key validation in translation config ## [v0.1.4-alpha] - 2026-03-30 ### Changed - Code reorganization: config.go split into 7 focused files (~1968 lines reorganized)- DMM helpers extracted to dedicated utilities (-482 lines)- Database utilities extracted (-402 lines)- Aggregator utilities extracted (-153 lines)- FlareSolverr config restructured from proxy to scrapers level for cleaner architecture ## [v0.1.2-alpha] - 2026-03-17 ### Added - Web UI embedded in binary for single-binary distribution- `web` command alias for unified API/web server entrypoint ### Changed - CI Node.js version bumped to 22 for builds ### Fixed - Web assets bundled in CI binaries- API and web usage clarified in documentation ## [v0.1.1-alpha] - 2026-03-17 ### Added - R18.dev language option support (en/ja)- GHCR Docker images with version-first tags ### Changed - Config profile inheritance for cleaner configuration- Legacy proxy fields removed in favor of profile-based approach ## [v0.1.0-alpha] - 2026-03-16 ### Added - **Multi-source scraping**: R18.dev, DMM/Fanza, JavLibrary, JavDB, JavBus, Jav321, LibreDMM, MGStage, TokyoHot, AVEntertainment, DLGetchu, Caribbeancom, FC2 scrapers- **Smart file organization**: Rename and organize files/folders using configurable templates- **Dry-run preview**: Full preview before making any changes- **NFO generation**: Kodi/Plex-compatible metadata files- **Media downloads**: Cover, poster, fanart, trailer, and actress image downloads- **Multiple interfaces**: CLI, interactive TUI (Bubble Tea), REST API, and web UI (SvelteKit)- **Interactive TUI**: Browse and scrape files with real-time progress display- **Tag system**: Per-movie custom tags stored in database- **Genre management**: Genre replacement rules with CLI commands- **History tracking**: File organization operation history with rollback support- **HTTP/SOCKS5 proxy support**: For all network requests including chromedp- **MediaInfo integration**: Video format probing with AVI/RIFF and FLV parsers, CLI fallback- **Actress alias system**: Alternative names mapping- **Template system**: Folder/file naming with conditional logic and multi-part support- **Docker deployment**: Container with user/group mapping, environment variable support- **Authentication**: Single-user auth with setup flow and secured sessions- **API documentation**: Scalar UI and Swagger UI at /docs and /swagger- **WebSocket progress**: Real-time progress streaming for batch operations- **Configurable umask**: File permission control- **Environment variables**: JAVINIZER_* overrides for config, database, logging, temp directory- **Amateur video scraping**: DMM support for amateur content- **Actress thumbnail extraction**: From DMM streaming pages- **Poster quality detection**: Intelligent cropping for DMM and R18Dev- **Chromium support**: In Docker for headless browser scraping ### Security - Path traversal protection for API endpoints- CORS origin validation- Directory traversal prevention- SQL injection prevention- Header injection and path traversal sanitization in frontend [v1.6.3]: https://github.com/javinizer/javinizer-go/compare/v1.6.2...v1.6.3 [v1.6.2]: https://github.com/javinizer/javinizer-go/compare/v1.6.1...v1.6.2