Linux support: fix mp4 export (no AAC encoder), ship deb/rpm/AppImage/Arch, and make dapi work - #58
Linux support: fix mp4 export (no AAC encoder), ship deb/rpm/AppImage/Arch, and make dapi work#58zbcoding wants to merge 6 commits into
Conversation
…code Every mp4 export template asks for AAC, and the encoder took that as a demand. In WebCodecs, AAC is the platform's encoder — AudioToolbox on macOS, Media Foundation on Windows — so Chromium on Linux has none, and an export failed after the video track had already started encoding. The codec asked for is now a preference: resolveAudioCodec keeps it when the container takes it and the browser can encode it, and otherwise picks the container's next encodable choice (Opus, which mp4 takes and Chromium bundles everywhere). It is resolved at the three places an export starts — the export panel, the dapi handler, and the encoder itself — so the progress panel, the echoed dapi config, and the file all name the same codec. The audio picker offers only codecs this machine can actually encode. Nothing changes where AAC exists: it is encodable, so it is what is used.
|
@zbcoding is attempting to deploy a commit to the Diffusion Studio Team on Vercel. A member of the Team first needs to authorize it. |
`npm run make` only had a darwin ZIP and the DMG, so a Linux checkout could be packaged but never made into an installable artifact. Adds deb and rpm makers plus a Linux ZIP, and scopes the DMG to darwin. The FreeDesktop entry both packages share registers `x-scheme-handler/diffusion`, which is what the auth and checkout deep links arrive on — the macOS bundle gets that from packagerConfig.protocols. The Linux build also renames the executable to `diffusion-studio`: electron-packager keeps the space from `name`, which is fine inside a .app bundle but not for the binary the packages symlink into /usr/bin. The name stays untouched on macOS, where the CLI wrapper and the signing pass both address `Contents/MacOS/Diffusion Studio`. Two runtime gaps go with it: Chromium needs `--ozone-platform-hint=auto` or it runs through XWayland on Wayland sessions, and `setupAppMenu` used to return early off darwin, leaving the dapi installer unreachable behind Electron's stock menu. The release workflow gains a publish-linux job so the artifacts reach the same draft release; it carries no Apple secrets.
Four macOS assumptions kept the CLI from working off darwin: - `dapi open` launched the app with `open -a`, gated to darwin, so elsewhere it could only talk to an instance that was already up. Linux now starts the executable the packaged wrapper points at, then one on PATH, then hands `diffusion://` to xdg-open — the scheme the deb and rpm packages register. A failure still falls through to the socket and its existing error. - `dapi fonts` hard-failed off darwin. It now enumerates through fontconfig, mapping fc-list's weight axis onto CSS weights and collapsing the one-line-per-file listing into families; the `local()` sources it emits are the same shape the macOS path builds, so <text> and the browser resolve them unchanged. - The staged `cli/bin/dapi` wrapper hardcoded the .app bundle's Electron binary, so the shipped CLI could not run at all on Linux. It now picks the layout it sits in — the bundle path on macOS, byte for byte as before, or the flat packager tree beside `resources/`. - The in-app installer linked into /usr/local/bin behind an osascript admin prompt. On Linux it links into ~/.local/bin, which the user owns, so nothing has to be elevated.
A deb and an rpm cover Debian/Ubuntu and Fedora; everywhere else there was nothing to hand a user. An AppImage is one executable file that runs without root or a package manager, so it is the artifact for the rest. The maker needs `mksquashfs` on the build host, which the release workflow now installs. Both Linux packages and the AppImage share the FreeDesktop metadata, and both now name the executable explicitly: `bin` defaults to the sanitized package name (`diffusionstudio-desktop`), which is not what electron-packager emits. The in-app CLI installer refuses inside an AppImage rather than linking into it: an AppImage runs from a mount under /tmp that disappears with the process, so the link would dangle. The bundled dapi still works from a running AppImage, and the message names the two options that persist — the deb/rpm, or an unpacked AppImage.
cf92eda to
145b4b9
Compare
Dry-running the deb and rpm makers against the packaged tree, rather than trusting their config, turned up two builds that could never have succeeded, and comparing notes with diffusionstudio#42 turned up a third: - The deb had no `Maintainer`, one of the five fields dpkg-deb requires. It defaults to `package.json`'s `author`, which this workspace does not set, and the control template silently omits the field when it is undefined - so the release job would have failed on its first run. - `@electron-forge/maker-rpm` (electron-installer-redhat 3.4.0) writes a spec whose `%install` runs `cp -r usr/*` relative to the build directory. rpm 4.20 moved that directory, so the copy fails on Fedora 41+ and on recent Debian and Ubuntu (electron-userland/electron-installer-redhat#343, electron/forge#3701). Diagnosed in diffusionstudio#42, which worked around it with a hand-written spec; this patches the template through the patch-package setup the repo already runs, so the maker keeps generating dependencies and metadata. - MakerZIP shells out to `zip`, which is not installed everywhere. Named in the README with the other per-maker tools, and installed in CI. Arch Linux support comes from diffusionstudio#42: it has no Electron Forge maker, so a script stages the forge output and runs makepkg. The PKGBUILD drops the launcher shim that PR needed, because the Linux executable is renamed to `diffusion-studio` and /usr/bin can link straight to it, and it puts `dapi` on PATH directly, which a package can do and the AppImage cannot. All four formats now install one desktop entry, kept in packaging/linux so the makers and the PKGBUILD cannot drift: two of the three formats in diffusionstudio#42 registered `x-scheme-handler/diffusion` and the deb did not, which is the difference between the auth and checkout deep links working and silently doing nothing. The CLI install dialog now only claims `dapi` is ready to run when the link target is on the session PATH, and otherwise says which directory to add - Debian and Fedora put ~/.local/bin there, Arch does not. Co-authored-by: Tsurg <info@tsurg.com>
…nheriting node mode Two bugs, both found by installing the CLI from a real AppImage and then quitting it - which is what a user does. The install wrote a symlink into `resources` under the image's mount. That mount lives under /tmp only while the app runs and its name changes every launch, so the link dangled the moment the app quit. The image file is stable and can run the CLI on its own Electron in node mode, so the install now writes a wrapper that does that: the image mounts itself for the duration of each call. The path inside the mount is read from the running process rather than assumed, since the maker decides the layout. The wrapper runs the CLI with `ELECTRON_RUN_AS_NODE=1`, and a spawned app inherited it - starting with no main script and exiting without a window, so `dapi open` could never launch the app from any packaged install, deb and rpm included. Earlier tests missed it because the launch target was a shell stub, which ignores the variable. `spawnDetached` now drops it, along with the AppImage variables, so a launched image mounts itself afresh instead of reading this process's mount.
|
#42 covers deb, rpm, zip, AppImage and Arch. This PR overlaps it and goes wider (the export bug, the CLI, the app menu). Credit on the commit (Co-authored-by: Tsurg). Kept from this branch: makers over hand-written specs for deb/rpm/AppImage, so dependency lists, chrome-sandbox 4755, icons and lintian overrides come from the makers (only Arch, which has no maker, is a script); executableName: 'diffusion-studio' instead of working around the space per format; one desktop entry for all four formats (in #42 the rpm and Arch entries register diffusion:// and the deb's generated entry does not, which is the difference between deep links working and silently doing nothing on Debian/Ubuntu); and no ozone flag. Two notes sent back to #42: the missing MimeType above, and that its publish-arch job runs as root in archlinux:latest while makepkg refuses to run as root — the version here creates a builder user and runs the step under sudo -u builder. |
Linux support with fixes and packages like .appimage
Claude AI Pull Request
Linux support in six commits that can be taken separately. Built, packaged, launched and driven on Linux (KDE Plasma 6.7 Wayland, NVIDIA, Chromium/Electron with no system AAC encoder); every change is either platform-gated or a strict superset, so macOS behaviour is unchanged.
1.
fix(export): resolve the audio codec against what the platform can encodeThe bug that started this: every mp4 export fails on Linux. Video is fine — Chromium's bundled OpenH264 encodes AVC — but every template in
export-templates.tsasks foraudio: { codec: "aac" }and the encoder took that as a demand. In WebCodecs, AAC is the platform's encoder (AudioToolbox on macOS, Media Foundation on Windows), so Chromium on Linux has none, and the export dies after the video track has already started.The requested codec is now a preference. New
resolveAudioCodec(format, requested, options)in@diffusionstudio/encoderkeeps it when the container takes it and the browser can encode it, otherwise picks the container's next encodable choice — Opus, which mp4 accepts and Chromium bundles everywhere. Resolved at all three places an export starts:context/export.tsx(⌘E / the panel) — before the save picker, so the progress overlay names what is actually writtencontext/dapi/export.ts(dapi export) — before the render, so the echoedconfigis truthfulpackages/encoder/src/encoder.ts— last line of defence, so a project authored on macOS with"codec": "aac"inpackage.jsonexports on Linux uneditedThe audio picker now offers only codecs this machine can encode into the chosen container.
createOutputFormatlost itsTargetBufferparameter and takes{ fastStart }, so container support is queryable without a write target.Verified in a real Chromium:
canEncodeAudio('aac')→false,resolveAudioCodec('mp4','aac')→opus, and an mp4 muxed through the sameAudioSampleSource/CanvasSourcepath reads back asavc+opuswith decodable audio. Where AAC exists it is encodable, so it stays what gets used.2.
feat(desktop): package and run on Linuxmakershad onlyMakerZIP(['darwin'])and the DMG, so a Linux checkout could bepackaged but nevermaked. AddsMakerDebandMakerRpmfor['linux']plus a Linux ZIP, and scopes the DMG to['darwin'].The Linux build renames the executable to
diffusion-studio: electron-packager keeps the space fromname, fine inside a.appbut not for the binary the packages symlink into/usr/bin. Gated to Linux, so macOS still producesContents/MacOS/Diffusion Studio— the name the CLI wrapper and the signing pass both address.setupAppMenu()returned early off darwin, so "Install dapi Command Line Tool…" was unreachable behind Electron's stock menu; there is now a non-macOS template carrying it under File, with no AppKit-only roles.No ozone/Wayland flag, and that is deliberate. I tried
--ozone-platform-hint=autofirst, since the Wayland/Vulkan line in the log invites it. It makes the packaged app unusable here: the window comes up blank or with every glyph missing, while a DevTools capture of the same renderer is pixel-correct — the renderer paints and Wayland presentation drops it ('--ozone-platform=wayland' is not compatible with Vulkan). Chromium's XWayland default renders correctly. Electron already readsELECTRON_OZONE_PLATFORM_HINT=autofrom the environment, so anyone whose driver handles native Wayland can opt in; the code says why it is not the default.release.ymlgains apublish-linuxjob onubuntu-latest(installingzip,dpkg-dev,fakeroot,rpm,squashfs-tools) so artifacts land on the same draft release. It carries no Apple secrets. Drop this hunk if you'd rather not build Linux in CI yet.Left alone deliberately:
build-native.mjsexiting 0 off darwin (AppKit addon),projects.ts'sfileProviderDomainsreturning[]off darwin (File Provider is a macOS service), andupdate-electron-app, which already no-ops on Linux.3.
feat(cli): make dapi work on Linuxdapi opencould not launch the app —launchAppshelled out toopen -abehind a darwin gate. Linux now spawns the executable the packaged wrapper points at (DIFFUSION_APP_PATH), then one onPATH, then handsdiffusion://toxdg-open; failure still falls through to the socket and its existing error.dapi fontshard-failed off darwin — now enumerates through fontconfig, mappingfc-list's weight axis onto CSS weights and collapsing the one-line-per-file listing into families. 304 families here, filters intact, and thelocal()sources it emits load in Chromium viaFontFace. Without fontconfig it says so and names the package.cli/bin/dapiwrapper hardcoded$DIR/../../../MacOS/Diffusion Studio— it now picks the layout it sits in: the bundle path on macOS, byte for byte as before, or the flat packager tree besideresources/. Tested against both simulated layouts, the symlink-on-PATH case, and the missing-executable error./usr/local/binbehind anosascriptadmin prompt; on Linux it links~/.local/bin, which the user owns, so nothing is elevated.4.
feat(desktop): build an AppImage as welldeb and rpm cover Debian/Ubuntu and Fedora; everywhere else there was nothing to hand a user. Adds
@reforged/maker-appimage(Forge has no first-party AppImage maker) — one executable file, no root, no package manager. Both makers now setbinexplicitly, because it defaults to the sanitized package name (diffusionstudio-desktop) rather than what electron-packager emits, and the AppImage build fails outright without it.Flatpak is not here on purpose: the sandbox fights what this app does — editing arbitrary project folders, spawning agent CLIs and
esbuild/Babel, puttingdapionPATH. Making it work means--filesystem=hostplus no CLI install, a worse deal than the AppImage. Happy to add it if you disagree.5.
feat(desktop): package for Arch, and fix what a real package build hitsDry-running the makers against the packaged tree — rather than trusting their config — turned up two builds that could never have succeeded, and comparing notes with #42 turned up a third:
Maintainer, one of the five fields dpkg-deb requires. It defaults topackage.json'sauthor, which this workspace does not set, and the control template omits the field silently — the release job would have failed on its first run. Caught by staging the deb tree withelectron-installer-debian's own installer class and reading the renderedDEBIAN/control, nodpkg-debneeded.@electron-forge/maker-rpmis broken on rpm ≥ 4.20. electron-installer-redhat 3.4.0 writes a spec whose%installrunscp -r usr/*relative to the build directory, which rpm 4.20 moved — it fails on Fedora 41+ and recent Debian/Ubuntu (Error while creating package on Fedora 41: cp: cannot stat 'usr/*': No such file or directory electron-userland/electron-installer-redhat#343, Failed to make RPM package on Ubuntu and Fedora 41 electron/forge#3701, both open). Diagnosed in Add Linux packaging (deb, rpm, AppImage, Arch) and CI #42. Fixed here through thepatch-packagesetup this repo already runs, so the maker keeps generating dependencies and metadata instead of the spec being hand-maintained.zip(cross-zip), which is not installed everywhere —spawn zip ENOENThere. Named in the README with the other per-maker tools and installed in CI.Arch Linux support comes from #42. Arch has no Forge maker, so a script stages the forge output and runs
makepkg, with apublish-archcontainer job. Adapted: the launcher shim that PR needed is gone, because the executable is renamed and/usr/bincan link straight to it; theirln -s … resources/cli/bin/dapiis kept, since a package can putdapion PATH.All four formats now install one desktop entry, in
packaging/linux/, so the makers and the PKGBUILD cannot drift. It registersx-scheme-handler/diffusion— how the auth and checkout deep links come back to the app, the jobpackagerConfig.protocolsdoes on macOS.6.
fix(cli): install a working dapi from an AppImage, and stop the app inheriting node modeTwo bugs found by installing the CLI from a real AppImage and then quitting it, which is what a user does:
/tmponly while the app runs, with a fresh random name each launch, so the link dangled the moment the app quit. The image file is stable and can run the CLI on its own Electron in node mode, so the install now writes a wrapper that does exactly that — the image mounts itself for the duration of each call. The path inside the mount is read from the running process, not assumed, since the maker decides the layout.dapi opencould never launch the app from any packaged install, deb and rpm included. The wrapper runs the CLI withELECTRON_RUN_AS_NODE=1and the spawned app inherited it, so it started with no main script and exited without a window. Earlier tests missed this because the launch target was a shell stub, which ignores the variable.spawnDetachednow drops it, along with the AppImage variables.Relationship to #42 (@Tsurgcom)
#42 landed first and covers packaging: deb, rpm, zip, AppImage and Arch. This PR overlaps it and goes wider (the export bug, the CLI, the app menu), so rather than leave two competing branches I folded the parts of #42 that are better than what I had into this one, credited on the commit (
Co-authored-by: Tsurg). Maintainers: prefer #42's authorship where the work is theirs — I'm happy to rebase this onto it instead if that merges more easily.Taken from #42: Arch support (adapted as above), the rpm ≥ 4.20 diagnosis — which would have broken my rpm build in CI and I had missed entirely — and
StartupWMClassin the desktop entry.Kept from this branch: makers over hand-written specs for deb/rpm/AppImage, so dependency lists,
chrome-sandbox4755, icons and lintian overrides come from the makers (only Arch, which has no maker, is a script);executableName: 'diffusion-studio'instead of working around the space per format; one desktop entry for all four formats (in #42 the rpm and Arch entries registerdiffusion://and the deb's generated entry does not, which is the difference between deep links working and silently doing nothing on Debian/Ubuntu); and no ozone flag.Two notes sent back to #42: the missing
MimeTypeabove, and that itspublish-archjob runs as root inarchlinux:latestwhilemakepkgrefuses to run as root — the version here creates abuilderuser and runs the step undersudo -u builder.What was actually built and run
Diffusion Studio-linux-x64(electron-packager)diffusion-studioresources/cli/bin/dapi--versionandfontsrun on the packaged Electron in node modeDiffusion Studio-0.204.1-x64.AppImage(128 MB)MimeType=x-scheme-handler/diffusion. Install-CLI driven through the realcli:installchannel, then the app quit: the wrapper survives,dapi --version→0.204.1,dapi fontsworks, anddapi openrelaunches the AppImage and opens the projectdiffusion-studio-0.204.1-1-x86_64.pkg.tar.zst(Arch)makepkg; root-owned,chrome-sandbox-rwsr-xr-x, both/usr/binsymlinks, shared desktop entry,dapi --version→0.204.1from the extracted tree.deb/.rpmdpkg-deb/rpmbuildand no root to install them.DEBIAN/controland the.specare verified by rendering them through the makers' own code; CI is the first archive buildAbsent-dependency paths, tested rather than assumed: no FUSE/
fusermount→ the AppImage prints the FUSE hint and--appimage-extract-and-runstarts the app; nofontconfig→dapi fontsnames the package to install, exit 1; noxdg-openwith the app down →dapi opensays "launch the app first", exit 1, no crash and no false success. GTK3/NSS/libcups are declared as debDependsand Archdepends. The binary floor is Electron's, not this host's: the executable references at mostGLIBC_2.25, so glibc ≥ 2.25 (Ubuntu 18.04+, Debian 10+, RHEL 8+).~/.local/binis on PATH on Debian and Fedora but not Arch, so the install dialog only claimsdapiis ready when the link target is on the session PATH, and otherwise names the directory to add.Checks
npm run checkandnpm run lintpass for every workspace touched (apps/web,apps/cli,apps/desktop,packages/encoder). Two pre-existing failures in this environment are unrelated and untouched:packages/assets/src/browser.tsshowSaveFilePicker(present before these commits) andexamples/missing optional deps (three,redraw,@typegpu/noise).Not covered: Windows. The AAC resolution helps it for free, but nothing else here was built or tested there.