Jesse A. Green / notebook

LosslessCut Admin & Developer Guide

Generated June 2026 from mifi/lossless-cut at 260605a

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the source‑verified architectural and operational reference for LosslessCut — the cross‑platform, FFmpeg‑powered lossless video/audio editor by Mikael Finstad. It is written for administrators deploying the app at scale, power users automating it, and developers building, packaging, or contributing to it.

Where the User Guide covers using the application, this guide covers how it is built: the Electron three‑process architecture, the FFmpeg integration, the configuration store, the security model, the packaging matrix, and the module‑level internals.


1. Executive Summary

LosslessCut is a desktop application (not a service or library) built with Electron 42, React 19, and TypeScript 6, bundled by electron‑vite (Vite 7). It wraps platform‑specific FFmpeg/FFprobe binaries to perform near‑instant, lossless stream‑copy operations on media files.

Attribute Value
Project mifi/lossless-cut
Version analysed 3.69.0 (commit 260605a)
App ID no.mifi.losslesscut
License GPL‑2.0‑only (FFmpeg bundled under GPL v2+)
Runtime Electron 42 (Chromium 148 renderer, Node 24 main)
UI stack React 19 + Radix UI Themes + SCSS/CSS Modules
Build tool electron‑vite 5 / Vite 7, Yarn 4.11
Languages TypeScript (strictest)
Localisation i18next, 37 locales
Packaging electron‑builder 26 (mac/win/linux + app stores)
Source size ~768 K chars / 311 files / 170 code files

Architectural essence: three Electron processes (main / preload / renderer) communicate over an RPC bridge. The renderer (React app) holds the editing logic and, unusually, has direct Node and FFmpeg access via @electron/remote. The main process owns the native menu, window lifecycle, configuration store, FFmpeg process spawning helpers, the optional HTTP API, update checks, and logging.

Important

LosslessCut deliberately runs the renderer with nodeIntegration: true and contextIsolation: false. This is a conscious trade‑off, not a vulnerability — the editing logic must call FFmpeg and the Node filesystem directly. See §5 Security Model for the full trust‑boundary analysis.


2. Architecture

2.1 Process model

┌──────────────────────────────────────────────────────────────┐
│ Electron App (no.mifi.losslesscut)                           │
│                                                              │
│  ┌────────────────┐   IPC / @electron/remote   ┌───────────┐ │
│  │  MAIN PROCESS  │ <························>  │ RENDERER  │ │
│  │  src/main/     │                            │ (React)   │ │
│  │                │   preload RPC Proxy        │ src/      │ │
│  │ • BrowserWindow│ <───── window.electron ──  │ renderer/ │ │
│  │ • Native menu  │                            │ src/      │ │
│  │ • configStore  │   webContents.send(chan)   │           │ │
│  │ • FFmpeg spawn │ ─────── menu events ─────> │ Chromium  │ │
│  │ • HTTP API     │                            │ 148       │ │
│  │ • updateCheck  │                            └─────┬─────┘ │
│  │ • winston log  │                                  │       │
│  └───────┬────────┘                                  │       │
│          │ execa spawn                               │ HTML5 │
│          ▼                                           ▼ <video>│
│   ┌─────────────┐                            playback engine  │
│   │ ffmpeg /    │  bundled per-platform                       │
│   │ ffprobe     │  (extraResources)                           │
│   └─────────────┘                                             │
└──────────────────────────────────────────────────────────────┘
Process Source Runtime Responsibility
Main src/main/ Node 24 (Electron) Window/menu lifecycle, config, FFmpeg spawning, HTTP API, updates, logging, i18n backend
Preload src/preload/ Node (sandbox‑less) Exposes window.electron RPC proxy to the renderer
Renderer src/renderer/src/ Chromium 148 React UI, timeline/segments logic, project import/export, FFmpeg arg construction, HTML5 video playback
Common src/common/ Shared Constants, shared types, ffprobe types, utilities, i18n maps

2.2 The three IPC surfaces

Communication between renderer and main happens over three distinct channels — extraction tools see none of these as "endpoints," so they are documented here from source.

1. The RPC proxy (window.electron). The preload script installs a Proxy that forwards every property access as an ipcRenderer.invoke('__electron_rpc__', method, args) call. The main process resolves the method against a remoteApi object:

// src/preload/index.ts
const apiProxy = new Proxy({} as RemoteRpcApi, {
  get(_t, method) {
    return (...args) => ipcRenderer.invoke('__electron_rpc__', method, args);
  },
});
window.electron = apiProxy;

The remoteApi (main process) exposes: pathExists, downloadMediaUrl, fileTypeFromFile, focusWindow, quitApp, setProgressBar, sendOsNotification, writeClipboardText, readClipboardText, openExternal.

2. @electron/remote legacy API. A richer remoteApiLegacy object is exposed via @electron/remote, giving the renderer direct in‑process access to main‑side modules: ffmpeg, i18n, compatPlayer, configStore, the platform flags (isLinux/isWindows/isMac/platform/arch), isDev, lossyMode, pathToFileURL, and hasDisabledNetworking.

3. Native‑menu → renderer events. The native application menu (src/main/menu.ts) drives the UI by calling mainWindow.webContents.send('<channel>'). These channel names form the command catalog — see §9.2.

2.3 FFmpeg integration

FFmpeg is the engine. The main process (src/main/ffmpeg.ts) spawns the bundled ffmpeg/ffprobe binaries via execa, tracking running processes in a Set so they can be aborted.

Binary resolution order (getFfPath):

  1. customFfPath setting, if configured (the --settings-json '{customFfPath:...}' override).
  2. process.resourcesPath when packaged (binaries shipped as extraResources).
  3. ffmpeg/<platform>-<arch>/[lib/] in local dev.

Note

On Linux, packaged builds set LD_LIBRARY_PATH=process.resourcesPath so the shared‑library FFmpeg build can find its .so files. This is skipped when a customFfPath is used. Custom FFmpeg paths do not work on the Mac App Store build due to sandbox restrictions.

Key FFmpeg‑backed operations in the main process:

Function FFmpeg behaviour
runFfmpegProcess / runFfmpegWithProgress Generic export with progress parsing
runFfmpegConcat Stream‑copy concatenation via stdin concat list
runFfprobe Metadata probe (-show_format, JSON), with timeout
renderWaveformPng Two‑stage pipe: extract audio → showwavespic filter → PNG
detectSceneChanges select='gt(scene,N)',metadata=print
blackDetect / silenceDetect blackdetect / silencedetect filters, parsed into segments
captureFrames / captureFrameToFile / captureFrameToClipboard Frame export (image2), JPEG quality mapped from 0–1
createMediaSourceProcess Compatibility playback: re‑encode to fragmented MP4 (libx264 ultrafast, AAC) for the renderer's MediaSource
downloadMediaUrl HTTP/HLS stream copy (gated by networking flag)

Progress is parsed from FFmpeg's stderr time= lines (parseFfmpegProgressLine) against the known duration.

2.4 Playback architecture

The renderer plays media with the native Chromium HTML5 <video> engine — the same one in Google Chrome. When Chromium can't decode a file (e.g. some 10‑bit/HEVC content), LosslessCut falls back to FFmpeg‑assisted software decoding (compatPlayer.ts + createMediaSourceProcess), streaming a re‑encoded fragmented MP4 into a MediaSource. This affects preview only — never the lossless export. The enable-blink-features=AudioVideoTracks Chromium switch is enabled to expose track selection.

2.5 Project/EDL formats

The renderer (src/renderer/src/edlFormats.ts, edlStore.ts, cmx3600.ts) implements bidirectional conversion between the timeline's segment model and ~13 import and 7 export formats (LLC/JSON5, CSV, EDL, FCPXML, XMEML, CUE, PBF, SRT, OTIO, YouTube chapters). The native .llc file is JSON5.


3. System Requirements

3.1 End‑user runtime

LosslessCut bundles its own FFmpeg and Electron runtime — there are no external runtime dependencies for end users. OS support tracks Electron:

OS Minimum (current builds)
macOS macOS 12+ (v3.66 dropped macOS 11)
Windows Windows 10+ 64‑bit (v3.52 dropped 8.1)
Linux 64‑bit; glibc per Electron (see issue #2909)

Warning

On Windows, the app must run from the C: drive, must not be relocated after extraction, and its path must not contain non‑ASCII characters. Violating these causes blank‑window/crash‑on‑launch failures — a frequent support issue.

3.2 Developer / build host

Requirement Version
Node.js 22+ (toolchain targets node24.15)
Package manager Yarn 4.11.0 (packageManager field, node-modules linker)
FFmpeg builds Downloaded per‑platform via download-ffmpeg-* scripts
7‑Zip Required for Windows FFmpeg extraction
Xcode + signing macOS notarization / MAS provisioning profiles

4. Configuration Reference

Configuration is managed by electron‑store and persisted to config.json inside the OS appData (userData) folder. The store is initialised in src/main/configStore.ts with a complete defaults object — this object is the authoritative configuration schema.

4.1 Storage location and precedence

getConfigPath() resolves to, in order:

  1. --config-dir <dir> CLI option (folder containing config.json).
  2. A neighbour config.json next to LosslessCut.exe (Windows non‑store builds only) — enables semi‑portable use.
  3. The default app.getPath('userData')/config.json.

The store is created with up to 5 retries (2 s apart) to tolerate transient file locks; failure throws Timed out while creating config store.

4.2 Configuration migrations

On init, configStore.init() performs in‑place migrations and backs up the config before risky changes (config.json.backup-v<appVersion>-<oldVersion>-<timestamp>):

Migration Action
enableTransferTimestamps → treatOutputFileModifiedTimeAsStart Renamed key
cleanupChoices.closeFile Defaulted to true if missing
customOutDir → recentCustomOutDirs Converted to list
Keybinding format v1 → v2 "Ctrl+Shift+S" strings → "ControlLeft+ShiftLeft+KeyS" code form (with backup)

The config carries a version field (currently 2).

4.3 Full settings catalog

Selected from the defaults map (grouped by concern). All keys are read/written via configStore.get/set.

Cutting & export

Key Default Meaning
keyframeCut true Keyframe (-ss before -i) cut mode
enableSmartCut false Experimental frame‑accurate cut
invertCutSegments false Export between segments (Yin‑Yang)
autoMerge false Default to merge‑cuts export mode
autoDeleteMergedSegments true Delete intermediate files after merge
segmentsToChapters / segmentsToChaptersOnly false Map segments to chapters
exportConfirmEnabled true Show export options dialog
movFastStart true MOV faststart
avoidNegativeTs make_zero FFmpeg avoid_negative_ts
preserveMetadata default Metadata retention level
preserveChapters true Keep chapters
preserveMetadataOnMerge false Keep metadata when merging
preserveMovData / preserveChapters varies MOV udta/chapters
ffmpegExperimental false Pass FFmpeg -strict experimental
cutFromAdjustmentFrames / cutToAdjustmentFrames 0 Shift start/end by N frames
outFormatLocked (unset) Lock output format
safeOutputFileName true Sanitize output names
outSegTemplate / mergedFileTemplate / mergedFilesTemplate (unset) Name templates
outputFileNameMinZeroPadding 1 Min zero‑padding for indices

Capture (snapshots)

Key Default Meaning
captureFormat jpeg jpeg/png/webp
captureFrameMethod videotag videotag or ffmpeg
captureFrameQuality 0.95 0–1 quality
captureFrameFileNameFormat timestamp Naming scheme

Playback & decoding

Key Default Meaning
playbackVolume 0.3 Default volume
enableAutoHtml5ify true Auto‑convert for preview
enableNativeHevc true Hardware HEVC decode
ffmpegHwaccel none HW accel for detect/export
waveformHeight 40 Waveform px height
waveformMode / thumbnailsEnabled / keyframesEnabled varies Timeline visualisations

Input/UX

Key Default Meaning
simpleMode true Simple vs advanced view
darkMode true Dark theme
language en UI language
keyBindings (60+ defaults) Keyboard bindings
wheelSensitivity 0.2 Mouse‑wheel seek sensitivity
mouseWheelZoomModifierKey / ...FrameSeek... / ...KeyframeSeek... ctrl/alt/shift Wheel modifier keys
segmentMouseModifierKey shift Drag‑to‑resize modifier
keyboardSeekAccFactor 1.03 Seek acceleration
keyboardNormalSeekSpeed / keyboardSeekSpeed2 / keyboardSeekSpeed3 1/10/60 Seek speeds
invertTimelineScroll (unset) Invert scroll direction

Files, instances & updates

Key Default Meaning
enableCustomOutDir / recentCustomOutDirs false / [] Custom output dir
storeProjectInWorkingDir true Where .llc lives
autoSaveProjectFile true Auto‑write .llc
enableOverwriteOutput true Allow overwriting outputs
cleanupChoices {trashTmpFiles, askForCleanup, closeFile, cleanupAfterExport} Cleanup behaviour
askBeforeClose false Confirm on quit
allowMultipleInstances false Allow multiple instances
enableUpdateCheck true GitHub update check
storeWindowBounds / windowBounds true / (unset) Persist window geometry
customFfPath (unset) Custom FFmpeg folder

Caution

Overriding settings via --settings-json writes directly into the store with no validation. Invalid values can corrupt config.json. The migration backup mechanism only triggers on keybinding‑format changes, not on arbitrary overrides.


5. Security Model

5.1 The renderer trust boundary (security inversion)

LosslessCut intentionally disables Electron's standard renderer hardening:

// src/main/index.ts — createWindow()
webPreferences: {
  contextIsolation: false,
  nodeIntegration: true,
  webSecurity: !isDev,           // enabled in production
  preload: '../preload/index.cjs',
}
// @electron/remote is initialised and enabled per-window

This is the opposite of the hardened contextBridge playbook. The reason is structural: the React editing logic calls FFmpeg, the filesystem, and Electron APIs directly in the renderer through @electron/remote. The honest trust model is therefore:

  • The renderer is fully trusted code shipped inside the app's ASAR — there is no untrusted web content and no plugin system.
  • The mitigating control is that the window only ever loads local content: out/renderer/index.html via loadFile in production (or http://localhost:3001 in dev). webSecurity is on in production.
  • There is no remote code execution surface by design: no arbitrary URL navigation, no third‑party plugins running in‑process.

Important

When hardening a managed deployment, do not treat nodeIntegration:true as a defect to "fix" — it is required for the app to function. The meaningful controls are OS‑level (sandboxing via the App Store builds) and the --disable-networking flag.

5.2 Networking controls

All outbound networking funnels through src/main/networking.ts. The --disable-networking CLI flag sets a global that makes throwIfDisabledNetworking() reject:

  • downloadMediaUrl (HTTP/HLS download) throws Networking is disabled.
  • openExternal is blocked (external links won't open).
  • The update check is skipped.

For an air‑gapped or privacy‑sensitive deployment, launch with --disable-networking. Store builds additionally never run the update checker (isStoreBuild).

5.3 HTTP API exposure

When --http-api is passed, an Express server (src/main/httpServer.ts) binds to 127.0.0.1 (forced IPv4) on the chosen port (default 8080). It exposes POST /api/action/:action and POST /api/await-event/:eventName.

Warning

The HTTP API has no authentication. Any local process that can reach 127.0.0.1:<port> can drive the app (open files, export, etc.) while it is enabled. Only enable it when needed, and be aware of multi‑user machines. It is off by default.

5.4 App Store sandboxing

The Mac App Store (MAS) and Microsoft Store (AppX) builds run inside the OS sandbox. Consequences:

  • Custom FFmpeg path (customFfPath) does not work on MAS.
  • The MAS build cannot open .vob files (Apple restriction) and must prompt for an output directory before writing.
  • Entitlements are declared in entitlements.mas.plist / entitlements.mas.inherit.plist / entitlements.mas.loginhelper.plist.

5.5 Single‑instance enforcement

By default the app enforces a single instance via requestSingleInstanceLock. A second launch passes its argv (files or --keyboard-action) to the running instance via the second-instance event. Setting allowMultipleInstances: true disables this (experimental — Electron does not fully support multiple instances).


6. Operations

6.1 Logging

Logging uses winston (src/main/logger.ts). A rotating file transport writes to app.log in the userData folder (or the working dir in dev), capped at 1 MB per file, 100 files, level debug. A console transport mirrors output.

Windows  %APPDATA%\LosslessCut\app.log
macOS    ~/Library/Application Support/LosslessCut/app.log
Linux    ~/.config/LosslessCut/app.log

Reach the log quickly via Help → Log file, and the config via Help → Configuration file.

6.2 Update checking

On launch (non‑store, networking enabled, enableUpdateCheck), updateChecker.ts queries the GitHub API (GET /repos/mifi/lossless-cut/releases/latest via Octokit) and uses semver.lt to compare. A newer version adds a "New version!" menu entry linking to the release. There is no auto‑download/auto‑install — updates are manual.

6.3 Window state persistence

Window geometry (position, size, maximized) is debounced (500 ms) and saved to windowBounds when storeWindowBounds is on. On launch, saved bounds are validated against the current display layout before being applied.

6.4 Crash & error handling

electron-unhandled is configured with showDialog: true, surfacing unhandled main‑process errors to the user and logging them. The renderer wraps the app in an ErrorBoundary.

6.5 Managed deployment checklist

  • [ ] Distribute the appropriate per‑platform artifact (see §7).
  • [ ] For offline/locked‑down environments, pin launch args to include --disable-networking.
  • [ ] To centralise settings, ship a config.json and point at it with --config-dir, or place it next to the EXE (Windows).
  • [ ] Confirm the app runs from a local, ASCII‑only path on C: (Windows).
  • [ ] Do not enable --http-api on shared/multi‑user hosts unless required.
  • [ ] Verify FFmpeg binaries are present in resources/ (bundled automatically by electron‑builder).

7. Build & Distribution

7.1 Build pipeline

yarn install            # postinstall runs electron-builder install-app-deps
yarn download-ffmpeg-<platform>-<arch>   # fetch FFmpeg/FFprobe binaries
yarn build              # generate-icon + electron-vite build (main/preload/renderer)
yarn pack-mac | pack-win | pack-linux    # electron-builder packaging

electron-vite builds three targets from electron.vite.config.ts:

Target Build target Notes
main node24.15, sourcemaps Dependencies externalized
preload node24.15, CJS output
renderer chrome148, React plugin Dev server on 127.0.0.1:3001

The full CI gate is yarn check: tsc (project references, strictest) → eslint → vitest → build → license check → i18n scan → dedupe --check → generate-docs.

Note

FFmpeg binaries are not in the repo; they are downloaded from mifi/ffmpeg-builds / mifi/ffmpeg-build-script (GPL shared builds, FFmpeg 8.0) into ffmpeg/<platform>-<arch>/ and packaged as electron‑builder extraResources.

7.2 Distribution matrix (electron‑builder)

App ID no.mifi.losslesscut; artifact name ${productName}-${os}-${arch}.${ext}. ASAR with smartUnpack: false.

Platform Targets Arches Signing
macOS dmg + mas (universal) x64, arm64 (dmg); universal (mas) hardenedRuntime + notarize (dmg); MAS provisioning profile
Windows 7z + appx x64, arm64 AppX identity 57275mifi.no.LosslessCut
Linux tar.bz2, AppImage, snap x64 (all), arm64 + armv7l (tar.bz2) Snap base core22, plugs default+removable-media

Caution

The MAS and AppX artifacts are named *-DONT-USE-THIS-FILE.${ext} — they are store‑submission artifacts, not direct downloads. The AppX is unsigned and does not work when sideloaded; Windows users should use the 7z build. macOS users should use the dmg.

7.3 File associations

Both Windows and macOS builds register file associations as a "Viewer" role for ~40 video extensions (mp4, mkv, mov, avi, webm, ts, m2ts, mxf, 3gp, flv, vob, …) and ~40 audio extensions (mp3, aac, flac, wav, m4a, opus, ogg, wma, …). These let users open media in LosslessCut from the OS file manager.

7.4 Internationalisation build

Translations live in locales/<lang>/translation.json (37 languages) and are packaged as extraResources. The i18n keys are extracted from source by i18next-cli (yarn scan-i18n); strings use the text itself as the key (keySeparator: false, nsSeparator: false). Both main and renderer initialise i18next; the main process uses i18next-fs-backend to load from the resources locales/ folder (overridable with --locales-path).


8. Repository Structure

lossless-cut/
├── src/
│   ├── main/                 # Electron main process (Node)
│   │   ├── index.ts          # Entry: window, IPC, CLI parse, init
│   │   ├── menu.ts           # Native application menu (command catalog)
│   │   ├── ffmpeg.ts         # FFmpeg/FFprobe spawning + operations
│   │   ├── ffmpegUtil.ts     # JPEG quality mapping, helpers
│   │   ├── compatPlayer.ts   # Software-decode playback stream
│   │   ├── configStore.ts    # electron-store config + defaults + migrations
│   │   ├── httpServer.ts     # Express HTTP API
│   │   ├── networking.ts     # --disable-networking gate
│   │   ├── updateChecker.ts  # GitHub release check (Octokit)
│   │   ├── progress.ts       # FFmpeg progress line parser
│   │   ├── logger.ts         # winston logging
│   │   ├── i18n*.ts          # i18next main-side init + locale paths
│   │   └── util.ts, isDev.ts, isStoreBuild.ts, aboutPanel.ts, contextMenu.ts
│   ├── preload/index.ts      # window.electron RPC Proxy
│   ├── renderer/
│   │   ├── index.html
│   │   └── src/              # React app
│   │       ├── App.tsx       # Root component / action dispatcher
│   │       ├── Timeline*.tsx, SegmentList.tsx, BottomBar.tsx, TopMenu.tsx
│   │       ├── ffmpeg.ts, smartcut.ts, segments.ts, edlFormats.ts, ...
│   │       ├── components/   # ~70 React components (dialogs, buttons, ...)
│   │       ├── hooks/        # ~30 hooks (useSegments, useFfmpegOperations, useKeyboard, ...)
│   │       └── util/         # streams, duration, outputNameTemplate, rate-calculator
│   └── common/               # constants, types, ffprobe, i18n, util (shared)
├── locales/                  # 37 translation.json files
├── script/                   # build/release scripts (TS)
├── docs/                     # authoritative user docs (md)
├── electron.vite.config.ts   # build config
└── package.json              # deps, scripts, electron-builder config

Note

The bulk of application logic is in src/renderer/src/ (485 K chars). The components/, hooks/, and util/ subfolders are where most editing behaviour lives — useFfmpegOperations, useSegments, useKeyboard, useFrameCapture, useWaveform, and smartcut.ts are the highest‑value modules for understanding cut/export behaviour.

8.1 Key dependencies

Dependency Role
electron 42 / @electron/remote Desktop runtime + renderer↔main bridge
execa Spawning FFmpeg/FFprobe
electron-store Persisted config
express + morgan + express-async-handler HTTP API
i18next + i18next-fs-backend + react-i18next Localisation
winston Logging
@octokit/core + semver Update checks
zod + json5 + yargs-parser CLI/settings parsing & validation
react 19 + @radix-ui/themes + sweetalert2 UI
@dnd-kit/* Segment drag‑and‑drop
leaflet + react-leaflet GPS track map
fast-xml-parser + cue-parser + csv-parse/csv-stringify Project format import/export
smpte-timecode + luxon Timecode handling

9. Internals Reference

9.1 Keyboard action registry

The action system is the backbone of the UI and the automation surface — the same action names drive keyboard shortcuts, --keyboard-action, and POST /api/action/:action. Default bindings are declared in configStore.ts. Representative actions (the full set is in the in‑app Keyboard shortcuts dialog):

addSegment, setCutStart, setCutEnd, removeCurrentCutpoint, splitCurrentSegment, labelCurrentSegment, export, captureSnapshot, captureSnapshotToClipboard, copySegmentsToClipboard, togglePlayResetSpeed, togglePlayNoResetSpeed, seekBackwards/seekForwards (+2/Percent/Keyframe variants), seekPreviousFrame/seekNextFrame, jumpPrevSegment/jumpNextSegment/jumpFirstSegment/jumpLastSegment (+Seek variants), jumpCutStart/jumpCutEnd, jumpTimelineStart/jumpTimelineEnd, timelineZoomIn/timelineZoomOut/timelineToggleComfortZoom, increaseRotation, toggleStripAll/toggleStripCurrentFilter, goToTimecode/goToTimecodeDirect, undo/redo, increaseVolume/decreaseVolume/toggleMuted, toggleFullscreenVideo, batchPreviousFile/batchNextFile/batchOpenPreviousFile/batchOpenNextFile/batchOpenSelectedFile, cleanupFilesDialog, makeCursorTimeZero, toggleKeyboardShortcuts.

Note

Unknown actions raise Action not found: ${action} (App.tsx). Keybindings are stored as KeyboardEvent.code tokens joined with + (e.g. ControlLeft+ShiftLeft+KeyZ), migrated from the legacy human string format.

9.2 Menu command catalog

src/main/menu.ts maps menu items to renderer webContents.send channels. These are the application commands triggered from the native menu:

Menu Commands (channel names)
File openFilesDialog, openDirDialog, promptDownloadMediaUrl, closeCurrentFile, closeBatch, importEdlFile (csv/csv-frames/cutlist/edl/youtube/xmeml/fcpxml/cue/pbf/srt/dv-analyzer-summary-txt/otio/llc), exportEdlFile (csv/csv-human/csv-frames/tsv-human/srt/llc), exportYouTube, html5ify, fixInvalidDuration, decimate, toggleSettings
Edit undo/redo/clipboard roles, extractAllStreams, showStreamsSelector
Segments createNumSegments, createFixedDurationSegments, createFixedByteSizedSegments, createRandomSegments, reorderSegsByStartTime, shuffleSegments, combineOverlappingSegments, combineSelectedSegments, splitCurrentSegment, invertAllSegments, fillSegmentsGaps, shiftAllSegmentTimes, alignSegmentTimesToKeyframes, clearSegments
Tools concatBatch, setStartTimeOffset, detectBlackScenes, detectSilentScenes, detectSceneChanges, readAllKeyframes, createSegmentsFromKeyframes, toggleLastCommands, dev tools
Help toggleKeyboardShortcuts, openSendReportDialog, external links (usage/FAQ/troubleshooting/feature‑request/licenses)

9.3 CLI argument parsing

src/main/index.ts parses argv with yargs-parser. Booleans: disable-networking. Strings: settings-json, config-dir, lossy-mode. Other recognised: http-api (number/boolean), keyboard-action, locales-path. The experimental --lossy-mode accepts a zod‑validated { videoEncoder: 'libx264'|'libx265'|'libsvtav1' }. Positional args are treated as file paths.

9.4 Error catalog

Errors surfaced from source (for log triage and support):

Error message Source
Unable to determine file format App.tsx
Action not found: ${action} App.tsx
Unsupported MIME type or codec: ${mimeCodec} MediaSourcePlayer.tsx
Media source process did not initialize MediaSourcePlayer.tsx
Root element <xmeml>/<fcpxml> not found in file edlFormats.ts
Invalid EDL type edlStore.ts
ffprobe returned malformed data ffmpeg.ts
Unsupported file (UnsupportedFileError) ffmpeg.ts
Can only smart cut video with exactly one video stream smartcut.ts
Smart cut only works on videos smartcut.ts
Unable to determine codec for smart cut smartcut.ts
Timed out while creating config store configStore.ts
Networking is disabled networking.ts

10. Troubleshooting (operational)

10.1 App won't launch / blank window (Windows)

Path‑related. Ensure the install path is on C:, ASCII‑only, and not relocated. Try --disable-gpu / --disable-features=VizDisplayCompositor. Whitelist in anti‑virus. Consider removing AppContainer restrictions.

10.2 App crashes on startup (Linux)

setuid_sandbox_host errors → run with --no-sandbox. For Snap/Flatpak issues, fall back to the GitHub tar.bz2/AppImage build.

10.3 FFmpeg not found / custom path ignored

Custom paths don't work on the MAS sandbox build. Confirm customFfPath points at the folder containing ffmpeg(.exe) and ffprobe(.exe). In packaged builds, binaries must be in resources/.

10.4 Config corruption

A bad --settings-json can corrupt config.json. Recovery: delete or restore config.json from a *.backup-* file in the userData folder (backups are written before keybinding migrations).

10.5 HTTP API not reachable

Confirm --http-api was passed and note the bound port (default 8080). It binds only to 127.0.0.1, so remote hosts cannot reach it. Check the log for HTTP API listening on.

10.6 Export produces wrong duration / black frames

Operationally, this is almost always an un‑cuttable track being copied as‑is. Reproduce with only the video track enabled; bisect tracks; switch container to MKV/MOV; adjust avoidNegativeTs.


11. Contributing & Project Status

  • License: GPL‑2.0‑only. Bundled FFmpeg is GPL v2+. Generated licenses.txt aggregates dependency licenses (yarn generate-licenses).
  • Maintainer: single maintainer (Mikael Finstad). The project accepts contributions via the GitHub repo; see CONTRIBUTING.md.
  • Tooling discipline: strictest TypeScript config, ESLint (with eslint-plugin-unicorn), Vitest unit tests, i18n extraction gate, license check, and a generate-docs step (docs/generated/types.md is generated from TS types via ts-morph).
  • Docs are first‑class: the authoritative user docs live in docs/*.md in‑repo and are the canonical reference reproduced (and expanded) in the User Guide.

Tip

When extending the app, note the three‑surface action model: a new user command should be wired as a keyboard action (so it's automatically available via --keyboard-action and the HTTP API), and optionally added to the native menu as a webContents.send channel.


12. Quick Reference

Item Value
App ID no.mifi.losslesscut
Config file <userData>/config.json (electron‑store)
Log file <userData>/app.log (winston, 1 MB ×100)
HTTP API --http-api, 127.0.0.1:8080, no auth
Offline mode --disable-networking
Custom config dir --config-dir <dir>
Override settings --settings-json '<json5>'
Run action --keyboard-action <name> or POST /api/action/<name>
FFmpeg Bundled (GPL shared 8.0), resources/ffmpeg[.exe]
Project file .llc (JSON5)
Update source GitHub releases (Octokit)
Renderer security nodeIntegration:true, contextIsolation:false (by design)
License GPL‑2.0‑only