diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 371c6150..955ea287 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,9 @@ on: pull_request: branches: [main, master, linux, linux-2.0] -# Keep CI read-only and free of secrets; release verification scans untrusted changes. +# No secrets are used anywhere in this workflow, and none should ever be added. +# The release-verification job exists partly to catch a credential that made it +# into the tree; giving that job access to credentials would be self-defeating. permissions: contents: read @@ -15,6 +17,10 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +defaults: + run: + shell: bash + jobs: # Python — the UI and its tests, on both platforms that ship a build. python: @@ -38,6 +44,7 @@ jobs: cache-dependency-path: | requirements-alpha.txt requirements-dev.txt + requirements-uploader.txt - name: Install system dependencies (Linux) if: runner.os == 'Linux' @@ -52,9 +59,11 @@ jobs: - name: Install pinned dependencies # The alpha lock is the ONLY install path. If it does not resolve on a # supported interpreter, that is a release bug, not a CI inconvenience. + # The uploader set is installed too: tests/test_sftp_provider.py + # imports paramiko from the optional uploader sources. run: | python -m pip install --upgrade pip - python -m pip install -r requirements-alpha.txt -r requirements-dev.txt + python -m pip install -r requirements-alpha.txt -r requirements-dev.txt -r requirements-uploader.txt - name: Assert the environment matches the lock # Catches a transitive dependency that resolved to something other than @@ -103,11 +112,10 @@ jobs: QT_QPA_PLATFORM: offscreen run: | if [ "${{ runner.os }}" = "Linux" ]; then + # Every Linux test runs here; test_capture_backend_detection uses + # the engine built in "Prepare Linux test artifacts" (FFmpeg on + # LD_LIBRARY_PATH). Only Windows-only suites are left out. python -m pytest tests/ -v --tb=short \ - --ignore=tests/test_linux_appimage_report.py \ - --ignore=tests/test_linux_runtime.py \ - --ignore=tests/test_linux_tools.py \ - --ignore=tests/test_capture_backend_detection.py \ --ignore=tests/test_windows_monitor.py \ --ignore=tests/test_windows_native_hotkeys.py \ --ignore=tests/test_windows_autostart.py \ @@ -214,6 +222,7 @@ jobs: cache-dependency-path: | requirements-alpha.txt requirements-dev.txt + requirements-uploader.txt - uses: microsoft/setup-msbuild@v2 @@ -239,6 +248,7 @@ jobs: } - name: Build (Release | x64) + shell: pwsh run: > msbuild FTHRcapture\FTHRcapture.sln -p:Configuration=Release @@ -263,18 +273,23 @@ jobs: "engine OK, x64" - name: Install pinned bundle dependencies + # requirements-uploader.txt is what the sealed uploader executable is + # frozen from. Without it PyInstaller silently builds a package that + # fails at runtime with "No module named 'paramiko'". run: | python -m pip install --upgrade pip - python -m pip install -r requirements-alpha.txt -r requirements-dev.txt + python -m pip install -r requirements-alpha.txt -r requirements-dev.txt -r requirements-uploader.txt python -m pip check - name: Build sealed optional upload packages run: python tools/build_optional_uploaders.py - name: Test built optional upload packages + # The python matrix has no sealed package, so the frozen-executable + # tests (including a real SFTP round trip) run here, after the build. run: > - python -m pytest tests/test_uploader_plugin_boundary.py - -k "test_built_" --tb=short + python -m pytest tests/test_uploader_plugin_boundary.py tests/test_sftp_provider.py + -k "test_built_ or test_frozen_" --tb=short - name: Build and verify the Windows bundle shell: pwsh @@ -391,6 +406,7 @@ jobs: cache-dependency-path: | requirements-alpha.txt requirements-dev.txt + requirements-uploader.txt - name: Install packaging dependencies run: | @@ -405,7 +421,7 @@ jobs: libxext-dev libxfixes-dev libwayland-dev wayland-protocols libwayland-bin \ libpipewire-0.3-dev libdbus-1-dev python -m pip install --upgrade pip - python -m pip install -r requirements-alpha.txt -r requirements-dev.txt + python -m pip install -r requirements-alpha.txt -r requirements-dev.txt -r requirements-uploader.txt python -m pip check - name: Build AppImage @@ -421,7 +437,9 @@ jobs: python tools/verify_linux_glibc.py \ --manifest tools/linux_release_baseline.json \ --target build_output/AppDir \ - --target "${image[0]}" + --target "${image[0]}" \ + --target build/optional-uploader-linux/dist/FTHR-Uploader \ + --target build/optional-uploader-linux/payload-elf - name: Upload verified Linux AppImage uses: actions/upload-artifact@v4 with: diff --git a/BUILDING.md b/BUILDING.md index 4a3d5416..bfbfeb83 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -23,6 +23,15 @@ python -m pip install -r requirements-alpha.txt -r requirements-dev.txt `requirements-alpha.txt` is the lock and the only file a release build installs from. +Building the optional uploader package (`tools/build_optional_uploaders.py` on +Windows, `tools/build_linux_uploader.py`, which `build_linux.sh` runs, on +Linux) also needs its own lock in the same environment. Both builders refuse +to run without it: + +```bash +python -m pip install -r requirements-uploader.txt +``` + > **Do not install `imageio-ffmpeg`.** It bundles a GPLv3 FFmpeg build. Having it > in the environment risks PyInstaller pulling it into the bundle and placing the > whole artifact under the GPL (AUDIT-005). It is excluded in `FTHR.spec` and diff --git a/FTHR.spec b/FTHR.spec index 486dbcf0..9a6158e6 100644 --- a/FTHR.spec +++ b/FTHR.spec @@ -204,15 +204,22 @@ import json as _json _REVIEWED_QT = _json.loads((ROOT / 'tools' / 'qt_runtime_manifest.json').read_text()) _APPROVED_QT_DLLS = {f'qt6{module.lower()}.dll' for module in _REVIEWED_QT['approved_runtime_qt_modules']['windows']} +# Destination of the PySide6 wheel's QML import tree as PyInstaller's Qt hooks +# lay it out ('PySide6/qml/' for the Windows wheel). The filter must be +# anchored here: the application's own scenes live under ui/qml/ and a bare +# '/qml/' match drops VideoPreview.qml, Snapshot.qml and color.frag.qsb. +from PyInstaller.utils.hooks.qt import pyside6_library_info as _pyside6_info # noqa: E402 +_QT_QML_DEST = (Path(_pyside6_info.qt_rel_dir) / 'qml').as_posix().casefold() + '/' +_KEPT_QML_DIRS = ('qtqml', 'qtqml/models', 'qtqml/workerscript', + 'qtquick', 'qtmultimedia') def _keep_reviewed_qt_runtime(entry): dest = str(entry[0]).replace('\\', '/').casefold() name = dest.rsplit('/', 1)[-1] - if '/qml/' in dest: - qml_parent = dest.split('/qml/', 1)[1].rsplit('/', 1)[0] - if qml_parent not in ('qtqml', 'qtqml/models', 'qtqml/workerscript', - 'qtquick', 'qtmultimedia'): + if dest.startswith(_QT_QML_DEST): + qml_parent = dest[len(_QT_QML_DEST):].rpartition('/')[0] + if qml_parent not in _KEPT_QML_DIRS: return False # Qt on supported Windows versions resolves the operating system ICU. # Never collect unrelated ICU copies which happen to be on PATH: a newer diff --git a/FTHR_linux.spec b/FTHR_linux.spec index 263f0dd2..7c19b8c3 100644 --- a/FTHR_linux.spec +++ b/FTHR_linux.spec @@ -155,6 +155,7 @@ a = Analysis( (str(ASSETS_DIR / 'fthr_logo.png'), 'assets'), (str(ASSETS_DIR / 'preview_desktop.png'), 'assets'), (str(ASSETS_DIR / 'gary.png'), 'assets'), + (str(ASSETS_DIR / 'gatze.png'), 'assets'), (str(PLUGIN_BUNDLE), 'plugin-packages'), # Licence paperwork must travel INSIDE the bundle (AUDIT-005), so a # portable copy is as complete as an installed one. @@ -170,11 +171,21 @@ a = Analysis( *_asset_tree(ASSETS_DIR / 'fonts', 'assets/fonts'), *_asset_tree(ASSETS_DIR / 'icons', 'assets/icons'), *_asset_tree(ASSETS_DIR / 'sounds', 'assets/sounds'), + # The capture card runs this file in a child interpreter, and the + # editor loads its QML scenes and precompiled shader (color.frag.qsb) + # from beside ui/quick_*.py at runtime. + (str(UI_DIR / 'ui' / 'capture_card_process.py'), 'ui'), + *_asset_tree(UI_DIR / 'ui' / 'qml', 'ui/qml'), ], hiddenimports=[ 'PySide6.QtDBus', 'PySide6.QtMultimedia', 'PySide6.QtMultimediaWidgets', + # The 2.0 editor renders previews and snapshots in QQuickWidget scenes. + 'PySide6.QtQuick', + 'PySide6.QtQuickWidgets', + 'PySide6.QtQml', + 'PySide6.QtNetwork', 'sounddevice', 'numpy', 'cv2', @@ -202,6 +213,7 @@ a = Analysis( 'core.ffmpeg_playback', 'core.game_detector', 'core.hotkey_manager', + 'core.instance_activation', 'core.mic_recorder', 'core.presets_manager', 'core.settings_manager', @@ -214,8 +226,9 @@ a = Analysis( # Exclude imageio_ffmpeg; use the project-pinned FFmpeg runtime beside # the engine. See core/ffmpeg_tools.py. 'imageio_ffmpeg', - # Qt modules we don't use (Widgets-only app) - 'PySide6.QtQuick', 'PySide6.QtQml', 'PySide6.QtWebEngine', + # Qt modules we don't use. Quick/QML power the editor's video preview + # and snapshots, so they are filtered below rather than excluded. + 'PySide6.QtWebEngine', 'PySide6.QtWebEngineCore', 'PySide6.QtBluetooth', 'PySide6.QtPositioning', 'PySide6.QtSensors', 'PySide6.QtLocation', 'PySide6.Qt3D', 'PySide6.QtPdf', 'PySide6.QtPdfWidgets', 'PySide6.QtNfc', @@ -236,10 +249,22 @@ a = Analysis( noarchive=False, ) -# QtGui's generic hook also sees the optional Virtual Keyboard plugin. It is a -# GPL-only Qt module and drags in unused QML/Quick libraries. Excludes stop +# Qt hooks collect every optional QML module and its libraries. Excludes stop # Python imports but not hook-added binaries, so filter the actual artifact -# TOCs as a second, deterministic boundary. +# TOCs as a second, deterministic boundary: keep the QML runtime the +# application's QtQuick and QtMultimedia scenes import, and only the Qt +# libraries reviewed in tools/qt_runtime_manifest.json. The Virtual Keyboard +# is a GPL-only Qt module and never ships. +import json as _json # noqa: E402 +_REVIEWED_QT = _json.loads( + (ROOT / 'tools' / 'qt_runtime_manifest.json').read_text(encoding='utf-8')) +_APPROVED_QT_SONAMES = { + f'libqt6{module.lower()}.so' + for module in _REVIEWED_QT['approved_runtime_qt_modules']['linux']} +# Destination of the wheel's QML import tree, e.g. pyside6/qt/qml/. +_QT_QML_DEST = (_QT6_PLUGIN_DEST.parent / 'qml').as_posix().casefold() + '/' +_KEPT_QML_DIRS = ('qtqml', 'qtqml/models', 'qtqml/workerscript', + 'qtquick', 'qtmultimedia') # The compiler runtime stays on the host. PyInstaller puts _internal first on # LD_LIBRARY_PATH, so a bundled libstdc++/libgcc_s built on the Ubuntu 22.04 # baseline shadows the host's newer copy for every library the process or its @@ -255,6 +280,17 @@ def _keep_reviewed_qt_runtime(entry): dest = str(entry[0]).replace('\\', '/').casefold() name = dest.rsplit('/', 1)[-1] forbidden_prefixes = ('libqt6virtualkeyboard',) + # QML debugger/profiler plugins are development tooling, and + # libqmldbg_quick3dprofiler links the GPL-only Quick 3D. + if '/plugins/qmltooling/' in dest: + return False + if dest.startswith(_QT_QML_DEST): + qml_parent = dest[len(_QT_QML_DEST):].rpartition('/')[0] + if qml_parent not in _KEPT_QML_DIRS: + return False + if name.startswith('libqt6') and '.so' in name: + if name.split('.so', 1)[0] + '.so' not in _APPROVED_QT_SONAMES: + return False return (name not in _SKIPPED_QT_PLUGINS and name not in _HOST_TOOLCHAIN_LIBS and 'virtualkeyboard' not in dest diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index a02fcd3b..a37c154f 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -10,9 +10,9 @@ identified below retain their respective licences. Full licence texts live in [`licenses/`](licenses/), and are installed alongside the application (Windows: `licenses\` in the install directory; -the `licenses/` directory). +Linux: `licenses/` inside the AppImage). -Last verified: **2026-08-24**. +Last verified: **2026-10-17**. --- @@ -93,6 +93,26 @@ remain fully available. > outside the scope of the LGPL fix — but it is worth a deliberate decision > before a large public release. +### FFmpeg (Linux engine and AppImage) + +| | | +|---|---| +| **Version** | `n8.1.2-34-g9b6c8969e0` (release branch 8.1) | +| **Source** | [BtbN/FFmpeg-Builds](https://github.com/BtbN/FFmpeg-Builds), release `autobuild-2026-07-31-14-10`, asset `ffmpeg-n8.1.2-34-g9b6c8969e0-linux64-lgpl-shared-8.1.tar.xz` | +| **SHA-256 (archive)** | `c882a80f06617149...` — full value, and the hash of every shipped library, recorded in [`tools/ffmpeg_manifest_linux.json`](tools/ffmpeg_manifest_linux.json) | +| **Licence** | **LGPL v3 or later** (`--enable-version3`, no `--enable-gpl`, no `--enable-nonfree`) — self-reported by the binaries as `LGPL version 3 or later` | +| **Linkage** | Dynamic. The native capture engine (`FTHRclips`) and `libFTHRPlaybackMixer.so` link the shared libraries through an `$ORIGIN` RPATH; the Python UI shells out to the bundled `ffmpeg` and `ffprobe`, which use the same libraries. | +| **Used for** | The same roles as the Windows build: encoding and muxing in the capture engine; audio mix, watermark, webcam overlay, clip export and share in the UI. | +| **Shipped files** | `libavcodec.so.62`, `libavdevice.so.62`, `libavfilter.so.11`, `libavformat.so.62`, `libavutil.so.60`, `libswresample.so.6`, `libswscale.so.9` (seven libraries, hash-pinned), and the `ffmpeg` and `ffprobe` command-line tools (RPATH `$ORIGIN/../lib:$ORIGIN`, verified by banner, configure flags, NEEDED and RPATH rather than by hash) | +| **Source route** | The binaries are the unmodified upstream build apart from the RPATH of the two command-line tools, which `tools/fetch_third_party.py --ffmpeg-linux` rewrites with `patchelf`. The corresponding source is FFmpeg tag `n8.1.2` plus the build scripts in the BtbN release named above; the exact asset is reproduced with `python tools/fetch_third_party.py --ffmpeg-linux`. | +| **Licence text** | [`licenses/FFmpeg-LICENSE.txt`](licenses/FFmpeg-LICENSE.txt) | + +The configuration is the Windows build's LGPL configuration (see above): no +GPL encoders, software H.264 through `libopenh264`, H.265 through `libkvazaar`, +AV1 through `libsvtav1`. The distribution's own FFmpeg (a GPL build on Ubuntu) +is never bundled; the licence gate refuses an AppDir that contains it, or an +`ffmpeg`/`ffprobe` that links it or carries a non-`$ORIGIN` RPATH. + ### Qt 6 | | | @@ -101,7 +121,8 @@ remain fully available. | **Source** | [Qt 6.11.1 source](https://download.qt.io/official_releases/qt/6.11/6.11.1/single/qt-everywhere-src-6.11.1.tar.xz) | | **Licence** | LGPL-3.0-only option selected | | **Linkage** | Dynamic (shared libraries bundled by PyInstaller) | -| **Used for** | Entire GUI, multimedia playback | +| **Used for** | Entire GUI, multimedia playback. Qt Quick and Qt QML render the editor's GPU video preview and snapshots (`FTHR_UI/ui/qml/`). | +| **Modules** | The bundled set is fixed per platform in [`tools/qt_runtime_manifest.json`](tools/qt_runtime_manifest.json). Since 2.0.0-alpha it includes the Qt Quick/QML runtime: `Qml`, `QmlCore`, `QmlMeta`, `QmlModels`, `QmlWorkerScript`, `Quick`, `QuickWidgets` and `MultimediaQuick`, plus only the `QtQml`, `QtQml.Models`, `QtQml.WorkerScript`, `QtQuick` and `QtMultimedia` QML import directories. All are part of Qt's LGPL-3.0 offering; the GPL-only Qt Quick add-ons (Quick 3D, Quick Timeline, Virtual Keyboard) are refused by the licence gate. | | **Licence/source notice** | [`licenses/Qt6-LICENSE.txt`](licenses/Qt6-LICENSE.txt), [`licenses/Qt6-SOURCE.txt`](licenses/Qt6-SOURCE.txt), [`licenses/Qt6-THIRD-PARTY-NOTICES.txt`](licenses/Qt6-THIRD-PARTY-NOTICES.txt) | ### FFmpeg bundled inside Qt Multimedia @@ -156,14 +177,22 @@ conflict. | **Version** | 0.5.5 · **Licence** MIT | | **Used for** | Microphone capture | | **Licence text** | [`licenses/sounddevice-LICENSE.txt`](licenses/sounddevice-LICENSE.txt) | -| **Note** | Wraps **PortAudio** (MIT). | +| **Note** | Wraps **PortAudio** (MIT). On Linux `libportaudio.so.2` is bundled explicitly by `FTHR_linux.spec`. | + +### jeepney + +| | | +|---|---| +| **Version** | 0.9.0 · **Licence** MIT | +| **Used for** | Linux only: D-Bus client for the XDG GlobalShortcuts portal (`core/portal_shortcuts.py`). Pure Python, no native code. | +| **Licence text** | [`licenses/jeepney-LICENSE.txt`](licenses/jeepney-LICENSE.txt) | ### keyboard | | | |---|---| | **Version** | 0.13.5 · **Licence** MIT | -| **Used for** | Global hotkeys (the only hotkey path on Windows) | +| **Used for** | Windows only: named as a hidden import by `FTHR.spec`. It is installed only on Windows (environment marker) and is not part of the Linux AppImage. | | **Licence text** | [`licenses/keyboard-LICENSE.txt`](licenses/keyboard-LICENSE.txt) | ### cffi and pycparser @@ -175,6 +204,40 @@ conflict. | **Used for** | Transitive runtime dependencies of python-sounddevice | | **Licence texts** | [`licenses/cffi-LICENSE.txt`](licenses/cffi-LICENSE.txt), [`licenses/pycparser-LICENSE.txt`](licenses/pycparser-LICENSE.txt) | +### Optional uploader package dependencies + +The optional SFTP uploader is not part of Core. It ships as a sealed, dormant +`.fthrplugin` package (`FTHR-Uploader.fthrplugin` on Windows, +`FTHR-Uploader-linux.fthrplugin` inside the AppImage) and is installed only +after the user's explicit in-app consent. Its frozen executable contains the +packages below, pinned in +[`requirements-uploader.txt`](requirements-uploader.txt). Each package carries +its full licence text inside the plugin under +`payload/licenses/optional-uploader/`; the same files are in +[`licenses/optional-uploader/`](licenses/optional-uploader/). + +| Package | Version | Licence | Platforms | +|---|---|---|---| +| paramiko | 4.0.0 | LGPL-2.1-or-later | both | +| bcrypt | 5.0.0 | Apache-2.0 | both | +| cryptography | 50.0.1 | Apache-2.0 OR BSD-3-Clause | both | +| PyNaCl (bundles libsodium, ISC) | 1.6.2 | Apache-2.0 | both | +| invoke | 3.0.3 | BSD-2-Clause | both | +| keyring | 25.7.0 | MIT | both | +| jaraco.classes, jaraco.context, jaraco.functools | 3.4.0, 6.1.2, 4.6.0 | MIT | both | +| more-itertools | 11.1.0 | MIT | both | +| cffi, pycparser | 2.0.0, 3.0 | MIT-0, BSD-3-Clause | both | +| pywin32-ctypes | 0.2.3 | BSD-3-Clause | Windows | +| SecretStorage | 3.5.0 | BSD-3-Clause | Linux (bundled for keyring's Secret Service backend) | +| jeepney | 0.9.0 | MIT | Linux (D-Bus client used by SecretStorage) | + +SecretStorage and jeepney are bundled for the Linux uploader, but saving an +SFTP password through the Secret Service is not wired up on Linux yet; the +uploader currently stores saved passwords only in Windows Credential Manager. + +paramiko is used unmodified as a separate library inside the plugin +executable; its LGPL source is the published paramiko 4.0.0 release. + ### NVIDIA Video Codec SDK header | | | @@ -185,6 +248,26 @@ conflict. | **Linkage** | **None at build time.** NVENC is resolved at runtime via `LoadLibraryA("nvEncodeAPI64.dll")` against the user's installed driver. The header is source-only and the NVIDIA runtime is **not** redistributed. | | **Licence text** | [`licenses/NVIDIA-NVENC-SDK-LICENSE.txt`](licenses/NVIDIA-NVENC-SDK-LICENSE.txt) | +### Wayland protocol definitions and generated bindings + +| | | +|---|---| +| **Files** | `FTHRcapture_linux/protocols/*.xml` and generated client bindings compiled into the Linux engine | +| **Copyright** | The wlroots, Chromium OS, and Wayland contributors named in the source notices | +| **Licence** | MIT-style permissive grants stated in the protocol sources | +| **Used for** | Linux compositor capture and related Wayland protocol integration | +| **Licence notices** | [`licenses/Wayland-Protocols-NOTICES.txt`](licenses/Wayland-Protocols-NOTICES.txt) | + +### PipeWire and D-Bus headers (Linux engine) + +| | | +|---|---| +| **Files** | System headers `pipewire-0.3/`, `spa-0.2/` and `dbus-1.0/` read at build time only; nothing from either project is copied into this repository or the AppImage | +| **Copyright** | PipeWire: Wim Taymans and contributors. D-Bus: Red Hat, Inc. and contributors | +| **Licence** | PipeWire headers: MIT. libdbus: dual AFL-2.1 / GPL-2.0-or-later, used under the AFL-2.1 option | +| **Linkage** | **None at build time.** The engine resolves `libpipewire-0.3.so.0` and `libdbus-1.so.3` with `dlopen()`/`dlsym()` on the user's system when the ScreenCast portal capture backend is needed, and reports that backend as unavailable otherwise. The libraries are **not** redistributed. | +| **Used for** | `org.freedesktop.portal.ScreenCast` session setup over the session bus and PipeWire video stream capture on compositors without a capture protocol | + ### Microsoft Visual C++ Redistributable | | | @@ -193,12 +276,37 @@ conflict. | **Licence** | Microsoft Visual Studio redistributable terms | | **Note** | Redistributed unmodified as permitted for VC++ runtime redistribution. | +### AppImage type-2 runtime + +| | | +|---|---| +| **Version** | `20251108`, upstream commit `dd6cebedcbddde9c82f89b011e8e1d40b6e43868` | +| **Source** | [AppImage/type2-runtime](https://github.com/AppImage/type2-runtime/releases/tag/20251108), asset `runtime-x86_64` | +| **SHA-256** | `2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d` | +| **Usage** | Embedded by the pinned Linux AppImage build as its type-2 runtime; it is not loaded by the FTHR application process. | +| **Licence** | MIT for the runtime project, with statically linked musl, libfuse, squashfuse, libzstd, and zlib components listed in the upstream notice. | +| **Licence text** | [`licenses/AppImage-type2-runtime-LICENSE.txt`](licenses/AppImage-type2-runtime-LICENSE.txt) | + +The runtime is fetched by the manifest-pinned build step and supplied to +`appimagetool` explicitly with `--runtime-file`. Its URL, release tag, commit, +size, and hash are recorded in +[`tools/appimage_tool_manifest.json`](tools/appimage_tool_manifest.json). + +### AppImage packaging tool (build-only) + +`appimagetool` itself is a build-time dependency and is not part of the +distributed AppImage. The build uses AppImage/appimagetool `1.9.1`, commit +`8c8c91f762b412a19f4e8d2c4b35afb98f2d7c81`, and verifies its size and SHA-256 +from the same manifest before executing it. + +--- + ## Project assets ### FTHR-generated media -The shipped logos, icons, installer artwork, repository social preview, and -four PCM WAV notification sounds are reproducibly generated from code in +The shipped logos, PNG icons, legacy installer bitmaps, the AppImage icon, and +the PCM WAV notification sounds are reproducibly generated from code in `tools/generate_release_assets.py`. The generator uses no external creative input, font, image, sound sample, or icon library. These outputs are FTHR project material under MIT; the notice and exact file list are in @@ -228,6 +336,32 @@ by the release asset script. It uses the project's MIT asset terms; see | **Verification** | Repository and pinned upstream files have SHA-256 `eb7d46f856dd57f18a8c03d033c57802692bf127f01dbd95ba8984338e6b5135` | | **Licence text** | `licenses/Oswald-OFL-1.1.txt` | +### Oswald Regular and SemiBold + +`Oswald-Regular.ttf` and `Oswald-SemiBold.ttf` come unmodified from the same +pinned `googlefonts/OswaldFont` revision as Oswald Bold, under the same SIL +Open Font License 1.1 (`licenses/Oswald-OFL-1.1.txt`). + +### Interface SVG icons and installer artwork + +The SVG interface icons in `FTHR_UI/assets/icons/` are project-supplied source +artwork under the project's GPL-3.0-only licence. The polished Windows +installer artwork in `installer_assets/polished/` is generated by +`tools/generate_installer_assets.py` from those icons and Oswald, also under +GPL-3.0-only; it is used only by the Windows installer. + +### User-provided images + +`preview_desktop.png`, the brand ICO files and the Gary Mode cat image +`gatze.png` were supplied by the workspace owner; see +`licenses/FTHR-USER-PROVIDED-ASSETS.txt`. + +### Ko-fi cup symbol + +`FTHR_UI/assets/icons/kofi_symbol.png` is Ko-fi's brand asset, used only on +the donation button under Ko-fi's promotional permission. It is not covered by +FTHR's licence; see `licenses/KO-FI-BRAND-ASSETS.txt`. + The authoritative per-file paths, hashes, origins, usage, and platform mappings are in `tools/release_asset_manifest.json`. @@ -239,9 +373,15 @@ Listed so future audits do not have to re-derive it: - **`imageio-ffmpeg`** — *removed* on 2026-08-05. Its bundled binary is a gyan.dev build with `--enable-gpl --enable-libx264 --enable-libx265` - (GPLv3). It is now excluded in the Windows PyInstaller spec and removed from + (GPLv3). It is now excluded in both PyInstaller specs and removed from `requirements.txt`. See `FTHR_UI/core/ffmpeg_tools.py`. +- **System FFmpeg on Linux** — if the bundled binary is absent, FTHR falls back + to `ffmpeg` on `PATH`. That copy belongs to the user's distribution and is not + redistributed by this project. - **Development tooling** — pytest, ruff, PyInstaller, Inno Setup, MSVC. +- **PipeWire and libdbus runtime libraries** — loaded from the user's system + with `dlopen()` by the Linux engine when the ScreenCast portal backend runs; + never bundled. See the headers entry above. --- @@ -255,6 +395,7 @@ files described here. Run it before every release: ```bash python tools/verify_release_licenses.py --tree . python tools/verify_release_licenses.py --windows-dist dist/FTHRClips +python tools/verify_release_licenses.py --appdir build_output/AppDir ``` **This is a technical verification, not legal advice.** diff --git a/build_linux.sh b/build_linux.sh index fc19bbf9..f8a0fdb0 100644 --- a/build_linux.sh +++ b/build_linux.sh @@ -164,7 +164,15 @@ cd "$SCRIPT_DIR" # 3. Build the optional Linux uploader bundle before the main AppImage. echo "" echo ">>> Building optional Linux upload extension..." +# Needs requirements-uploader.txt installed in the same interpreter; the +# builder refuses to freeze an executable without paramiko and friends. "$PYTHON_BIN" "$SCRIPT_DIR/tools/build_linux_uploader.py" --python "$PYTHON_BIN" +UPLOADER_PAYLOAD_ELF="$SCRIPT_DIR/build/optional-uploader-linux/payload-elf" +UPLOADER_EXECUTABLE="$SCRIPT_DIR/build/optional-uploader-linux/dist/FTHR-Uploader" +[ -d "$UPLOADER_PAYLOAD_ELF" ] && [ -x "$UPLOADER_EXECUTABLE" ] || { + echo "ERROR: the uploader build left no executable or unpacked payload for the glibc gate." >&2 + exit 1 +} # 4. Bundle with PyInstaller echo "" @@ -422,10 +430,16 @@ cp "$NATIVE_OUTPUT" "$OUTPUT" # and the success banner so a failed portability check cannot look releasable. echo "" echo ">>> Verifying Linux glibc baseline..." +# The optional uploader's one-file payload is compressed inside its +# .fthrplugin, so check the executable (its bootloader) and the ELF members +# tools/build_linux_uploader.py unpacks from it; the plugin runs on the same +# hosts as the AppImage. if ! "$PYTHON_BIN" "$SCRIPT_DIR/tools/verify_linux_glibc.py" \ --manifest "$GLIBC_MANIFEST" \ --target "$APPDIR" \ - --target "$OUTPUT"; then + --target "$OUTPUT" \ + --target "$UPLOADER_EXECUTABLE" \ + --target "$UPLOADER_PAYLOAD_ELF"; then rm -f "$NATIVE_OUTPUT" "$OUTPUT" "$OUTPUT.sha256" echo "ERROR: Linux glibc baseline verification failed; refusing to publish the AppImage." >&2 exit 1 diff --git a/docs/linux.md b/docs/linux.md index 9d403299..fe72c2ad 100644 --- a/docs/linux.md +++ b/docs/linux.md @@ -273,6 +273,10 @@ not automatic: enable it in Settings and complete the required consent and provider terms first. The uploader remains a separate subprocess from the main application and only receives approved clip paths through the existing boundary. +Saving an SFTP password is not available on Linux yet. The bundle already +contains keyring's Secret Service backend (SecretStorage), but the uploader +only stores passwords in Windows Credential Manager so far. + Catbox and custom-server providers do not require the separate hardware-identity component. Lustful hardware-bound account and upload flows remain unavailable on Linux until a Linux hardware-identity component is implemented and qualified. diff --git a/licenses/keyboard-LICENSE.txt b/licenses/keyboard-LICENSE.txt new file mode 100644 index 00000000..4f3cb7b8 --- /dev/null +++ b/licenses/keyboard-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Lucas Boppre Niehues + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/licenses/optional-uploader/SecretStorage/LICENSE b/licenses/optional-uploader/SecretStorage/LICENSE new file mode 100644 index 00000000..076e6425 --- /dev/null +++ b/licenses/optional-uploader/SecretStorage/LICENSE @@ -0,0 +1,25 @@ +Copyright 2012-2025 Dmitry Shachnev +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the University nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/optional-uploader/jeepney/LICENSE b/licenses/optional-uploader/jeepney/LICENSE new file mode 100644 index 00000000..b0ae9dbc --- /dev/null +++ b/licenses/optional-uploader/jeepney/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Thomas Kluyver + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/pyproject.toml b/pyproject.toml index b795e8c1..cffb57a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ # Tooling configuration only. # # FTHR Clips is NOT distributed as a Python package — it ships as a PyInstaller -# bundle (Windows installer). There is deliberately no +# bundle (Windows installer / Linux AppImage). There is deliberately no # [project] table and no build-system: adding one would imply `pip install .` # is a supported path, which it is not. @@ -9,6 +9,7 @@ minversion = "9.0" testpaths = ["tests"] addopts = "-q --strict-markers" +pythonpath = [".", "FTHR_UI"] # QT_QPA_PLATFORM=offscreen is set in tests/conftest.py rather than here, so it # works without the pytest-env plugin. markers = [ @@ -22,8 +23,8 @@ target-version = "py312" # lowest interpreter CI exercises exclude = [ "build", "dist", "dist_old_gpl_*", ".ui-port", ".ux-backup", ".shortcut-backup", - "FTHRcapture", - "Output", "redist", + "FTHRcapture", "FTHRcapture_linux", + "AppDir", "Output", "redist", ] [tool.ruff.lint] diff --git a/requirements-alpha.txt b/requirements-alpha.txt index 95cf49da..c6231241 100644 --- a/requirements-alpha.txt +++ b/requirements-alpha.txt @@ -7,6 +7,7 @@ PySide6==6.11.1 opencv-python-headless==4.13.0.92 numpy==2.4.4 sounddevice==0.5.5 +keyboard==0.13.5; sys_platform == "win32" jeepney==0.9.0; sys_platform != "win32" # Transitive dependencies — pinned so a silent upstream release cannot change diff --git a/requirements-uploader.txt b/requirements-uploader.txt index 160317ed..50fcc05e 100644 --- a/requirements-uploader.txt +++ b/requirements-uploader.txt @@ -12,3 +12,7 @@ jaraco.context==6.1.2 jaraco.functools==4.6.0 more-itertools==11.1.0 pywin32-ctypes==0.2.3 ; sys_platform == 'win32' +# keyring's Secret Service backend on Linux (D-Bus, pure Python). Bundled for +# the Linux uploader; saved SFTP passwords are not wired up on Linux yet. +SecretStorage==3.5.0 ; sys_platform == 'linux' +jeepney==0.9.0 ; sys_platform == 'linux' diff --git a/requirements.in b/requirements.in index b66c9178..d85cde52 100644 --- a/requirements.in +++ b/requirements.in @@ -16,5 +16,11 @@ numpy>=2.4,<3 sounddevice>=0.5,<0.6 # Input -# Windows keyboard integration. -keyboard>=0.13.5,<0.14 +# Windows keyboard integration (named as a PyInstaller hidden import by +# FTHR.spec). Never installed on Linux. +keyboard>=0.13.5,<0.14; sys_platform == "win32" +# Linux global hotkeys go through the XDG GlobalShortcuts portal over D-Bus +# (core/portal_shortcuts.py). jeepney is pure Python and can marshal the +# a(sa{sv}) struct arrays that PySide6's QtDBus cannot. Windows uses +# RegisterHotKey natively; the app never reads input devices itself. +jeepney>=0.8,<1.0; sys_platform != "win32" diff --git a/tests/test_ci_contract.py b/tests/test_ci_contract.py index eabe174f..39277bfc 100644 --- a/tests/test_ci_contract.py +++ b/tests/test_ci_contract.py @@ -13,12 +13,36 @@ def _workflow() -> str: return WORKFLOW.read_text(encoding="utf-8") +def test_linux_release_ci_fetches_and_uses_pinned_ffmpeg() -> None: + workflow = _workflow() + linux_job = workflow[workflow.index(" linux-engine:") : workflow.index( + " windows-engine:" + )] + + assert "fetch_third_party.py --ffmpeg-linux" in linux_job + assert "-DFTHR_FFMPEG_ROOT=${{ github.workspace }}" in linux_job + assert "ctest --test-dir" in linux_job + assert "verify_release_licenses.py --tree ." in linux_job + assert "git diff --exit-code" in linux_job + + +def test_optional_uploader_builds_install_the_uploader_lock() -> None: + workflow = _workflow() + windows_job = workflow[workflow.index(" windows-engine:") : workflow.index( + " release-verification:" + )] + appimage_job = workflow[workflow.index(" linux-appimage-package:") :] + + for job in (windows_job, appimage_job): + assert "-r requirements-uploader.txt" in job + assert "build/optional-uploader-linux/payload-elf" in appimage_job + assert "build/optional-uploader-linux/dist/FTHR-Uploader" in appimage_job def test_windows_bundle_is_built_only_after_the_engine() -> None: workflow = _workflow() python_job = workflow[workflow.index(" python:") : workflow.index( - " windows-engine:" + " linux-engine:" )] windows_job = workflow[workflow.index(" windows-engine:") : workflow.index( " release-verification:" @@ -90,3 +114,13 @@ def test_release_ci_runs_response_and_exception_contracts() -> None: assert "verify_engine_response_contract.py" in release_job assert "verify_exception_handling.py" in release_job + + +def test_linux_python_job_runs_every_linux_test() -> None: + workflow = _workflow() + tests_step = workflow[workflow.index(" - name: Tests") :] + linux_branch = tests_step[: tests_step.index(" else")] + + for name in ("test_linux_appimage_report", "test_linux_runtime", + "test_linux_tools", "test_capture_backend_detection"): + assert f"--ignore=tests/{name}.py" not in linux_branch diff --git a/tests/test_qml_scene_gate.py b/tests/test_qml_scene_gate.py new file mode 100644 index 00000000..55673fb0 --- /dev/null +++ b/tests/test_qml_scene_gate.py @@ -0,0 +1,76 @@ +"""The release gate refuses a bundle without the editor's QML scenes.""" + +from __future__ import annotations + +import importlib.util +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +_SPEC = importlib.util.spec_from_file_location( + 'verify_release_licenses_qml', ROOT / 'tools' / 'verify_release_licenses.py') +vrl = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(vrl) + + +def _source_tree(root: Path) -> None: + for name in vrl.QML_SCENE_FILES: + path = root / vrl.QML_SCENE_SOURCE_DIR / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(f'source {name}\n'.encode()) + + +def _artifact(root: Path, source: Path, skip: str | None = None) -> None: + for name in vrl.QML_SCENE_FILES: + if name == skip: + continue + path = root / vrl.QML_SCENE_ARTIFACT_DIR / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes((source / vrl.QML_SCENE_SOURCE_DIR / name).read_bytes()) + + +def test_complete_bundle_passes(tmp_path): + source, artifact = tmp_path / 'src', tmp_path / 'dist' + _source_tree(source) + _artifact(artifact, source) + for platform in ('windows', 'linux'): + rep = vrl.Report() + vrl.check_qml_scenes(artifact, rep, platform, manifest_root=source) + assert not rep.failures, rep.failures + + +def test_each_missing_scene_fails(tmp_path): + source = tmp_path / 'src' + _source_tree(source) + for name in vrl.QML_SCENE_FILES: + artifact = tmp_path / f'dist-{name}' + _artifact(artifact, source, skip=name) + rep = vrl.Report() + vrl.check_qml_scenes(artifact, rep, 'windows', manifest_root=source) + assert rep.failures == [ + f'windows artifact: application QML file missing: ' + f'{vrl.QML_SCENE_ARTIFACT_DIR}/{name}'] + + +def test_stale_scene_fails(tmp_path): + source, artifact = tmp_path / 'src', tmp_path / 'dist' + _source_tree(source) + _artifact(artifact, source) + (artifact / vrl.QML_SCENE_ARTIFACT_DIR / 'color.frag.qsb').write_bytes(b'old') + rep = vrl.Report() + vrl.check_qml_scenes(artifact, rep, 'linux', manifest_root=source) + assert len(rep.failures) == 1 and 'differs' in rep.failures[0] + + +def test_gate_files_match_the_real_sources(): + for name in vrl.QML_SCENE_FILES: + assert (ROOT / vrl.QML_SCENE_SOURCE_DIR / name).is_file() + + +def test_specs_anchor_the_qml_filter_to_the_qt_tree(): + # A bare '/qml/' match also hits the application's ui/qml/ scenes. + for spec in ('FTHR.spec', 'FTHR_linux.spec'): + text = (ROOT / spec).read_text(encoding='utf-8') + assert "'/qml/' in dest" not in text, spec + assert re.search(r'dest\.startswith\(_QT_QML_DEST\)', text), spec diff --git a/tests/test_qt_license_gate.py b/tests/test_qt_license_gate.py index 8e461f0f..b8d69699 100644 --- a/tests/test_qt_license_gate.py +++ b/tests/test_qt_license_gate.py @@ -68,7 +68,8 @@ def _source_tree(tmp_path: Path) -> Path: 'shiboken6==6.11.1', )), ) - for name in ('requirements.in', 'FTHR.spec'): + for name in ('requirements.in', 'FTHR.spec', 'FTHR_linux.spec', + 'build_linux.sh'): _write(root / name, 'PySide6\n') return root @@ -144,5 +145,37 @@ def test_artifact_rejects_gpl_only_qt_module(tmp_path, module): assert any('GPL-only' in failure for failure in rep.failures) +def test_linux_artifact_requires_xcb_and_wayland_plugins(tmp_path): + manifest_root = _manifest_root(tmp_path / 'source') + artifact = tmp_path / 'artifact' + _license_files(artifact) + _write(artifact / 'PySide6' / 'QtCore.so') + for module in ('Core', 'Gui', 'Widgets', 'Multimedia', + 'MultimediaWidgets'): + _write(artifact / 'PySide6' / 'Qt' / 'lib' / + f'libQt6{module}.so.6') + rep = vrl.Report() + vrl.check_qt_artifact(artifact, rep, 'linux', manifest_root=manifest_root) + assert any('libqxcb.so missing' in failure for failure in rep.failures) + assert any('Wayland Qt platform plugin missing' in failure + for failure in rep.failures) + + +def test_linux_artifact_accepts_every_reviewed_runtime_module(tmp_path): + manifest_root = _manifest_root(tmp_path / 'source') + artifact = tmp_path / 'artifact' + _license_files(artifact) + _write(artifact / 'PySide6' / 'QtCore.so') + data = json.loads(REAL_MANIFEST.read_text(encoding='utf-8')) + for module in data['approved_runtime_qt_modules']['linux']: + _write(artifact / 'PySide6' / 'Qt' / 'lib' / + f'libQt6{module}.so.6') + _write(artifact / 'PySide6' / 'Qt' / 'plugins' / 'platforms' / + 'libqxcb.so') + _write(artifact / 'PySide6' / 'Qt' / 'plugins' / 'platforms' / + 'libqwayland.so') + rep = vrl.Report() + vrl.check_qt_artifact(artifact, rep, 'linux', manifest_root=manifest_root) + assert not rep.failures, rep.failures diff --git a/tests/test_sftp_provider.py b/tests/test_sftp_provider.py index fa40ff38..76f04746 100644 --- a/tests/test_sftp_provider.py +++ b/tests/test_sftp_provider.py @@ -392,7 +392,14 @@ def test_core_never_persists_password(tmp_path, monkeypatch): assert 'secret' not in (tmp_path / 'settings.json').read_text() +_BUILT_WINDOWS_UPLOADER = ( + Path(__file__).resolve().parents[1] / 'plugin-packages' / 'FTHR-Uploader.fthrplugin') + + @pytest.mark.skipif(sys.platform != 'win32', reason='Windows frozen uploader') +@pytest.mark.skipif( + not _BUILT_WINDOWS_UPLOADER.is_file(), + reason='build the package with tools/build_optional_uploaders.py first') def test_frozen_extension_connects_to_real_sftp(server, tmp_path, monkeypatch): from core import upload_manager from test_uploader_plugin_boundary import _CoreSettings diff --git a/tests/test_uploader_builders.py b/tests/test_uploader_builders.py new file mode 100644 index 00000000..1377ccfd --- /dev/null +++ b/tests/test_uploader_builders.py @@ -0,0 +1,81 @@ +"""The Windows and Linux uploader builders share one plugin identity and +cooperate on FTHR_UI/core/uploader_bundle_manifest.py.""" + +from __future__ import annotations + +import runpy +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +WINDOWS_BUILDER = runpy.run_path(str(ROOT / 'tools' / 'build_optional_uploaders.py')) +render = WINDOWS_BUILDER['render_core_manifest'] + + +def _manifest(windows: str, linux: str | None = None) -> str: + return render('', windows_hash=windows, linux_hash=linux) + + +def test_windows_build_keeps_the_linux_bundle_hash(): + existing = _manifest('old-windows', 'linux-hash') + result = render(existing, windows_hash='new-windows') + assert "EXPECTED_UPLOADER_BUNDLE_SHA256 = 'new-windows'" in result + assert "EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 = 'linux-hash'" in result + + +def test_linux_build_keeps_the_windows_bundle_hash(): + existing = _manifest('windows-hash') + result = render(existing, linux_hash='new-linux') + assert "EXPECTED_UPLOADER_BUNDLE_SHA256 = 'windows-hash'" in result + assert "EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 = 'new-linux'" in result + + +def test_rendering_is_stable_for_the_committed_manifest(): + committed = (ROOT / 'FTHR_UI' / 'core' / 'uploader_bundle_manifest.py').read_text( + encoding='utf-8') + assert render(committed) == committed + + +def test_regenerated_manifest_takes_versions_from_the_shared_builder(): + stale = '''UPLOADER_PLUGIN_VERSION = '1.1.0' +UPLOADER_TERMS_VERSION = '2026-08-24-v1' +EXPECTED_UPLOADER_BUNDLE_SHA256 = 'windows' +EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 = 'linux' +''' + namespace: dict[str, object] = {} + exec(render(stale, linux_hash='new-linux'), namespace) # noqa: S102 + assert namespace['UPLOADER_PLUGIN_VERSION'] == WINDOWS_BUILDER['UPLOADER_PLUGIN_VERSION'] + assert namespace['UPLOADER_TERMS_VERSION'] == WINDOWS_BUILDER['UPLOADER_TERMS_VERSION'] + assert namespace['UPLOADER_PRIVACY_VERSION'] == WINDOWS_BUILDER['UPLOADER_PRIVACY_VERSION'] + assert namespace['EXPECTED_UPLOADER_BUNDLE_SHA256'] == 'windows' + assert namespace['EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256'] == 'new-linux' + + +def test_linux_builder_has_no_private_copy_of_the_plugin_constants(): + source = (ROOT / 'tools' / 'build_linux_uploader.py').read_text(encoding='utf-8') + for literal in ('1.1.0', '1.4.0', '2026-08-24-v1', '2026-10-15-v3'): + assert literal not in source + assert 'FTHR_Hardware_ID' not in source + assert "'sftp_provider'" in source + + +def test_requirement_check_reports_missing_and_mismatched_pins(tmp_path): + requirements = tmp_path / 'requirements.txt' + requirements.write_text( + '# comment\n' + 'pytest==0.0.1\n' + 'fthr-package-that-does-not-exist==1.0\n' + "never-on-this-platform==1.0 ; sys_platform == 'nonexistent-os'\n", + encoding='utf-8') + problems = WINDOWS_BUILDER['missing_uploader_requirements'](requirements) + assert any(problem.startswith('pytest ') for problem in problems) + assert 'fthr-package-that-does-not-exist is not installed' in problems + assert not any('never-on-this-platform' in problem for problem in problems) + assert len(problems) == 2 + + +def test_every_bundle_carries_the_whole_uploader_licence_inventory(): + files = WINDOWS_BUILDER['uploader_license_files']() + packages = {path.relative_to(ROOT / 'licenses' / 'optional-uploader').parts[0] + for path in files} + assert {'paramiko', 'keyring', 'SecretStorage', 'jeepney', 'pywin32-ctypes'} <= packages diff --git a/tools/build_linux_uploader.py b/tools/build_linux_uploader.py index cbd4efae..d73c9cef 100644 --- a/tools/build_linux_uploader.py +++ b/tools/build_linux_uploader.py @@ -1,29 +1,51 @@ #!/usr/bin/env python3 -"""Build and seal the optional Linux upload extension bundle.""" +"""Build and seal the optional Linux upload extension bundle. + +The plugin identity, legal document versions, licence inventory and the Core +binding format come from tools/build_optional_uploaders.py, so the Linux and +Windows bundles always describe the same plugin release and neither build +erases the other platform's bundle hash in +FTHR_UI/core/uploader_bundle_manifest.py. +""" from __future__ import annotations import argparse import hashlib import json import os -import re +import shutil import subprocess import sys import zipfile from pathlib import Path -ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(Path(__file__).resolve().parent)) +import build_optional_uploaders as shared # noqa: E402 + +ROOT = shared.ROOT BUILD_ROOT = ROOT / 'build' / 'optional-uploader-linux' DIST = BUILD_ROOT / 'dist' WORK = BUILD_ROOT / 'work' SPEC = BUILD_ROOT / 'spec' +# Every ELF frozen inside the one-file payload, unpacked so that +# tools/verify_linux_glibc.py can hold the plugin to the same glibc ceiling as +# the AppImage. The compressed payload inside the .fthrplugin is invisible to +# a scan of the AppDir. +PAYLOAD_ELF_DIR = BUILD_ROOT / 'payload-elf' BUNDLE = ROOT / 'plugin-packages' / 'FTHR-Uploader-linux.fthrplugin' -MANIFEST_MODULE = ROOT / 'FTHR_UI' / 'core' / 'uploader_bundle_manifest.py' +UPLOADER_DIR = ROOT / 'FTHR_Uploader' ENTRYPOINT = 'FTHR-Uploader' -PLUGIN_ID = 'com.fthrclips.uploader' -PLUGIN_VERSION = '1.1.0' -TERMS_VERSION = '2026-08-24-v1' -PRIVACY_VERSION = '2026-08-24-v1' +_ELF_MAGIC = b'\x7fELF' + +# sftp_provider is imported lazily by the service; keyring resolves backends +# through entry points that a frozen executable does not carry, so name the +# Secret Service backend (SecretStorage over jeepney) explicitly. +HIDDEN_IMPORTS = ( + 'sftp_provider', + 'keyring.backends.SecretService', + 'secretstorage', + 'jeepney.io.blocking', +) def sha256(path: Path) -> str: @@ -38,79 +60,110 @@ def run(command: list[str]) -> None: subprocess.run(command, cwd=ROOT, check=True) -def bind_linux_bundle_hash(existing: str, bundle_hash: str) -> str: - """Update only the generated Linux hash while preserving other bindings.""" - linux_hash_line = ( - f'EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 = {bundle_hash!r}') - if 'EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256' in existing: - return re.sub( - r"^EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256 = .*?$", - linux_hash_line, - existing, - count=1, - flags=re.MULTILINE) - marker_match = re.search( - r"^EXPECTED_UPLOADER_BUNDLE_SHA256 = .*?$", - existing, - flags=re.MULTILINE) - if not marker_match: - raise ValueError('uploader manifest is missing the Windows bundle hash') - marker = marker_match.group(0) - return existing.replace(marker, marker + '\n' + linux_hash_line, 1) +def _remove_build_dir(path: Path) -> None: + resolved = path.resolve(strict=False) + if BUILD_ROOT.resolve(strict=False) not in (resolved, *resolved.parents): + raise ValueError(f'Refusing to remove a path outside {BUILD_ROOT}: {resolved}') + if resolved.exists(): + shutil.rmtree(resolved) -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument('--python', default=sys.executable) - args = parser.parse_args() - if sys.platform == 'win32': - parser.error('This builder is for the Linux AppImage only.') +def extract_payload_elves(executable: Path, destination: Path) -> list[Path]: + """Unpack every ELF member of a PyInstaller one-file executable.""" + from PyInstaller.archive.readers import CArchiveReader # noqa: PLC0415 + + _remove_build_dir(destination) + destination.mkdir(parents=True) + root = destination.resolve() + extracted = [] + archive = CArchiveReader(str(executable)) + for name in sorted(archive.toc): + data = archive.extract(name) + if not data.startswith(_ELF_MAGIC): + continue + target = (root / name).resolve() + if root not in target.parents: + raise ValueError(f'unsafe member name in {executable.name}: {name!r}') + target.parent.mkdir(parents=True, exist_ok=True) + target.write_bytes(data) + extracted.append(target) + return extracted - for path in (DIST, WORK, SPEC): - path.mkdir(parents=True, exist_ok=True) - run([ - args.python, '-m', 'PyInstaller', '--noconfirm', '--clean', - '--onefile', '--console', '--name', ENTRYPOINT, - '--paths', str(ROOT / 'FTHR_Uploader'), - '--paths', str(ROOT / 'FTHR_Hardware_ID'), - '--distpath', str(DIST), '--workpath', str(WORK), - '--specpath', str(SPEC), - str(ROOT / 'FTHR_Uploader' / 'uploader_service.py'), - ]) - executable = DIST / ENTRYPOINT - if not executable.is_file(): - raise FileNotFoundError(executable) - executable.chmod(executable.stat().st_mode | 0o111) - - payload_hash = sha256(executable) + +def seal(executable: Path) -> Path: + licenses = shared.uploader_license_files() manifest = { 'schema_version': 1, - 'plugin_id': PLUGIN_ID, - 'plugin_version': PLUGIN_VERSION, - 'terms_version': TERMS_VERSION, - 'privacy_version': PRIVACY_VERSION, + 'plugin_id': shared.UPLOADER_PLUGIN_ID, + 'plugin_version': shared.UPLOADER_PLUGIN_VERSION, + 'terms_version': shared.UPLOADER_TERMS_VERSION, + 'privacy_version': shared.UPLOADER_PRIVACY_VERSION, 'entrypoint': ENTRYPOINT, 'activation': 'in_app_explicit_consent_only', 'files': [{ 'path': f'payload/{ENTRYPOINT}', - 'sha256': payload_hash, + 'sha256': sha256(executable), 'size': executable.stat().st_size, - }], + }, *[{ + 'path': 'payload/' + path.relative_to(ROOT).as_posix(), + 'sha256': sha256(path), + 'size': path.stat().st_size, + } for path in licenses]], } BUNDLE.parent.mkdir(parents=True, exist_ok=True) temporary = BUNDLE.with_suffix('.tmp') + temporary.unlink(missing_ok=True) with zipfile.ZipFile(temporary, 'w', compression=zipfile.ZIP_DEFLATED) as archive: archive.writestr('manifest.json', json.dumps(manifest, indent=2, sort_keys=True)) - archive.write(ROOT / 'FTHR_Uploader' / 'TERMS_OF_SERVICE.txt', 'TERMS_OF_SERVICE.txt') - archive.write(ROOT / 'FTHR_Uploader' / 'PRIVACY_POLICY.txt', 'PRIVACY_POLICY.txt') + archive.write(UPLOADER_DIR / 'TERMS_OF_SERVICE.txt', 'TERMS_OF_SERVICE.txt') + archive.write(UPLOADER_DIR / 'PRIVACY_POLICY.txt', 'PRIVACY_POLICY.txt') + for license_file in licenses: + archive.write(license_file, 'payload/' + license_file.relative_to(ROOT).as_posix()) archive.write(executable, f'payload/{ENTRYPOINT}') os.replace(temporary, BUNDLE) - bundle_hash = sha256(BUNDLE) + return BUNDLE + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--python', default=sys.executable) + args = parser.parse_args() + if sys.platform == 'win32': + parser.error('This builder is for the Linux AppImage only.') + if Path(args.python).resolve() != Path(sys.executable).resolve(): + # The requirement check below inspects this interpreter's packages. + parser.error('run this script with the interpreter passed as --python') + + try: + shared.require_uploader_requirements() + for path in (DIST, WORK, SPEC): + _remove_build_dir(path) + path.mkdir(parents=True) + command = [ + args.python, '-m', 'PyInstaller', '--noconfirm', '--clean', + '--onefile', '--console', '--name', ENTRYPOINT, + '--paths', str(UPLOADER_DIR), + '--distpath', str(DIST), '--workpath', str(WORK), + '--specpath', str(SPEC), + ] + for module in HIDDEN_IMPORTS: + command.extend(['--hidden-import', module]) + command.append(str(UPLOADER_DIR / 'uploader_service.py')) + run(command) + executable = DIST / ENTRYPOINT + if not executable.is_file(): + raise FileNotFoundError(executable) + executable.chmod(executable.stat().st_mode | 0o111) - existing = MANIFEST_MODULE.read_text(encoding='utf-8') - MANIFEST_MODULE.write_text( - bind_linux_bundle_hash(existing, bundle_hash), encoding='utf-8') - print(f'Built {BUNDLE} ({bundle_hash})') + elves = extract_payload_elves(executable, PAYLOAD_ELF_DIR) + bundle = seal(executable) + bundle_hash = sha256(bundle) + shared.write_core_manifest(linux_hash=bundle_hash) + except (OSError, ValueError, subprocess.CalledProcessError) as exc: + print(f'ERROR: {exc}', file=sys.stderr) + return 1 + print(f'Unpacked {len(elves)} payload ELF file(s) to {PAYLOAD_ELF_DIR} for the glibc gate') + print(f'Built {bundle} ({bundle_hash})') return 0 diff --git a/tools/build_optional_uploaders.py b/tools/build_optional_uploaders.py index 93368029..fa9bcd0a 100644 --- a/tools/build_optional_uploaders.py +++ b/tools/build_optional_uploaders.py @@ -4,20 +4,28 @@ The uploader is frozen as a one-shot executable, optionally signed, wrapped in integrity-declared ``.fthrplugin`` archives, and then bound into Core through generated SHA-256 constants. + +This module is also the single source of the uploader plugin version, legal +document versions, licence inventory and Core binding format; +tools/build_linux_uploader.py imports them so the two platform builds cannot +drift apart or erase each other's bundle hash. """ from __future__ import annotations import argparse +import ast import hashlib import json import os +import re import shlex import shutil import subprocess import sys import zipfile from dataclasses import dataclass +from importlib import metadata from pathlib import Path @@ -26,12 +34,17 @@ PACKAGES = ROOT / 'plugin-packages' CORE_MANIFEST = ROOT / 'FTHR_UI' / 'core' / 'uploader_bundle_manifest.py' ICON = ROOT / 'FTHR_UI' / 'assets' / 'favicon.ico' +REQUIREMENTS = ROOT / 'requirements-uploader.txt' +LICENSE_ROOT = ROOT / 'licenses' / 'optional-uploader' UPLOADER_PLUGIN_ID = 'com.fthrclips.uploader' UPLOADER_PLUGIN_VERSION = '1.4.0' UPLOADER_TERMS_VERSION = '2026-10-15-v3' UPLOADER_PRIVACY_VERSION = '2026-10-15-v3' +WINDOWS_BUNDLE_BINDING = 'EXPECTED_UPLOADER_BUNDLE_SHA256' +LINUX_BUNDLE_BINDING = 'EXPECTED_UPLOADER_LINUX_BUNDLE_SHA256' + @dataclass(frozen=True) class Package: @@ -71,6 +84,92 @@ def _sha256(path: Path) -> str: return digest.hexdigest() +def uploader_license_files() -> list[Path]: + """Every reviewed uploader licence file; each bundle carries all of them.""" + if not LICENSE_ROOT.is_dir(): + return [] + return sorted(path for path in LICENSE_ROOT.rglob('*') if path.is_file()) + + +def missing_uploader_requirements(requirements: Path = REQUIREMENTS) -> list[str]: + """Pins from requirements-uploader.txt that this interpreter does not satisfy. + + PyInstaller silently freezes an executable without a module it cannot + find, so a missing paramiko only shows up when the sealed package runs. + """ + from packaging.requirements import Requirement # noqa: PLC0415 (PyInstaller dep) + + problems = [] + for raw in requirements.read_text(encoding='utf-8').splitlines(): + line = raw.split('#', 1)[0].strip() + if not line: + continue + requirement = Requirement(line) + if requirement.marker is not None and not requirement.marker.evaluate(): + continue + try: + installed = metadata.version(requirement.name) + except metadata.PackageNotFoundError: + problems.append(f'{requirement.name} is not installed') + continue + if not requirement.specifier.contains(installed, prereleases=True): + problems.append( + f'{requirement.name} {installed} does not match {requirement.specifier}') + return problems + + +def require_uploader_requirements() -> None: + problems = missing_uploader_requirements() + if problems: + raise ValueError( + 'the uploader build environment does not match ' + f'{REQUIREMENTS.name}: ' + '; '.join(problems) + + f'. Run: {Path(sys.executable).name} -m pip install -r {REQUIREMENTS.name}') + + +def _existing_binding(text: str, name: str) -> str | None: + match = re.search(rf'^{re.escape(name)} = (.+)$', text, flags=re.MULTILINE) + if match is None: + return None + try: + value = ast.literal_eval(match.group(1).strip()) + except (ValueError, SyntaxError): + return None + return value if isinstance(value, str) and value else None + + +def render_core_manifest(existing: str, *, windows_hash: str | None = None, + linux_hash: str | None = None) -> str: + """Regenerate Core's uploader bindings from this module's constants. + + Each platform builder passes only its own bundle hash. The other + platform's hash is carried over from ``existing`` so a Windows build does + not erase the Linux binding and vice versa. + """ + windows_hash = windows_hash or _existing_binding(existing, WINDOWS_BUNDLE_BINDING) + linux_hash = linux_hash or _existing_binding(existing, LINUX_BUNDLE_BINDING) + if not windows_hash: + raise ValueError(f'uploader manifest is missing {WINDOWS_BUNDLE_BINDING}') + linux_line = (f'{LINUX_BUNDLE_BINDING} = {linux_hash!r}\n' if linux_hash else '') + return f'''"""Generated release bindings for dormant optional uploader packages.""" + +UPLOADER_PLUGIN_ID = {UPLOADER_PLUGIN_ID!r} +UPLOADER_PLUGIN_VERSION = {UPLOADER_PLUGIN_VERSION!r} +UPLOADER_TERMS_VERSION = {UPLOADER_TERMS_VERSION!r} +UPLOADER_PRIVACY_VERSION = {UPLOADER_PRIVACY_VERSION!r} +{WINDOWS_BUNDLE_BINDING} = {windows_hash!r} +{linux_line} +''' + + +def write_core_manifest(*, windows_hash: str | None = None, + linux_hash: str | None = None) -> None: + existing = CORE_MANIFEST.read_text(encoding='utf-8') if CORE_MANIFEST.is_file() else '' + CORE_MANIFEST.write_text( + render_core_manifest(existing, windows_hash=windows_hash, linux_hash=linux_hash), + encoding='utf-8') + + def _run(command: list[str], label: str) -> None: print(f'\n== {label} ==') subprocess.run(command, cwd=ROOT, check=True) @@ -135,8 +234,7 @@ def _build_executable(package: Package) -> Path: def _seal(package: Package, executable: Path) -> Path: destination = PACKAGES / package.bundle - licenses = (sorted(path for path in (ROOT / 'licenses' / 'optional-uploader').rglob('*') - if path.is_file()) if package.name == 'uploader' else []) + licenses = uploader_license_files() if package.name == 'uploader' else [] manifest = { 'schema_version': 1, 'plugin_id': package.plugin_id, @@ -172,19 +270,6 @@ def _seal(package: Package, executable: Path) -> Path: return destination -def _write_core_manifest(hashes: dict[str, str]) -> None: - text = f'''"""Generated release bindings for dormant optional uploader packages.""" - -UPLOADER_PLUGIN_ID = {UPLOADER_PLUGIN_ID!r} -UPLOADER_PLUGIN_VERSION = {UPLOADER_PLUGIN_VERSION!r} -UPLOADER_TERMS_VERSION = {UPLOADER_TERMS_VERSION!r} -UPLOADER_PRIVACY_VERSION = {UPLOADER_PRIVACY_VERSION!r} -EXPECTED_UPLOADER_BUNDLE_SHA256 = {hashes['uploader']!r} - -''' - CORE_MANIFEST.write_text(text, encoding='utf-8') - - def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( @@ -194,6 +279,7 @@ def main() -> int: if sys.platform != 'win32': parser.error('The current optional package entrypoints are Windows executables.') try: + require_uploader_requirements() _safe_remove_tree(BUILD_ROOT) hashes: dict[str, str] = {} for package in PACKAGE_DEFINITIONS: @@ -202,7 +288,7 @@ def main() -> int: _sign(args.sign_command, executable) bundle = _seal(package, executable) hashes[package.name] = _sha256(bundle) - _write_core_manifest(hashes) + write_core_manifest(windows_hash=hashes['uploader']) except (OSError, ValueError, subprocess.CalledProcessError) as exc: print(f'ERROR: {exc}', file=sys.stderr) return 1 diff --git a/tools/generate_release_assets.py b/tools/generate_release_assets.py index b303c1af..001110bc 100644 --- a/tools/generate_release_assets.py +++ b/tools/generate_release_assets.py @@ -381,8 +381,10 @@ def sound(notes: list[tuple[float, float]], gap: float = 0.025) -> bytes: def generated_files() -> dict[Path, bytes]: logo = draw_mark(512).png() + # The repository social preview (social_preview()) is no longer committed: + # upstream removed .github/social_preview.png in 2.0.0-alpha. Regenerate it + # by hand if the GitHub repository card needs it again. files = { - ROOT / '.github' / 'social_preview.png': social_preview().png(), ROOT / 'AppDir' / 'fthr-clips.png': logo, ROOT / 'installer_assets' / 'wizard_banner.bmp': installer_banner(164, 314).bmp(), diff --git a/tools/qt_runtime_manifest.json b/tools/qt_runtime_manifest.json index ba06afb2..c9fe5d00 100644 --- a/tools/qt_runtime_manifest.json +++ b/tools/qt_runtime_manifest.json @@ -18,7 +18,10 @@ "Gui", "Widgets", "Multimedia", - "MultimediaWidgets" + "MultimediaWidgets", + "Qml", + "Quick", + "QuickWidgets" ], "approved_runtime_qt_modules": { "windows": [ @@ -31,7 +34,15 @@ "OpenGLWidgets", "Pdf", "Svg", - "Widgets" + "Widgets", + "Qml", + "QmlCore", + "QmlMeta", + "QmlModels", + "QmlWorkerScript", + "Quick", + "QuickWidgets", + "MultimediaQuick" ], "linux": [ "Concurrent", @@ -41,16 +52,19 @@ "EglFsKmsSupport", "Gui", "Multimedia", + "MultimediaQuick", "MultimediaWidgets", "Network", "OpenGL", "OpenGLWidgets", "Pdf", "Qml", + "QmlCore", "QmlMeta", "QmlModels", "QmlWorkerScript", "Quick", + "QuickWidgets", "Svg", "WaylandClient", "WaylandEglClientHwIntegration", @@ -95,6 +109,8 @@ ], "official_sources": { "qt": "https://download.qt.io/official_releases/qt/6.11/6.11.1/single/qt-everywhere-src-6.11.1.tar.xz", - "qt_for_python": "https://download.qt.io/official_releases/QtForPython/pyside6/PySide6-6.11.1-src/pyside-setup-everywhere-src-6.11.1.tar.xz" + "qt_for_python": "https://download.qt.io/official_releases/QtForPython/pyside6/PySide6-6.11.1-src/pyside-setup-everywhere-src-6.11.1.tar.xz", + "qml_license": "https://doc.qt.io/qt-6/qtqml-index.html#licenses-and-attributions", + "quick_license": "https://doc.qt.io/qt-6/qtquick-index.html#licenses-and-attributions" } } diff --git a/tools/release_asset_manifest.json b/tools/release_asset_manifest.json index f641b498..111f1188 100644 --- a/tools/release_asset_manifest.json +++ b/tools/release_asset_manifest.json @@ -27,6 +27,11 @@ ".woff2" ], "origins": { + "user-provided-gary-cat": { + "type": "user-provided", + "description": "Workspace owner supplied gatze.png on 2026-10-15 for the one-in-ten alternate Gary Mode appearance; included unchanged.", + "notice": "licenses/FTHR-USER-PROVIDED-ASSETS.txt" + }, "fthr-original-gary": { "type": "project-original", "description": "Original Gary artwork created by FTHR, confirmed by the project owner on 2026-09-17; retained as a source image, not an output of the release asset generator.", @@ -58,39 +63,2582 @@ "type": "user-provided", "description": "Workspace owner supplied the FTHR brand ICO used by the application window, executable, and Windows installer.", "notice": "licenses/FTHR-USER-PROVIDED-ASSETS.txt" + }, + "oswald-4.103-regular": { + "type": "upstream-unmodified", + "publisher": "The Oswald Project Authors / Google Fonts", + "version": "4.103", + "source_revision": "89795261ac9eeb9aa8cd99f43982c4e4b0e53261", + "source_file": "https://raw.githubusercontent.com/googlefonts/OswaldFont/89795261ac9eeb9aa8cd99f43982c4e4b0e53261/fonts/ttf/Oswald-Regular.ttf", + "source_license": "https://raw.githubusercontent.com/googlefonts/OswaldFont/89795261ac9eeb9aa8cd99f43982c4e4b0e53261/OFL.txt", + "notice": "licenses/Oswald-OFL-1.1.txt" + }, + "oswald-4.103-semibold": { + "type": "upstream-unmodified", + "publisher": "The Oswald Project Authors / Google Fonts", + "version": "4.103", + "source_revision": "89795261ac9eeb9aa8cd99f43982c4e4b0e53261", + "source_file": "https://raw.githubusercontent.com/googlefonts/OswaldFont/89795261ac9eeb9aa8cd99f43982c4e4b0e53261/fonts/ttf/Oswald-SemiBold.ttf", + "source_license": "https://raw.githubusercontent.com/googlefonts/OswaldFont/89795261ac9eeb9aa8cd99f43982c4e4b0e53261/OFL.txt", + "notice": "licenses/Oswald-OFL-1.1.txt" + }, + "fthr-ui-reference-svg": { + "type": "project-source", + "description": "Interface SVG artwork ported unchanged from the user-supplied FTHR-Clips-experimental/FTHR_UI/assets/icons source tree. Governed by the project source license; distinct from the generated MIT asset set.", + "notice": "LICENSE" + }, + "fthr-installer-polish-v1": { + "type": "project-generated", + "generator": "tools/generate_installer_assets.py", + "generator_version": 1, + "external_creative_inputs": [ + "FTHR_UI/assets/icons/close.svg", + "FTHR_UI/assets/fonts/Oswald-Bold.ttf" + ], + "description": "Antialiased step indicators using the existing Oswald font, and a proportion-preserving raster of the current application close SVG.", + "notice": "LICENSE" + }, + "kofi-promotional-symbol": { + "type": "third-party-brand-asset", + "publisher": "Ko-fi / Ko-fi Labs Limited", + "description": "Existing Ko-fi cup PNG visually verified against the official creator kit on 2026-10-13. Original acquisition URL not recorded. Used solely to promote the project Ko-fi page under the official promotional permission.", + "source_url": "https://more.ko-fi.com/brand-assets", + "permission_url": "https://help.ko-fi.com/hc/en-us/articles/360007169493-Can-I-use-the-Ko-fi-logo-to-promote-my-page", + "notice": "licenses/KO-FI-BRAND-ASSETS.txt" } }, "assets": [ - {"path": "FTHR_UI/assets/gary.png", "sha256": "4ccfb741b009977f0af444ba114c08340a070769d250dd163e279a28ccdc8571", "media_type": "image/png", "usage": ["Gary mode default image"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/gary.png"], "linux": ["_internal/assets/gary.png"]}, "packaging_references": ["FTHR.spec", "FTHR_linux.spec"], "license": "MIT", "copyright": "Copyright (c) 2026 Haakon Schaller, trading as FTHR-Software", "origin": "fthr-original-gary", "redistribution_status": "approved"}, - {"path": ".github/social_preview.png", "sha256": "ea90be692d4c58f23cc70f139021938ca196b7039255d98e098820962cbefe14", "media_type": "image/png", "usage": ["repository social preview"], "distributions": ["repository-only"], "artifact_paths": {}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "AppDir/fthr-clips.png", "sha256": "0266ea11270d48a927c09805d59a2fd957e2cceb035f6793bff29fd7d8b50e08", "media_type": "image/png", "usage": ["AppImage desktop icon"], "distributions": ["linux"], "artifact_paths": {"linux": ["fthr-clips.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/fonts/Oswald-Bold.ttf", "sha256": "eb7d46f856dd57f18a8c03d033c57802692bf127f01dbd95ba8984338e6b5135", "media_type": "font/ttf", "usage": ["application display font"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/fonts/Oswald-Bold.ttf"], "linux": ["_internal/assets/fonts/Oswald-Bold.ttf"]}, "license": "OFL-1.1", "copyright": "Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)", "origin": "oswald-4.103", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/fthr_logo.ico", "sha256": "026af246059efffd9bf899b62e898f74efb6bb6b3ae80f6a168376c42f4cc936", "media_type": "image/vnd.microsoft.icon", "usage": ["Windows executable icon"], "distributions": ["windows-executable-embedded"], "artifact_paths": {}, "packaging_references": ["FTHR.spec"], "license": "User-provided", "copyright": "Workspace owner supplied brand icon", "origin": "user-provided-brand-icon", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/favicon.ico", "sha256": "026af246059efffd9bf899b62e898f74efb6bb6b3ae80f6a168376c42f4cc936", "media_type": "image/vnd.microsoft.icon", "usage": ["window icon", "system tray icon", "watermark default", "theme default", "Windows installer icon and title-bar mark"], "distributions": ["windows", "linux", "windows-installer-embedded"], "artifact_paths": {"windows": ["_internal/assets/favicon.ico"], "linux": ["_internal/assets/favicon.ico"]}, "packaging_references": ["installer_windows.iss"], "license": "User-provided", "copyright": "Workspace owner supplied brand icon", "origin": "user-provided-brand-icon", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/fthr_logo.png", "sha256": "1164d541d01f2b71ba2003892389152f0f0535954f44c020bda3f0412919e90e", "media_type": "image/png", "usage": ["main window logo", "theme default"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/fthr_logo.png"], "linux": ["_internal/assets/fthr_logo.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/preview_desktop.png", "sha256": "4ba07a6b9b478f63d4897952a2bc02e9bc23ace1de18470da58cbdabe24cda55", "media_type": "image/png", "usage": ["standard visual-overlays preview background"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/preview_desktop.png"], "linux": ["_internal/assets/preview_desktop.png"]}, "license": "User-provided", "copyright": "Workspace owner supplied image", "origin": "user-provided-preview", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/clip.png", "sha256": "7dc91171c1d16bc9edd58501058ca597fe7fb9932abb869f35466b3d267a83c2", "media_type": "image/png", "usage": ["clip tab icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/clip.png"], "linux": ["_internal/assets/icons/clip.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/close.png", "sha256": "751d4c4b00852fa0fbd153e4f89360c51a22192a2fd3df51f864658b4403923f", "media_type": "image/png", "usage": ["close button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/close.png"], "linux": ["_internal/assets/icons/close.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/dropdown.png", "sha256": "8918f835bb6c8404932020d8d1f09ea420801a800ebd840f16d4ed0170b3b09f", "media_type": "image/png", "usage": ["combo-box arrow"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/dropdown.png"], "linux": ["_internal/assets/icons/dropdown.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/home.png", "sha256": "e2d589bfab6e06e63cc676dc8dcb924808b9e7776230738678dde6364faae313", "media_type": "image/png", "usage": ["home button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/home.png"], "linux": ["_internal/assets/icons/home.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/maximize.png", "sha256": "ee2c602cbc8ca68d8b6e8e0a7e744544d4613695eb0e8ea5095a355078578809", "media_type": "image/png", "usage": ["maximize button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/maximize.png"], "linux": ["_internal/assets/icons/maximize.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/minimize.png", "sha256": "e7861bdc5ba185c22f295408bda8cbf588b325b3ffd69bac43852d31dc8c38c4", "media_type": "image/png", "usage": ["minimize button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/minimize.png"], "linux": ["_internal/assets/icons/minimize.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/pause.png", "sha256": "1c25e339c453542fb3159ab102e22d7302bcc8ae6a253cdd44f2c26a1dc1fef7", "media_type": "image/png", "usage": ["media pause icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/pause.png"], "linux": ["_internal/assets/icons/pause.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/personalize.png", "sha256": "c3fea9fedd4f37f372f3dbe38928b4d7674709ae4cf9ef1451921b8457f1f983", "media_type": "image/png", "usage": ["customization tab icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/personalize.png"], "linux": ["_internal/assets/icons/personalize.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/play.png", "sha256": "3c9c4ba3d8a9794005c9d31ddac7cb8a237c17f4939fdaadac935c8ff0b2f9f8", "media_type": "image/png", "usage": ["media play icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/play.png"], "linux": ["_internal/assets/icons/play.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/refresh.png", "sha256": "101d18adaba7c478bcf65e6a39e27da545d6e4eb88a0aff43eda47d5f409c7c1", "media_type": "image/png", "usage": ["refresh button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/refresh.png"], "linux": ["_internal/assets/icons/refresh.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/settings(general).png", "sha256": "da070779137d109cbc175a0c8c64eecf4dac0277b01ae9fde90babdad20e04bf", "media_type": "image/png", "usage": ["settings tab icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/settings(general).png"], "linux": ["_internal/assets/icons/settings(general).png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/sound.png", "sha256": "d1b08687a69d0743fa1cfe39608e6931740fb2dd7d7469d4e24269a833b52b39", "media_type": "image/png", "usage": ["audio tab icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/sound.png"], "linux": ["_internal/assets/icons/sound.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/shutdown.png", "sha256": "964597788bd296f4843c7114b1e6af3899499c9eb3afa8da5a726180dca14bff", "media_type": "image/png", "usage": ["shutdown button icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/shutdown.png"], "linux": ["_internal/assets/icons/shutdown.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/updates.png", "sha256": "da8f7dadb9aa1ece5bfee82e1d5568e8506eef656153f591c4f33cb46b990032", "media_type": "image/png", "usage": ["updates and performance icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/updates.png"], "linux": ["_internal/assets/icons/updates.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/icons/visuals.png", "sha256": "e3176bb051eae62705e309248ca5320c037c0c218b9612dc2452c63fd3cbfd48", "media_type": "image/png", "usage": ["visuals tab icon"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/icons/visuals.png"], "linux": ["_internal/assets/icons/visuals.png"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/clip_captured.wav", "sha256": "e0da4dd3382d3e91347c59796e904a775afbfb81ef3752defffbb9769bf2863b", "media_type": "audio/wav", "usage": ["clip-captured notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/clip_captured.wav"], "linux": ["_internal/assets/sounds/clip_captured.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/error.wav", "sha256": "4af4ae3d084043b0c3cb7bda6943ad3ff10444c67722ca48c616b3a0827297a0", "media_type": "audio/wav", "usage": ["error notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/error.wav"], "linux": ["_internal/assets/sounds/error.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/screenshot_saved.wav", "sha256": "80a3e9e313d6b825177eb5a82513c5907a81550d0d4d16e744f49ffb42356efb", "media_type": "audio/wav", "usage": ["screenshot-saved notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/screenshot_saved.wav"], "linux": ["_internal/assets/sounds/screenshot_saved.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/startup.wav", "sha256": "56a04ecc28578f8cd83adfb2f37f2fa7d68ad3a7d29cb0068b6ec4cc556d4152", "media_type": "audio/wav", "usage": ["startup notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/startup.wav"], "linux": ["_internal/assets/sounds/startup.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/upload_failed.wav", "sha256": "8e99c82a31e66f85a86d97df68c5fc05b0601dafd566b3b15e29b84770b35f1c", "media_type": "audio/wav", "usage": ["upload-failed notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/upload_failed.wav"], "linux": ["_internal/assets/sounds/upload_failed.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "FTHR_UI/assets/sounds/upload_successful.wav", "sha256": "9eb8a4605642b28663697bb8045434208aeb2198359d5c5ee2c236ed9bd62137", "media_type": "audio/wav", "usage": ["upload-success notification"], "distributions": ["windows", "linux"], "artifact_paths": {"windows": ["_internal/assets/sounds/upload_successful.wav"], "linux": ["_internal/assets/sounds/upload_successful.wav"]}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "installer_assets/wizard_banner.bmp", "sha256": "22c0a8a0d2687cb4fd903856988aca208487948c4f92b007d8b068947e95d45a", "media_type": "image/bmp", "usage": ["legacy installer artwork retained in source but not embedded"], "distributions": [], "artifact_paths": {}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"}, - {"path": "installer_assets/wizard_small.bmp", "sha256": "55448c510e3562627239f18cd7ad4e384654dc8abcda7a319d44ef99794c0852", "media_type": "image/bmp", "usage": ["legacy installer artwork retained in source but not embedded"], "distributions": [], "artifact_paths": {}, "license": "MIT", "copyright": "Copyright (c) 2026 FTHR-Community", "origin": "fthr-generator-v1", "redistribution_status": "approved"} + { + "path": "FTHR_UI/assets/gatze.png", + "sha256": "cbbbdf11da3d21f1fe1710798396c3f21f9f35cb47ecda07d0b6540b8250799f", + "media_type": "image/png", + "usage": [ + "Gary mode one-in-ten alternate image" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/gatze.png" + ], + "linux": [ + "_internal/assets/gatze.png" + ] + }, + "packaging_references": [ + "FTHR.spec", + "FTHR_linux.spec" + ], + "license": "User-provided", + "copyright": "Workspace owner supplied image", + "origin": "user-provided-gary-cat", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/gary.png", + "sha256": "4ccfb741b009977f0af444ba114c08340a070769d250dd163e279a28ccdc8571", + "media_type": "image/png", + "usage": [ + "Gary mode default image" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/gary.png" + ], + "linux": [ + "_internal/assets/gary.png" + ] + }, + "packaging_references": [ + "FTHR.spec", + "FTHR_linux.spec" + ], + "license": "MIT", + "copyright": "Copyright (c) 2026 Haakon Schaller, trading as FTHR-Software", + "origin": "fthr-original-gary", + "redistribution_status": "approved" + }, + { + "path": "AppDir/fthr-clips.png", + "sha256": "0266ea11270d48a927c09805d59a2fd957e2cceb035f6793bff29fd7d8b50e08", + "media_type": "image/png", + "usage": [ + "AppImage desktop icon" + ], + "distributions": [ + "linux" + ], + "artifact_paths": { + "linux": [ + "fthr-clips.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/fonts/Oswald-Bold.ttf", + "sha256": "eb7d46f856dd57f18a8c03d033c57802692bf127f01dbd95ba8984338e6b5135", + "media_type": "font/ttf", + "usage": [ + "application display font" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/fonts/Oswald-Bold.ttf" + ], + "linux": [ + "_internal/assets/fonts/Oswald-Bold.ttf" + ] + }, + "license": "OFL-1.1", + "copyright": "Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)", + "origin": "oswald-4.103", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/fthr_logo.ico", + "sha256": "026af246059efffd9bf899b62e898f74efb6bb6b3ae80f6a168376c42f4cc936", + "media_type": "image/vnd.microsoft.icon", + "usage": [ + "Windows executable icon" + ], + "distributions": [ + "windows-executable-embedded" + ], + "artifact_paths": {}, + "packaging_references": [ + "FTHR.spec" + ], + "license": "User-provided", + "copyright": "Workspace owner supplied brand icon", + "origin": "user-provided-brand-icon", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/favicon.ico", + "sha256": "026af246059efffd9bf899b62e898f74efb6bb6b3ae80f6a168376c42f4cc936", + "media_type": "image/vnd.microsoft.icon", + "usage": [ + "window icon", + "system tray icon", + "watermark default", + "theme default", + "Windows installer icon and title-bar mark" + ], + "distributions": [ + "windows", + "linux", + "windows-installer-embedded" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/favicon.ico" + ], + "linux": [ + "_internal/assets/favicon.ico" + ] + }, + "packaging_references": [ + "installer_windows.iss" + ], + "license": "User-provided", + "copyright": "Workspace owner supplied brand icon", + "origin": "user-provided-brand-icon", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/fthr_logo.png", + "sha256": "1164d541d01f2b71ba2003892389152f0f0535954f44c020bda3f0412919e90e", + "media_type": "image/png", + "usage": [ + "main window logo", + "theme default" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/fthr_logo.png" + ], + "linux": [ + "_internal/assets/fthr_logo.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/preview_desktop.png", + "sha256": "4ba07a6b9b478f63d4897952a2bc02e9bc23ace1de18470da58cbdabe24cda55", + "media_type": "image/png", + "usage": [ + "standard visual-overlays preview background" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/preview_desktop.png" + ], + "linux": [ + "_internal/assets/preview_desktop.png" + ] + }, + "license": "User-provided", + "copyright": "Workspace owner supplied image", + "origin": "user-provided-preview", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/clip.png", + "sha256": "7dc91171c1d16bc9edd58501058ca597fe7fb9932abb869f35466b3d267a83c2", + "media_type": "image/png", + "usage": [ + "clip tab icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/clip.png" + ], + "linux": [ + "_internal/assets/icons/clip.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/close.png", + "sha256": "751d4c4b00852fa0fbd153e4f89360c51a22192a2fd3df51f864658b4403923f", + "media_type": "image/png", + "usage": [ + "close button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/close.png" + ], + "linux": [ + "_internal/assets/icons/close.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/dropdown.png", + "sha256": "8918f835bb6c8404932020d8d1f09ea420801a800ebd840f16d4ed0170b3b09f", + "media_type": "image/png", + "usage": [ + "combo-box arrow" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/dropdown.png" + ], + "linux": [ + "_internal/assets/icons/dropdown.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/home.png", + "sha256": "e2d589bfab6e06e63cc676dc8dcb924808b9e7776230738678dde6364faae313", + "media_type": "image/png", + "usage": [ + "home button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/home.png" + ], + "linux": [ + "_internal/assets/icons/home.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/maximize.png", + "sha256": "ee2c602cbc8ca68d8b6e8e0a7e744544d4613695eb0e8ea5095a355078578809", + "media_type": "image/png", + "usage": [ + "maximize button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/maximize.png" + ], + "linux": [ + "_internal/assets/icons/maximize.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/minimize.png", + "sha256": "e7861bdc5ba185c22f295408bda8cbf588b325b3ffd69bac43852d31dc8c38c4", + "media_type": "image/png", + "usage": [ + "minimize button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/minimize.png" + ], + "linux": [ + "_internal/assets/icons/minimize.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/pause.png", + "sha256": "1c25e339c453542fb3159ab102e22d7302bcc8ae6a253cdd44f2c26a1dc1fef7", + "media_type": "image/png", + "usage": [ + "media pause icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/pause.png" + ], + "linux": [ + "_internal/assets/icons/pause.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/personalize.png", + "sha256": "c3fea9fedd4f37f372f3dbe38928b4d7674709ae4cf9ef1451921b8457f1f983", + "media_type": "image/png", + "usage": [ + "customization tab icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/personalize.png" + ], + "linux": [ + "_internal/assets/icons/personalize.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/play.png", + "sha256": "3c9c4ba3d8a9794005c9d31ddac7cb8a237c17f4939fdaadac935c8ff0b2f9f8", + "media_type": "image/png", + "usage": [ + "media play icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/play.png" + ], + "linux": [ + "_internal/assets/icons/play.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/refresh.png", + "sha256": "101d18adaba7c478bcf65e6a39e27da545d6e4eb88a0aff43eda47d5f409c7c1", + "media_type": "image/png", + "usage": [ + "refresh button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/refresh.png" + ], + "linux": [ + "_internal/assets/icons/refresh.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/settings(general).png", + "sha256": "da070779137d109cbc175a0c8c64eecf4dac0277b01ae9fde90babdad20e04bf", + "media_type": "image/png", + "usage": [ + "settings tab icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/settings(general).png" + ], + "linux": [ + "_internal/assets/icons/settings(general).png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/sound.png", + "sha256": "d1b08687a69d0743fa1cfe39608e6931740fb2dd7d7469d4e24269a833b52b39", + "media_type": "image/png", + "usage": [ + "audio tab icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/sound.png" + ], + "linux": [ + "_internal/assets/icons/sound.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/shutdown.png", + "sha256": "964597788bd296f4843c7114b1e6af3899499c9eb3afa8da5a726180dca14bff", + "media_type": "image/png", + "usage": [ + "shutdown button icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/shutdown.png" + ], + "linux": [ + "_internal/assets/icons/shutdown.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/updates.png", + "sha256": "da8f7dadb9aa1ece5bfee82e1d5568e8506eef656153f591c4f33cb46b990032", + "media_type": "image/png", + "usage": [ + "updates and performance icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/updates.png" + ], + "linux": [ + "_internal/assets/icons/updates.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/visuals.png", + "sha256": "e3176bb051eae62705e309248ca5320c037c0c218b9612dc2452c63fd3cbfd48", + "media_type": "image/png", + "usage": [ + "visuals tab icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/visuals.png" + ], + "linux": [ + "_internal/assets/icons/visuals.png" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/clip_captured.wav", + "sha256": "e0da4dd3382d3e91347c59796e904a775afbfb81ef3752defffbb9769bf2863b", + "media_type": "audio/wav", + "usage": [ + "clip-captured notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/clip_captured.wav" + ], + "linux": [ + "_internal/assets/sounds/clip_captured.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/error.wav", + "sha256": "4af4ae3d084043b0c3cb7bda6943ad3ff10444c67722ca48c616b3a0827297a0", + "media_type": "audio/wav", + "usage": [ + "error notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/error.wav" + ], + "linux": [ + "_internal/assets/sounds/error.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/screenshot_saved.wav", + "sha256": "80a3e9e313d6b825177eb5a82513c5907a81550d0d4d16e744f49ffb42356efb", + "media_type": "audio/wav", + "usage": [ + "screenshot-saved notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/screenshot_saved.wav" + ], + "linux": [ + "_internal/assets/sounds/screenshot_saved.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/startup.wav", + "sha256": "56a04ecc28578f8cd83adfb2f37f2fa7d68ad3a7d29cb0068b6ec4cc556d4152", + "media_type": "audio/wav", + "usage": [ + "startup notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/startup.wav" + ], + "linux": [ + "_internal/assets/sounds/startup.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/upload_failed.wav", + "sha256": "8e99c82a31e66f85a86d97df68c5fc05b0601dafd566b3b15e29b84770b35f1c", + "media_type": "audio/wav", + "usage": [ + "upload-failed notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/upload_failed.wav" + ], + "linux": [ + "_internal/assets/sounds/upload_failed.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/sounds/upload_successful.wav", + "sha256": "9eb8a4605642b28663697bb8045434208aeb2198359d5c5ee2c236ed9bd62137", + "media_type": "audio/wav", + "usage": [ + "upload-success notification" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/sounds/upload_successful.wav" + ], + "linux": [ + "_internal/assets/sounds/upload_successful.wav" + ] + }, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/wizard_banner.bmp", + "sha256": "22c0a8a0d2687cb4fd903856988aca208487948c4f92b007d8b068947e95d45a", + "media_type": "image/bmp", + "usage": [ + "legacy installer artwork retained in source but not embedded" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/wizard_small.bmp", + "sha256": "55448c510e3562627239f18cd7ad4e384654dc8abcda7a319d44ef99794c0852", + "media_type": "image/bmp", + "usage": [ + "legacy installer artwork retained in source but not embedded" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT", + "copyright": "Copyright (c) 2026 FTHR-Community", + "origin": "fthr-generator-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/fonts/Oswald-Regular.ttf", + "sha256": "0d9980da817ee71ad9c34bb46950fcf15dd53e928031f39d2e11ae19178ab826", + "media_type": "font/ttf", + "usage": [ + "application regular interface font" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/fonts/Oswald-Regular.ttf" + ], + "linux": [ + "_internal/assets/fonts/Oswald-Regular.ttf" + ] + }, + "license": "OFL-1.1", + "copyright": "Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)", + "origin": "oswald-4.103-regular", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/fonts/Oswald-SemiBold.ttf", + "sha256": "8c5e61737c100d0a1ca87a9a1608afc53246b8a77f8d73c56b03406adc2d9282", + "media_type": "font/ttf", + "usage": [ + "application semibold interface font" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/fonts/Oswald-SemiBold.ttf" + ], + "linux": [ + "_internal/assets/fonts/Oswald-SemiBold.ttf" + ] + }, + "license": "OFL-1.1", + "copyright": "Copyright 2016 The Oswald Project Authors (https://github.com/googlefonts/OswaldFont)", + "origin": "oswald-4.103-semibold", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/camera.svg", + "sha256": "852987633643a36ae2ac2ba75ae306b7a4af1bf12fc602b4053ce63de70a919e", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/camera.svg" + ], + "linux": [ + "_internal/assets/icons/camera.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/chevronleft.svg", + "sha256": "eb36867087cf0074c67029c848c749ae7bf40d2a058efdc48a346a3780858dac", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/chevronleft.svg" + ], + "linux": [ + "_internal/assets/icons/chevronleft.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/chevronright.svg", + "sha256": "3cb67583cf220d219a490db86ee209645866d0619db36f47be04ea18f12f137b", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/chevronright.svg" + ], + "linux": [ + "_internal/assets/icons/chevronright.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/clips.svg", + "sha256": "1c4d1fe273ccaf209f31983a0f448f4800e95e88e6d5ffb1c3a695a1c1f607b5", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/clips.svg" + ], + "linux": [ + "_internal/assets/icons/clips.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/close.svg", + "sha256": "026a61b97ae9c76662d1ae2ecd42029e0fa56cb61ce948bf4c2edc22c8b19103", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/close.svg" + ], + "linux": [ + "_internal/assets/icons/close.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/copylink.svg", + "sha256": "04fa1ee1894b458e4f6575293c0fcbbf8dcffc908c74aec0594260af68192a07", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/copylink.svg" + ], + "linux": [ + "_internal/assets/icons/copylink.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/customise.svg", + "sha256": "95610614af94201e590e22571763538440afa11a8c3fb5f067c531ef0fa9e530", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/customise.svg" + ], + "linux": [ + "_internal/assets/icons/customise.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/delete.svg", + "sha256": "429b3f711367f4072e89cd670346471fc73b26e061283d114f17323cbc8f11ac", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/delete.svg" + ], + "linux": [ + "_internal/assets/icons/delete.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/dropdown.svg", + "sha256": "e2e2b9bdea6cc43937d3894b98df0c7207344fc9530c02aa896e75dcadc0daec", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/dropdown.svg" + ], + "linux": [ + "_internal/assets/icons/dropdown.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/home.svg", + "sha256": "1c4d1fe273ccaf209f31983a0f448f4800e95e88e6d5ffb1c3a695a1c1f607b5", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/home.svg" + ], + "linux": [ + "_internal/assets/icons/home.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/maximise.svg", + "sha256": "f608bf3e31c5fca36ff472f95932928ed20e1f6a6d89dc3683c6b3398090f0d3", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/maximise.svg" + ], + "linux": [ + "_internal/assets/icons/maximise.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/minimise.svg", + "sha256": "450dbeda48d38f703b4c1ecbd3b17a70c0dd2b5ec1738d0f076bc60c77bd4b90", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/minimise.svg" + ], + "linux": [ + "_internal/assets/icons/minimise.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/pause.svg", + "sha256": "2a3c86dc08ba30efbd4b9f021c1dd9ee7318be41541137e7069e17fc425a6446", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/pause.svg" + ], + "linux": [ + "_internal/assets/icons/pause.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/performance.svg", + "sha256": "d5e13ffdcf86e32d19e2c62db1b743aadaf50526378ee83034c1de6c24f70b97", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/performance.svg" + ], + "linux": [ + "_internal/assets/icons/performance.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/play.svg", + "sha256": "99287ca034fc58d3eb984111c50fe2277505ba8e9f8e6b1aba8243b053dded1b", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/play.svg" + ], + "linux": [ + "_internal/assets/icons/play.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/plus.svg", + "sha256": "3fb969804f21bf7e3746d9a845a7eaed49d3c0c45c19ef70cd0f5d51de26128d", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/plus.svg" + ], + "linux": [ + "_internal/assets/icons/plus.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/refresh.svg", + "sha256": "aa3b373577d10c4e4a2e695bca8a21875e0c4dfed52660c0ed5d581d5544554a", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/refresh.svg" + ], + "linux": [ + "_internal/assets/icons/refresh.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/settings.svg", + "sha256": "52ec0340493277455b1f2e6a7e05dfdf0b05f0211b2728b1a8ad4633dd3acc9b", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/settings.svg" + ], + "linux": [ + "_internal/assets/icons/settings.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/shortcuts.svg", + "sha256": "f43ecb9b857fffc8cf2108f69e510f05e70910d63a448ffa849d453f981ffc8c", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/shortcuts.svg" + ], + "linux": [ + "_internal/assets/icons/shortcuts.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/shutdown.svg", + "sha256": "2aac6a5fadad6a25e866d6b7d214541f50da45fdbcd3804bf8a8291e45d82dad", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/shutdown.svg" + ], + "linux": [ + "_internal/assets/icons/shutdown.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/sound.svg", + "sha256": "21107ee03ba994ac0f9f26808eef1e3f28268e4f7e4d983dae5c68c04c53d1c8", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/sound.svg" + ], + "linux": [ + "_internal/assets/icons/sound.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/update.svg", + "sha256": "1b3f9dd2f5741465d0790817f75f6ecec76815fc4f7e95df32f5590ad7f1a555", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/update.svg" + ], + "linux": [ + "_internal/assets/icons/update.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/upload.svg", + "sha256": "2c6b59d1f061056228a1176766a8b71fabc7a52ccd2713dc6dbe0ca0a328619d", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/upload.svg" + ], + "linux": [ + "_internal/assets/icons/upload.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/visuals.svg", + "sha256": "f0166a1ec71eb48db378114e2cbd57700f4f690dc1bea73909c3a94df55ee698", + "media_type": "image/svg+xml", + "usage": [ + "application interface icon" + ], + "distributions": [ + "windows", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/visuals.svg" + ], + "linux": [ + "_internal/assets/icons/visuals.svg" + ] + }, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Software (project-supplied source artwork)", + "origin": "fthr-ui-reference-svg", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/close-current.png", + "sha256": "940a88bf9bffcdccdefe519046c588233d505ed1906c21eda57b94211798bf23", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "GPL-3.0-only", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-108.png", + "sha256": "6278873a7ce8a218772da2b6456454d1ff1fd48439e8676e64ca503e022e7633", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-122.png", + "sha256": "4b0d4b07184be1adbb9e9aac208339bf89eb8782f363f220215b89341f2e3e35", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-135.png", + "sha256": "5d8685c5299fb373762d9e57ef9a28304eaab6f0c9dfbd703472ee89a2500d2a", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-162.png", + "sha256": "e5abb277168e4a749e95143b0787754ed2b9f8f0f102729b553d7703d9f1caaa", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-216.png", + "sha256": "1c2dca7306dcf3b95b18fc9c17946634717dbf6b19b09d1ac4de5bfbf1af90c9", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-54.png", + "sha256": "246d54ea75cd401e38f02dbd5563b90a343e2b82cc96d3db6e83b54765095a6a", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-68.png", + "sha256": "f01d327fb7f57c540960a47f3d6d113da8e0f88d287ef67b4b811a5bf84a61a3", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-81.png", + "sha256": "d715e657a069a5f97cccfcd1cd6d6cdc901c046003ef5d5dcee37e2f7d340554", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-active-95.png", + "sha256": "d42df6c61b3e8e1e5db65409c84f9098f063277992bc53852014d9980f7c725a", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-108.png", + "sha256": "ea3cdb0de27ef861768910b5cdcb8cab173e94f3a60f120e091c0ac60145f90f", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-122.png", + "sha256": "0c3905ed3137c9096c37a3740787ed89490c5b5c087230cbc90c58936fdd8a6b", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-135.png", + "sha256": "370dabaf4ff1e906d63ec4cd11ae9094feee5fef579cdb40c862c06d16c7f331", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-162.png", + "sha256": "cbbff0dfd6712e8d32c102bde2f8b0fa6e455bce8fdca1de2c121e91ece29328", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-216.png", + "sha256": "153fb4a2f9bc7da3a08aeb19b4b1f5e2125d5243a3ce67f448deb8f14f1ed97f", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-54.png", + "sha256": "57d6966f8ab4ad244eec113d7171c0e7e09703d4244df3c5e9e42bff55942c04", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-68.png", + "sha256": "1ae1ec134ca5e6bb3c6c2753007325fdfe383549b6d810a6ac23a0d27f10df01", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-81.png", + "sha256": "1cf25cad987430648ab7f98d56f7c94105c3ed8190790e36216f4b840fd67d42", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-done-95.png", + "sha256": "36affb0a5f129e256f47d581f9e757fed1b7cae1b54613056daa2c260d117936", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-108.png", + "sha256": "a30429707c2875dcc210ff3a99d128ee245950556bcaec619b039d0bc6496405", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-122.png", + "sha256": "4bb3f9bec9cea5311748bbf0f33d4ef90221d50f35f269308b78d750ebe3f0cc", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-135.png", + "sha256": "927508ff69a4d790391907dbb08259bf7bd426c7b600178186246bec8a4074f2", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-162.png", + "sha256": "4dc3cd5b09cc69665c43a094bf0080de9798ce2b5926b210a549c7ef833a192c", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-216.png", + "sha256": "11906f84a16c17a6c342be334505e463cb2932764f06647f5e668cc1a2264564", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-54.png", + "sha256": "b908f6f6da476d5d29b1c7551fff154a6449881bde2f0b80dd66a12af7623228", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-68.png", + "sha256": "1156b100803eb9cc5919d4099964350472a98433fa4cb0e331f9be0e96e9c00b", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-81.png", + "sha256": "8844b483784a2b3945b2a2aee92d4c65fcb9ee1dec3ca363c2e55b4daca80e9e", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-1-pending-95.png", + "sha256": "f643a52c9ee7a1b3e82314e586074bda0b2b54ce650237097be13e97ae710726", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-108.png", + "sha256": "b2cc27b12da0875632bde63ac210380becc0936ed4b461f6466cd92fb8a8e367", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-122.png", + "sha256": "2213d648fe012fb6b9a9f4ab5a2f19f69d136b095ea9cb47eee6570f547712b2", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-135.png", + "sha256": "be98c53a70d37ac7dc264dbba0768b6fcd3507d62900fa91756b360bd9b7c621", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-162.png", + "sha256": "bc876b2f180a678fddb94d400c3b1d54e6d555a5e644bb4f71d2c31532c9a054", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-216.png", + "sha256": "c189fe81df6ae3efcf605c2f396e555b89e519f247576f497a3f409342f1d9d4", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-54.png", + "sha256": "53bd8e25e5d50701cb694b612fd403f28ff2561cfb41c0c6d0dd52d389ffcb37", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-68.png", + "sha256": "d5d091fb56814348397f40b37a442f3319a219cb9164f3d4f67e679307aa447c", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-81.png", + "sha256": "0d12657c460d2c88e434142ab50bb780f94be92756847d3baa6fd11b0ded3fd5", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-active-95.png", + "sha256": "3eee2c6e2f1f9082db7e804ec8e1f9769fcc9a50da260a1c3d7cbc05e2975b53", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-108.png", + "sha256": "d0e59e4aac5e74a485ea6a154717daf61d2a1f4fac3906be07d934abccddd143", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-122.png", + "sha256": "84b5992c566088309a48161f58221bd4b050ff2aa4b1394820767c13439f5d53", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-135.png", + "sha256": "2972e698ac572bc915bb991eaa7d7ac682d36f4a9f8cfefca883f57ebe9039ea", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-162.png", + "sha256": "40f1b37fcf16ec5fdd040047f06beff29569f46452a1038435e1ec49a0a75131", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-216.png", + "sha256": "ef7a4b5bb49c5f507142090d0a87688bdd6d064d5b42714d47b4d4a9fa765c1b", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-54.png", + "sha256": "4abc50f7a1acf19e69c62e8eaee97581cc10c62c843db5bd50148284556198d9", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-68.png", + "sha256": "dd3e3e263364a9595ddb7dfcb11c0315feea6230019812855d843ef872fd148c", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-81.png", + "sha256": "ab5aab216e0e98943e3af4343781acbbe8d96dd2dd666d9e761f5d47975c9ea6", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-done-95.png", + "sha256": "485c99cb2d0712cf54d0d8f6acda6617c219e6e118b24a25aba4bec6d495240a", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-108.png", + "sha256": "c31a0b6168c7a87d6b2da2537dda7b1a6a33065eb208bcfa7262c015c10ab7fb", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-122.png", + "sha256": "d6aa3608bdc56c90c6f11a7c72ef7d1de4cd2037ad48f22f25affd1fd8668666", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-135.png", + "sha256": "e560d391f0c0d2a5560bc8fb90fcb39534b55996f525837ec7facada35369110", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-162.png", + "sha256": "65c7854ca52cc13673cec4efd6be7be669b71eeb4e952f04d44f37adc0d485c8", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-216.png", + "sha256": "cea6f0b3f4302bd2bf778d678d22ecc605238c41db948deb9d199305d5d0fb59", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-54.png", + "sha256": "301c0135d2253af0e5a59b1245b4550794a61092163b9372a314598f7a837ab1", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-68.png", + "sha256": "40ca8a77d8568bba9e5604c31c8405ca699dfef1205277033771cfdfc08ba87d", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-81.png", + "sha256": "939955ecf08af064ad6df8e35e01f0891e48ef915275083b16864cd4aa6d2bf5", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-2-pending-95.png", + "sha256": "00e73facf55b9596b5ed5d14217d2405507e8fbd4244d0b1147ffe89e4bab2e1", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-108.png", + "sha256": "660c4a970d432e411ffa2af8d94be3072e5a53af86eb9b296671628a4e12140d", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-122.png", + "sha256": "d0b26a5aa3e7c14a94f490a345fe75825dcd01090f822b2dcb7bf3dd71cb6e0c", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-135.png", + "sha256": "a366cc8cadd171276de95a116b6581a23ada4db816381e05a812331f3c588088", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-162.png", + "sha256": "ae8910fd51c3ffb0465ba3e60a3b63e8fab6e278bb015c293d8ced9d901c8ced", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-216.png", + "sha256": "ef45d48d4d6fb239c2a332d396a626a95a4d76033b04735f0a02e23b6ecf33ca", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-54.png", + "sha256": "49ed06bd3eb4d4d141be2b934a4b67a6a669bc45b83409e0a8222ba2517abb48", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-68.png", + "sha256": "0aa316d200c7491f76ff65965c081e079521ea86351ec8ae61e6231fae7323bb", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-81.png", + "sha256": "3a4468cd8448de923b5b5e2654f193cdd8aa3f344c21163841d204d544cc015f", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-active-95.png", + "sha256": "ef9ea3259de3dcfa0afdee6f7251607e1e556b71e65054f50bd9a010d9b0ca9f", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-108.png", + "sha256": "dcc19050fd4a815402703cbe765cb6662ce3514930b9c4311a47564a0319a1cf", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-122.png", + "sha256": "7347818e59e452093ae7da2af6f39da4e1255cff526cbf6e597c1c2390d9cf65", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-135.png", + "sha256": "7299e550e1f595b7b2e8351ba22d566c49cc05eae667def84ec8a7cb6d47bb26", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-162.png", + "sha256": "75cb2a39c2bbe420b9edb835707782e4a11f3d6c47f63451696d11b526a3ba81", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-216.png", + "sha256": "ddf8dbcc372fdb04b4841ccdb98d51ec2364a3199d1fdc5580e2e9210ca25d3a", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-54.png", + "sha256": "9717a71cc79dc4894fc5c6722477477b60d3f78708da064c11e97d7164468171", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-68.png", + "sha256": "6de20add10a7f5edd4b1865be0be01a9cb27a8149dc7294229af67915b49cd12", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-81.png", + "sha256": "1f83db156b5571447c48e265524faffb44b4ba60b035f22cb89b13d32c477ff5", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-done-95.png", + "sha256": "ac9ca5ba8d19650eceac5162867bca04bad6b8ebce51fe1f22270810a32db4a6", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-108.png", + "sha256": "c88caacd9a2ed1e6ca0e20dd06b3b1e33ba3cbe0eaaea180362d6a4ef916e698", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-122.png", + "sha256": "6a09419396ffb33671eee62ba6a4a98062c7fbc9515790a2cf89e6b7ee748a30", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-135.png", + "sha256": "b7dd38ec3f14ad788ec8be2eef47d0c8c614b0760200a664491672d40d95415d", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-162.png", + "sha256": "dff2e52c337b423c2c56902ab7ce075eb97ba47dfd183841efa672b903722327", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-216.png", + "sha256": "1e3244141a480cd1d2e4ab3fd04092088a7d5d56f692281f45990d8913a71e04", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-54.png", + "sha256": "a00785e95ca455beaf4ebb62dcf39309e3c48acadacaa12bc02198eda225d0f2", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-68.png", + "sha256": "5a0b848b7119283487dc943ae4301e6b61095ef2c1440a334c16bfec543a8873", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-81.png", + "sha256": "47590197aff3c0ec686ea5c4e289e1bc3a9c6e1d2292c76ca96495d776b6a02c", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "installer_assets/polished/step-3-pending-95.png", + "sha256": "8e784531a552f6bd4f08a0e49d11b152a28be4060a762ff23ccb57ed7984aae9", + "media_type": "image/png", + "usage": [ + "Windows installer artwork" + ], + "distributions": [], + "artifact_paths": {}, + "license": "MIT; font rendering from OFL-1.1 Oswald", + "copyright": "Copyright (c) 2026 FTHR-Community; close artwork: FTHR-Software; font: The Oswald Project Authors", + "origin": "fthr-installer-polish-v1", + "redistribution_status": "approved" + }, + { + "path": "FTHR_UI/assets/icons/kofi_symbol.png", + "sha256": "6b0f958d50f68c84e235473a151f9e365a02e5a9abae4de63048d5f1210c6637", + "media_type": "image/png", + "usage": [ + "Donation button linking to the project Ko-fi page" + ], + "distributions": [ + "windows", + "linux", + "linux" + ], + "artifact_paths": { + "windows": [ + "_internal/assets/icons/kofi_symbol.png" + ], + "linux": [ + "_internal/assets/icons/kofi_symbol.png" + ] + }, + "packaging_references": [ + "FTHR_UI/main.py" + ], + "license": "Ko-fi promotional brand-asset permission; subject to content guidelines", + "copyright": "Ko-fi / Ko-fi Labs Limited", + "origin": "kofi-promotional-symbol", + "redistribution_status": "approved" + } ] } diff --git a/tools/verify_release_licenses.py b/tools/verify_release_licenses.py index 9ce9148f..1d18da4a 100644 --- a/tools/verify_release_licenses.py +++ b/tools/verify_release_licenses.py @@ -638,6 +638,31 @@ def check_asset_artifact(root: Path, rep: Report, platform: str, else: rep.ok(f'{platform} artifact contains exactly {len(expected)} ' 'approved asset files') +# Scenes and the precompiled shader the editor loads from beside +# ui/quick_*.py at runtime. Without them the GPU preview silently falls back to +# the CPU path and snapshots fail, so a bundle that lacks them is broken. +QML_SCENE_FILES = ('VideoPreview.qml', 'Snapshot.qml', 'color.frag.qsb') +QML_SCENE_ARTIFACT_DIR = '_internal/ui/qml' +QML_SCENE_SOURCE_DIR = 'FTHR_UI/ui/qml' + + +def check_qml_scenes(root: Path, rep: Report, platform: str, + manifest_root: Path | None = None) -> None: + """Require the application's QML scenes, byte-identical to the source.""" + print(f'\n-- Application QML scenes in {platform} artifact --') + source_root = manifest_root or _repo_root() + for name in QML_SCENE_FILES: + rel = f'{QML_SCENE_ARTIFACT_DIR}/{name}' + shipped = root / rel + source = source_root / QML_SCENE_SOURCE_DIR / name + if not shipped.is_file(): + rep.fail(f'{platform} artifact: application QML file missing: {rel}') + elif source.is_file() and _file_sha256(source) != _file_sha256(shipped): + rep.fail(f'{platform} artifact: {rel} differs from {QML_SCENE_SOURCE_DIR}/{name}') + else: + rep.ok(f'{platform} artifact: {rel}') + + def check_linux_manifest(root, rep) -> None: """The Linux FFmpeg provenance manifest (AUDIT-014).""" print('\n-- Linux FFmpeg provenance manifest --') @@ -926,6 +951,7 @@ def main() -> int: check_qt_artifact(cand, rep, platform, manifest_root=root) check_asset_artifact( cand, rep, platform, manifest_root=root) + check_qml_scenes(cand, rep, platform, manifest_root=root) if args.windows_dist: d = args.windows_dist.resolve() @@ -935,6 +961,7 @@ def main() -> int: check_qt_artifact(d, rep, 'windows', manifest_root=_repo_root()) check_asset_artifact( d, rep, 'windows', manifest_root=_repo_root()) + check_qml_scenes(d, rep, 'windows', manifest_root=_repo_root()) if args.appdir: d = args.appdir.resolve() @@ -943,6 +970,7 @@ def main() -> int: scan_dir(d, rep, 'appdir') check_qt_artifact(d, rep, 'linux', manifest_root=_repo_root()) check_asset_artifact(d, rep, 'linux', manifest_root=_repo_root()) + check_qml_scenes(d, rep, 'linux', manifest_root=_repo_root()) # AUDIT-014: the artifact is where it actually matters. A GPL library # the engine never loads is still a GPL library being distributed, and # PyInstaller collects the system FFmpeg through cv2 and Qt.