Most of these were never released. Two builds have been made available
outside the private development repository:
- v4.0 Community Edition — published to
GitHub
on December 30, 2025 under the MIT license.
- v6.0, v6.1 and v6.2 —
the commercial releases, available at
idocinthebox.com.
v6.2 is current.
Everything else is an internal build: feature
work, testing and bug fixes versioned in a private repository and never published as a
release. They are listed here because the engineering record is worth showing, not because
they were ever downloadable. Your license covers all v6.x releases at no extra cost.
v6.2.2
2026-09-05
Current release
Changed
- Film Reconstruction: harmonization inside the picture. The tone harmonization pass replaced every filled region's low frequencies with the diffused field of its surround — right beyond the picture edge, wrong for a repair fill whose content legitimately differs from what is around it (a dark coat in snow came back as a bright rectangle, +11 to +16 levels per channel; the pan reel's own hold-outs lost 2.5 robust / 4.3 raw to a colour cast). Repair fills now level by one constant per fill: the median tone step where the fill meets clean picture, applied only when it is exposure-sized (a few levels is content, tens is a cast). Equals harmonization-off on every hold-out truth rig while still lifting the sprocket-edge fills whose fill-time fit the black surround dragged 45 levels dark. Scene expand keeps the field pass;
--harmonize-mode {field,seam} is the A/B override. In the Film Reconstruction card the method is the advanced "Tone Matching" combo: Auto (the engine's choice), Match at seam, Match to surround, Off — each described with its failure mode in the tooltip; Auto emits no key. Design doc §3.22.
- Film Reconstruction: the donor exposure fit measures inside the picture. A damage component that straddles the picture gate into the black scanner surround put that surround in the 6 px ring its offset was fitted on, and each donor was dragged dark by a different amount — so exposure-matched donors disagreed, the rejection gate read that as structural disagreement and threw away the correct fills. On the pan reel's sprocket case that was 157k px of real content reported unrecoverable and a misaligned patch in its place; with the fit restricted to the gate the log courses run straight through the fill (516k → 673k px recovered, 329k → 172k unrecoverable). Plain scene expand (
--extend without --fill-surround) had the same fault — its pads bordered the black surround — and now lands at the picture's exposure (+29 levels; right pad −31/−33/−40 → +12/+11/+8 against the adjacent picture). Fill-surround renders are byte-identical. Design doc §3.22.1.
- Film Reconstruction: Fill Recipe presets on the card. Seven items, Auto default: Auto (recommended), Match to surround (legacy look), Match at seam, Auto + flow rescue, Auto with no temporal polish, Auto + moving subjects, and Custom. Four new advanced controls (Temporal Polish, Flow Rescue, Lift Picture Edges, Place Moving Subjects Separately) are governed by the preset and released under Custom. One table drives the card, the options builder and the CLI's
--fill-recipe; Auto emits no key, so the engine's defaults stay its own; every setting is reachable by name from the MCP job path. Design doc §3.22.10.
- Film Reconstruction: Auto (max) Scene Expand, Output Aspect crop, Auto (detect) source. Scene Expand now defaults to "Auto (max)": the engine measures the camera path over the clip and expands exactly as far as the camera ever looked (rounded to 8 px), so no pad number has to be guessed; fixed pads remain one switch away. A new Output Aspect combo crops the rendered canvas (frames and provenance map together) to 16:9, 4:3, 3:2, 1:1, 9:16, Academy 1.37, IMAX 1.43 and 1.90, Flat 1.85, Scope 2.39 or Ultra Panavision 2.76, centred by default, or leaves it Uncropped; the anchor accepts
picture or an explicit point so a future stabilizer anchor mode can drive the crop with no card change. Source Type gains "Auto (detect)". CLI: --extend auto, --aspect, --aspect-anchor. Card text and tooltips verified in both dark and light themes. Design doc §3.22.11.
- Film Reconstruction: hold-out diagnostics say WHY a number moved. Beside the three numbers, the hold-out report now carries a fixed-coordinate vector (multiscale gradient error, signed low-pass residual with gain/offset, per-band energy + correlation, displacement with per-block spread, edge sharpness and overshoot) and seven extra probe controls (shift, gain, offset, unsharp, ringing, deformation), each verified to trigger its own diagnostic. Real pixels from another frame never correlate in the fine band (grain does not repeat), which is the calibration for telling recovered from derived. Design doc §3.13.8.
- Film Reconstruction: Scene Expand "Auto" is the geometry rule. Fills outside the original picture (pads, surround) are matched to the surround as before, so extensions sit at the picture's brightness rather than the donors' edge vignette; repairs inside the picture keep the seam rule. The report says
seam+field-outside-picture. Design doc §3.22.8.
- Film Reconstruction: the second opinion is never the donor's twin. On projector captures with a duplicate cadence the frame verifying a fill could be a duplicate capture of the frame that made it; verification now moves to the next independent frame (
film_second_opinion_independent, default on; a no-op without duplicate pairs). Moved no number on owned footage; pinned synthetically. Design doc §3.22.3.
- Film Reconstruction: lossless RGB output. The pass wrote the stabilizer's FFV1 yuv420p intermediate, which moved every pixel of the frame by ~1.5 levels (matrix, legal-range squeeze, 4:2:0). It now writes lossless planar RGB, full range, so pixels outside the mask are byte-identical in the file (verified 15.17M of 15.17M on the pan reel).
--output-pix-fmt yuv420p restores the legacy intermediate. Design doc §3.22.7.
- Film Reconstruction: picture-edge devignette is its own switch. Scene Expand lifts the ~16 px vignette band inside the picture gate toward the surrounding tone; this now runs under the seam default too (it was a side effect of "Match to surround") and can be turned off with
--no-edge-devignette / film_edge_devignette. Design doc §3.22.7.
- Film Reconstruction: Scene Expand tone-matching default is now "Match at seam". Pseudo-edge hold-outs (frames cropped inward at the picture edge, the strip recreated from far donors and scored against the original) put the surround-field pass at 9-11 robust error against 1.9-2.3 for the seam rule on extension fills, lifting them 6-9 levels; the fault it used to rescue (dark extensions from a contaminated exposure fit) is fixed. "Match to surround" remains selectable. Design doc §3.22.6.
- Film Reconstruction: flow rescue samples the donor once. The opt-in dense-flow rescue resampled fill pixels twice; composing the affine and the flow into one map restores its texture (brush box 0.469 → 0.950) and halves the pan hold-out error (1.84 → 0.81) — re-opened as a default candidate, still off. Design doc §3.22.5.
- Film Reconstruction: donor exposure gain is the spread ratio, not a regression slope. The least-squares slope of target on donor is pulled below 1 by every pixel that does not line up (grain, parallax), and the ring offset then compensates at the ring's brightness, lifting dark fills and softening every fill's grain (gain 0.95 with the frames at equal exposure). Gain is now std(target) / std(donor). Texture ratios move toward 1 on every truth rig (pan hold-outs 0.60 → 0.81, BRAW 0.97 → 0.99, brush box 0.94 → 1.00); the sprocket fills' residual cool cast is gone (fill edge vs picture −5.6/−16.8/−20.8 → −0.1/+0.6/+1.1); the per-motion fill's brightness lift halves. The bake-off harness numbers move with it. Design doc §3.22.2.
- Film Reconstruction: multi-motion check. A fill that contains content moving independently of its registered surround (a subject against a pan) is placed by one affine and lands displaced; the content-disagreement gate cannot see a smooth object shifted 10 px. Placement is now checked by tracking the donor against a second real frame inside the fill; disputed pixels are labelled
MISREGISTERED (orange in the provenance map, counted separately in the report) or reverted with film_multi_motion_revert. Found via the LTX Lab's brush on the pan reel (97 % "recovered", error 23, visible seam). Design doc §3.19.
- Film Reconstruction: in-memory API.
core/film_reconstruction/pipeline.py gains reconstruct_frames() — the complete evidence ladder on frames already in memory, no FFmpeg round trip. run_film_reconstruction_pass now wraps it. First consumer: the LTX Generative Repair add-on 1.3.0, whose headline backend becomes this engine (ProPainter demoted to a greyed, user-installed, non-commercial option after the 2026-09-04 bake-off). Design doc §3.18.
Fixed
min_atr_version is now enforced. Both marketplaces published a minimum ATR version per entry, the field was validated as required, and nothing ever compared it to anything — there were zero reads of it in the app. So a build older than an add-on required would offer it anyway, and for a paid add-on that meant showing Purchase, taking the money, and only then failing: the LTX Generative Repair vendor flip to Lemon Squeezy was documented as safe to publish precisely because the version bump would hold older builds back, and it would not have. The gate now runs in three places — before the download in EngineInstallOrchestrator.install(), against the package manifest after extraction (the manifest outranks the index entry, which is hand-maintained and can lag), and in install_from_local() — and both marketplace cards state the requirement instead of offering a button that cannot work. An incompatible paid add-on shows no Purchase button at all. Deliberately permissive at both ends: an absent or unparseable requirement means "no requirement", so a typo in the index cannot brick installs. Installed content is left alone; only new installs and updates are gated. (core/version_gate.py, core/engine_installer.py, gui/pages/addons_page.py, gui/pages/plugins_page.py, tests/unit/test_engine_min_atr_version.py, tests/gui/test_min_atr_version_cards.py)
Added
- Donor Reach is now a control, not a CLI secret (film card, "Donor Reach (frames)"). How far ahead and behind a damaged frame the engine may look for one that actually photographed the missing content — the CLI's
--max-search. Left at "Automatic" it stays exactly as it was: 12 frames for repair, the whole clip for Scene Expand, and nothing is written into the job at all. Raised, it costs search time and buys content the camera only revealed later in the shot: on the pan reel the GUI recovered 477,908 px where the CLI's published run at reach 24 recovers 573,348 — a number the window could not reach until now. The engine's repair default and the card's mirror of it are asserted equal by a test, the way the seam feather is. (gui/widgets/control_tabs.py, core/options_builder.py, tests/unit/test_film_mode_parity.py, tests/gui/test_film_reconstruction_card.py)
- Extended Detection reports what it found. The switch used to be silent: the counts existed in
<output>.reconstruction.json under anomaly_px_total / anomaly_frames and nowhere a user looks. The film pass's summary line — the one the CLI prints, the processor logs and the GUI shows on completion — now carries them, through a single formatter (format_detection_summary) so the CLI and the GUI cannot word it differently. Zero is stated as a finding, not as silence: "Extended Detection: no frame-varying marks found — a mark that stays at one position all clip is invisible to this test". Whole-frame flags are named separately, because one cross-faded capture explains a million-pixel count. (core/film_reconstruction/pipeline.py, tests/unit/test_film_reconstruction.py)
- Source Analysis, phase 2 — the capture-path classifier (
core/source_analysis/capture_path.py, plan docs/design/ATR_SOURCE_ANALYSIS_PLAN.md §5 phase 2, L-25). The vote is now size- and share-aware: a frame-locked bright feature votes scanner in proportion to its area (full weight from 1 % of the frame; a speck under 0.1 % casts nothing) and only when it sits in the surround and notches the picture — a feature lying inside the picture rectangle is a bright object in the scene and casts nothing, whatever its size. That is the L-25 defect: a 327-px feature on C0051.MP4 (0.055 % of the frame, 100 % inside the picture) earned the pan reel's +3 and read scanner 0.56 against a detected period-3 cadence; it now reads projector capture 0.80 with the feature named as "not a punchout". A detected cadence is read per 30-frame window of the profile: windows inside a leader range do not vote (L-07) and the windows that contributed are recorded; the audit's independent read (sharpness+diff, or a strong quiet/loud) corroborates it with its own weight; a camera rate with a period outweighs any small static feature. Confidence is the top-two margin over the total evidence cast (the shared surround vote counts once; under 4 units of evidence it is damped), not a vote ratio, and a confidence_note says why when the top two are within 0.15 ("scanner vs projector_capture undecided: no cadence detected and the bright feature is small — run the profile on a longer window") or when a label rests on geometry and flicker alone. unknown only when no evidence field could be computed, with the missing fields listed. The verdict (label, confidence, note, evidence, votes, margin, total, windows, missing) travels in the sidecar (profile_version 1.1), the atr probe --film message and summary, and the Auto Analysis dialog's Film block (still read-only). Gate: 100 % on the owned clips — pan reel scanner 0.57, every BRAW window and C0040 / C0051 projector capture 0.80–0.86, DV video 0.99 with the planner profile byte-identical to main. C009 / C020, the two audit clips with period none, carry the period-3 cadence in the profile's 300-frame window (the audit's was 96 frames) and read 0.86 like the rest; on the audit's own 96 frames they read 0.75 (period 2, uncorroborated — a weak vote, named) and 0.50 (all leader). The geometry-only path — the cadence vote withheld because every window is leader — is exercised by the C035 / C023 heads and C002's setup take (0.50, "profile a picture window to confirm"). Wall time unchanged. 11 synthetic tests + the recorded verdict table in the real-footage gate.
- Source Analysis, phase 1 (
core/source_analysis/, plan docs/design/ATR_SOURCE_ANALYSIS_PLAN.md) — one pre-restoration profile of a clip, measurements only: capture path (scanner / projector capture / telecine / video / unknown, with the evidence that voted and never a silent unknown), projector cadence (period, roles, slips, film rate, the cadence audit's smear / drift / diff-phase / quiet-loud numbers and a per-window duplicate-vs-blend state), picture rectangle and punchout geometry (and the notch's share inside the picture), leader ranges with the new leader_marking class (printed lab text, countdown numerals, mould — flat lit ground carrying dendritic or moving structure; never damage, never picture), damage inventory (the shipped detector; the anomaly detector opt-in with its false-positive record beside every count), evidence (whole-shot donor coverage; hold-out on request), signal quality at a recorded analysis width, and the colour chain. Every field names the module that produced it. atr probe --film PATH [--holdout N] [--anomaly], MCP probe_film, a read-only Film block in the Auto Analysis dialog, <input>.source_profile.json, and SourceProfile.film for the planner (default None; no planner behaviour changes). Reproduces every recorded measurement of the damage-repair arc on the owned clips (pan reel 23,468 / 3,834 px, C035 freeze 0–50 and text from 51, C023 mould, the 5090's cadence audit rows, C0040 / C0051 lag-3 0.85 / 0.89, DV byte-identical planner profile).
- Projector Cadence ingest (
core/film_cadence.py, design doc §3.16) — for film that was filmed off a projector: the capture cadence (three captures per film frame at 50 fps, three per two at 24 fps, with the smear slot drifting within a clip) is classified frame by frame on the reconstruction engine's own registration, the cleanest capture of each film frame is kept and the repeats and straddles dropped, and the clip is written at the film rate (auto, snapped to 16 / 16.67 / 18 / 24 / 25, or the user's choice) with a <output>.cadence.json sidecar. Deterministic, CPU only, no model; a clip with no cadence passes through untouched (the Super-8 scanner reel reconstructs to identical counts). Runs first (processor Step 1c) so reconstruction, deflicker, stabilization and face work see one frame per film frame. "Projector Cadence" card (output rate, analysis-only), atr media cadence-analyze / cadence-decimate, planner node film_cadence. Measured against the 5090's 160 hand-labelled frames: duplicate and blend precision/recall 1.00 on C021, duplicates 1.00 on C013/C015, and on C008 exact on the self-consistent 71 % of the labels. Also an opt-in donor veto for the reconstruction engine (film_cadence_donor_veto, --cadence-veto, "Skip Smeared Frames as Donors"): off by default because on hold-out truth the straddle capture measured as the closer donor (C029: 2.4 → 8.7 robust error with it vetoed); its case is film-borne damage a straddle carries into the pool, which the harvest has no example of. The extended detector now takes its cadence period from this stage when the pass has run it.
- Projector Cadence — a phase slip is followed within one cycle (design doc §3.16.5). The 5090's re-anchored labels (v2) showed C008's slip at 463 is a gradual crossover of the duplicate and blend slots; the classifier held the old phase for five cycles and reported the drift 13 frames late (duplicate P/R 0.69 / 0.69, blend 0.56 / 0.64 on that window). The quiet slot is now decided per period group on the local differences with one cycle of hysteresis, the arrival phase is held the same way, roles are positional as the labels are, the slip is timestamped at the crossover (
slip_frames in the sidecar) and the donor veto reads clean repeats from the measurements (repeat_of_kept) rather than the role. C008 duplicate and blend P/R 1.00 / 1.00, C013 / C015 / C021 unchanged at 1.00, pan reel byte-identical, gate 2 sharpness-by-phase ≥ 0.97 on every clip; three new synthetic tests (crossover, single noisy cycle, alternating duplicate/blend state).
- Film Reconstruction — recovered-pixel sharpness parity (design doc §3.17). The donor colour warp resampled with bilinear interpolation, which low-passes film grain at sub-pixel offsets: on hold-out truth the recovered pixels carried 24 % (Super-8 pan reel) to 68 % (BRAW C036) of the truth's high-frequency energy, so repairs read softer than their surroundings in motion. The colour warp is now Lanczos-4 by default (
--donor-interp linear|cubic|lanczos4, film_donor_interp, "Donor Resampling" in the card; masks stay nearest-neighbour): texture ratio 0.24 → 0.61 on the reel and 0.68 → 0.97 on C036 with the robust error within 1 %. Every decision the repair makes (registration, exposure, the second-opinion gate) reads a bilinear measurement composite, so coverage, counts and flags are identical for every interpolator and only the shipped pixels change; resampler ringing is under the plan's bar on both clips and invisible at 3×. New opt-in --recovered-grain / film_recovered_grain / "Match Grain on Repairs" re-synthesizes grain on recovered pixels only, scaled to the measured deficit against the frame's own grain around each repair (0.99–1.00 on both clips); it runs after the temporal polish, which would otherwise median most of it away. The hold-out report and summary line now carry texture_ratio beside the robust error.
- Film Reconstruction — hold-out validation in the product — the engine can now score itself on your clip and tell you the number. "Validate This Clip" in the Frame Reconstruction card (off by default) runs a second, discarded reconstruction pass that hides copies of the clip's own damage footprint on good picture, rebuilds them with the same machinery, and scores the result against the pixels that were underneath all along; a Test Regions spinner (default 8, 1–12) sets how many. It roughly doubles reconstruction time and changes nothing about the output. The result — regions, recovered pixels, robust error, p95, texture ratio, and either the donor gate's rejection precision or "gate not exercised" — is appended to the pass summary (CLI result and log) and shown on completion in the GUI, always with the design §3.13.3 limitation: it is an upper bound on machinery quality, not a promise that a given tear is recoverable.
--holdout N (already shipped) and the card emit the same film_holdout key; <output>.reconstruction.json remains the full archival record.
- Film Reconstruction —
metric_probe and the three-number rule (opt-in) — no metric gates a promotion until it ships with a truth version, a two-sided texture ratio, and degenerate-input probes beside every candidate (validation.metric_probe(), design §3.13.7, plan Q7). --holdout N --metric-probe (option film_metric_probe, default off) scores five controls on the hold-out regions — the truth itself, truth blurred σ=3, truth + noise σ=8, a ring-mean fill, and the shipped render scored twice for repeat variance — with a two-sided Laplacian texture ratio beside each, and reports per frame whether the metric can gate a promotion or only veto one. tools/metric_probe_report.py <render> <truth> <mask> [--baseline …] runs the same function on any pair of clips; docs/design/SUMMARY_TEMPLATE.md is the required shape of every write-up. Measured on the Super-8 pan reel: the engine's own robust content_diff is not gate-capable — blur and noise both score better than the shipped repair on 8 of 8 frames — so it is a veto, never a promotion path, and the texture ratio (blur 0.008, noise 2.96 against truth's 1.000) is the number that tells them apart.
- Film Reconstruction — Extended Detection (opt-in) — a deterministic temporal-anomaly detector (
core/film_reconstruction/anomaly.py, design doc §3.15) for frame-varying damage of either polarity anywhere in the picture (dark scorch, dirt, blotches) — the case the bright edge-band detector is blind to. Classic three-frame test on the engine's own registration and content_diff(), cadence-aware for projector captures (duplicated frames are skipped, the physical neighbour is compared) and with a block-match motion veto so moving limbs are not mistaken for damage. Cannot find damage that stays at one position across frames (use --mask). --anomaly-detect / --no-anomaly-detect / --anomaly-sensitivity, film_anomaly_detect, and the "Extended Detection" switch in the Film Reconstruction card. Off by default: measured false positives are 0 on two near-static clean clips and 0.11 % of the picture on one frame of a clip with fast hand motion, above the 0.05 % bar the plan set for default-on.
- Film Reconstruction Engine (Phase 1) — evidence-based sprocket/film-edge repair for scanned film, with zero AI and zero third-party model licenses (
core/film_reconstruction/, design in docs/design/ATR_FILM_RECONSTRUCTION_DESIGN.md). Deterministic damage detection separates the frame-locked gate/perforation geometry (bright in the same pixels every frame) from real damage (light blowing through a tear on one physical film frame); damaged pixels are then recovered from neighbouring frames using the stabilizer's exact-affine temporal donor machinery (mask-aware global motion, scene-cut fencing, donor-side damage respected, exposure matching). Every output pixel carries provenance — original, donor frame ±d, or unrecovered (left as source and flagged; the engine never invents content) — written to a .reconstruction.json report and an optional colorized .provenance.mkv sidecar. A second-opinion validation sweep compares each repair against an independent donor and flags frames whose disagreement suggests parallax or local motion at the tear. CLI: atr media film-reconstruct (auto-detect or --mask). Colorspace tags are preserved via the shared stabilizer decode/encode path. Hardened end-to-end on a real Super-8 pan scan (design doc §3.7): --include-static repairs the frame-locked punchout itself (a pan recovers real content behind it); temporal-minimum glow hysteresis masks the warm flare halo however wide it is; donors are direct-registered against the target frame so gate weave can't drift distant donors off; exposure matching is gain+offset (film flicker is multiplicative); a rejection pass reverts any filled pixel where two independent donors disagree on content — a lingering subject is never pasted in as a ghost, and never-photographed background is honestly reported unrecovered; and unrecoverable pixels inside the punchout are deterministically redrawn as film base (Telea diffusion from their own surroundings, matched grain, distinct provenance label). GUI: a "Frame Reconstruction & Scene Expand" card in the AI Models tab (source type, punchout repair, film edge, Scene Expand per-side pixel controls, generative fill and its seam feather), running as pipeline Step 3b — before any inpainting, on the rawest scan. Scene Expand (prototype): --extend LEFT TOP RIGHT BOTTOM grows the canvas and recreates the extension from frames where the camera revealed that content — temporal outpainting with pixel provenance, zero generation; never-photographed areas stay black and are reported unrecovered. For film scans, the new picture-gate detector (median-luminance profiles) keeps donors inside the image gate, and --fill-surround grows the picture into and past the scanner surround for a genuinely wider shot. Scene Expand finishes with a temporal flow polish (default on; --no-temporal-polish): dense RAFT flow over the finished frames — verifiable everywhere, unlike flow into a hole — aligns each frame's reconstructed pixels with their temporal neighbours and takes the median, erasing residual strip breaks while leaving originals and honest-unrecovered pixels untouched. --flow-assist additionally feeds dense flow correspondences into the affine registration and steers donor selection away from high-parallax donors.
- All five Film Reconstruction modes are reachable from the GUI. The card gained a Source Type choice — Film scan (scanner surround + frame-locked picture gate: punchout repair, film edges,
film_fill_surround) versus Video (non-film) (ordinary tape/digital footage: Scene Expand only, with the film-only controls and keys suppressed, since growing the picture past a gate that does not exist is wrong). And an explicit Generative Fill switch: synthesis used to be an implicit engine default that turned itself on for any expand run, so a GUI Scene Expand silently invented pixels. It is now visible, defaulted on for expand modes (matching the engine) and off for pure repair, with an advanced Fill Seam Feather spinner (film_fill_feather). Recovered real pixels stay the product's default; synthesized pixels are an opt-in, labelled SYNTHESIZED (magenta) in the provenance sidecar — the card text now says so in every mode. Parity tests assert the options the GUI emits for each of the five modes match what the equivalent atr media film-reconstruct flags produce.
- Hold-out validation for film reconstruction (
--holdout [N]). Every gate in the engine scores where real pixels already exist, so none can catch two donors being wrong the same way. Hold-out manufactures truth: it hides known-good pixels under copies of the clip's REAL damage footprints — translated onto undamaged ground, preserving shape, area and edge-band position where placement allows — reconstructs them with the same machinery, and scores against the pixels that were underneath. A separate pass whose frames are discarded, so the output clip is unchanged; results land in the .reconstruction.json report, scored by provenance category rather than in aggregate. Reports honestly what it cannot measure: it is an upper bound on machinery quality (a hold-out region was photographed and sits beside good pixels, unlike some real damage), it counts how many regions had to leave the edge band to find clean ground, and it returns null for rejection_precision when the donor gate was never exercised instead of a flattering 1.0.
- ATR-Fill-Film generative fill (Phase 2B). The last rung of the film reconstruction evidence ladder: pixels that survive every evidence-recovery mechanism as honestly unrecoverable can now be synthesized by ATR's own inpainting model — a from-scratch FFC generator trained on ATR's film harvest with real provenance-derived damage masks (no third-party weights; big-lama evaluated and retired for film — it wove canvas texture into large holes). Synthesized pixels get their own provenance label (magenta in the sidecar,
synthesized_px in the report) so generated content is never conflated with recovered real pixels. Default on for Scene Expand / --fill-surround, off for pure sprocket repair (--generative-fill / --no-generative-fill). The checkpoint (ATR_Fill_Film_v1.pt) downloads on demand, sha256-verified; without it the stage logs and skips, leaving pixels honestly unfilled (core/film_reconstruction/generative_fill.py, atr_fill_net.py). Synthesized regions are seam-feathered into the real pixels around them: the model necessarily paints at its own tone, so the band just inside each mask boundary is matched to the real neighbours it meets (measured on the ring the model repainted, clamped and ramped to nothing over 40 px), removing the soft edge the fill otherwise leaves. Width is tunable with --fill-feather PX; 0 disables. A structural edge-snap (--fill-snap PX, option film_fill_snap) is the geometric analogue of that feather and ships experimental, off by default: the same ring of repainted real pixels also yields a dense RAFT displacement between generated and real structure, extended inward by the same normalized convolution and smoothstep ramp and used to warp the fill before compositing (geometry first, tone second). It is gated on forward-backward consistency, a 4 px magnitude cap, gradient weighting, and a verified improvement on the ring — and on the reference reel those gates reject it on every frame, so the render is bit-identical to the baseline. Measured negative result: the ring cannot be widened (outside the model's dilated mask the network composites the real input verbatim, so the flow there is zero by construction), only 26-60% of ring pixels are FB-consistent at all, and what survives measures 0.3-0.5 px. With every reliability gate removed the reel moves edge disagreement 28.25 -> 27.28 deg while |tone step| grows 1.96 -> 2.27, and the difference is invisible at 3x zoom. See design doc §3.8.
- De-flicker is now oscillation-gated by default (g3c "Preserve"). The ATR De-flicker default strength is now Preserve, backed by the new g3c checkpoint. An analytic oscillation gate (from the frame-mean brightness) applies the full correction where brightness oscillates (flicker) and passes through where it moves monotonically (a genuine lighting change — a pan into sun, a lamp switched on), so real exposure rides are left intact instead of being flattened. Validated on real Video8/DV/film captures it looks as clean as the old default on pure flicker, clearly better on genuine lighting changes, and softens the image less (much higher do-no-harm). The former g2 gain-field default remains available as the Balanced strength, with Gentle / Strong alongside it, for harder pump removal (
core/deflicker_processor.py, SUMMARY_DEFLICKER_G3.md). A Film strength (g3b) is also offered — a lower-threshold oscillation gate for film transfers whose slow, drifting pump Preserve under-corrects, while still keeping deliberate fades. All checkpoints download on demand (sha256-verified); the g3 mid-threshold step remains download-available but not UI-selectable.
- De-flicker "Analyze for oscillation" button. Measures the loaded clip's brightness flicker and oscillation with the gate's own math and recommends a strength — enabling de-flicker and applying the pick for you. It only ever auto-selects Preserve or (for film-cadence sources) Film; because a slow unwanted pump and a genuine lighting change are the same low-oscillation signal, it never auto-picks the flattening strengths, offering them as manual escalation in the message instead (
core/deflicker_processor.py::analyze_oscillation).
- Guided setup, rebuilt around a versioned schema. The first-run dialog was a flat checklist of ten dependency rows with an "Install" button and a "Don't show again" box — it never explained what anything was, never looked at the machine, and gated itself on a
setup_complete boolean that could only ever say "never ask again". It is now an eight-step wizard (Welcome → Your computer → Core components → Storage → AI features → Integrations → Check → Ready) that scans the system in the background, labels every component Required / Recommended / Optional, explains problems in language that does not assume the reader knows what VapourSynth is, and offers the fix inline.
- Setup schema version, not a boolean (
core/setup/schema.py, core/setup/state.py). Each step records the schema version that introduced it, so a release that adds a step shows only that step to existing users instead of replaying the whole wizard or staying silent forever. The legacy setup_complete: true flag is read as "finished schema v1", so upgrading installs keep their configuration and are asked only about what is new. ATR_SKIP_SETUP_WIZARD=1 disables auto-launch for development.
- Hardware-aware recommendations (
core/setup/recommendations.py). Encoder, memory ceiling, thread count and GPU acceleration are proposed from the detected machine — encoder selection delegated to MultiGPUManager's capability flags rather than re-derived — each with the sentence that justifies it ("Your RTX 5070 has a dedicated video encoder…"). Nothing is written until the user accepts it, and declining leaves every setting untouched.
- Optional never blocks. Only REQUIRED components can produce a blocking result, and even those do not prevent finishing — the user acknowledges what will not work and is let through, rather than trapped. Closing the wizard records nothing, so an interrupted first run resumes.
docs/SETUP_GUIDE.md — the canonical setup reference, and a contextual documentation link registry (core/setup/doc_links.py) that maps ~25 UI topics to sections of it. Every ? button on the Settings page and every "Learn more" in the wizard opens that section in an in-app viewer, so links land on the paragraph that answers the question. Topics are asserted against the guide in CI, so a renamed heading fails the build instead of shipping a dead link.
- Settings page: tooltips and section help. Every path, switch, combo and slider now explains what it does, whether it is required, what the recommended value is, and the consequence of changing it (memory limit above your card's VRAM fails part-way through a render; below it tiles the frame). Each card carries a ? affordance, and a new Setup & status card offers Run Setup Wizard (a pre-filled re-run, explicitly not a reset — Reset to Defaults remains separate) plus an on-demand Check my setup.
atr system paths — the setup scripts now ask the app where it keeps things. The .bat files hardcoded %LOCALAPPDATA%\Advanced_Tape_Restorer while the app honoured a model_root override, so on a machine with an override the installer told you to put weights in one folder and the app looked in another — with both sides internally consistent, so neither reported an error. The new verb resolves the roots through the same AssetService the app uses; --bare KEY prints one absolute path and nothing else so a .bat can capture it with for /f. DISTRIBUTION/Setup/_atr_paths.bat wraps it and falls back to the default layout on a source checkout or an older CLI.
DISTRIBUTION/Setup/Patch_ATR_6.2.1.ps1 — field repair for an install you already have. Diagnoses by default and changes nothing; -Apply acts. It repairs the storage split and the dead temp folder, reports and repairs the ONNX runtime state, and downloads the de-flicker weights with SHA256 verification. It states plainly what it cannot fix — the fixes that live inside the EXE need a rebuild — and instead puts the machine into the state those fixes would produce.
atr-jobs MCP server — 27 tools mirroring the restoration CLI. An agent can probe media, pick presets, create/validate/patch/dry-run jobs, start a render behind a prepare/confirm token, monitor and cancel it, read logs, run QA compare and frame-sample, transcribe, and query the solutions DB and knowledge graph — without shell access. Phase 1 of docs/design/AGENT_SURFACE_COVERAGE_PLAN.md.
- Move my files — relocate the app data folder, files and all. Changing the storage folder redirects where ATR writes; it never moved what was already there, so the user who switched to a roomier drive because the old one was full kept the model library on the full drive. The Settings page now offers Move my files beside the folder field, and the same operation is available as
atr system move-storage --to "D:\ATR_Data" (which prints a plan — how many files, how much data, whether there is room — and does nothing without --apply). The move is built to be interrupted: each file is copied and checked before the original is deleted, so stopping part-way, or a power cut, always leaves every file whole at one end or the other. Run it again and it resumes. ATR is pointed at the new folder only once the whole move has succeeded, so an interrupted move leaves your setup working exactly as it was. A move within one drive renames rather than copies — near-instant, and it needs no extra space.
Changed
- Restore/AI card state no longer pretends to persist.
closeEvent had written the restore / ai_models snapshot to restoration_settings.json since 6.0 and nothing ever read it back, so every launch started at the card defaults anyway. That is now the deliberate default: workflows live in presets, only app-level preferences (export codec, directories, capture folder, theme, ...) survive a restart, and the stale snapshot is dropped from the file. New opt-in Settings > Advanced > "Remember Restore/AI Cards" (restore_cards_on_launch) restores last-used card state at launch, minus per-run diagnostics (film hold-out validation, cadence analysis-only, inpainting mask path). Guarded by tests/gui/test_card_settings_persistence.py (two real MainWindows).
- Film reconstruction: the edge-snap experiment is deleted, not parked. Both measured negatives (design §3.8) are gone from the code —
generative_fill._snap_boundary and its _SNAP_* constants, the snap_px parameter, the film_fill_snap option and the --fill-snap flag, and their tests. A permanently-off path earns its keep only if someone would turn it on, and with the structure-conditioned generator closed too, nobody will; the write-ups stay as the record. Two things survive the branch and are new here: atr media film-reconstruct --fill-model PATH overrides the ATR-Fill-Film checkpoint so a candidate can be A/B'd against the shipped one, and the loader now reads the input width out of the checkpoint (ATRFillFilm(in_channels=) / in_channels_of()) so a checkpoint that is not the shipped 4-channel shape fails with its path and its width named instead of a bare tensor-shape error. The v1 render is bit-identical across the whole change (max delta 0 over 36 frames of the reference reel).
- Setup detection/validation/recommendation logic now lives in
core/setup/, a Qt-free package shared by the Settings page, the wizard and the startup dependency check. Previously the wizard carried its own copy of PATH refreshing, Python discovery and binary probing, so the two UIs could disagree about whether a component was installed. SetupWizardDialog._refresh_runtime_environment() is kept as a shim for existing callers.
- DaVinci Resolve detection no longer claims you have the free edition. Resolve 21 Studio does not write the registry key
discovery.py reads, so the edition probe reports "Free" for a Studio install. The setup check now reports what it can prove — the scripting API and library are present — and says the edition could not be confirmed, instead of telling a Studio owner they need to upgrade.
scripts/build_hook.py — manifest-driven demo-hook assembler. Takes a JSON timeline (intro clip, demo cards from make_demo_card.py, animated logo; narration lines placed absolutely or relative to a segment; background-bed pieces), conforms every segment to one 1080p59.94 timeline, writes VO / BED / MIXED stems for Resolve and the final mp4, plus a *_timeline.json. Needs only Python + ffmpeg, so hooks can be built on a box without the ATR pipeline. docs/demo_hook/hook_v16c.json reproduces the shipped cut; the kit is documented in docs/demo_hook/HOOK_KIT_README.md and docs/DEMO_CARD_GUIDE.md §7.
storage.app_data_dir is now the master storage root. ATR resolved storage through two systems that never referenced each other, so setting the master root moved the solutions DB and planner output but not the AI models. That made shipped UI text false: the wizard's Storage card called it "the folder that grows — a full model set runs to tens of gigabytes", and the low-disk recommendation offers to relocate it when free space drops under 25 GB — so the user with a full disk, the exact person the feature exists for, accepted the advice and the models did not move. Everything without its own override now derives from the master root (ai_models, onnx_models, jit_cache, temp), and a per-purpose key still wins when set, which is what lets an existing install keep its library exactly where it is while everything new follows the master. No files are moved. A one-time reconciliation runs before any path resolves and pins the override for a library already sitting at a legacy location, so the app keeps finding it; it writes settings keys only. Logs and the Visual QA models are not covered by either root — they are hardcoded to %LOCALAPPDATA% — and the wizard and Settings text no longer claim otherwise.
- One answer for "the system Python", and it is 3.12. Three places reached for an interpreter and disagreed: two used the
py launcher, which resolves to the newest install. On a machine with both 3.12 and 3.13, Add-ons could pip-install into 3.13, the frozen app inject 3.12's site-packages onto sys.path, and the setup checks probe a third answer — the user sees "installed, but ATR cannot see it" with no way to tell why. core/setup/env is now the single resolver and prefers 3.12, because that is what the shipped VapourSynth binds to. Migration note: on a machine where packages were previously installed into 3.13 through the old Add-ons path, ATR now looks in 3.12 and will report those dependencies missing; re-running the install puts them in the right interpreter.
- The first-run scan can now be cancelled while it is checking hardware. The scan probes PyTorch and the ONNX runtime, and a cold
import torch holds Python's interpreter lock for tens of seconds — so Cancel set a flag that was not read until the import it was meant to abandon had already finished. Those probes now run in a separate process, which can simply be stopped.
Fixed
- A render into a folder that does not exist yet now creates it. Nothing in the export path did: FFmpeg failed hundreds of steps in with "No such file or directory", and every sidecar written beside the output vanished quietly — Step 1c's
<output>.cadence.json above all, whose writer swallows OSError by design, so the projector-cadence record was simply lost. The destination folder (and its parents) is created once, at the top of process_video, before the first thing that writes into it, with a log line saying so; a path that genuinely cannot be created stops the job immediately with a readable reason instead of an FFmpeg error much later. One chokepoint, so the GUI, the CLI, the batch runner and MCP all get it. (core/processor.py, tests/unit/test_film_reconstruction.py)
- Film card copy that could not be acted on. Extended Detection's tooltip told the user to "supply a mask for that" — the GUI has no mask control, and
film_mask_path bypasses the detector entirely, so the advice was unfollowable and wrong; it now says plainly that a mark sitting at the same position all clip is not found by this test. "Skip Cadence Donors" named an internal concept and is now "Skip Smeared Frames as Donors", with a tooltip that says when it helps (damage on the film itself, carried into the repair by the smear frames), when it hurts (a smear mostly shows one of its two film frames, so for that frame it is the closest source there is — banning it measured several times worse on hidden-truth tests), and that the ordinary fix for smeared frames is the Projector Cadence stage, which removes them before any repair sees them. The seam feather tooltip is confirmed stating the 40 it ships. (gui/widgets/control_tabs.py, tests/gui/test_film_reconstruction_card.py)
- Presets: temporal smoothing came back ON.
AIModelsTab.apply_settings set the switch from the preset, then re-ran the model-change handler which reset it to the model's recommendation, so a preset with smoothing off loaded with it on. The preset's explicit value now wins.
- Plugin settings panels raised on every change. Their
settings_changed signal carries no argument but was wired straight to controls that emit one, so each edit raised a TypeError inside Qt and the change never reached the AI-settings listeners. Both guarded by tests/gui/test_card_preset_roundtrip.py, which scrambles every control on both card tabs and demands save -> apply to be lossless.
- Importing the RFDA engine no longer hijacks
import utils for the rest of the process. The engine put its vendored rfda_source checkout on sys.path permanently, and that tree ships its own top-level utils, model, ops, config and dataset packages - so after any RFDA import, every later import utils in the process resolved to RFDA's copy. The GUI escaped by accident (main.py imports utils.python_utils before any engine), but a CLI or MCP job that used RFDA and then reached a lazy utils import - BRAW decoding, the SeedVR2 engine - died mid-job on No module named 'utils.python_utils'. The vendored path now lives only for the duration of RFDA's own imports, and every generic name the checkout claimed is handed back afterwards (the loaded modules are kept privately, so the engine keeps working). The interpreter-resolution tests drop their load-by-file-path workaround, and a subprocess-based regression suite pins the isolation down. (ai_models/engines/rfda.py, tests/unit/test_rfda_import_isolation.py, tests/unit/test_interpreter_resolution.py)
- Setup accepted an FFmpeg the app then refused.
Install_Prerequisites_Auto.bat only required FFmpeg 6.x, two releases below the 8.1.2 floor that core/ffmpeg_security.py enforces for CVE-2026-8461 (MagicYUV heap OOB write), so a machine with, say, 8.0 on PATH was left in a state ATR permanently flags UNSAFE with no in-app way out. Both setup scripts now parse the full MAJOR.MINOR.PATCH and enforce 8.1.2: the installer replaces an older build instead of keeping it, and Check_Prerequisites.bat reports a below-floor build as a failed check rather than [OK]. Snapshot builds that report no release version (N-119534-g…, 2026-08-27-git-…) are treated as unsafe, the same way the app's gate treats them. (DISTRIBUTION/Setup/*.bat, DISTRIBUTION/Documentation/PREREQUISITES.md)
- Removed a bundled-FFmpeg story the build never implemented. The CVE-2026-8461 work described a vetted FFmpeg shipped in
runtime/ffmpeg/bin - an installer release note, build_scripts/ffmpeg_manifest.json claiming version 8.1.2, vetted: true with placeholder SHA256s, a manifest recorder, and a resolver escape hatch - but no build step ever staged a binary there, so the startup integrity check warned "Bundled FFmpeg is missing" on every launch and record_ffmpeg_manifest.py never ran (it hangs off Build_Distribution_v6.0.ps1, which is not in the 6.2 build path). ATR does not redistribute FFmpeg by design - the Inno installer runs the setup scripts post-install instead - so the manifest, the recorder, the vetted escape hatch and the integrity warning are gone. Startup now warns only when the resolved FFmpeg actually fails the gate, and prints how to fix it. Docs and the installer release note describe the setup-script path they always used. (build_scripts/, core/ffmpeg_security.py, main.py, docs/ROADMAP_v6.0.md)
- Installer wizard called a too-old FFmpeg "Detected".
CheckFFmpeg in ATR_Installer_v6.0.iss only tested that ffmpeg -version exited 0, so a machine with 7.x got "All prerequisites detected. Ready to go!" from Setup and a permanent UNSAFE flag from the app. The wizard now parses the version and holds it to the same 8.1.2 floor, naming the offending version in the status message. (build_scripts/ATR_Installer_v6.0.iss)
- An FFmpeg that failed the security gate gave the user nowhere to go. The block message said "Please update ATR", which is wrong - ATR does not ship FFmpeg and deliberately does not redistribute it, so updating ATR can never fix it.
core/ffmpeg_security.py now owns a single set of remediation steps (remediation_steps() / remediation_text(), pointing at the gyan.dev release build and at Setup\Install_Prerequisites_Auto.bat), reused verbatim by every layer: Settings prints them under the status line and shows a Get FFmpeg 8.1.2+ button that opens the download page, the CLI/MCP FFMPEG_SECURITY_BLOCK payload carries them as remediation + download_url, and the mid-restore block message names the download instead of ATR. The "Use custom/system FFmpeg" tooltip no longer claims a bundled vetted build the installer has never shipped. (core/ffmpeg_security.py, gui/pages/settings_page.py)
- BRAW: upside-down-but-not-mirrored film scans came out mirrored. The only orientation option was Rotate 180° (
hflip,vflip), so a transfer rig that projects the image upside-down without mirroring it — writing reads backwards after the rotation — had no correct setting. The Import page's BRAW card now has two Orientation toggles, Flip (rotate 180°) and Mirror (left-right), which reach every orientation a rig can produce (none / flip / mirror / flip + mirror); DevelopSpec gained independent flip_vertical / flip_horizontal (preset keys braw.flip_vertical, braw.flip_horizontal) that compose with rotate_180, so Flip + Mirror is a vertical flip only. Exposed on atr media braw-preview (--flip-v, --flip-h), the chat tool and the MCP server; the preview now applies the same orientation as the render instead of a hardcoded rotation. Existing presets are unchanged. (core/braw_ingest.py, core/options_builder.py, gui/pages/import_page.py, cli/commands/media_braw.py, core/chat/tool_registry.py, mcp_server/atr_media_mcp.py, docs/BRAW_TO_RESOLVE_GUIDE.md)
- BRAW: a trimmed render was refused on disk space for the whole reel. The pre-stage sized its FFV1 intermediate from the clip's full frame count even when
braw_in_frame / braw_out_frame limited the decode, so a 15 s excerpt of a 185 s 4K clip was rejected as needing ~246 GB. The estimate now uses the frames that will actually be decoded. (core/processor.py)
- CLI:
atr media braw-preview … --out x.png failed with "required: --out". The global --out (JSON response file) was extracted before the subparser ran, the same collision already special-cased for media export-markers. Subcommands that own --out (media export-markers, media braw-preview, orchestrate token mint) are now listed in one place and left alone. (cli/main.py, tests/unit/test_cli_global_out_flag.py)
- Settings saved through the UI were never read back in a packaged build. Three Qt-free modules located
restoration_settings.json with Path(__file__).parent.parent, which in a frozen build is sys._MEIPASS — the read-only PyInstaller payload — not the writable copy in {app} that the settings manager reads and writes. So choosing a storage folder in the wizard did nothing, "use custom/system FFmpeg" always read as off, and a custom FFmpeg path was never honoured. All three now resolve through core/settings_file.py, which explicitly refuses any candidate inside the frozen payload. The build also no longer bundles restoration_settings.json at all: the file is gitignored, so what was actually baked into the payload was the developer's live settings — local paths and setup_complete: true, which on a virgin machine would have downgraded the first run from the full wizard to a two-step upgrade run.
- A fresh install pointed ATR's temp folder at a directory Setup had already deleted. The installer wrote Inno's own
{tmp} scratch directory as temp_dir, and Inno deletes it when Setup exits. The installer no longer writes the key, and an existing install with the dead path has it cleared during reconciliation.
- The installer offered to overwrite your settings on upgrade, and answering yes orphaned your model library. The settings template installed with
confirmoverwrite, so "yes" reset model_root to empty while the library stayed on disk — after which the app looked in the wrong folder. It now installs onlyifdoesntexist; new template keys still reach existing installs through the settings manager's default merge. Installs already broken this way are healed by the reconciliation pass, which probes the installer's %LOCALAPPDATA%\ATRPro location as well as the app's own.
- BiT downloaded its weights to one folder and then looked for them in another.
core/ai_bridge.py hardcoded %LOCALAPPDATA%\Advanced_Tape_Restorer\ai_models for the BiT model, ignoring model_root entirely — a live bug on every installer-made machine, where the Models page downloaded the weights to the configured root while the engine reported them missing.
- Setup scripts and the support report read a hardcoded storage root.
Install_SeedVR2_Dependencies.bat, Install_HuggingFace_Runtime.bat and Install_ONNX_Runtime_NPU.bat wrote to the default layout regardless of the configured one. Collect_Support_Report.bat was the worst of them: on a machine with an override it silently collected no models and no logs, producing a support report that looked complete and was empty. All four now ask the app.
- ATR De-flicker — ATR's own engine — was the one engine you could not install from the UI. Its weights auto-download from R2, but the engine reports missing weights as a missing dependency, and with no installer mapped for that dependency the Add-ons row fell through to "Manual setup" with a Guide button telling the user to do it themselves. The row now offers Download Model. The guard is narrow — an engine with no auto-downloadable weights is unaffected, so DMDNet stays manual by design.
- External-binary engines could only find tools that were on
PATH. The generic probe in ai_models/engines/base.py used shutil.which() alone, so a tool installed to a fixed location it never adds to PATH could not be detected no matter what the user did. It now uses the same PATH-then-known-locations lookup the setup checks use. VideoCleaner is deliberately left on PATH-only detection: it ships no invokable executable — it is an AviSynth suite driven through VirtualDub — so "detecting" it would turn an honest not found into a Ready that opens a GUI and hangs the render worker.
- DeOldify's install steps could not succeed as written. The Model Manager told users to run
pip install vsdeoldify, which fails with "No matching distribution found" — the plugin has never been on PyPI at any version and ships as a wheel attached to a GitHub release. Upstream has also renamed itself (Dan64/vs-deoldify → dan64/vs-havc, package vsdeoldify → vshavc in v5.8.1), and the rename replaced the entry point ATR calls, so ATR is compatible with v5.8.0 and older. The steps now name that wheel URL directly and explicitly warn against the pip command users would otherwise reach for. A second contradiction is fixed alongside it: the steps said to copy the weights into the plugin's models folder while the "these files must exist" list pointed at ATR's — both are true, neither alone works, so the guide now asks for both copies and says why. The vshavc migration is deferred and logged in docs/design/DEOLDIFY_HAVC_MIGRATION.md; it changes the user-facing parameter surface, so it needs a deliberate mapping rather than an import rename.
- A version bump produced an installer looking for the previous build. The build script passed only the variant suffix to ISCC, so the
.iss fell back to the version hardcoded in its own #ifndef guard — a 6.2.1 EXE next to an installer expecting ATRPro_v6.2.0<suffix>.exe, which the build's own post-check would not catch until after the full PyInstaller pass had already run. The version now comes from version.py.
- Installing an add-on could silently switch your ONNX engines from the graphics card to the processor.
onnxruntime and onnxruntime-directml are separate packages that install into the same folder and own the same files. ATR asks for the accelerated build for four engines and also ships WhisperX, which requires the standard build through faster-whisper — so ATR created the collision itself. Installing WhisperX afterwards overwrote the accelerated files: with ATR running that failed as "Access is denied" part-way through, and with ATR closed it succeeded quietly and DarkIR, BiT, Video Swin-UNet, Restormer and LavaSR lost GPU acceleration with no error and nothing in the UI to explain it. ATR now installs the standard runtime alongside the accelerated one, pinned to the same version and with the accelerated build written last, which is a state pip leaves alone from then on. Add-on installs that would cause the collision are anchored first, Install_ONNX_Runtime_NPU.bat no longer removes the standard runtime (which produced exactly the fragile state it was meant to prevent), and any repair that has to rewrite those files refuses up front with an explanation if they are in use, rather than failing half way through.
- Setup could not see the two ONNX states that matter. Check my setup now distinguishes "graphics acceleration is installed but not protected — a later add-on install could replace it" from a healthy install, and reports "acceleration was replaced by a processor-only copy" instead of what previously looked indistinguishable from never having installed it.
- The first-run scan locked the ONNX runtime files it was reporting on. Checking which providers were available imported the runtime into ATR's own process, and Python never unloads a native module — so the files stayed locked for as long as ATR was open. On a fresh install that happened before the user ever reached the Add-ons page, which is what turned the next ONNX install into "Access is denied". Nothing is imported into ATR for this any more.
- VideoCleaner could hang a render indefinitely. The wrapper assumed a command-line interface that VideoCleaner does not provide — it is an AviSynth suite driven through a GUI — and ran it with no time limit, so pointing ATR at the installed executable blocked the job until the application was killed. It also handed the result to the decoder without checking a file had been written, so a tool that exited cleanly having done nothing produced an error about the wrong thing. It now times out with an explanation, suppresses any window, reports the tool's own error text, and verifies the output exists before using it.
- Uninstalling with "remove my data" left half of it behind. It cleared only one of the two locations ATR has used: installs made by an older installer keep their models under
ATRPro, current ones under Advanced_Tape_Restorer. Both are now removed. A folder you chose yourself is deliberately left alone -- reading a path out of a settings file and handing it to a recursive delete is how an uninstaller removes something it should not, so that one is yours to delete.
- One wedged FFmpeg check no longer freezes every preflight after it. The security check that vets your FFmpeg build cached its result behind a lock it also held while doing the work, so if a single probe ever hung, every later thumbnail and render preflight in that session queued behind it forever -- observed as workers stuck for 15+ minutes with nothing on screen. The lock now guards only the cached answer, and a check that was already in flight when you change FFmpeg settings can no longer overwrite the cache with stale information about the old binary.
- Rapid video switching could strand background workers. Loading a new video or regenerating thumbnails while the previous worker was still winding down dropped that worker unowned -- at best wasted work, at worst a hard crash when its Python wrapper was collected mid-run. Superseded workers are now disconnected and parked until they exit on their own, and joined at window close.
- The pre-render VRAM reading now describes the card your job will use. It queried CUDA device 0 unconditionally; on a multi-GPU machine that could be a different card than the one selected for AI work, and on a non-NVIDIA selection it overwrote the correct figure with an irrelevant one. The reading is matched to the selected GPU by name and falls back to the static capacity figure when the card cannot be identified.
- Paid add-on licensing moved to Lemon Squeezy. The LTX Generative Repair add-on was sold and activated through Gumroad; when that product was unpublished during the store transition, the marketplace's Buy button led to a 404 and there was no way to purchase or activate the add-on at all. Add-on activation now supports Lemon Squeezy keys (the engine index says which store issued each product; Gumroad keys still verify for anyone who holds one). Activation pins the key to the add-on's own product -- a base ATR license key and an add-on key are indistinguishable UUIDs from the same store, so without that check either key would have unlocked the other product -- and a key pasted into the wrong dialog has its activation slot released again rather than burned. Deactivating an add-on now releases its activation on the store side too.
- AI Inpainting settings never reached the engine. Enabling inpainting in the current interface (or via a preset through the command line) sent the processor option names it does not read: the mode always fell back to auto-detect regardless of your choice, and the auto-mask switch, detection mode, sensitivity, manual mask path and memory preset were dropped entirely -- with no mask ever arriving, the pass logged "Skipping ProPainter" and did nothing. Only the legacy interface wired these correctly. The option builder now emits the names the processor reads, translates the detection and memory labels to engine values, and a test reads the processor's option names directly from its source so the two sides cannot drift apart unnoticed again.
v6.2.0
2026-08-21
Internal build
Wide-Gamut Color & DaVinci Resolve Interchange (6.2)
- Wide (BT.2020) working space. The new Color Management card offers an opt-in Wide Gamut working space: BT.2020 sources keep their full gamut through the pipeline while every AI stage still receives the Rec.709 input it was trained on (per-stage color-space transforms, verified to 1-LSB round-trip fidelity). The default Rec.709 path is byte-identical to before.
- HDR sources are tone-mapped, not clipped. PQ/HLG material is converted to SDR at ingest with a hue-preserving knee'd rolloff (no plugin required). Output is always SDR; ATR does not produce HDR masters.
- DaVinci Wide Gamut / Intermediate mezzanine. The "Resolve Wide Gamut Master" preset renders a wide master and writes a ProRes 4444 DWG/DI mezzanine beside it, and Send to DaVinci Resolve configures the Resolve project automatically (DaVinci YRGB Color Managed, DWG/Intermediate timeline and clip input space) — verified live against Resolve 21 Studio.
- Blackmagic RAW ingest intelligence. Selecting a
.braw shows a BRAW card: native-science probe, develop control (Automatic follows Color Management), exposure lift (±5 stops), optional 180° rotation for upside-down film-transfer rigs (normal camera BRAW is upright — off by default), and Scan Reel, which classifies every clip (content / black setup take / empty projector gate) and sets the render in-point past the leader. The same workflow is available as atr media braw-* CLI verbs, an atr-media MCP server, and tools the built-in AI assistant uses on request. Guide: docs/BRAW_TO_RESOLVE_GUIDE.md.
- Solutions knowledge base ships seeded. Fresh installs now create the solutions DB on first use with curated recipes (starting with "BRAW reel to Resolve"), so the assistant can recommend proven workflows out of the box; user-harvested entries are never touched.
Packaged build
- Fixed the packaged GUI crashing on launch / job start (
0xC0000409) and restored in-process torch features in the packaged app. The build had excluded the standard-library unittest module since v6.0.0; torch's bootstrap imports it, so import torch failed half-way inside the EXE (silently disabling GFPGAN / ATR Face Restore, NVIDIA VFX, LavaSR JIT and friends), and the next import torch re-ran the native bootstrap on the half-loaded extension and aborted with no traceback. unittest is bundled again, and a torch import fence (utils/frozen_torch_guard.py) guarantees a failed torch import is never retried in-process — callers get a clear ImportError and the dependency dialog explains what is unavailable. New support switch ATRPro.exe --diag-torch reports in-process torch health to logs\torch_diag.txt. Design note: docs/design/FROZEN_TORCH_GUARD.md.
- Hugging Face runtime installer named for what it installs.
Setup\Install_Stream_DiffVSR_Dependencies.bat is now Install_HuggingFace_Runtime.bat (transformers, diffusers, accelerate) — the same packages serve both Stream-DiffVSR and Visual QA, and a Visual-QA-only user had no cue to run a script named for another feature. The old name still works as a shim; the prerequisites installer, Support Wizard, and the Visual QA "backend not installed" message all point at the new name.
- Engine Diagnostics no longer aborts on a single broken dependency. A dependency whose import raises something other than ImportError (the bundled
voicefixer package ran @torch.jit.script at import time, which cannot work from bytecode: "Can't get source for fused_add_tanh_sigmoid_multiply") now shows as that engine's error instead of killing the whole scan. voicefixer is no longer bundled — like torch it is installed into the system Python (pip install voicefixer), where TorchScript can read its source.
- scipy is bundled in the packaged app. It had been excluded as optional, but the ATR Stabilizer and the LavaSR ONNX audio runner require it and no setup script installed it, so the Diagnostics page reported it missing on a fresh install.
Face Recognition & Reference Library
- Selectable face-recognition backend. The Reference Library identifies and matches people with a commercial-safe stack by default — the YuNet detector plus the SFace recogniser (OpenCV) — with InsightFace
buffalo_l retained only as an EXPERIMENTAL research-mode option (its license records corrected to research-only). A track-voting study settled SFace as the shipping recogniser, and the selected backend is shared with the GFPGAN identity-drift guard.
- Gallery matching with an ambiguity margin, per track. Query faces are scored against the identity gallery with a measured similarity threshold and an ambiguity margin, and identity is assigned at the face-track level rather than per frame, so a person is labelled once across a shot instead of flickering.
- Cluster-then-name workflow and a Face Naming page. Detected faces are clustered first and the user names the clusters, which keeps the feature usable without a heavyweight licensed model. The workflow and face engine are wired into a dedicated Face Naming page in the GUI.
- Enrolment quality bar and reference selection. Enrolling reference faces applies a quality gate (blur, pose, size), an import summary, SHA-based duplicate reconciliation, and best-reference selection — and the same quality bar now governs photos imported from iCloud, Google Photos, and Amazon.
- Legacy libraries upgraded losslessly. Existing reference records are re-embedded from their original photographs, gaining the new backend's vectors without a re-import.
- Multi-frame fusion evaluated, then hidden. Reference multi-frame fusion was wired into the GUI and render pipeline, measured, and its control hidden after it showed no reliable gain — consistent with the measure-before-shipping approach documented under 6.1.0.
- Files:
core/reference_library.py, core/face_backends.py, core/face_recognition_pipeline.py, core/face_tracker.py, core/face_naming_workflow.py, core/face_enrolment.py, core/face_reconcile.py, core/face_multiframe.py, gui/pages/reference_library_page.py, gui/pages/face_naming_page.py, docs/FACE_RECOGNITION_GUIDE.md, docs/design/ATR_FACE_RECOGNITION_DESIGN.md
ATR Face Restore - Personalized Model Selection
- Per-user personalized restoration. When a personalized ATR Face Restore model (a self-contained per-user fine-tune of the blind v2 base, trained from a curated reference library) is installed, it becomes the face restorer for every detected face — one model, no per-identity routing — with the identity-drift guard still applied on top. Absent a personalized model, the blind v2 base is used, exactly as before.
- Auto-detect + override. By default the pipeline auto-detects a personalized model: the active slot (
ATR_Face_Restore_pers_active.pt) if present, otherwise a lone installed ATR_Face_Restore_pers_<user>.pt (the legacy ATR_RFPP_pers_* prefix is still recognised for back-compat). A new selector on the Reference Library settings (ATR Face Restore model) lets the operator force the blind base or pick a specific person's model; a requested-but-missing model degrades safely to auto. The selector is gated on the ATR-Face-Restore toggle.
- Files:
core/rf_restore/resolver.py, core/rf_restore/__init__.py, core/processor.py, core/options_builder.py, gui/settings_manager.py, gui/pages/reference_library_page.py
ATR Face Restore - On-Device Personalization Training
- Train your own model in-app. The Reference Library's Train personalized model button fine-tunes a per-user ATR Face Restore model from the curated library, on the user's own machine, and drops it where the resolver auto-detects it — closing the loop curate → train → personalized restore. The trainer is an optional component (it carries the torch stack) that is never bundled in the base EXE.
- Three editions, matched to the GPU. The trainer ships in three editions — CUDA Full (offline WSL image,
wsl --import), CUDA Lite (small WSL kit + install.sh), and DirectML (Windows-native portable Python) — all training the identical model. NVIDIA machines are offered Full then Lite; AMD/Intel get DirectML. CUDA editions run under WSL2 (GPU via /dev/dxg, non-elevated); DirectML runs natively and defaults to batch 1 on weak/integrated GPUs.
- In-app install with a hosting-agnostic manifest. When the trainer is absent, the app offers to download and install the right edition. The download source is a configurable base URL (
rf_trainer_download_base_url) plus a manifest.json (per-edition filename/sha256/size_mb), so the kits can be hosted on a NAS, object storage, or a CDN with no code change. Every download is streamed to a .part file, sha256-verified, and only then swapped in.
- Files:
core/rf_train_install.py, core/rf_train_launch.py, gui/rf_install_worker.py, gui/rf_train_worker.py, gui/pages/reference_library_page.py, docs/FACE_PERSONALIZATION_GUIDE.md, docs/design/RF_ONDEVICE_TRAINING_DESIGN.md, docs/examples/atr_trainer_manifest.json
Face Harvest - Ground-Truth Labelling Tooling
scripts/harvest_faces.py builds labelled face ground truth from real tape captures. It samples frames, detects with YuNet, gates on quality, aligns, embeds with both SFace and ArcFace, clusters with the stronger recogniser to ease labelling, and emits an HTML page for human naming — so backend comparison is driven by footage ATR actually restores, not stock photos.
- Incremental runs. New footage can be merged into an existing run without losing labels, and finished runs can be combined without re-detecting.
- Files:
scripts/harvest_faces.py
Reference Library - Google Photos Retrieval Guidance
- Three retrieval routes, not just Takeout. The Google Photos help dialog (renamed How to Get Photos) now leads with the fastest option for building a reference set: selecting photos at photos.google.com and downloading the originals — no multi-gigabyte export and no wait. It also documents copying from a phone over USB (
DCIM\Camera in File transfer mode), whose on-device files stay full quality even when the cloud backup uses Storage saver, and keeps Takeout for whole-library export. The dialog offers open buttons for both photos.google.com and takeout.google.com; the hint text and button label were updated to match.
- APIs deliberately not used. Since 2025-03-31 the Google Photos Library API exposes only app-uploaded media, and the sanctioned Picker API would require shipping OAuth in the EXE and returns transcoded downloads with stripped location EXIF — unjustified overhead for a local folder-scan feature. The routes above deliver the same original files with no API surface.
- Files:
gui/pages/reference_library_page.py, docs/REFERENCE_LIBRARY_GUIDE.md, FEATURES.md
Hardware Capture - Live Monitor Workflow
- Reworked the live monitor and capture-output flow (confirmed on hardware). Captures now default to the system Videos folder while still honouring an explicit user-selected path. Adds pre-capture source monitoring, aspect-fit behaviour for the embedded preview, live-monitor toggle handling, and alternate S-Video input routing.
- Files:
gui/pages/capture_page.py, gui/main_window_fluent.py
Build, Packaging & CLI
- libmpv is bundled into the frozen app. The in-app player previously had no libmpv in the EXE; it is now packaged so playback works from the installed build. A dead
quality_analyzer import was removed in the same pass.
atr-cli exposes all frozen subcommands. The packaged CLI previously surfaced only plan; every subcommand is now available in the frozen build.
- Versioned packaging cleanup. The packaged EXE name derives from the build version, the abandoned
build_scripts/ATR_Installer.iss was removed in favour of the versioned installer script, and the MediaPipe research asset is excluded from packaging (the telemetry-free landmark ONNX support asset still ships).
- Files:
build_scripts/, atr_cli.py, cli/, version.py
Distribution & Activation
- Landing page synced from the live site.
DISTRIBUTION/index.html was refreshed from the deployed idocinthebox.com (v6.1, external stylesheet, post-relicense licensing footer), with Cloudflare edge injections stripped for the repo copy.
- Activation dialog corrected. The Lemon Squeezy activation dialog showed v6.0 and v4.1-era pricing and linked a dead path; it now reads v6.1 with the current early-adopter price and points at the site root.
- Files:
DISTRIBUTION/index.html, DISTRIBUTION/assets/site.css, licensing/lemonsqueezy_dialog.py
v6.1.0
2026-07-26
Previous release
Identity-Preserving Face Restoration Research (Experimental, Hidden)
- Single-sourced the runtime application version at 6.1.0 so GUI, CLI, legacy CLI shim, tests, and
VERSION.txt no longer drift.
- Built wide same-track donor search: ±30 sequentially decoded frames, a strict 1.25x aligned-crop donor bar, raw face-region pre-ranking capped at 12 detector calls, and up to three donors fused by per-pixel correspondence confidence.
- Added a donor detail-energy gate and a measured 112px identity-band guard. Transfer edits only locations where registered donor detail exceeds the probe; the final uint8 output is measured and scaled back if recogniser-scale luma RMSE exceeds the configured cap.
- Added Google's official MediaPipe Face Mesh landmark network as a bundled, telemetry-free ONNX support asset. ATR ships no MediaPipe runtime or TensorFlow. The 468-point model runs through ONNX Runtime with CUDA → DirectML → CPU fallback, a persistent DirectML-safe session, SHA-256/graph validation, and a face-presence gate. Published training: synthetic rendered 3D landmarks plus annotated real-world 2D contours, iteratively bootstrapped for difficult pose, expression, and occlusion; it is not an identity model.
- Conversion record: official TFLite hash
1055cb9d4a9ca8b8c688902a3a5194311138ba256bcc94e336d8373a5f30c814; tf2onnx 1.16.1 / opset 17 ONNX hash a0f2baf70a5f93c84baaebbc31a69018c7e58963ccf463609514aa419d16108e. TFLite/ONNX max absolute error 0.00013733; warm real-crop inference 6.06 ms CPU / 1.84 ms DirectML, max provider landmark difference 6.6e-7.
- Added guarded piecewise-affine face warping. Flipped, degenerate, extreme-area, and low-coverage meshes are refused; low presence, missing runtime/model, or any inference/warp error falls back to ECC+Farneback and never breaks a render.
- Replaced GFPGAN's proportional identity-drift formula with a measured one-dimensional gallery-anchored blend search. It selects the least original contribution that restores the confirmed identity; no passing partial blend returns the untouched source face.
- Removed the reference pipeline's direct
FaceAnalysis("buffalo_l") path. Reference-guided matching now uses face_backends.get_backend(), so commercial builds enforce YuNet/SFace and cannot instantiate or auto-download InsightFace weights. The selected backend is shared with the drift guard; failure falls back to blind GFPGAN. Existing libraries add SFace vectors through Reindex.
- Split reference-match policy by embedding backend. OpenCV/SFace now defaults to the measured
0.25 threshold and 0.12 ambiguity margin; the Reference Library UI persists those values separately and retains 0.60/0.05 for buffalo_l research mode.
- Retired the stale SFace experimental warning. Its measured automatic policy produced zero wrong identities on the 52-track archival study with 65.4% coverage; reduced recall remains documented as conservative abstention.
- Fixed GFPGAN model routing: v1.3, v1.4 and RestoreFormer now resolve to distinct registry checkpoints. Runtime logs record the exact checkpoint filename and computed SHA-256, and RestoreFormer receives its declared architecture at enhancer initialization.
- Wired the Face Enhancement card's Reference-Guided switch to the same
reference_pipeline_enabled setting as the Reference Library page. Removed the dead option key and updated the tooltip to describe the commercial YuNet/SFace, per-face GFPGAN and identity-drift path.
- Removed CodeFormer from commercial face and fallback selectors and added a runtime licence guard for stale presets. Research builds retain it only as a clearly labelled non-commercial, user-installed option.
- Propagated cancellation to the active GFPGAN checkpoint processor and final face-pass FFmpeg process. Paused enhanced frames now survive cleanup and are migrated into the next run with updated checkpoint paths.
- Preserved selected/source pixel formats and codec parameters through the GFPGAN face-pass re-encode. Encoder capabilities are queried before fallback; a real FFV1 smoke encode retained
yuv422p10le.
- Removed dead face-card option emission and hid unwired background/detection controls. Added a persisted GFPGAN Strength slider, removed the inert
original architecture choice, and corrected the Python device tooltip.
- Added an inward-feathered MediaPipe face-oval mask for aligned GFPGAN paste-back. It leaves background outside the oval untouched (0.000 mean ring change versus 4.919/5.218 for the legacy square) and passed 12-identity SFace non-inferiority (-0.0278, 95% CI [-0.0763,+0.0165]). The support model and notice ship again but remain hidden from model browsers; transfer modules do not.
- Extended the identity drift guard with a calibrated inner-feature geometry axis (threshold 0.10). It caught 2/12 visible GFPGAN reshapes missed by embeddings, fired on 0/48 controls, reused the measured blend search, and passed SFace non-inferiority (-0.0372, CI [-0.0823,+0.0020]).
- Added dense-pose attenuation inside blind SmartGFP. Measured yaw/pitch bands gated 3/12 high-risk faces with 62.5% source contribution, left 9 ordinary poses byte-identical, visibly reduced invented shape/glasses, and passed SFace non-inferiority (-0.0368, CI [-0.0820,+0.0025]).
- Measured tracked mesh/NCC temporal smoothing and rejected it: progressive temporal variance improved only 0.032% and DV worsened 0.005%; private side-by-side media was visually indistinguishable. Identity passed and guard fires did not rise, but the research module remains excluded from builds.
- Made the pinned GFPGAN stack runnable on Python 3.12 without changing Torch, added eager dependency checks, cancellable model acquisition with progress and partial cleanup, exact passthrough frame extraction, and aligned-crop output.
- Produced private progressive/DV GFPGAN release evidence: exact frame counts, audio, BT.601 matrix and
yuv422p10le passed; cancel/resume completed; visual checks found no doubled facial features. A 12-identity SFace gate passed the -0.10 non-inferiority floor (mean -0.0380, 95% CI [-0.0828,+0.0014]).
- Dense landmarks did not revive cross-photo transfer: synthetic GT luma RMSE changed
+0.086 for a real same-person donor and +0.069 for a wrong-person donor, with confidence 0.432 versus 0.437. The self ceiling still improved all 36/36 cases.
- The full guarded dense+residual-flow gate remained identity-neutral but not visibly restorative: 89/141 selected attempts transferred (63.1%; 89/242 labelled), with 72 scored probes / 10 identities. Margin changed
+0.0018 (95% CI [-0.0024,+0.0049]); native pixels changed only 0.294 levels on average (p95 1.483, 112px RMSE 0.144). A 4x unguarded ceiling became visible (1.596 mean / p95 8.32) only by producing obvious doubled/rippled features, so it is rejected.
- No restoration UI was added. The support model, registration modes, and harness remain available for controlled evaluation; the production drift referee ships because it safely constrains existing GFPGAN output.
DMDNet Relicensed as User-Installed, Disabled by Default (BREAKING)
- Corrected license. Upstream DMDNet (csxmli2016/DMDNet) is CC-BY-NC-SA-4.0 — non-commercial with a share-alike obligation. ATR had recorded it three different ways, all wrong:
MODEL_LICENSES.md said Apache-2.0/commercial-OK, the engine source header said MIT, and EngineMetadata.license_spdx said "MIT". The Apache-2.0 claim came from DMDNet crediting BasicSR; a dependency's license does not relicense the derivative built on it.
- Four gates were silently inert. With
non_commercial unset, the preflight job warning, is_commercial_safe("dmdnet"), AutoPilot's license_flags (so allow_non_commercial: false could not block a DMDNet plan), and the model-browser filter all treated DMDNet as commercial-safe. Fixed by license_spdx="CC-BY-NC-SA-4.0" + non_commercial=True.
- Removed ~627 lines of inlined upstream architecture from
face_dmdnet.py. That code was compiled into the shipped EXE, which is a redistribution problem no runtime gate can fix (NonCommercial, ShareAlike, and — until now — no attribution entry at all).
- DMDNet is now user-installed and disabled by default. ATR bundles no DMDNet code and no weights and never downloads either. To use it for non-commercial work: clone the upstream repo, fetch
DMDNet.pth, set dmdnet_dir (or ATR_DMDNET_DIR), and set ATR_ENABLE_DMDNET=1.
- Removed from commercial feature and tier tables. DMDNet is no longer presented as a capability of the paid product.
- Migration: users who had DMDNet working now fall back to GFPGAN for matched reference faces until they install the upstream checkout. Their existing
dmdnet.pth stays valid — the official checkpoint is a bare state_dict, so the architecture must come from the checkout.
- Files:
ai_models/engines/face_dmdnet.py, ai_models/models/registry.yaml, core/processor.py, core/planner/registries/nodes.yaml, gui/pages/reference_library_page.py, licensing/MODEL_LICENSES.md, licensing/THIRD_PARTY_LICENSES.html, docs/commercial/README.html, docs/commercial/RELEASE_NOTES.md
- Design doc:
docs/design/DMDNET_LICENSING.md
- Still open: written commercial authorization from the upstream authors.
Full-Frame Stabilizer - Temporal Donor Search Radius 5 -> 15
Full-Frame AI Stabilize now searches 15 neighbouring frames for real border content instead of 5. At 5 the mode was not clean — 23 frames of the validation clip still carried replicated edge pixels, at 9.99% mean crop. The radius is a CEILING, not a fixed cost: the fill stops walking outward the moment the border is covered, so easy footage still finishes at distance 1-2 and pays nothing.
- Controlled timing, 3 reps on real DV (900 frames, 60p, Balanced, 12% budget), medians — crop and over-budget figures were bit-identical across every rep:
| radius | wall | mean crop | over budget | residual |
| 5 | 254 s | 9.99% | 83 | 0.071 (peak 9) |
| 15 | 295 s | 6.12% | 42 | 0.000 |
| atlas (whole window) | 377 s | 3.84% | 32 | 0.000 |
- +16% wall time to make the mode clean and hand back ~3.9% of frame. The two full-frame modes now differ by how much frame they save rather than by whether the output is honest:
Full-Frame AI Stabilize 6.1% crop at 295 s, Full-Frame AI + Border Rebuild 3.8% at 377 s.
- The 5 was stale rather than tuned: it predates the crop budget, when a wider search bought little. With the budget behind it every extra donor directly buys back frame area that would otherwise be cropped.
Full-Frame Stabilizer - Border Artifact Fixes
Both full-frame border policies produced visible edge artifacts on real DV footage (720x480 BFF, QTGMC bob to 60fps, Balanced), on runs whose neural path succeeded completely — so neither was a degraded-path symptom.
- Curved seam under roll (
Full-Frame AI + Border Rebuild). Donor content was mapped into the frame by translation alone, with rotation explicitly dropped. Across the whole-window donor pool the atlas policy uses, that dropped rotation bent straight architectural lines into a curve where synthesized border met real frame. Donors are now mapped by the exact composed affine (correction_i . D(P_i) . D(P_j)^-1), which also warps the ORIGINAL source frame rather than an already-resampled render, removing a resampling generation.
- Horizontal smearing where no donor exists (
Full-Frame AI Stabilize). The renderer kept BORDER_REPLICATE, stretching the outermost column outward — measured at 8.8 mean / 40 peak replicated columns (5.5% of frame width) against a source with none. Added a crop/zoom budget: a geometry-only planning pass finds the pixels no temporal donor can cover and the renderer zooms just enough to push them out of frame, rate-limited across each continuous path run so the zoom never pumps or steps at a window boundary. Only affected frames pay any crop.
- Added Restore → Stabilization → Max border crop (Off / 6% / 12% / 20%, default 12%) for the full-frame AI modes.
Off restores the previous behavior exactly, edge artifacts included. Disabled for Gaussian, which renders its own borders.
- Stabilization reports and the job log now carry the crop outcome, including
frames_over_budget — frames whose artifacts the budget could not cover and which therefore still ship replicated edges. With the budget off the counts are marked unmeasured rather than reported as zero.
- Classic fallback windows now honour the budget too. A window the neural path refuses renders through
classic_2d, which defaulted to the same full-frame + BORDER_REPLICATE policy — and unlike the LDI path it has no border fill behind it, so that smear was final. On the validation clip those windows carried mean 6.0 / peak 11 replicated lines that the LDI-side crop budget never touched. Note this is the classic_2d fallback INSIDE the full-frame engine, not the shipping crop-based presets in core/stabilizer.py, which were never affected.
- Added replicated-edge-column and straight-line-continuity regression tests, plus
quality_metrics.replicated_edge_extent(), the detector that found the defect.
- Validated on real DV graduation footage (720x480 BFF, QTGMC Very Slow bob to 60p, 900 frames, Balanced; 27 LDI / 0 degraded / 2 classic-fallback windows):
- Genuine replicated lines per frame, whole clip: mean 2.94 / peak 24 -> 0.000 / 0 with
Full-Frame AI + Border Rebuild at the 12% default — cleaner than the source itself (0.006 / 1).
- Border reconstruction accuracy against ground truth (real plate, synthetic camera with +/-3.5 deg roll, so the lost border is recoverable and scoreable): MAE 14.34 -> 0.99, PSNR 19.78 -> 42.48 dB, a 93% error reduction — the direct measure of the seam-geometry fix.
- Atlas needs materially less crop than temporal for a better result (mean 3.8% vs 10.0%, 32 vs 83 over-budget frames): the wider donor pool means less frame sacrificed, which is what the donor-aware planner is for.
- Cut border-fill planning cost from ~300 ms to ~15 ms per frame at DV resolution by testing only surviving border pixels and skipping donors that map to within half a pixel of one already tried.
Professional GUI - ProPainter Setup
- Added Settings → Default Paths → ProPainter with a read-only configured path, readiness summary, and Configure action that opens the existing guided setup/test assistant.
- Added nested-install detection for the standard
C:\ProPainter\ProPainter layout and immediate propainter_path persistence through the shared settings manager.
- Wired the Professional processing service to pass the latest configured path into each job's
VideoProcessor instead of silently constructing ProPainter without it.
Full-Frame AI Stabilizer - Run 5 Model
- Promoted the calibrated Run 5 feed-forward predictor as model v1.1: blind-holdout mask IoU improved from 0.1694 to 0.2027, depth error improved from 0.2069 to 0.1986, and the CUDA end-to-end stabilization gate passed.
- Versioned the calibrated dynamic-mask threshold (
0.7) in checkpoint and ONNX metadata so PyTorch and ONNX runtimes apply identical postprocessing.
- Updated the guided Model Manager import to require the pinned v1.1 artifact at
fullframe_stabilizer/1.1/predictor.onnx; v1.0 remains on disk as rollback material but is no longer accepted as the current model.
Full-Frame Stabilizer - Phase 5 Product Surface
- Added
Full-Frame AI + Border Rebuild, which uses the trained predictor and LDI renderer with a full-scene-window temporal atlas for exposed-edge reconstruction.
- Added
Tape-Aware Stabilize, which bridges isolated bad tape frames identified by adjacent motion resets while preserving real scene cuts and longer damage runs.
- Added Fast Preview, Balanced, Pro Full Frame, and Labs Gaussian operating modes with bounded scene windows and 720p/1080p/full-resolution analysis policies; output remains at source resolution.
- Wired new modes through Restore settings, preset restoration, processing options, runtime routing, missing-model UI guidance, and headless model preflight.
- Added a Draft 2020-12 JSON Schema for classic, LDI, and gsplat stabilization reports and normalized classic report fields across success, give-up, and empty-input paths.
- Hardened mixed-quality footage handling with frame-weighted consistency telemetry, scene-cut-aware honesty gating, uniform LDI re-rendering after Gaussian degradation, deinterlace-first routing tests, and an explicit completion warning when stabilization is safely skipped.
Security - CVE-2026-8461 (FFmpeg PixelSmash / MagicYUV)
- Added centralized FFmpeg resolver and security gate (
core/ffmpeg_security.py): ATR now prefers the bundled, vetted FFmpeg (runtime/ffmpeg/bin) and treats any FFmpeg below 8.1.2 as unsafe for decoding MagicYUV media (heap out-of-bounds write).
- All decode/preview/analysis paths (
core/processor.py, core/preview_generator.py, core/auto_analyzer.py), the CLI (plan render-variants, new exit code 7 with structured FFMPEG_SECURITY_BLOCK JSON error), and the MCP atr_render_preview_variants tool now run a MagicYUV preflight and refuse to silently use an unsafe FFmpeg.
- Settings page gained an FFmpeg status panel, a "Recheck FFmpeg" action, and an opt-in "Use custom FFmpeg" toggle (off by default).
- Startup now logs the resolved FFmpeg path/version/source and warns if the bundled binary fails integrity checks.
- Build pipeline records the bundled FFmpeg version + SHA256 into
build_scripts/ffmpeg_manifest.json via build_scripts/record_ffmpeg_manifest.py; the installer carries a security release note.
Model Manager - 1-Click Installs
- Added 1-click Install for pip-based VapourSynth plugin models (BasicVSR++, SwinIR, RIFE): new
PipPluginInstallWorker runs pip install <pkg> + python -m <pkg> in the system Python with streaming progress, cancel, and timeout handling.
- Made BiT++ Real-World and BiT++ Synthetic 1-click: registry now points at the direct Google Drive checkpoint files (
latest.ckpt saved as bit_standard.pth / bit_light.pth) instead of the folder link.
- Corrected BiT/BiT++ licensing from Apache-2.0 to the upstream MIT license, added the required copyright/license notice to installer payloads, and documented that upstream does not publish a separate checkpoint-specific license statement; ATR continues to download checkpoints externally rather than bundling them.
- Made Video Swin-UNet (Tape-Specialized) 1-click: downloads
best.ckpt from the authors' Google Drive and auto-converts it to ONNX via a new registry post_install hook (download_target + _run_post_install in ModelManager).
- Fixed FLAVR registry file entries (
filename + path split form was ignored by ModelManager), so the existing Google Drive 1-click downloads now land at flavr/FLAVR_{2,4,8}x.pth and install status is detected correctly; FLAVR ONNX entries now point at the real onnx_models/flavr/ export paths.
- Model cards now always preserve the manual path: a "Manual Install" button (opening the step-by-step guide) is shown next to Install/Retry for every auto-downloadable model with guidance, and the Setup Guide remains available after installation.
- Models that stay manual-only and why: CodeFormer (NTU S-Lab non-commercial license requires user install), DarkIR Large (authors' OneDrive only), Stable Video Diffusion (complex diffusers setup), DeOldify (must install into VapourSynth's bundled Python), InsightFace (self-managed auto-download), FLAVR ONNX (local export), MiniCPM (scaffold).
Visual QA Completion
- Added Visual QA planner arbitration and manifest embedding for
plan promote --use-visual-qa-hints and plan create-job --with-visual-qa, including persisted visual_qa payloads in restore job manifests.
- Added Visual QA influence mode wiring (
off / note / guidance) from Restore UI through processing options and planner integration.
- Added per-job and aggregate batch Visual QA report generation (
qa_report.json/qa_report.html, qa_aggregate.json/qa_aggregate.html) with runtime metadata propagation to output sidecars.
- Added Visual QA model acquisition workflow in Diagnostics (download, install-from-folder, progress, cancel, license acceptance persistence).
- Added Visual QA marketing explain mode via
atr visual explain --marketing and new prompt template core/visual_qa/prompts/v1/explain_marketing.j2.
- Added Visual QA sidecar client backends and sidecar launcher scaffolding for transformers/llama.cpp integration paths.
- Added Visual QA sampling improvements (scene-aware, defect-triggered, face-aware composition), evidence thumbnail generation, and clickable evidence seek integration in Scene Editor and Compare Presets ranking explanation.
- Added new unit/plugin coverage for arbitration, aggregate reporting, model manager install/download behavior, marketing explain output, and planner Visual QA integration paths.
- Added Addendum A runtime bootstrap flow for sidecar Visual QA venv provisioning and transformers capability probing with resolved-version persistence.
- Added Addendum A hardware preflight, MiniCPM chat-path backend updates, bounded JSON retry extraction, warmup + idle-eviction lifecycle, download progress/cancel/resume handling, transformers self-test gates, and diagnostics live-runtime status actions.
Visual QA - Packet 3
- Added
atr visual install CLI verb with fake/folder installs, status, uninstall/purge, and GUI-required guard for licensed modes.
- Wired Diagnostics "Manage in Plugins page" to a concrete navigation contract that opens Add-ons, switches to Community Plugins, and highlights the featured Visual QA card.
- Added constrained suite-crash triage workflow and quarantine guardrails for non-slow/non-gpu test execution.
Visual QA - Packet 4
- Rewrote the transformers in-process backend to use native transformers multimodal generation (
AutoProcessor + apply_chat_template + AutoModelFor* + generate) and removed .chat() usage.
- Added runtime strategy bootstrap enforcement during folder/download installs so transformers installs fail fast when runtime bootstrapping is unavailable instead of writing a false-installed state.
DaVinci Resolve Connector
- Added
core/resolve_bridge/ library: discovery, connection, MediaPool, markers, render, job contract.
- Added
tools/atr_resolve_bridge.py helper EXE (atr-resolve-bridge.exe) with ping, ingest, and status subcommands (JSON protocol on stdout).
- Added
resolve_scripts/atr/ Resolve-hosted scripts: send_to_atr.py (Workspace > Scripts entry point), _dialog.py (Tk preset picker), _runner.py (atr-cli subprocess driver), _state.py (marker state I/O).
- Added
gui/widgets/resolve_status_card.py: colour-coded status pill, Test Connection, Install Resolve Scripts, and Open Scripts Folder actions.
- Added "Send to DaVinci Resolve…" button to the Post-Process page External Tools card; runs via
ResolveIngestWorker(QThread) to avoid UI blocking.
- Added
resolve.* settings defaults in SettingsManager (api/lib path overrides, bin template, marker/timeline preferences).
- Added
build_scripts/atr_resolve_bridge_v6.0.spec for standalone PyInstaller packaging of the bridge helper.
- Added
docs/design/RESOLVE_CONNECTOR_v6.0.md design notes.
- Requires DaVinci Resolve Studio edition; Free edition is detected and shows a clear error rather than silently failing.
v6.0.0
2026-05-09
Internal build
ATR AutoPilot Headless Batch & Signed Tokens (Phase 5 Preview)
- Added headless serial batch orchestration via
orchestrate batch <manifest> for unattended multi-source runs.
- Added signed token file workflows with
orchestrate token mint and orchestrate token verify.
- Added per-entry batch progress events and batch summary reporting with deterministic reason codes.
- Added Phase 5 orchestration tests covering manifest validation, serial execution, policy blocks, stop-on-failure, and signed token handling.
ATR AutoPilot External Agent Adapter (Phase 4 Preview)
- Added AutoPilot external-agent adapter with a tool-allowlisted orchestration runner and full runlog trail.
- Added provider support for
scripted (offline deterministic) and optional openai_agents (env-key gated) modes.
- Added hard safety caps for external-agent runs (
max_steps, max_wallclock_s) plus cancellation and failure handling.
- Added AutoPilot GUI controls for
Use external agent, provider, model, and API key environment variable name.
ATR AutoPilot Policy Engine & Editor (Phase 3 Preview)
- Added AutoPilot policy engine with runtime/VRAM/license gates enforced before approval token mint.
- Added deterministic proposal estimation for
est_runtime_seconds and est_vram_mb.
- Added canonical policy file
policies/auto_run.yaml and new CLI commands orchestrate policy show|validate.
- Added optional
--override-policy on orchestrate approve with audit trail stamping.
- Added a new top-level
Policy GUI page for editing and validating policy thresholds.
ATR AutoPilot GUI Approval Surface (Phase 2 Preview)
- Added AutoPilot GUI approval surface (Phase 2 Preview).
- Added a new
AutoPilot navigation page that runs orchestrate plan in a background worker, renders proposal/policy/preview outputs, and supports Approve & Execute.
- Added GUI cancellation handling for active AutoPilot subprocesses with clean UI recovery.
- Added GUI test coverage for plan/approve/execute flow and cancellation handling.
ATR AutoPilot Orchestration CLI (Phase 1 Preview)
- Added new top-level CLI verb
orchestrate with subcommands plan, approve, execute, and run for approval-gated restoration orchestration.
orchestrate plan now runs planner stages through existing CLI verbs (profile, candidates, validate, compile, render-variants, compare) and writes proposal.json plus orchestration_log.jsonl.
orchestrate approve validates/mints signed approval tokens and persists approval.json.
orchestrate execute enforces approval, then chains promote -> create-job -> job run using existing CLI operations only, and writes restoration_report.json.
- Added core orchestration primitives under
core/orchestration/ and initial pytest coverage under tests/orchestration/.
Scene Editor Subtitle Export & Transcription Integration
- Subtitle export modes (NEW): Scene Editor export card now includes 4-option
Subtitles combo: None (default), Sidecar transcript (SRT), Burn-in subtitles, Sidecar + Burn-in. Matches the full transcription workflow from Transcription page.
- Auto-transcription gate (NEW): When exporting from Scene Editor with subtitle modes requiring transcription, a modal QProgressDialog prompts the user to transcribe first if not already cached. Uses the same
TranscriptionWorker from gui/pages/transcription_page.py for consistency.
- Per-scene subtitle clipping (NEW): Transcription segments are automatically clipped to the export trim window and timestamps are shifted to start at 0, ensuring subtitles align perfectly with the rendered scene.
- Post-processing pipeline (NEW): After rendering, subtitle post-processing writes sidecar SRT files and/or invokes
core.transcription.burn_in.burn_in_subtitles() to re-encode with burned captions.
- Merged-mode sidecar (NEW): When rendering multiple scenes as a batch, a final merged SRT is written alongside the concatenated output (if sidecar mode requested).
- MCP & CLI exposure (NEW): Four new CLI verbs under
scene subcommand: write-srt (extract SRT from transcription JSON), burn-in (FFmpeg burn SRT), render (single scene with optional subs), transcribe-and-burn (end-to-end). Wrapped in separate MCP server atr_scene_mcp.py with 4 tools: atr_scene_write_srt, atr_scene_burn_in, atr_scene_render, atr_scene_transcribe_burn.
- Files:
gui/pages/scene_editor_page.py, gui/pages/transcription_page.py, gui/main_window_fluent.py, cli/commands/scene.py, mcp_server/atr_scene_mcp.py, core/transcription/burn_in.py, core/transcription/result.py
v6.0.0
2026-04-20 (Continued)
Internal build
Temporal Shadow Recovery (TSR)
- New deterministic restoration engine: Temporal Shadow Recovery adds motion-aware shadow detail recovery for underexposed tape footage without requiring model downloads.
- Restore UI card: Added a Temporal Shadow Recovery card after Denoise with presets (Off / Light / Medium / Heavy / Custom), strength, temporal window, highlight protection, chroma protection, flicker guard, denoise coordination, and expert luma controls.
- Processing pipeline integration: TSR runs as a direct Python/FFmpeg pre-filter before VapourSynth script generation, then feeds a lossless FFV1 intermediate into the normal restoration pipeline while final audio remains mapped from the original source.
- Preview parity: Preview generation runs TSR in preview mode before VapourSynth so before/after samples reflect the enabled settings.
- Engine diagnostics and preflight: Registered TSR through the EngineBase system and device scheduler/preflight model map with no external model assets required.
- Auto-Analyzer support: Underexposed footage can now receive TSR recommendations with an Analysis-page apply button for recommended TSR settings.
- Tests: Added unit coverage for TSR masks, highlight protection, gain stabilization, and luma reconstruction behavior.
- Files:
core/tsr/*, ai_models/engines/tsr_engine.py, core/processor.py, core/preview_generator.py, core/options_builder.py, core/auto_analyzer.py, gui/widgets/restore_tab_simple.py, gui/pages/analysis_page.py, gui/main_window_fluent.py, tests/unit/test_tsr_phase1.py
v6.0.0
2026-04-10
Internal build
ACE-Step 1.5 Music Source Integration (Experimental, Local AI)
- New optional music source: ACE-Step 1.5 local generation provider added to
ONLINE_SOURCES in core/music_engine.py
- Guarded availability: ACE-Step source is only registered when
acestep package is installed; no dead source appears in UI when dependency is missing
- Local generation backend:
AceStepMusicSource wraps ACE-Step Python API (GenerationParams, GenerationConfig, generate_music) and returns generated files as MusicTrack entries
- Model path convention: ACE-Step checkpoints are routed to
%LOCALAPPDATA%\Advanced_Tape_Restorer\acestep_models via ACESTEP_CHECKPOINTS_DIR
- Music Library UI wiring: source-aware behavior for ACE-Step with dedicated generation options card (lyrics, duration, BPM, key, instrumental, reasoning, inference steps, batch size, output format)
- API key gating fixed: API-key controls now depend on source capability (
requires_api_key), so local ACE-Step generation runs without key prompts
- Settings persistence: Added
music_acestep_* keys in MusicLibraryPage settings snapshot for ACE-Step generation options
- Files:
core/music_engine.py, gui/pages/music_library_page.py
v6.0.0
2026-04-05
Internal build
DMDNet ONNX Backend Archived
- DMDNet ONNX removed from active backends — Architecture uses three ONNX-incompatible patterns:
.cpu().numpy() on location tensors mid-forward, data-dependent tensor slicing in roi_align_self(), and data-dependent paste-back in reconstruct(). These prevent torch.onnx.export().
- Code archived in-place — All ONNX inference methods preserved as commented
_ARCHIVED_ONNX block in face_dmdnet.py with restoration instructions and notes on possible future solutions (hardcoded locs, opset improvements, community .onnx files).
- Metadata updated —
DMDNetEngine.metadata().backends now (PYTORCH,) only.
- Asset service updated — Removed
dmdnet.onnx from fallback ONNX lookup dict.
- PyTorch path unaffected — DMDNet face restoration continues to work normally via PyTorch.
- Files:
ai_models/engines/face_dmdnet.py, ai_models/asset_service.py
v6.0.0
2026-04-06
Internal build
LavaSR ONNX Model Export & All-Modes Benchmark
- ATR ONNX model export —
scripts/export_lavasr_onnx.py exports 3 ONNX models from PyTorch LavaSR checkpoint (backbone 49.4MB, spec_head 4.0MB, denoiser 1.1MB)
- Static denoiser shapes — Removed dynamic batch axis from denoiser export, fixing 45% DirectML slowdown vs external models
- ONNX runner refactored —
lavasr_onnx_runner.py now loads ATR models by default, falls back to Topping1 if missing
- All-modes benchmark —
scripts/benchmark_all_modes.py compares 6 inference modes: ONNX ATR+CPU, ATR+DirectML, Topping1+CPU, Topping1+DirectML, PyTorch CPU eager, TorchScript JIT CPU
- Result: ONNX ATR + DirectML is fastest — 3.2ms neural total (4.3x faster than PyTorch CPU, 3.5x faster than ONNX CPU)
- All modes numerically equivalent — Max diff < 0.0002, PSNR > 102dB, correlation 1.000000 across all modes
- Benchmark documentation — Full results in
docs/LAVASR_INFERENCE_BENCHMARK.md
- Files:
scripts/export_lavasr_onnx.py (modified), scripts/benchmark_all_modes.py (new), scripts/compare_onnx_models.py (new), ai_models/engines/lavasr_onnx_runner.py (modified), docs/LAVASR_INFERENCE_BENCHMARK.md (new)
v6.0.0
2026-04-05
Internal build
Dependency Version Registry & Diagnostics Improvements
- Dependency version registry — New
core/dependency_registry.py validates package versions at startup (torch, torchaudio, PySide6, numpy, opencv, etc.)
- Sync group validation — Detects torch↔torchaudio major.minor+CUDA tag mismatches automatically
- Startup dependency check —
main_window_fluent.py shows QMessageBox with detailed version-mismatch report on launch (500ms timer, non-blocking)
- Diagnostics incremental scanning — Engine cards appear immediately with "🔵 Scanning..." status, then update in-place as each engine check completes
- First-run fix —
_prepopulate_scanning_cards() now calls get_all_engine_metadata() to trigger lazy engine loading on first page visit
- Fork bomb fix — Removed accidental
DiagnosticsWorker spawn inside _on_engine_checked() that caused exponential worker creation (20 engines → 20 new scans → CPU spike)
- base.py probe caching reverted — Reverted failed system-python probe caching optimization back to v5.0 original (caching caused deoldify timeout on py-3.13 instead of falling through to py-3.12)
- Files:
core/dependency_registry.py (new), gui/main_window_fluent.py (extended), gui/pages/diagnostics_page.py (extended), ai_models/engines/base.py (reverted)
v6.0.0
2026-04-04
Internal build
Per-Frame Temporal Quality Metrics
- Per-frame VMAF/PSNR/SSIM capture —
QualityMetrics dataclass extended with vmaf_per_frame, psnr_per_frame, ssim_per_frame, reference_fps fields
- VMAF per-frame parsing —
_compute_vmaf() now extracts frame-level scores from libvmaf JSON frames[] array
- PSNR/SSIM per-frame retention —
_compute_psnr() and _compute_ssim() return per-frame lists alongside mean values
- Temporal quality graph — New matplotlib-based chart card on Analysis page with multi-metric overlay (VMAF blue, PSNR green normalized, SSIM orange x100)
- CSV export — Export per-frame metrics as CSV with frame number, timecode, VMAF, PSNR, SSIM columns
- PNG export — Export temporal graph as high-resolution (150 DPI) PNG image
- Frame/Timecode toggle — X-axis mode switch between frame numbers and timecode (seconds)
- Graceful fallback — matplotlib import guarded; notice shown if not installed
- Files:
core/quality_metrics.py (extended), gui/pages/analysis_page.py (extended ~1100 lines)
Track A Core Reliability (All 4 Tiers)
- Tier 1: Colorspace injection, Source Type selector
- Tier 2: Engine loading race fix, error surfacing, preflight coverage
- Tier 3: Non-commercial license warning, user-friendly import errors
- Tier 4: Dead code removal (
lazy_imports.py, async_io.py), spec cleanup
- Signal-storm fix: blockSignals guards in
apply_settings()
Planning & Documentation
- Updated
docs/planning/v6.0.md with Track A/C audit results
- Updated
docs/planning/resolved.md with 15+ newly verified items
- Updated
docs/design/IN_OUT_MARKERS_DESIGN.md status to FULLY IMPLEMENTED
v6.0.0
2026-03-29
Internal build
Initial Development Space
- Created v6.0 development space from v5.0.10 baseline
- Updated all version references to 6.0.0
- Renamed build files (spec, ISS, PS1, BAT) to v6.0 naming
- Fresh venv with Python 3.12, PyTorch 2.9.1+cu128, PySide6 6.10.0
- 144 packages installed including full AI/ML stack
---
v5.0.10
2026-03-28
Internal build
Music Library Page (✅ COMPLETE — Opus APPROVED)
- Jamendo integration — Real royalty-free music backend via Jamendo API v3.0 (client_id authentication, CC-licensed tracks)
- ElevenLabs AI Music — Text-to-music generation via ElevenLabs
music.compose() API (model: music_v1, MP3 44.1kHz 128kbps output)
- Local library scanning — Import music from local folders with metadata extraction via ffprobe (title, artist, album, genre, duration, sample rate)
- Download-before-add workflow — Online tracks are downloaded locally before being added to the restoration project
- Preview streaming — Stream track previews directly from Jamendo (96kbps MP3)
- License metadata — Each track carries
preview_url, license_url, and Creative Commons license hints
- Source-aware UI — ElevenLabs source shows "Generate" button and prompt placeholder; Jamendo shows "Search" button
- NAS snapshot pruning —
_SKIP_DIRS set (13 NAS system directory names) prevents duplicate tracks from snapshot directories
- Deferred initialization —
QTimer.singleShot for library scan; _normalize_path() replaces Path.resolve() to eliminate NAS network I/O on startup
- Mutagen notice removed — ffprobe handles all needed metadata; mutagen is not required
- Files:
core/music_engine.py (new, ~530 lines), gui/pages/music_library_page.py (rewritten, ~1070 lines)
Dead Code Cleanup
- Deleted:
core/parallel_processor.py, core/frame_cache.py (zero imports in codebase)
- Removed: 4 Stream-DiffVSR commented-out references in
core/vapoursynth_engine.py
Documentation Updates
- ARCHITECTURE.md — Rewritten from v2.0 to v5.0 content
- FEATURES.md — Added Music Library section (Jamendo + ElevenLabs), updated version to v5.0.10
- CLAUDE.md — Added Music Library section, updated version to v5.0.10
- Reconciliation audit — 21 audit items verified against codebase: 12 resolved, 3 should-fix, 6 future/P2
- docs/planning/v5.0.md — Updated with reconciliation results and success criteria check-off
- docs/audits/audit-2026-03-25.md — Reconciliation appendix added
Dependency Changes
- Added:
elevenlabs>=2.0.0 to requirements_pip.txt (guarded import — optional)
Code Quality
- Deprecated
datetime.utcnow() replaced — All 4 call sites in core/music_engine.py now use datetime.now(timezone.utc) (Python 3.12+ deprecation fix)
---
v5.0.9
2026-03-24
Internal build
Timeline In/Out Markers (✅ COMPLETE)
- In/Out range selection — Set start and end markers on the timeline to process only a subclip
- Keyboard shortcuts —
I sets In marker, O sets Out marker at current playhead position
- Focus-aware shortcut guard — I/O keys are suppressed when QLineEdit, QSpinBox, QComboBox, or other text input widgets have focus
- Draggable handles — Blue triangle handles on the timeline ruler can be dragged to adjust In/Out range
- Independent handle rendering — Single markers render individually (triangle visible immediately); shaded blue region appears only when both are set
- Right-click context menu — "Set In Here", "Set Out Here", "Clear In/Out" from any timeline position
- Audio trim sync — FFmpeg audio input now receives
-ss and -to flags to trim audio to match the video range. Previously, only video was trimmed (via VapourSynth core.std.Trim), causing audio to continue past the Out marker.
- VapourSynth trim —
core.std.Trim(video, first=N, last=M) applied as first filter stage after source loading
- Minimum gap enforcement — In and Out markers maintain at least 1 second of separation
- Export injection — Trim range injected into both standard export and quick export paths
- Scrollbar overlay fix — Synthetic
QResizeEvent workaround applied to PostProcessPage and DiagnosticsPage to fix QFluentWidgets scrollbar disappearance after page navigation
- LazyPageHost resize —
resizeEvent propagation added to refresh scroll geometry on window resize/maximize
- Files:
gui/widgets/timeline_widget.py, gui/widgets/bottom_strip.py, gui/main_window_fluent.py, core/options_builder.py, core/vapoursynth_engine.py, core/processor.py, core/ffmpeg_encoder.py, gui/pages/postprocess_page.py, gui/pages/diagnostics_page.py
- Opus-review: ✅ APPROVED (2 issues caught and resolved: shortcut focus guard, independent handle rendering)
---
v5.0.8
2026-03-22
Internal build
Plugin System — pre_filter Hook & Drag-to-Reorder (✅ COMPLETE)
- pre_filter pipeline hook — Plugins declaring
pipeline_hooks: ["pre_filter"] now run before VapourSynth restoration (Step 4b in processor pipeline), using the same separate-pass approach as post_filter
- filter_frame support for pre_filter —
plugin_manager.py::run_pipeline_plugin() extended to support per-frame numpy processing for both pre_filter and post_filter hooks
- Drag-to-reorder plugin chain — Pipeline Plugins card now displays reorderable rows with ▲/▼ buttons per plugin per hook stage; visual order persists in settings and drives execution order
- Pre-Filter UI section — New "Pre-Filter Plugins (before VapourSynth restore)" section in Pipeline Plugins card, alongside existing Post-Filter and Post-Encode sections
- Non-destructive layout rebuild —
_detach_layout_widgets() safely reorders rows without deleteLater() crash (caught in Opus review)
- Settings persistence —
pipeline_plugins dict extended with pre_filter key; order values serialized from visual layout order and restored on apply_settings
- Files:
core/processor.py, core/plugin_manager.py, gui/widgets/control_tabs.py
- Opus-review: ✅ APPROVED (1 MUST-FIX caught and resolved: deleteLater crash on reorder)
---
v5.0.7
2026-03-23
Internal build
Email Notification System (✅ COMPLETE)
- SMTP email notifications —
core/email_notifier.py sends batch completion and per-job error alerts via configurable SMTP (Gmail, Outlook, custom servers)
- Batch integration —
batch_processor.py emits batch_completed / job_error signals wired to email dispatch
- Async sending —
EmailWorkerThread (QThread) in main_window.py prevents UI freeze during SMTP operations
- Test email —
EmailTestThread validates SMTP settings with a one-click test button
- Settings UI — Email card in Settings page: SMTP server, port, username, password, recipient, TLS toggle, enable checkbox, test button
- closeEvent guard — Both
main_window.py and main_window_fluent.py check for in-progress batch before allowing window close
Settings Page — Collapsible Cards (✅ COMPLETE)
- CollapsibleCardWidget migration — All 5 settings cards (Appearance, Paths, Performance, Email Notifications, Advanced) converted from CardWidget to animated CollapsibleCardWidget
- QScrollArea wrapper — Settings page wrapped in scrollable container for smaller displays
- Default expand states — Appearance/Paths/Performance expanded by default; Email/Advanced collapsed
- Email grid layout fix — Fixed email settings alignment on card layout
Batch Scheduling & Per-Job ETA (✅ COMPLETE)
- Scheduled start — Batch jobs can be scheduled for delayed start (date/time picker in batch dialog)
- Per-job ETA — Real-time ETA display per job in batch queue based on frame count and processing speed
Roadmap: SeedVR2 Temporal Consistency Enhancements
- Added to ROADMAP_v4.0.md — Two planned enhancements under SeedVR2 section:
- ☑ SeedVR2 chunk overlap blending (cosine-blend overlap regions between chunks)
- ☐ RIFE-assisted post-upscale temporal smoothing (use existing RIFE engine on chunk boundaries)
Documentation Updates
- ONNX conversion status — Closed GFPGAN and DeOldify ONNX items in
docs/planning/v5.0.md as not beneficial (see ONNX_MODEL_STATUS.md)
- Plugin e2e tests — Minor test updates in
test_plugin_e2e.py
---
v5.0.6
2026-03-22
Internal build
Plugin System Phase 3 — Pipeline Integration (✅ COMPLETE)
- Pipeline hook points —
post_filter (runs after VapourSynth/FFmpeg encode, before NVIDIA VFX) and post_encode (runs after all processing including audio enhancement)
- filter_frame() contract — Per-frame numpy processing:
filter_frame(frame: ndarray, frame_index: int, total_frames: int, **kwargs) -> ndarray with shape/dtype validation
- Frame-wise execution — FFmpeg decode (rawvideo bgr24 pipe) → filter_frame per frame → FFmpeg encode with audio copy
- Pipeline Plugins UI — CollapsibleCardWidget in AI Models tab with CheckBox toggles per discovered pipeline plugin, Refresh button, info label for empty state
- Processor integration — Step 9a (post_filter) and Step 12 (post_encode) in VideoProcessor pipeline with skip-on-failure, temp-file swap, cancel propagation
- Options passthrough —
pipeline_plugins dict flows from AIModelsTab → options_builder → VideoProcessor
- End-to-end tested — 20/20 tests passed (discovery, post_filter filter_frame, post_encode process fallback, cancellation, processor integration)
- Files:
core/plugin_manager.py, core/processor.py, core/options_builder.py, gui/widgets/control_tabs.py, docs/PLUGIN_DEVELOPMENT_GUIDE.md, docs/PLUGIN_SYSTEM_ROADMAP.md
- Opus-review: ✅ APPROVED
---
v5.0.5
2026-03-21
Internal build
Plugin System Phase 2 — Settings & Progress (✅ COMPLETE)
- Per-plugin settings UI — Dynamically generated from
settings_schema() declarations: sliders with live value labels, checkboxes, dropdowns, text inputs
- Progress reporting — Real-time progress bar via
progress_callback parameter during plugin execution
- Cancel support — Cancel in-flight plugin operations via
threading.Event
- Settings persistence — Per-plugin settings saved to and restored from
restoration_settings.json
- Files:
gui/pages/plugins_page.py, core/plugin_manager.py, gui/main_window_fluent.py
- Opus-review: ✅ APPROVED
Personal Reference Pipeline — Phase 2 DMDNet Hardening (✅ COMPLETE)
- DMDNet checkpoint validation gate —
face_dmdnet.py now verifies loaded weights match expected architecture; incompatible checkpoints raise RuntimeError with clear message instead of silent failure
- InsightFace import guard —
gfpgan_checkpoint_processor.py sets _face_app = False sentinel when InsightFace unavailable; per-frame processing skips gracefully with warning
- Registry installation steps —
registry.yaml DMDNet entry now includes specialty.installation.steps with guided setup instructions
- Files:
ai_models/engines/face_dmdnet.py, core/gfpgan_checkpoint_processor.py, ai_models/models/registry.yaml
- Opus-review: ✅ APPROVED
Personal Reference Pipeline — Phase 3 Speaker Matching (✅ COMPLETE)
- Speaker embedding — Resemblyzer-based 256-d d-vector extraction from reference audio clips
- Speaker diarization — Identify who is speaking in each segment of restored audio
- Resemblyzer import guard —
_get_encoder() wraps import in try/except ImportError for graceful degradation
- blockSignals guard —
reference_library_page.py wraps combo restoration in blockSignals to prevent signal storms
- Files:
core/speaker_matcher.py, gui/pages/reference_library_page.py
- Opus-review: ✅ APPROVED
SeedVR2 Model Download UI (✅ COMPLETE)
- Setup Guide button — Models page shows "Setup Guide" for auto-downloadable models that have
specialty.installation.steps (visible in both NOT_INSTALLED and INSTALLED states)
- 6-step installation guide —
registry.yaml SeedVR2 entry includes guided setup: clone repo, place alongside models, install deps, click Install, GPU requirements, first-run note
- SeedVR repo dependency check —
seedvr2_engine.py::check_dependencies() now calls _resolve_seedvr_repo() and surfaces missing-repo error in diagnostics
- Files:
gui/pages/models_page.py, ai_models/models/registry.yaml, ai_models/engines/seedvr2_engine.py
- Opus-review: ✅ APPROVED
---
v5.0.4
2026-03-20
Internal build
Documentation Audit & Consistency Pass
- Updated docs/planning/v5.0.md — Marked SeedVR2 DiT as ✅ SUBSTANTIALLY COMPLETE (was listed as TODO), Echoes Over Time as ✅ COMPLETE (was listed as TODO), Reference Pipeline phases 1 & 4 as ✅ COMPLETE with accurate status indicators
- Updated FEATURES.md — Added missing "AI Audio Enhancement" section (LavaSR, Echoes Over Time, Chain mode), fixed engine migration count from "11 of 13" to "13 of 13", added Restormer to AI Restoration list
- Fixed stale documentation — Removed references to deleted files (google_photos.py, amazon_photos.py), corrected audio processor description from "hardcoded LavaSR" to "3-way routing"
- No code changes — Documentation-only update to align docs with actual codebase state
---
v5.0.3
2026-03-20
Internal build
Echoes Over Time — Second Audio Enhancement Engine (✅ COMPLETE)
- Echoes Over Time engine —
ai_models/engines/audio_echoes.py — VoiceFixer-based speech restoration (mode=2 "train mode")
- Dry/wet mixing — Strength slider (0.0–1.0) blends original with VoiceFixer output to preserve non-speech content
- 3-way audio routing —
core/audio_processor.py now routes by model selection: LavaSR / Echoes Over Time / Chain (LavaSR → Echoes)
- Chain mode — Two-stage enhancement: LavaSR broadband cleanup (16kHz→48kHz) then Echoes speech refinement
- Per-engine sample rates — Echoes extracts at 44.1kHz; LavaSR/Chain extracts at 16kHz
- Dependency checking — EchoesEngine declares torch, torchaudio, numpy, soundfile, voicefixer as dependencies
- Graceful degradation — Chain mode falls back to LavaSR output if Echoes dependencies missing
- Commit:
cee42b8 (2026-03-17)
Face Reference Library — Cloud Provider Redesign
- Removed Google Photos API — Google OAuth app verification makes API unusable for non-verified apps (403 errors)
- Removed Amazon Photos API — Amazon Drive API deprecated since 2023, no programmatic access
- Removed rclone sync — Google Photos API restricts downloads to photos uploaded by the same third-party app
- New local-folder workflow — All 3 cloud providers (iCloud, Google, Amazon) now import via local folder scanning
- iCloud for Windows checkbox — Auto-detects system iCloud Photos sync folder, or user picks custom folder
- Google Photos — Guided export via Google Takeout with "How to Export" help button
- Amazon Photos — Guided export via Amazon Photos desktop app with "How to Export" help button
- 3 provider buttons — iCloud Photos, Google Photos, Amazon Photos with per-provider folder settings
- Help dialogs — Each provider has a "How to Export" button with step-by-step instructions and optional link to open the provider's export page
- No OAuth required — All cloud import is local-folder based, zero API credentials needed
- Deleted files:
google_photos.py, amazon_photos.py, rclone_google.py
- New files:
core/cloud_providers/__init__.py, core/cloud_providers/base.py, core/cloud_providers/icloud_photos.py
---
v5.0.2
2026-03-16
Internal build
Roadmap Updates
- FLUX.2 Klein 4B (Apache 2.0) — Added to roadmap as primary generative backbone candidate
- Replaces FLUX.1 [schnell] compatibility test (Goal #16) — FLUX.2 Klein is newer, more capable, same license
- 4B distilled: ~0.3s inference, 8.4 GB VRAM; 4B Base: designed for LoRA fine-tuning, 9.2 GB VRAM
- Unblocks Genfocus deblurring LoRA integration (previously blocked by FLUX.1 [dev] non-commercial license)
- Added as alternative local generative backend in LTX Generative Repair v6.0 design brief
- Note: only 4B variants are Apache 2.0; 9B variants remain Non-Commercial
---
v5.0.1
2026-07-17
Internal build
NVIDIA VFX SDK Integration (✅ COMPLETE)
- NVIDIA VFX AI Upscaling — TensorRT-accelerated super-resolution via
nvvfx.VideoSuperRes
- Quality levels: Bicubic, Low, Medium, High, Ultra
- 2x and 4x upscaling with hardware-optimized inference
- NVIDIA VFX AI Denoising — Real-time video denoising
- Quality levels: Low, Medium, High, Ultra
- NVIDIA VFX AI Deblurring — Motion deblur for tape sources
- Quality levels: Low, Medium, High, Ultra
- Direct Python Processing Path — FFmpeg decode → CUDA tensor → nvvfx → FFmpeg encode
- Bypasses VapourSynth for VFX effects (runs entirely in-process)
core/nvidia_vfx_processor.py — single-effect and chained multi-effect paths
ai_models/engines/nvidia_vfx_engine.py — EngineBase wrapper
- Multi-Effect Chaining — Single decode/encode pass for combined denoise + deblur + upscale
- Intelligent effect ordering (denoise → deblur → upscale)
- Resolution tracking through chain (intermediate sizes computed per effect)
- 3 Independent UI Cards — Separate collapsible cards for upscale, denoise, deblur
- Hardware-gated: cards disabled with message when SDK not installed
- Independent enable toggles and quality dropdowns per effect
- Width Alignment Fix — NVIDIA VFX SDK requires 8-pixel width alignment
- Automatic reflect-padding to nearest multiple of 8
- Output cropped back to original width (zero quality loss)
- Zero overhead for already-aligned widths (720, 1920, 3840)
- SeedVR2 Improvements — VRAM gating, OOM recovery, progress reporting fixes
---
v5.0.0
2026-03-03
Internal build
New Development Cycle
- Forked from v4.5.0 (commercial-release-1, commit 28358cb)
- Version bump to 5.0.0 for next major development phase
- See
docs/planning/v5.0.md for development goals and roadmap
Planned Features (P0 — Must Ship)
- SeedVR2 Full DiT Model — production video super-resolution
- Echoes Over Time — second audio enhancement engine
- Plugin System Phase 2 — settings & progress UI
- Personal Reference Pipeline — identity-preserving restoration using user photos/audio
---
v4.5.0
2026-02-28
Internal build
Plugin-Style AI Engine Architecture (Phases 1–6)
Phase 1 — Engine ABC + Dependency Checking (✅ COMPLETE)
EngineBase abstract base class (ai_models/engines/base.py) with metadata(), apply(), check_dependencies()
EngineMetadata frozen dataclass: category, backends, VRAM estimates, temporal window, license, scale factors, resolution limits
DependencySpec: declarative dependency manifest (VapourSynth plugins, Python packages, external binaries, model files)
_IMPORT_ERRORS dict captures engine import failures with full error messages (replaces silent None returns)
_register_engine() supports both ABC subclasses and legacy duck-typed apply() functions
- DarkIR engine migrated as first ABC reference implementation
Phase 2 — Bulk Engine Migration (✅ COMPLETE — 13/13 engines on ABC)
- All 13 registered engines are on
EngineBase ABC: DarkIR, BiT, RFDA, Video Swin-UNet, RealESRGAN, RIFE, BasicVSR++, SwinIR, DAIN, FILM, DeOldify, GFPGAN, VideoCleaner
- Each engine declares full
EngineMetadata with backends, VRAM requirements, scale factors, and dependencies
- Backward-compatible:
ENGINE_REGISTRY[name] callable interface unchanged
ENGINE_METADATA and ENGINE_INSTANCES dicts populated for all ABC engines
Phase 3 — Self-Reported Capabilities (✅ COMPLETE)
EngineMetadata.max_resolution — engines declare maximum supported input resolution
EngineMetadata.scale_factors — supported upscale factors (e.g., (2, 4) for RealESRGAN)
EngineMetadata.supports_tiling — tile-based inference capability flag
EngineMetadata.needs_rgb_input — colorspace requirement for pipeline integration
EngineMetadata.non_commercial — license constraint flag for UI gating
Phase 4 — Device Scheduler Integration (✅ COMPLETE)
core/device_scheduler.py (~800 lines) — VRAM-aware per-model device assignment
schedule_devices() reads ENGINE_METADATA for VRAM estimates and backend support
preflight_vram_check() validates total VRAM budget before processing
apply_schedule() injects per-model device and inference_mode into processing options
- Wired into
processing_thread.py, processing_service.py, main_window_fluent.py, diagnostics_page.py
Phase 5 — Diagnostics GUI (✅ COMPLETE)
gui/pages/diagnostics_page.py — Engine diagnostics page with Fluent UI cards
- Per-engine cards show: status (🟢 Ready / 🟡 Missing Deps / 🔴 Import Error), VRAM estimate, backends, ONNX availability, device assignment
- Summary bar: total engines, ready count, missing deps, import errors, ONNX available, CUDA/DirectML status, VRAM
- Filter: All / Ready Only / Problems Only
- Copy Support Report: generates markdown clipboard export with system info, GPU names, all engine details
generate_support_report() standalone function for programmatic use
Phase 6 — Plugin Architecture Foundation (✅ COMPLETE)
ai_models/engines/plugin_api.py — EnginePlugin(EngineBase) contract (API Level 1)
AssetRequirement frozen dataclass: filename, engine_subdir, sha256, size_mb, optional, description
SettingsField frozen dataclass: key, label, field_type, default, choices, min/max, advanced, description
settings_schema(), asset_requirements(), runtime_adapter() extension points
ai_models/engines/plugin_loader.py — Safe plugin discovery from ai_models/engines/*/plugin.py subdirectories
- Reuses
_import_engine() and _register_engine() for consistent error handling
- Errors captured in
_IMPORT_ERRORS (never crashes startup)
ai_models/engines/engine_capabilities.py — Readiness gating and capability routing
is_engine_ready() → (bool, reason) readiness check per engine
get_engines_for_category() — query engines by category with optional ready-only filter
get_eligible_upscalers(), get_eligible_interpolators() — convenience helpers
check_resolution_compatible() — validate input resolution against engine limits
ai_models/asset_service.py — Canonical asset path management
AssetService with model_root, onnx_root, jit_cache_root properties
- Plugin-aware ONNX path resolution with legacy fallback dict
- SHA256 validation via
check_asset_exists()
check_plugin_assets() — validate all declared assets for plugin-style engines
core/preflight.py — Pre-job validation guardrails
run_preflight() — checks engine readiness, asset presence, resolution compatibility, VRAM budget
PreflightReport dataclass with ok, engine_errors, asset_errors, resolution_errors, warnings, summary
format_preflight_errors() — human-readable error formatting for logs and dialogs
- Integrated into
ProcessingWorker.run() — logs preflight results before every job
- Updated
ai_models/__init__.py with 7 new lazy imports: AssetService, discover_plugins, is_engine_ready, get_engines_for_category, EnginePlugin, AssetRequirement, SettingsField
New files in v5.0.0:
ai_models/engines/base.py — EngineBase ABC, EngineMetadata, DependencySpec, EngineCategory, EngineBackend
ai_models/engines/plugin_api.py — EnginePlugin contract, AssetRequirement, SettingsField
ai_models/engines/plugin_loader.py — Subdirectory plugin discovery
ai_models/engines/engine_capabilities.py — Readiness and capability helpers
ai_models/asset_service.py — Asset path resolution and validation
core/preflight.py — Pre-job validation guardrails
core/device_scheduler.py — VRAM-aware device scheduling
gui/pages/diagnostics_page.py — Engine diagnostics UI
Version bump from v4.2.3 → v5.0.0
Hardware Capture System (✅ COMPLETE)
Real-Time Analog Capture via FFmpeg
CaptureWorkerThread(QThread) — non-blocking FFmpeg subprocess with user_stopped flag
- DirectShow device detection: USB analog dongles (USB2.0DEVICE, etc.)
- QFileDialog save prompt before capture with codec-appropriate file extensions
- Success/failure InfoBar notifications after capture completes
- Stop button gracefully terminates FFmpeg subprocess
Capture Codecs (7 options)
- HuffYUV — lossless, fast, large files
- FFV1 — lossless archival (smallest lossless)
- Raw AVI — uncompressed (rgb24, uyvy422, yuyv422, nv12)
- UT Video — lossless, fast decode
- MagicYUV — lossless, GPU-accelerated decode (yuv422p, yuv420p, yuv444p, rgb)
- DV — native DV stream copy
- ProRes — Apple ProRes via prores_ks (Proxy, LT, Standard, HQ profiles)
Codec UI
- Dynamic codec settings: ProRes shows Profile dropdown, others show Pixel Format
CODEC_PRESETS dict in capture/analog_capture.py for all codec/variant combinations
- Codec selection persists across sessions
Embedded ffplay Preview Player
- Win32
SetParent/SetWindowLong pattern to embed ffplay inside a QWidget
- PID-based HWND discovery via
win32gui.EnumWindows + win32process.GetWindowThreadProcessId
eventFilter() on preview widget catches resize events → SetWindowPos + WM_SIZE
- All Win32 calls wrapped in
try/except — falls back to external window on failure
- Play/Stop buttons with
_last_capture_file tracking
set_last_capture(filepath) wired from _on_capture_finished on success
New/modified files:
gui/pages/capture_page.py — Full capture page with device selection, codec settings, preview player
gui/main_window_fluent.py — CaptureWorkerThread, capture event handlers, set_last_capture wiring
capture/analog_capture.py — CODEC_PRESETS with all codec variants
---
v4.2.0
2026-01-31
Internal build
🎉 Major New Feature: AMD ROCm GPU Support
AMD Radeon GPU Acceleration
- Added full AMD GPU support via ROCm/HIP for AI acceleration
- Compatible with Radeon RX 6000/7000 series (RDNA2/RDNA3)
- Performance: 10-20x faster than CPU (RX 7900 XTX: 20x, RX 6800 XT: 14x)
- Supports all AI models: RealESRGAN, RIFE, BasicVSR++, SwinIR, GFPGAN
New Files:
scripts/Install_ROCm.bat - Automated ROCm installation
scripts/Install_ROCm_PyTorch.ps1 - PowerShell installer with verification
DOCUMENTATION/ROCM_SUPPORT.md - Complete 500+ line guide
DOCUMENTATION/ROCM_QUICK_START.txt - Quick reference
WHATS_NEW_V4.2.txt - Release announcement
Core Updates:
core/multi_gpu_manager.py
- Added
InferenceMode.ROCM_FP32 and ROCM_FP16
- Added
_detect_pytorch_rocm_gpus() method
- Added
_estimate_amd_stream_processors() with RX 6000/7000 estimates
- Added
get_pytorch_backend() - detects CUDA vs ROCm vs CPU
- Updated
get_recommended_inference_mode() with ROCm logic
- Added
_has_directml() for ONNX fallback detection
core/gpu_accelerator.py
- Updated
GPUAccelerator class to support ROCm backend
- Added
is_rocm attribute to track AMD vs NVIDIA
- Renamed
_init_cuda() to _init_pytorch_gpu() (supports both)
- Updated
get_info() to include hip_version and is_rocm
- Updated all VRAM methods to work with ROCm
- Added AMD GPU performance estimates (RX 6000/7000 series)
- Updated
check_gpu_requirements() for ROCm detection
Documentation:
DOCUMENTATION/ROCM_SUPPORT.md - Comprehensive guide
- Installation instructions (automated + manual)
- Performance benchmarks and VRAM requirements
- Troubleshooting guide with common issues
- Hardware compatibility matrix
- Architecture details and FAQ
DOCUMENTATION/ROCM_QUICK_START.txt - Plain text quick reference
- Updated
README.md with ROCm information
Installation Scripts:
scripts/Install_ROCm.bat - Simple batch launcher
scripts/Install_ROCm_PyTorch.ps1 - Full PowerShell installer
- Auto-detects AMD GPU
- Checks HIP SDK installation
- Opens AMD download page if needed
- Installs PyTorch ROCm 7.1
- Runs verification test
- Colorized output with troubleshooting tips
Supported Hardware:
- AMD Radeon RX 6600/6650/6700/6750/6800/6900/6950 XT
- AMD Radeon RX 7600/7700/7800/7900 XT/XTX
- AMD Radeon PRO W6xxx/W7xxx
- Minimum: RDNA2 architecture (RX 6000 series)
Performance Gains:
- RX 7900 XTX: ~20x faster than CPU
- RX 6800 XT: ~14x faster than CPU
- RX 7700 XT: ~12x faster than CPU
- RX 6600: ~7x faster than CPU
Inference Modes:
- ROCm FP16 (Recommended): 2x faster, 50% less VRAM
- ROCm FP32 (Max Quality): Full precision, needs 16+ GB VRAM
- Auto: Automatically selects best mode based on VRAM
DirectML Fallback:
- Older AMD GPUs (RX 500/5000, Vega) use DirectML
- Still 5-10x faster than CPU
- Works with any DirectX 12 GPU
🐛 Bug Fixes
- Fixed VRAM usage reporting for non-CUDA backends
- Fixed GPU detection priority order
📚 Documentation
- Added complete ROCm documentation (500+ lines)
- Added quick start guide for AMD users
- Updated README with GPU acceleration section
- Added WHATS_NEW_V4.2.txt for release announcement
🔧 Technical Improvements
- Unified PyTorch GPU backend detection (CUDA + ROCm)
- Improved error messages for GPU initialization
- Added version detection for HIP SDK
---
v4.1.0
2026-01-25
Internal build
🚀 Major Features
ONNX Model Conversion & NPU Acceleration
- Convert PyTorch AI models to ONNX format (95-99% size reduction)
- NPU offloading via DirectML (frees 6-8GB GPU VRAM)
- 40x speedup vs CPU inference (2.5ms vs 100ms per frame)
- Enables 4K video processing on 8GB GPUs
Performance Enhancements
- PyTorch JIT compilation (20-30% AI performance boost)
- Threaded I/O operations (2-4x speedup for batch operations)
- Multi-GPU support (NVIDIA + AMD + Intel heterogeneous)
✨ New Features
- GUI inference mode dropdown (Auto/PyTorch/TorchScript/ONNX)
- Automatic VRAM-based mode selection
- Distribution setup script (
Install_ONNX_Runtime_NPU.bat)
---
v4.0.0
2025-12-20
Community Edition — public
🚀 Major Features
Real DirectShow Capture Device Detection
- Full DirectShow device enumeration via FFmpeg
- Analog capture engine (Composite, S-Video, Component)
- DV/FireWire capture engine with stream copy
- Lazy device loading (fixes loading screen hang)
Build & Distribution
- Standalone EXE distribution strategy
- Automatic PyInstaller cache cleanup
- Utilities folder for troubleshooting
✨ New Features
- Real-time device detection on Capture tab access
- CLI test utilities for device detection
- Comprehensive capture documentation (6 new MD files)
---
v3.0.0
2025-11-15
Internal build
🚀 Initial Public Release
Core Features
- VapourSynth-based video processing pipeline
- QTGMC deinterlacing (7 quality presets)
- AI upscaling (RealESRGAN, BasicVSR++, SwinIR)
- Frame interpolation (RIFE)
- Face restoration (GFPGAN)
- Colorization (DeOldify)
- PySide6 GUI with presets and live preview
---
Legend:
- 🚀 Major Features
- ✨ New Features
- 🐛 Bug Fixes
- 📚 Documentation
- 🔧 Technical Improvements
- 🎉 Major Milestones