feat(openshot): add OpenShot Video Editor (org.openshot.OpenShot) (#293)

OpenShot ships its Linux build only as an AppImage — no .deb, .rpm or plain
tarball — which FlatPark's playbook has so far treated as a hard stop. It is
not one: a type-2 AppImage is an ELF stub with a SquashFS image appended, and
that image unpacks directly without the AppImage ever being executed or
FUSE-mounted. This is the first app to use that route.

Packaging:
- extra-data, org.freedesktop.Platform//25.08. The AppImage is fully
  self-contained (its own Python 3.8, PyQt5, libopenshot, FFmpeg, Blender), so
  no KDE/GNOME runtime is needed — verified nothing in the bundle resolves
  against the runtime's Qt/Python, and the bundle ships no libGL/EGL/fontconfig
  to shadow it.
- Second extra-data source: flatpark/prebuilt's appimage-tools-v1
  (appimage-offset + unsquashfs, 54 KB). apply_extra computes the SquashFS
  offset from the ELF header and unpacks with unsquashfs -o; the AppImage and
  the extractor are deleted afterwards. Passes check-apply-extra.sh (root, all
  caps dropped, offline, /usr + /app/extra only).
- Wrapper execs the AppDir's own usr/bin/openshot-qt-launch and pins
  QT_QPA_PLATFORM=xcb: the bundle ships the Qt5 wayland plugins but not the
  libQt5WaylandClient.so.5 they link, so wayland cannot initialise and Qt does
  not fall back on its own. Runs through XWayland.

Sandbox: ipc, x11, dri, pulseaudio, network (update check / title templates /
AI model download), xdg-videos/pictures/music/download for project footage, and
--persist=.openshot_qt so OpenShot's state dir lands in the per-app directory
rather than tmpfs or the real home. No --filesystem=home. dangerous_permissions
is empty.

OpenShot is on Flathub (currently 3.5.1, behind upstream's 4.0.0); this entry
is primarily the AppImage-extraction mechanism proven end to end.


Claude-Session: https://claude.ai/code/session_016F22CmZ197QR9hW3qiVxQC

Co-authored-by: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Komh
2026-09-01 13:51:35 +08:00
committed by GitHub
co-authored by Claude Sonnet 5
parent ae12c30eab
commit 94bb4f5ec9
9 changed files with 337 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
#!/bin/sh
set -eu
# Runs offline at install time inside org.freedesktop.Platform. Upstream ships
# OpenShot for Linux ONLY as an AppImage — no .deb, no .rpm, no plain tarball —
# so this repackages that. An AppImage is not a special format: a type-2 one is
# an ELF stub with a SquashFS filesystem concatenated after it. It never has to
# be executed or FUSE-mounted to read it; the appended filesystem is unpacked
# directly.
#
# Two tools do that, both from the appimage-tools extra-data fetched alongside
# the AppImage:
# appimage-offset prints the byte offset where the SquashFS begins
# unsquashfs unpacks from that offset (gzip/xz/lz4/zstd; OpenShot's is
# gzip)
#
# The result is the AppDir, staged whole at a stable path the wrapper execs:
# /app/extra/openshot. Its layout is AppRun + usr/, and usr/bin holds a
# self-contained tree — the openshot-qt launcher, its bundled Python/PyQt5/
# libopenshot/FFmpeg .so files, Qt plugins, and openshot-qt-launch, the vendor's
# own wrapper that sets LD_LIBRARY_PATH / QT_PLUGIN_PATH relative to itself.
#
# The desktop file, icon, MIME definition and AppStream metainfo are shipped by
# the manifest at *build* time — extra-data is fetched later on the user's
# machine, so anything Flatpak must export cannot come from here.
extra_root="${EXTRA_ROOT:-/app/extra}"
cd "$extra_root"
[ -f openshot.AppImage ] || { echo "missing extra-data: openshot.AppImage" >&2; exit 1; }
[ -f appimage-tools.tar.xz ] || { echo "missing extra-data: appimage-tools.tar.xz" >&2; exit 1; }
# --no-same-owner: on a system-wide install Flatpak runs apply_extra as root
# with every capability dropped, so restoring an archive's recorded uid/gid
# fails and aborts the unpack even though every member extracted fine. Both the
# tarball below and unsquashfs (via -no-xattrs, and it never chowns) are safe
# under that constraint.
bsdtar --no-same-owner -xf appimage-tools.tar.xz
tools="$extra_root/appimage-tools/bin"
[ -x "$tools/appimage-offset" ] && [ -x "$tools/unsquashfs" ] \
|| { echo "appimage-tools stack incomplete" >&2; exit 1; }
offset="$("$tools/appimage-offset" openshot.AppImage)"
case "$offset" in
''|*[!0-9]*) echo "appimage-offset did not return a number: '$offset'" >&2; exit 1 ;;
esac
rm -rf openshot
# -no-xattrs: the apply_extra sandbox has all capabilities dropped and cannot
# set security xattrs; -d writes straight to the final path, no rename needed.
"$tools/unsquashfs" -no-xattrs -o "$offset" -d openshot openshot.AppImage
[ -x openshot/usr/bin/openshot-qt-launch ] || { echo "openshot-qt-launch missing after unpack" >&2; exit 1; }
[ -x openshot/usr/bin/openshot-qt ] || { echo "openshot-qt binary missing after unpack" >&2; exit 1; }
# Drop the 254 MB AppImage and the extractor now that the tree is staged —
# otherwise both sit on the user's disk for the life of the install.
rm -rf openshot.AppImage appimage-tools.tar.xz appimage-tools
@@ -0,0 +1,23 @@
id: org.openshot.OpenShot
name: OpenShot Video Editor
summary: An easy to use, quick to learn, and surprisingly powerful video editor
website: https://www.openshot.org/
source_url: https://github.com/OpenShot/openshot-qt
build:
manifest: org.openshot.OpenShot.yml
branch: stable
mode: extra-data
catalog:
category: AudioVideo
upstream_approved: false
tags:
- Video
- Editor
- Movie
- Timeline
update:
command: ./resolve-update.sh
policy:
proprietary: false
extra_data_first: true
dangerous_permissions: []
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
# OpenShot's AppImage already carries the right launch logic in
# usr/bin/openshot-qt-launch: it sets LD_LIBRARY_PATH and QT_PLUGIN_PATH to the
# bundled tree, selects the xdg-desktop-portal platform theme for native file
# dialogs, and points OPENSSL_CONF at /dev/null (the bundle's OpenSSL predates
# the host config format). Repackaging only has to call it.
#
# realpath inside that script resolves to /app/extra/openshot/usr/bin, so the
# bundled Python, PyQt5, libopenshot and Qt plugins are all found there without
# anything set here.
#
# QT_QPA_PLATFORM=xcb: the bundle ships the Qt 5 wayland plugins without the
# libQt5WaylandClient.so.5 they need, so "wayland" cannot initialise and Qt does
# not fall back on its own — it just exits. Pinning xcb runs it through XWayland.
# Respect an explicit override so someone who has supplied the missing library
# can still try wayland.
: "${QT_QPA_PLATFORM:=xcb}"
export QT_QPA_PLATFORM
exec /app/extra/openshot/usr/bin/openshot-qt-launch "$@"
@@ -0,0 +1,15 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=OpenShot Video Editor
GenericName=Video Editor
Comment=Create and edit amazing videos and movies
Exec=openshot-qt %F
Icon=org.openshot.OpenShot
Terminal=false
Categories=AudioVideo;AudioVideoEditing;Video;
Keywords=video;editor;movie;clip;film;timeline;
StartupNotify=true
StartupWMClass=openshot-qt
X-GNOME-UsesNotifications=true
MimeType=application/vnd.openshot-qt-project;
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>org.openshot.OpenShot</id>
<launchable type="desktop-id">org.openshot.OpenShot.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
<name>OpenShot Video Editor</name>
<summary>An easy to use, quick to learn, and surprisingly powerful video editor</summary>
<developer id="org.openshot">
<name>OpenShot Studios, LLC</name>
</developer>
<url type="homepage">https://www.openshot.org/</url>
<url type="bugtracker">https://github.com/OpenShot/openshot-qt/issues</url>
<url type="help">https://www.openshot.org/user-guide/</url>
<url type="donation">https://www.openshot.org/donate/</url>
<url type="vcs-browser">https://github.com/OpenShot/openshot-qt</url>
<description>
<p>This is a community package of OpenShot, maintained independently and unaffiliated with the OpenShot project. It repackages the official upstream AppImage unmodified — extracted at install time, not rebuilt — and follows upstream releases automatically.</p>
<p>OpenShot Video Editor is a free, open-source video editor with an easy-to-use interface and a broad feature set for cutting, compositing and animating video.</p>
<ul>
<li>Supports most video, audio and image formats (based on FFmpeg).</li>
<li>Advanced timeline with drag and drop, snapping, scrolling, panning and zooming.</li>
<li>Clip trimming, alpha, scaling, rotation, shearing and curve-based keyframe animation (linear, Bézier, constant).</li>
<li>Compositing, image overlays, watermarks and transparency, with unlimited tracks and layers.</li>
<li>Video transitions, masks and wipes; brightness, gamma, hue and chroma-key effects.</li>
<li>Animated 3D titles via a bundled Blender, plus editable SVG titles and credits.</li>
<li>Audio mixing, waveforms and editing; time mapping and speed changes.</li>
<li>Hardware-accelerated encode and decode; EDL and Final Cut Pro import/export.</li>
</ul>
<p>Permissions: this package grants read-write access to your Videos, Pictures, Music and Downloads folders, because an OpenShot project (.osp) references its source clips by absolute path and they have to stay reachable when the project is reopened — per-file portal access cannot carry a project across sessions. App state (settings, caches, project backups) stays inside the sandbox. Footage kept elsewhere needs an <code>flatpak override</code>:</p>
<ul>
<li>Footage or projects in a dedicated folder under home: <code>flatpak override --user --filesystem=~/Projects org.openshot.OpenShot</code> (name your own folder), or <code>--filesystem=home</code> for all of it.</li>
<li>Footage on an external drive or another mount point: <code>flatpak override --user --filesystem=/run/media/$USER org.openshot.OpenShot</code> (use the path your system mounts removable media under).</li>
<li>Work fully offline with no update check or template/model downloads: <code>flatpak override --user --unshare=network org.openshot.OpenShot</code></li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<image>https://www.openshot.org/static/img/gallery/ui-example.jpg</image>
<caption>Editing a project on the OpenShot timeline</caption>
</screenshot>
<screenshot>
<image>https://www.openshot.org/static/img/gallery/title-editor.jpg</image>
<caption>The title editor</caption>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
<categories>
<category>AudioVideo</category>
<category>AudioVideoEditing</category>
</categories>
<keywords>
<keyword>video</keyword>
<keyword>editor</keyword>
<keyword>movie</keyword>
<keyword>film</keyword>
<keyword>timeline</keyword>
<keyword>FFmpeg</keyword>
</keywords>
<releases>
<release version="4.0.0" date="2026-07-25"/>
</releases>
</component>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/vnd.openshot-qt-project">
<comment>OpenShot project</comment>
<glob pattern="*.osp"/>
<generic-icon name="openshot-qt-doc"/>
</mime-type>
</mime-info>
Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

@@ -0,0 +1,115 @@
id: org.openshot.OpenShot
runtime: org.freedesktop.Platform
runtime-version: "25.08"
sdk: org.freedesktop.Sdk
command: openshot-qt
separate-locales: false
build-options:
no-debuginfo: true
finish-args:
# OpenShot's Linux build is a fully self-contained AppImage: it brings its own
# Python 3.8, PyQt5, libopenshot, a complete FFmpeg, and a bundled Blender for
# the 3D title templates. Nothing Qt or Python comes from the runtime, so this
# is the freedesktop base rather than a KDE or GNOME one. Verified against the
# bundle's NEEDED set: everything it does not carry — the display, GL, X11 /
# Wayland, fontconfig, NSS and audio stacks — org.freedesktop.Platform 25.08
# provides, and the bundle deliberately does NOT ship libGL / libEGL / libgbm
# / fontconfig, so there is nothing to shadow the runtime's.
- --share=ipc
# X11 only. OpenShot's AppImage ships the Qt 5 wayland platform plugins but
# not the libQt5WaylandClient.so.5 they link, so the bundle can only bring up
# the xcb platform; the wrapper pins QT_QPA_PLATFORM=xcb and it runs through
# XWayland on a Wayland session. --socket=fallback-x11 would withhold the X
# socket whenever a compositor is present, which is exactly when it is needed
# here, so this is a plain --socket=x11.
- --socket=x11
# A Wayland host session exports QT_QPA_PLATFORM=wayland, which flatpak
# forwards into the sandbox and which would beat the wrapper's xcb default.
# Drop it here; the wrapper then sets xcb, and `flatpak override
# --env=QT_QPA_PLATFORM=…` still wins for anyone who wants to try otherwise.
- --unset-env=QT_QPA_PLATFORM
# Real-time video preview and the OpenGL compositor; also VA-API / NVDEC for
# hardware-accelerated decode and encode.
- --device=dri
# Preview playback and audio scrubbing.
- --socket=pulseaudio
# Update check, downloadable title templates, and the model download for the
# AI effects (object detection / motion tracking). OpenShot is otherwise a
# local editor; withholding this only disables those.
- --share=network
# OpenShot edits a *project*: an .osp file plus source clips (video, audio,
# images) referenced by absolute path, so re-opening a project needs
# persistent read access to wherever that footage lives — the file-chooser
# portal's per-file grants cannot carry a project across sessions. These are
# the standard media locations; footage kept elsewhere (a dedicated projects
# folder, an external drive) is an opt-in `flatpak override` documented in the
# metainfo. NOT --filesystem=home: OpenShot writes all of its own state to
# ~/.openshot_qt (classes/info.py: USER_PATH = expanduser("~") +
# "/.openshot_qt"), and granting home or host would repoint $HOME at the real
# home and create that directory there instead of in the sandbox. With only
# these xdg subdirectories granted, $HOME stays the per-app directory and the
# state lives under ~/.var/app/org.openshot.OpenShot.
- --filesystem=xdg-videos
- --filesystem=xdg-pictures
- --filesystem=xdg-music
- --filesystem=xdg-download
# Flatpak does not redirect $HOME — it stays /home/<user> and, with no home
# grant, is a tmpfs that is discarded on exit. OpenShot puts everything under
# ~/.openshot_qt (expanduser("~") in classes/info.py), so without this its
# settings, keyframe presets, project backups and the autosave file would not
# survive a restart. --persist binds ~/.openshot_qt to
# ~/.var/app/org.openshot.OpenShot/.openshot_qt — persistent, and inside the
# sandbox rather than in the user's real home. (This works precisely because
# no broader --filesystem=home mount is present to shadow it.)
- --persist=.openshot_qt
# "Export complete" / error notifications.
- --talk-name=org.freedesktop.Notifications
modules:
- name: openshot
buildsystem: simple
build-commands:
- install -Dm755 apply_extra.sh /app/bin/apply_extra
- install -Dm755 openshot-wrapper /app/bin/openshot-qt
- install -Dm644 org.openshot.OpenShot.desktop /app/share/applications/org.openshot.OpenShot.desktop
- install -Dm644 org.openshot.OpenShot.metainfo.xml /app/share/metainfo/org.openshot.OpenShot.metainfo.xml
# OpenShot's own MIME definition for .osp projects, so the exported desktop
# entry can match them on the host.
- install -Dm644 org.openshot.OpenShot.mime.xml /app/share/mime/packages/org.openshot.OpenShot.xml
- install -Dm644 org.openshot.OpenShot.png /app/share/icons/hicolor/512x512/apps/org.openshot.OpenShot.png
sources:
# The two-tool stack that lets apply_extra open the AppImage offline with
# no libfuse: appimage-offset prints the appended-SquashFS byte offset and
# unsquashfs unpacks from it. Fetched next to the AppImage into /app/extra
# rather than staged at /app/bin, because a system-wide install runs
# apply_extra in a sandbox that binds only the runtime /usr and /app/extra
# (scripts/check-apply-extra.sh reproduces this). Kept OUTSIDE the managed
# block: update-pins.mjs rebuilds that block from the resolver output and
# would drop anything else in it.
- type: extra-data
filename: appimage-tools.tar.xz
only-arches:
- x86_64
url: https://github.com/flatpark/prebuilt/releases/download/appimage-tools-v1/appimage-tools-appimage-tools-v1-fd-25.08-x86_64.tar.xz
sha256: fdbaf3b8dfa1e9705c644dafeb25b6afcd7890af36ae23744e58130beb48396b
size: 54752
# BEGIN MANAGED EXTRA-DATA
- type: extra-data
filename: openshot.AppImage
only-arches:
- x86_64
url: https://github.com/OpenShot/openshot-qt/releases/download/v4.0.0/OpenShot-v4.0.0-x86_64.AppImage
sha256: 277b27aae97080a1dae8831be8c6d2c576377292f559fb1b94675f6db24aa70f
size: 254457024
# END MANAGED EXTRA-DATA
- type: file
path: apply_extra.sh
- type: file
path: openshot-wrapper
- type: file
path: org.openshot.OpenShot.desktop
- type: file
path: org.openshot.OpenShot.metainfo.xml
- type: file
path: org.openshot.OpenShot.mime.xml
- type: file
path: org.openshot.OpenShot.png
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Update resolver for OpenShot.
#
# Prints the current version + the Linux x86_64 AppImage as JSON on stdout:
# { "version": "4.0.0", "releaseDate": "YYYY-MM-DD",
# "sources": [ { "filename": "openshot.AppImage", "url": "..." } ] }
# Logs go to stderr. No hashing, no manifest rewriting — FlatPark downloads the
# URL and computes the extra-data sha256/size at build time. The version is
# compared against the latest <release> in the AppStream metainfo.
#
# Only openshot.AppImage is resolved here; appimage-tools.tar.xz is pinned
# by hand outside the managed block in the manifest.
set -euo pipefail
repo="OpenShot/openshot-qt"
need() { command -v "$1" >/dev/null 2>&1 || { echo "missing command: $1" >&2; exit 1; }; }
need curl; need jq
# releases/latest excludes prereleases and drafts, so this tracks the stable
# channel OpenShot publishes its AppImage on.
rel="$(curl -fsSL ${GITHUB_TOKEN:+-H "Authorization: Bearer $GITHUB_TOKEN"} \
"https://api.github.com/repos/$repo/releases/latest")"
version="$(jq -r '.tag_name | ltrimstr("v")' <<<"$rel")"
date="$(jq -r '.published_at' <<<"$rel" | cut -c1-10)"
# The lone x86_64 AppImage asset: OpenShot-v<version>-x86_64.AppImage (the other
# assets are the .exe / .dmg installers and the .torrent / .sha256sum sidecars).
url="$(jq -r '.assets[] | select(.name | test("-x86_64\\.AppImage$")) | .browser_download_url' <<<"$rel" | head -n1)"
[ -n "$version" ] && [ -n "$url" ] || { echo "failed to resolve openshot release" >&2; exit 1; }
echo "resolved openshot $version ($date): $url" >&2
jq -n --arg v "$version" --arg d "$date" --arg u "$url" \
'{version:$v, releaseDate:$d, sources:[{filename:"openshot.AppImage", url:$u}]}'