Loki Studio v1.2.0

The big one. May 2026.

← All updates
CURRENT Released: May 2026

Premiere-style EDL workflow, end-to-end theme system with live dark/light swap and user-loadable JSON themes, caption pipeline rework (semantic chunking, multi-language storage, per-word timing fixes), transcription accuracy overhaul, GPU work moved into a crash-isolated child process, metadata generation reliability fixes, per-language thumbnails, faster multi-track transcribes, caption-burn rework, and a major code cleanup pass. Installer is ~180 MB lighter than v1.1.x and every Norse module now statically links FFmpeg, so no more avcodec/avformat/swresample/swscale DLLs at runtime and no ffmpeg.exe shell-outs anywhere in the export pipeline.

New features

Import EDL

  • Assemble a working video directly from a Premiere CMX3600 EDL. The Timeline Editor reads the shuffled, repeated, and trimmed segment list as authored in Premiere, concatenates the source clips with FFmpeg stream-copy, and opens the result as the working timeline. No re-encoding unless captions or streaming require it.
  • Order and repeats are preserved. A clip used multiple times on the Premiere timeline (teaser earlier, full segment later) shows up that way in Loki — each appearance is a distinct segment, not a single span.
  • Source-clip resolution mirrors Premiere: when a clip referenced in the EDL isn't found next to the EDL file, you're prompted to locate it, and the directory you pick is remembered for the rest of the EDL.
  • Segment-boundary markers on the assembled timeline, labeled with the source filename, so repeated clips stay visually distinguishable.
  • Assembled videos are cached, not auto-saved. The .mp4 produced by an EDL import lives in the application cache; Render Final Video is still the explicit step that produces an _edited.mp4.
  • Cache invalidation is automatic. Each cached assembly carries a sidecar JSON recording the EDL's mtime and every source clip's mtime + size. Editing the EDL or any source clip triggers a rebuild; identical re-imports reuse the cached file.
  • Clear Cache in Timeline Editor now also clears assembled EDL videos.

Caption-burn quality presets

  • Low / Medium / High presets under Render Final Video. Trade encode speed and file size against caption-text sharpness.
  • Low — 4 Mbps, quality 30 (fastest encode, smallest file).
  • Medium — 8 Mbps, quality 70 (default, visually-lossless text).
  • High — 16 Mbps, quality 95 (slowest, largest, archival).
  • Setting persists across runs (saved in app_settings.json).

Pipelined Prep

  • Under Application Settings → Transcription → Advanced Performance Tuning. When enabled, the next audio track's CPU-side prep (VAD + mel-spectrogram) runs on a worker thread while the GPU is still transcribing the current one. Multi-track gain only; default off so behavior is identical out of the box.

Per-language thumbnails

  • When you have localized variants (_fr / _de / _ja…) the Thumbnail tab generates a thumbnail per language, and the Upload tab pairs each upload variant with its language-specific thumbnail automatically.

Smaller new bits

  • Language pills on the Transcribe tab — "Transcribed [en] [es] [ru]" with native first, translations dimmer.
  • "Translating → <language>…" row status while NLLB is actually running. Replaces the premature "Transcribed" status.
  • Aesir audio mixer module — native multi-track amix + AAC re-encode, replaces the previous ffmpeg.exe -filter_complex shell-out from the Finalize tab.
  • Idun caption-burn module — libavfilter ass= filter chain + Windows-native h264_mf encoder, replaces the ffmpeg.exe -vf ass + libx264 shell-out. LGPL-clean.
  • Splash screen on launch.
  • Beltalowda personality — Belter narrator from The Expanse, with Belter Creole phrasing and station-life perspective.

Theme system

  • End-to-end light + dark theme with live mid-session swap. Every tab follows the active theme; no restart required.
  • User-loadable JSON themes. Drop a theme file into %APPDATA%/LokiStudio/themes/, edit it while the app is running, and the change applies live via QFileSystemWatcher.
  • Theme picker in Application Settings. Selection persists across restarts.
  • A C++ ThemeManager singleton drives every binding; Theme.qml is a thin facade. One themeChanged() signal flips every property in every view at once.

Caption pipeline rework

  • Split native vs translated rendering — caption display picks the right text source based on the selected language pill, instead of always rendering the source language.
  • Semantic SubtitleChunker — long Whisper segments are reflowed into reader-friendly chunks anchored on sentence boundaries and a configurable character cap.
  • Unified multi-language storage — each language's captions live in a single canonical store keyed by language code. Switching language in the Edit tab is constant-time and doesn't reload from disk.
  • Translated word timings redistributed across segment ranges so OneWordPop / WindowSlide caption styles highlight the right word in the right language.

Metadata generation reliability

  • Skuld grammar trigger fix. The lazy-grammar sampler in llama.cpp was throwing "Unexpected empty grammar stack after accepting piece: {" on every metadata call when used with Qwen3 — the BPE tokenizer packs {" into a single token, which over-consumed past the { trigger. Removed the { trigger and rely on the cleaner <|assistant|> single-token trigger. Net result: ~4-5× faster end-to-end on metadata batches.
  • Grammar circuit breaker. If grammar attempts fail 3 times in a row on the current model, future calls skip grammar and route to the non-grammar fallback. Resets on successful grammar generation or model reload.
  • Unhandled-exception filter in skuld.dll logs exception code + faulting address to skuld_debug.log when a fault escapes the per-call SEH wrappers.
  • Tested clean on a 14+ video Qwen3-8B batch: 91 consecutive successful grammar generations, zero nullptr returns, no crash.

Metadata output quality

  • Three-tier multi-track audio weighting. Tracks are classified into host (Mic 1 / "Host"), guest (Mic 2+ / "Guest" / "Voice Chat"), and background (System / Game / Movie / Music audio). The LLM sees up to three clearly labeled blocks rather than a flat concatenated transcript. Brief gen uses 50/30/20 host/guest/background — all tiers inform named_entities and key points but host drives tone, hook, and thumbnail text. Description and title use 70/20/10 with a "do NOT quote or describe game dialog" rail on the background block. Empty tiers redistribute their share, so solo streams don't waste budget on empty blocks.
  • Anti-clickbait thumbnail text. Brief and standalone Thumbnail-tab prompts were rewritten away from "punchy / attention-grabbing" coaching that was reliably producing clichés like "VR Sword Mayhem" or "Wrist weights = epic chaos." New prompts ask for the specific moment, object, or technique from the actual video. Explicit banned-word list (epic, ultimate, insane, chaos, mayhem, finale, mastery, …), banned formula structures (X+Y=Z, "Real X Real Y"), banned ALL-CAPS emphasis. Personality voice is threaded into brief gen so thumbnail strings carry tone. A post-generation detector catches survivors and falls back to primary_topic / first key point if cliché slips through.
  • Thumbnail fields retry on empty output. When the brief LLM returns valid JSON but blank thumbnail_title / thumbnail_subtitle, a focused follow-up prompt fills just those two strings rather than regenerating the whole brief.
  • Truncated-tag rejection. The tag sanitizer drops 3-5 character lowercase fragments that aren't on a keep-list ("ame", "vid", "gam") so partial-word slices from an LLM token cutoff don't ship.
  • Chapter title fallbacks dropped. Empty-context and LLM-failure branches in the chapter enhancer no longer emit "Wrap-up and final thoughts" / "Content continues" / "Topic discussion." Chapters with no usable context are removed.
  • Key-moment diversity pass. After importance-ranked greedy pick, candidates within 60 seconds of a selected moment, or sharing ≥3 distinct content tokens with one, are skipped — so a 90-second outburst can't claim three "key moments" in a row.
  • Frame-number thumbnail filenames. Thumbnails are saved as <base>_<frameNumber>_thumb.jpg using the source video's actual FPS, with a profile-configurable minimum frame distance (default 30) from the previous pick. Stops back-to-back regens from landing on the same visible frame.

Process isolation

  • GPU-bound work (transcription, translation, LLM inference for metadata) runs in a child LokiWorker.exe process rather than the main UI process. A CUDA driver fault or OOM in the worker no longer takes down the application — the parent surfaces the error and lets you retry.
  • Worker exits cleanly via _exit(0) after sendResult() to bypass CTranslate2's ThreadPool destructor hang on Windows + CUDA.

Accuracy improvements (transcription)

  • VAD-aware chunking. Whisper's per-chunk decode is now anchored on silence boundaries from the VAD output instead of a fixed 30-second window. Sentences no longer get cut mid-word at chunk seams. Single biggest accuracy lever in this release.
  • Hallucination-silence filter on by default — segments whose timestamps fall in regions VAD reported as <50% speech get dropped. Catches "Thank you for watching"-type Whisper ghosts over silent pauses.
  • VAD speech-pad default raised from 300 ms to 500 ms — captures quiet onsets and tails.
  • compression_ratio_threshold back to 2.4 (faster-whisper-python default). Catches Whisper repetition loops while letting real repetitive content through (vocal effects, gaming reactions).
  • Translated transcripts now carry redistributed per-word timings. Previously the timing array was copied verbatim from the source-language transcript, so a French translation rendered with OneWordPop / WindowSlide highlighted English words while displaying French segment text. Re-translating on this version regenerates _<lang>.json with correct timings.

Performance

  • Parallel multi-track audio extraction. Each enabled audio track decodes on its own worker thread. Previously sequential (~5s/track); now ~5s total regardless of track count.
  • Pipelined prep + GPU inference (opt-in) — the CPU-side VAD + mel computation for the next track runs while the current track is still on the GPU.
  • Batched word-timestamp alignment. Muninn's batched generate() now posts all alignment jobs in one GPU batch instead of a per-chunk loop. Up to 4 alignment passes used to serialize; now they run in parallel.
  • Muninn statically links FFmpeg. muninn.dll no longer needs avcodec/avformat/avutil/swresample/swscale at runtime. Removes ~50 MB of shared DLLs from the installer and eliminates DLL-version mismatch failures.
  • Keyframe-only thumbnail-strip extraction in the Timeline Editor — ~6× faster than the previous all-frames decode.
  • Keyframe-fast seek + 30 Hz scrub throttle for live timeline scrubbing. Drag latency is no longer dominated by decoder round-trips.
  • Tab-level cached NLLB translator in the Thumbnail tab. Each manual per-language regen used to create a fresh translator wrapper, which (because muninn::Translator deliberately skips model.reset() to dodge CTranslate2's CUDA-teardown hang) left its CTranslate2 ThreadPool alive. N regens accumulated N ThreadPools' worth of idle worker threads, climbing CPU. Now: one translator per tab session, one ThreadPool.
  • Skuld VRAM-headroom guard on the Metadata tab. After each successful generation, the available VRAM is checked; if it drops below 1.5 GB, the Skuld model is explicitly unloaded so the next video reloads on a clean allocator. Small / medium models never trigger this; 20B-class models recycle automatically before the OOM crash window.
  • CTranslate2 clear_cache() between tracks. Previously a no-op placeholder; now actually frees the allocator cache, smoothing VRAM peak on long multi-track runs.

Upload safety

  • Localized description length cap. Spanish / Russian / Portuguese translations expand 10-20% over their English source, which routinely bust YouTube's 5000-character cap on localizations.<lang>.description. The translation worker now truncates at the nearest word boundary, and the uploader also defensively truncates at upload time for existing on-disk metadata.
  • Friendly YouTube error dialog. Upload-failure messages are now parsed and translated into plain-language explanations of the most likely cause with concrete next steps. Known reasons handled: alreadyExists (409), invalidVideoMetadata (400), videoLimitExceeded, mediaBodyRequired, quotaExceeded (403), plus 401 auth and 5xx outage. Full raw API JSON moves to a Details button.

Code cleanup / internal

  • ffmpeg.exe + ffprobe.exe retired. Every code path that used to shell out to them now goes through a Norse module (Fenrir for stream copy and concat, Idun for caption burn, Aesir for amix, Mimir for metadata, Bragi for LUFS). Saved ~130 MB from the installer.
  • Static FFmpeg across all Norse modules via a new build pipeline (vcpkg x64-windows-static-md triplet + scripts/build_ffmpeg_msvc.bat).
  • Sweeping tab-subsystem refactor (Transcribe, Metadata, Thumbnail, Finalize, Upload, ApplicationSettings, PowerUserSettings, ProfileSettings). Each tab dropped 20-30+ duplicated slots and migrated to shared invokeQml / setQmlProperty helpers. ~20% line reduction across the tab subsystem.
  • Dead-code purge — utils/FfmpegRunner, utils/FFmpegUtils, ExportService::ffmpegPath(), waitForProcessWithCancel(), and their QProcess/QMutex dependencies all removed.
  • setup_cpp_environment.ps1 rewritten as a generic prereq checker (Visual Studio 2022, CMake, Qt 6.10.1, CUDA 13.2, cuDNN 9, GStreamer, vcpkg, MSYS2) — no side effects, tracked in-repo for contributors.

Bug fixes

  • Transcribed status no longer flips prematurely while a translation pass is still running.
  • Translated captions render in the target language, not the source, when the caption style uses per-word timing.
  • Transcribe-tab language pills refresh mid-batch — the new translation pill appears as soon as _<lang>.json is written.
  • Worker process no longer hangs at the end of a translate task on Windows + CUDA (CTranslate2 ThreadPool destructor workaround).
  • Idun caption burn preserves every input audio track (was dropping all but the first, losing commentary / music).
  • Dashboard pipeline status respects profile subdirectories — videos with full pipeline output no longer falsely show as "Thumbnail only."
  • Localized thumbnails (_<lang>.jpg) detected by the Upload tab and paired with the matching _<lang>.mp4.
  • Finalize tab shows localized burns (_fr, _de, …) so multi-track language outputs can be re-finalized when "Merge Audio Tracks" was off at render time.
  • Help tab internal markdown links route through the in-app navigation handler instead of launching the system browser.
  • LogViewer reads from the correct log path and honors the Data Directory setting.
  • Timeline scrubber updates the preview frame while paused.
  • Delete All Thumbnails / Delete All Metadata now catch per-language variants (*_*_thumb_<lang>.jpg, *_yt_<lang>.json) instead of leaving them behind.
  • Series export in the Timeline Editor drops the _edited suffix when loading multipart episodes — Ep05a + Ep05b + Ep05c exports as Ep05.mp4, matching the rest of the pipeline.

Profile / config migrations

  • Profile URLs migrated from https://nordiqai.io/ to https://www.lokistudio.ai/ across all built-in profiles.
  • distil-large-v3.5 added to Whisper model downloads — faster than large-v3 with comparable accuracy in English.
  • Thumbnail zoom slider (100-200%) for tighter framing before compositing.
  • About tab rewritten in the human voice of the origin story.

Compatibility

  • EDL assemble-and-load fires only when no video is loaded before Import EDL. If a video is already loaded, the existing overlay-cuts behavior is unchanged.
  • CMX3600 (.edl) is the supported format for the new EDL workflow. JSON/FCPXML imports continue to work in overlay-cuts mode.
  • Save-back-to-Premiere (CMX3600 export) is queued for a follow-up release.
  • Existing _<lang>.json translations on disk still carry the old English-word timing array. Re-translate on this version to regenerate.
  • User themes live in %APPDATA%/LokiStudio/themes/. The directory is created on first run; Default Light / Default Dark ship with the app.
  • GPU work runs in LokiWorker.exe as a child process. Antivirus or application-whitelisting tools may need to allow LokiWorker.exe alongside LokiStudio.exe.

← All updates  ·  Source-of-truth changelog on GitHub

Ready to update?

If you've already paid, this version is free. Just download and reinstall.

Download v1.2.0 Discord
Buy me a coffee