Two config keys come with this. `enableScripts` flipped its default to
false in the 4.13 line as supply-chain hardening, so it has to be set
explicitly here — esbuild, @swc/core and @parcel/watcher all need their
install scripts, as does the `electron-builder install-app-deps`
postinstall. `yarn set version` also wrote `approvedGitRepositories: **`,
a blanket opt-out of the new git-dependency approval prompt; that is
dropped again, since the lockfile has no git dependencies and leaving the
guard armed costs nothing until one is added.
The lockfile moves to metadata version 10 and one builtin patch hash for
`resolve` changes; no package resolutions move.
Incidentally this makes `--no-time-gate` available on `yarn add`/`yarn up`,
which 4.11 did not have — the npmMinimalAgeGate quarantine previously had
to be worked around with YARN_NPM_MINIMAL_AGE_GATE=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BtCagWFcr4MLHQD4zkPVf5
26.16.1 carries the fix for electron-userland/electron-builder#10167:
`security set-key-partition-list` was passed the .p12 import password
(CSC_KEY_PASSWORD) instead of the temporary keychain's own password, so
macOS signing could fail in CI whenever those two differ. The fix landed
after 26.16.0 was cut, so 26.16.1 is the first release that has it.
The version is spelled out explicitly because the `latest` dist-tag still
points at 26.15.3 — 26.16.x ships under the `v26` tag.
Verified unchanged in 26.16.1, since both are load-bearing here:
- the legacy snap template table (base/plugs pairing behind #3002)
- the appimage 1.0.3 toolset checksums (libfuse2-free runtime, #3022)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BtCagWFcr4MLHQD4zkPVf5
The { index, channels, channelLayout } shape was repeated inline in the
preview's main and renderer signatures. Move it to src/common/types.ts as
AudioStreamInfo, and let getFixChannelLayoutFilter take the narrower
AudioChannelInfo it actually needs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCf7BBiPqNLUjVoN7rTCF6
The previous check only looked for unknown/unused channels (UNSD/UNK), but
swresample's sane_layout() rejects any custom layout that doesn't form a
standard speaker set. So e.g. a surround stem pair stored as
"2 channels (BL+BR)", or "4 channels (FL+FR+FC+BL)", failed the same way
even though every channel is properly labelled.
ffprobe reports a layout as "N channels (A+B+...)" exactly when ffmpeg
could not express it as a native layout; anything that does form a standard
set is reported under its native name ("stereo", "quad", "5.1") and always
works. So key off that instead.
Verified against a .mov roundtrip of every layout: all native layouts are
left untouched, and BL+BR, FL+BL, SL+SR, FL+FR+SL, TFL+TFR and FL+FR+FC+BL
all go from failing to working.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCf7BBiPqNLUjVoN7rTCF6
Excluding src/common tests from tsconfig.common.json kept vitest from
running the compiled copies twice, but it also meant those test files were
no longer type checked at all by `tsc --build` (eslint only catches lint
issues, not type errors).
Exclude common-ts-dist from test discovery instead, so the tests are both
type checked and run exactly once.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCf7BBiPqNLUjVoN7rTCF6
DV/DVCPRO .mov files (e.g. captured by Final Cut / iMovie) carry a 'chan'
atom that labels every audio channel as "unused", so ffmpeg describes the
stream as "4 channels (UNSD+UNSD+UNSD+UNSD)".
swresample only accepts native or fully specified custom layouts, so as
soon as anything needs to resample or downmix such a stream it fails with:
[SWR] Input channel layout '4 channels (UNSD+UNSD+UNSD+UNSD)' is not supported
[af#0:1] Error reinitializing filters!
Nothing was written into output file
This broke both preview playback and "convert to supported format" for
these files.
Prepend a `channelmap` filter that re-labels the channels, which turns the
layout into a plain "N channels" (unspecified) layout that swresample does
support. It only re-labels, the samples are untouched, and the stream then
behaves exactly as if it had carried no channel layout information at all.
Only applied to streams that actually have an unsupported layout, so files
with a proper layout keep their correct downmix.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YCf7BBiPqNLUjVoN7rTCF6
An attached_pic packet has no position on the timeline, so the demuxer
emits it at pts 0. When seeking before the input (`-ss` before `-i`),
ffmpeg applies the seek offset to it anyway, dragging it to -(cut from),
and it reaches the muxer first. `-avoid_negative_ts make_zero` then
derives its file-wide correction from that packet and shifts every other
stream back by +(cut from), undoing the rebase to zero that the audio and
video already got.
The mov muxer records that as an empty edit, so each exported segment
declares the segment's end time in the source as its duration instead of
its length - e.g. splitting a 3.5h m4a into 7 parts yields parts that
report 30, 60, 90, 120... minutes. ffprobe subtracts the empty edit and
looks correct, but players reading mvhd (such as VLC) show the inflated
value. The media itself is intact, only the declared duration is wrong.
Downgrade `make_zero`/`make_non_negative` to `auto` when a cover art
stream is being copied. `auto` doesn't use the attached pic as its
reference, so the output gets the right duration and keeps the cover art.
Reproduced against the bundled ffmpeg (n8.0-23-gd1f31a829d-20251022) on
an AAC m4a with an mjpeg attached_pic stream. Not format specific - mp4
behaves identically. A file without a cover art stream is unaffected, and
for a file that also has a real video track `auto` additionally avoids the
keyframe-backtrack overshoot that `make_zero` produces.
Closes#3009
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W5WHGj7byjqxDEfSxRyuJD
Follow-up to #2797. `cutMultiple` already calls `assertFileExists` before
spawning ffmpeg, so a source file that was moved or deleted after being
opened produces a clear "Source file no longer exists or is not
accessible" message instead of a raw ExecaError plus the generic "Unable
to export this file" checklist (change output format, try keyframe cut,
change working directory...), none of which can help.
The other export paths had no such guard, so they still fail with a bare
ffmpeg "No such file or directory". Add the same check to:
- `captureFramesRange` (export segment frames as images)
- `captureFrameFromFfmpeg` (capture snapshot)
- `extractStreams` (extract all tracks / extract single track)
`extractAllStreams`/`extractSingleStream` swallowed the error message and
showed a generic toast, so let UserFacingError through there as well,
matching how the export and merge flows already handle it.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqUn6Bh7hWqPqWejqR8p7u
`-movflags` (mov/mp4 family) and `-default_mode` (matroska family) were
added to every ffmpeg command regardless of the output format, so e.g.
exporting to matroska produced:
... -movflags "+faststart" -default_mode infer_no_subs -f matroska ...
ffmpeg tolerates private options belonging to a different muxer, so this
was harmless, but it adds noise to the command line we log, show under
"Last commands" and include in problem reports, which makes
troubleshooting harder than it needs to be.
Gate each set of flags on the output format. The format lists are derived
from ffmpeg's movenc.c and matroskaenc.c rather than the existing
util/streams.ts `isMov`/`isMatroska` helpers, because those are narrower
UI oriented lists that omit movenc formats we can output (3gp, 3g2, f4v,
psp) - reusing them would have silently dropped +faststart for those.
When the output format is unknown, ffmpeg infers the muxer from the file
extension, so the flags are kept as before.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PqUn6Bh7hWqPqWejqR8p7u
Setting `base: core22` left the snap declaring a core22 base while still
plugging the core18-era gnome-3-28-1804 content snap for its GNOME platform.
Mesa's DRI drivers in that platform cannot resolve their dependencies against
core22, so every GL/EGL path fails including the swrast software fallback, the
GPU process exits, and the main process segfaults before a window appears.
The base override never moved the platform. electron-builder's snap template
pairs `base: core20` with a hardcoded `gnome-3-28-1804` plug, and its legacy
snap path rewrites only the `base` field, leaving the plugs block untouched.
The two halves have to match, so the fix is to stop overriding the base and
let the template stay self-consistent.
Dropping the key rather than pinning core20 explicitly means the snap keeps
following whatever electron-builder's template ships.
This restores the pre-3.68 behaviour, which reopens#2614 for very new GPUs
whose hardware needs a newer Mesa than the platform provides. That trade is
deliberate: #2614 affected some GPUs, this affects every user.
Fixes#3002
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KboMatXCuTgjD6KfC1ik12
electron-builder's default AppImage toolset ("0.0.0") uses the legacy
AppImageKit runtime, which dlopens libfuse.so.2 at startup and fails on
distros that no longer ship libfuse2.
Opting into toolset appimage 1.0.3 uses AppImage/type2-runtime 20251108,
a static-pie runtime with libfuse/squashfuse linked in, so the AppImage
runs without any FUSE packages installed.
Fixes#3022
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmsuZZPx6pFKSjN6UZmGcJ
On Windows, when the ffmpeg/ffprobe executable does not exist (e.g. a
custom FFmpeg directory pointing to a location without ffmpeg.exe),
cross-spawn (used by execa) falls back to running the command through
cmd.exe, which fails with exit code 1 and a confusing
"'...' is not recognized as an internal or external command" error
instead of ENOENT. This bypassed the "FFmpeg executable not found"
dialog and instead triggered the error report dialog.
Now check that the executables exist as part of the startup check
(which also re-runs whenever the custom FFmpeg directory setting
changes), so a missing executable yields a proper ENOENT. Also mention
the custom FFmpeg directory setting in the error dialog when one is
configured.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PE6aFnysh85Ryndeu6zXYG
Gating steps on the exact matrix image name (windows-latest,
windows-11-arm) breaks silently whenever the image in the matrix is
renamed, as happened with the windows-2022 switch. runner.os and
runner.arch describe what we actually care about and are independent
of the image label.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWvFPqUcXUzsYf3DY7RziJ
The matrix was switched from windows-latest to windows-2022, but the
Windows x64 ffmpeg download, electron-builder, e2e and screenshot upload
steps were still gated on matrix.os == 'windows-latest', so they never
ran and the artifact upload failed with
'File dist/LosslessCut-win-x64.7z does not exist'.
Gate them on runner.os == 'Windows' && matrix.os != 'windows-11-arm'
instead, so they are independent of the exact Windows runner image.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PWvFPqUcXUzsYf3DY7RziJ