diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 57944f81..783be0c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,24 @@ jobs: rpmbuild -bs --define "_topdir $RPM_TOP" "$RPM_TOP/SPECS/hydra.spec" ls "$RPM_TOP"/SRPMS/hydra-${VERSION}-*.src.rpm + flatpak-validate: + name: Flatpak Metadata Validation + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install validation tools + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends appstream desktop-file-utils + + - name: Validate Flatpak desktop and metainfo files + run: | + set -euo pipefail + desktop-file-validate packaging/flatpak/io.github.ja7ad.hydra.desktop + appstreamcli validate --no-net packaging/flatpak/io.github.ja7ad.hydra.metainfo.xml + fmt: name: Code Formatting runs-on: ubuntu-latest diff --git a/Makefile b/Makefile index f0a6df86..57444045 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ # make rpm Fedora/RHEL/openSUSE package -> target/dist/*.rpm # make linux both deb and rpm # make appimage portable, self-updating AppImage -> target/dist/*.AppImage +# make flatpak Flatpak package and metadata -> target/dist/*.flatpak # make package the right artifact(s) for the OS make runs on # # make extensions browser extensions: packed .xpi + .zip (+ .crx with a @@ -32,7 +33,7 @@ VERSION := $(shell sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1) PROFILE ?= release CARGO ?= cargo -.PHONY: all build cli gui host app dmg deb rpm linux appimage windows package clean \ +.PHONY: all build cli gui host app dmg deb rpm linux appimage flatpak windows package clean \ extensions \ require-macos require-linux ffi header header-check ffi-compat \ ffi-test ffi-dist ffi-android ffi-apple @@ -147,6 +148,14 @@ linux: require-linux appimage: require-linux scripts/package-appimage.sh $(ARGS) +# Flatpak build, metadata validation, or bundle generation: +# +# make flatpak ARGS=--bundle export a standalone .flatpak file +# make flatpak ARGS=--install build and install into user Flatpak repo +# make flatpak ARGS=--validate-only validate metadata only +flatpak: + scripts/package-flatpak.sh $(ARGS) + windows: scripts/build-windows-installer.sh x64 diff --git a/README.md b/README.md index 74a19692..87d71421 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,20 @@ paru -S hydra-download-manager-bin > AUR Packages: [hydra-download-manager](https://aur.archlinux.org/packages/hydra-download-manager) | [hydra-download-manager-bin](https://aur.archlinux.org/packages/hydra-download-manager-bin) +### Flatpak (Flathub) + +Install from Flathub: + +```bash +flatpak install flathub io.github.ja7ad.hydra +``` + +Run Hydra: + +```bash +flatpak run io.github.ja7ad.hydra +``` + ### AppImage (portable, self-updating) One file, no installation, no root. Download it from the diff --git a/crates/hydra-gui/src/app.rs b/crates/hydra-gui/src/app.rs index e42365d7..ff2ec9f3 100644 --- a/crates/hydra-gui/src/app.rs +++ b/crates/hydra-gui/src/app.rs @@ -1786,7 +1786,7 @@ impl App { // hydra.desktop (scripts/package-linux.sh, install.sh). #[cfg(target_os = "linux")] let platform_specific = window::settings::PlatformSpecific { - application_id: "hydra".to_string(), + application_id: std::env::var("FLATPAK_ID").unwrap_or_else(|_| "hydra".to_string()), ..Default::default() }; #[cfg(not(any(target_os = "windows", target_os = "linux")))] diff --git a/crates/hydra-gui/src/autostart.rs b/crates/hydra-gui/src/autostart.rs index b178824d..d4c36d6e 100644 --- a/crates/hydra-gui/src/autostart.rs +++ b/crates/hydra-gui/src/autostart.rs @@ -80,7 +80,11 @@ fn entry_path() -> Option { #[cfg(all(unix, not(target_os = "macos")))] fn entry_path() -> Option { - Some(dirs::home_dir()?.join(".config/autostart/hydra.desktop")) + if let Ok(id) = std::env::var("FLATPAK_ID") { + Some(dirs::home_dir()?.join(format!(".config/autostart/{id}.desktop"))) + } else { + Some(dirs::home_dir()?.join(".config/autostart/hydra.desktop")) + } } /// Sync the login item with the settings. Safe to call every save. @@ -193,9 +197,16 @@ pub fn apply(enabled: bool, minimized: bool) { } #[cfg(all(unix, not(target_os = "macos")))] { - content = format!( - "[Desktop Entry]\nType=Application\nName=Hydra Download Manager\nExec=\"{exe}\" {arg}\nX-GNOME-Autostart-enabled=true\n" - ); + if let Ok(id) = std::env::var("FLATPAK_ID") { + let space_arg = if minimized { " --minimized" } else { "" }; + content = format!( + "[Desktop Entry]\nType=Application\nName=Hydra Download Manager\nExec=flatpak run {id}{space_arg}\nIcon={id}\nX-GNOME-Autostart-enabled=true\n" + ); + } else { + content = format!( + "[Desktop Entry]\nType=Application\nName=Hydra Download Manager\nExec=\"{exe}\" {arg}\nIcon=hydra\nX-GNOME-Autostart-enabled=true\n" + ); + } } // Already correct: leave it alone (a rewrite would only bump the // Background Task Management generation on macOS for no change). diff --git a/crates/hydra-updater/src/lib.rs b/crates/hydra-updater/src/lib.rs index fbebe582..ea467cb4 100644 --- a/crates/hydra-updater/src/lib.rs +++ b/crates/hydra-updater/src/lib.rs @@ -479,18 +479,23 @@ pub fn update_method(dir: &Path) -> UpdateMethod { } } +/// True when running inside a Flatpak sandbox (`FLATPAK_ID` set or `/.flatpak-info` exists). +pub fn is_flatpak() -> bool { + std::env::var_os("FLATPAK_ID").is_some() || Path::new("/.flatpak-info").exists() +} + /// Whether a package manager owns the install in `dir`. /// /// Linux: the deb and rpm both install into `/usr/bin` (scripts/package-linux.sh), -/// while `/usr/local` is by convention exactly the part of the filesystem no -/// package manager touches. macOS: the `.pkg` records a receipt per package -/// identifier and lands in `/Applications`; a dragged `.dmg` leaves no -/// receipt, which is what separates the two installs that otherwise look -/// identical. Windows: the setup installer owns whatever it wrote, and there +/// Flatpak runs from an immutable `/app` mount, while `/usr/local` is by convention +/// exactly the part of the filesystem no package manager touches. macOS: the `.pkg` +/// records a receipt per package identifier and lands in `/Applications`; a dragged +/// `.dmg` leaves no receipt, which is what separates the two installs that otherwise +/// look identical. Windows: the setup installer owns whatever it wrote, and there /// is no unprivileged way to rewrite `Program Files`. fn package_managed(dir: &Path) -> bool { if cfg!(target_os = "linux") { - dir.starts_with("/usr") && !dir.starts_with("/usr/local") + is_flatpak() || (dir.starts_with("/usr") && !dir.starts_with("/usr/local")) } else if cfg!(target_os = "macos") { dir.starts_with("/Applications") && Path::new("/var/db/receipts/io.github.ja7ad.hydra.plist").exists() diff --git a/packaging/flatpak/README.md b/packaging/flatpak/README.md new file mode 100644 index 00000000..f50fbe11 --- /dev/null +++ b/packaging/flatpak/README.md @@ -0,0 +1,67 @@ +# Flatpak Packaging for Hydra + +This directory contains the Flatpak package definition and AppStream metadata for **Hydra Download Manager** (`io.github.ja7ad.hydra`). + +## Files + +- `io.github.ja7ad.hydra.yml`: Flatpak manifest for building with `flatpak-builder`. +- `io.github.ja7ad.hydra.metainfo.xml`: AppStream 1.0+ metadata for Flathub, GNOME Software, and KDE Discover. +- `io.github.ja7ad.hydra.desktop`: XDG Desktop entry for Flatpak environments. + +## Building Locally + +### Prerequisites + +Install Flatpak, `flatpak-builder`, and the Freedesktop 24.08 runtime and SDK: + +```bash +flatpak install flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 org.freedesktop.Sdk.Extension.rust-stable//24.08 +``` + +### Build and Install + +To build and install the Flatpak into your user installation: + +```bash +flatpak-builder --user --install --force-clean build-dir packaging/flatpak/io.github.ja7ad.hydra.yml +``` + +### Run + +```bash +flatpak run io.github.ja7ad.hydra +``` + +Or run via CLI: + +```bash +flatpak run --command=hydra io.github.ja7ad.hydra --help +``` + +## Creating a Standalone Bundle (.flatpak) + +To build a standalone `.flatpak` single-file installer: + +```bash +# Build into local repo +flatpak-builder --repo=repo --force-clean build-dir packaging/flatpak/io.github.ja7ad.hydra.yml + +# Export single-file bundle +flatpak build-bundle repo Hydra-0.4.2-x86_64.flatpak io.github.ja7ad.hydra +``` + +Or use the helper script: + +```bash +scripts/package-flatpak.sh --bundle +``` + +## Flathub Submission & Maintenance + +1. Fork the [Flathub repository](https://github.com/flathub/flathub). +2. Generate `cargo-sources.json` for offline cargo dependencies: + ```bash + flatpak-cargo-generator Cargo.lock -o packaging/flatpak/cargo-sources.json + ``` +3. Update the `sources` in `io.github.ja7ad.hydra.yml` to reference the release git tag and `cargo-sources.json`. +4. Submit a pull request to `flathub/flathub` with the `io.github.ja7ad.hydra` branch/directory. diff --git a/packaging/flatpak/io.github.ja7ad.hydra.desktop b/packaging/flatpak/io.github.ja7ad.hydra.desktop new file mode 100644 index 00000000..d8274b5c --- /dev/null +++ b/packaging/flatpak/io.github.ja7ad.hydra.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Name=Hydra Download Manager +GenericName=Download Manager +Comment=Multi-connection download accelerator and manager +Exec=hydra-gui @@u %u @@ +Icon=io.github.ja7ad.hydra +Terminal=false +Categories=Network;FileTransfer; +Keywords=download;manager;accelerator;torrent;http;metalink;hls;dash; +StartupWMClass=hydra +MimeType=x-scheme-handler/hydra; diff --git a/packaging/flatpak/io.github.ja7ad.hydra.metainfo.xml b/packaging/flatpak/io.github.ja7ad.hydra.metainfo.xml new file mode 100644 index 00000000..b0c79eae --- /dev/null +++ b/packaging/flatpak/io.github.ja7ad.hydra.metainfo.xml @@ -0,0 +1,71 @@ + + + io.github.ja7ad.hydra + Hydra Download Manager + Multi-connection download accelerator and manager + CC0-1.0 + GPL-3.0-or-later + + Javad Rajabzadeh + + +

+ Hydra is an advanced, high-performance download accelerator and manager written in Rust. + It splits downloads across multiple parallel HTTP/HTTPS connections with dynamic chunk sizing, + automatic resume capability, and end-to-end cryptographic checksum verification (SHA-256, BLAKE3, MD5, SHA-1). +

+

Key features include:

+
    +
  • High-speed multi-connection HTTP/HTTPS and streaming download engine
  • +
  • Adaptive stream assembly for HLS and MPEG-DASH audio/video streams
  • +
  • Metalink 3.0 and 4.0 XML parser and mirror failover handling
  • +
  • Remote zip preview and selective single-file extraction without full archive download
  • +
  • Reed-Solomon forward error correction (PAR2 parity) verification and repair
  • +
  • Full-featured graphical user interface, lightweight CLI (hydra / hya), and browser native messaging host
  • +
  • Browser integration for Firefox, Chrome, Edge, and Chromium derivatives
  • +
  • System tray integration (StatusNotifierItem) and dark/light system theme support
  • +
+
+ io.github.ja7ad.hydra.desktop + https://github.com/ja7ad/hydra + https://github.com/ja7ad/hydra/issues + https://github.com/ja7ad/hydra + https://github.com/ja7ad/hydra/blob/main/README.md + + + https://raw.githubusercontent.com/ja7ad/hydra/main/docs/img/screenshot1.jpg + Hydra Download Manager main window + + + https://raw.githubusercontent.com/ja7ad/hydra/main/docs/img/screenshot2.png + Queue scheduling and download progress + + + https://raw.githubusercontent.com/ja7ad/hydra/main/docs/img/screenshot3.png + Stream sniffer and media stream capture + + + https://raw.githubusercontent.com/ja7ad/hydra/main/docs/img/screenshot4.png + Zip directory preview and selective archive download + + + + hydra + hya + hydra-gui + hydra-host + + + + +

Features and improvements in release 0.4.2:

+
    +
  • Enhanced streaming parser and parallel segment fetcher
  • +
  • Optimized memory consumption and UI responsiveness
  • +
  • Flatpak packaging and Flathub distribution support
  • +
+
+
+
+ +
diff --git a/packaging/flatpak/io.github.ja7ad.hydra.yml b/packaging/flatpak/io.github.ja7ad.hydra.yml new file mode 100644 index 00000000..4684539f --- /dev/null +++ b/packaging/flatpak/io.github.ja7ad.hydra.yml @@ -0,0 +1,84 @@ +app-id: io.github.ja7ad.hydra +runtime: org.freedesktop.Platform +runtime-version: '24.08' +sdk: org.freedesktop.Sdk +sdk-extensions: + - org.freedesktop.Sdk.Extension.rust-stable +command: hydra-gui + +finish-args: + # Display & Windowing + - --share=ipc + - --socket=fallback-x11 + - --socket=wayland + # Network & Hardware + - --share=network + - --socket=pulseaudio + - --device=dri + # Filesystem permissions for download directories + - --filesystem=xdg-download + - --filesystem=home + # StatusNotifierItem system tray integration + - --talk-name=org.kde.StatusNotifierWatcher + - --own-name=org.kde.StatusNotifierItem-* + # Desktop notifications + - --talk-name=org.freedesktop.Notifications + # XDG Portals (File dialogs, settings, dark theme detection, URI open) + - --talk-name=org.freedesktop.portal.Desktop + - --talk-name=org.freedesktop.portal.FileChooser + - --talk-name=org.freedesktop.portal.OpenURI + - --talk-name=org.freedesktop.portal.Settings + - --talk-name=org.freedesktop.portal.Background + - --talk-name=org.freedesktop.portal.FdoStyle + - --system-talk-name=org.freedesktop.login1 + +build-options: + append-path: /usr/lib/sdk/rust-stable/bin + env: + CARGO_HOME: /run/build/hydra/cargo + +modules: + - name: hydra + buildsystem: simple + build-commands: + # Build GUI, CLI, and Native Host + - cargo build --release --locked -p hya-cli -p hya-gui -p hya-host + # Binaries + - install -Dm755 target/release/hydra-gui /app/bin/hydra-gui + - install -Dm755 target/release/hydra /app/bin/hydra + - install -Dm755 target/release/hydra-host /app/bin/hydra-host + - ln -sf hydra /app/bin/hya + # Desktop launcher & AppStream metadata + - install -Dm644 packaging/flatpak/io.github.ja7ad.hydra.desktop /app/share/applications/io.github.ja7ad.hydra.desktop + - install -Dm644 packaging/flatpak/io.github.ja7ad.hydra.metainfo.xml /app/share/metainfo/io.github.ja7ad.hydra.metainfo.xml + # Application icons (docs/logo.png -> hicolor) + - install -Dm644 docs/logo.png /app/share/icons/hicolor/512x512/apps/io.github.ja7ad.hydra.png + - | + for s in 16 32 48 64 128 256; do + if command -v magick >/dev/null 2>&1; then + install -d "/app/share/icons/hicolor/${s}x${s}/apps" + magick docs/logo.png -resize "${s}x${s}" "/app/share/icons/hicolor/${s}x${s}/apps/io.github.ja7ad.hydra.png" + elif command -v convert >/dev/null 2>&1; then + install -d "/app/share/icons/hicolor/${s}x${s}/apps" + convert docs/logo.png -resize "${s}x${s}" "/app/share/icons/hicolor/${s}x${s}/apps/io.github.ja7ad.hydra.png" + fi + done + # Shell completions + - install -d /app/share/bash-completion/completions /app/share/zsh/site-functions /app/share/fish/vendor_completions.d + - target/release/hydra completions bash > /app/share/bash-completion/completions/hydra + - target/release/hydra completions zsh > /app/share/zsh/site-functions/_hydra + - target/release/hydra completions fish > /app/share/fish/vendor_completions.d/hydra.fish + - target/release/hydra completions bash --bin-name hya > /app/share/bash-completion/completions/hya + - target/release/hydra completions zsh --bin-name hya > /app/share/zsh/site-functions/_hya + - target/release/hydra completions fish --bin-name hya > /app/share/fish/vendor_completions.d/hya.fish + # Man pages + - install -d /app/share/man/man1 + - for page in docs/man/*.1; do install -Dm644 "$page" "/app/share/man/man1/$(basename "$page")"; done + # Documentation & Licenses + - install -Dm644 LICENSE /app/share/licenses/io.github.ja7ad.hydra/LICENSE + - install -Dm644 LICENSING.md /app/share/licenses/io.github.ja7ad.hydra/LICENSING.md + - install -Dm644 README.md /app/share/doc/io.github.ja7ad.hydra/README.md + - install -Dm644 CHANGELOG.md /app/share/doc/io.github.ja7ad.hydra/CHANGELOG.md + sources: + - type: dir + path: ../.. diff --git a/scripts/package-flatpak.sh b/scripts/package-flatpak.sh new file mode 100755 index 00000000..0af8cb35 --- /dev/null +++ b/scripts/package-flatpak.sh @@ -0,0 +1,104 @@ +#!/usr/bin/env bash +# Copyright (C) 2026 Javad Rajabzadeh +# SPDX-License-Identifier: GPL-3.0-or-later +# +# Build or validate Flatpak package for Hydra. +# +# scripts/package-flatpak.sh [--bundle] [--install] [--validate-only] [--out DIR] +# +# Options: +# --bundle Export a standalone .flatpak bundle +# --install Install the built flatpak locally for the current user +# --validate-only Validate metainfo XML and desktop file without building +# --out DIR Directory to write output bundles to (default: target/dist) +# -h, --help Show this help message + +set -euo pipefail +REPO="$(cd "$(dirname "$0")/.." && pwd)" +cd "$REPO" + +APP_ID="io.github.ja7ad.hydra" +MANIFEST="packaging/flatpak/${APP_ID}.yml" +METAINFO="packaging/flatpak/${APP_ID}.metainfo.xml" +DESKTOP="packaging/flatpak/${APP_ID}.desktop" + +BUNDLE=0 +INSTALL=0 +VALIDATE_ONLY=0 +OUT="$REPO/target/dist" + +while [ $# -gt 0 ]; do + case "$1" in + --bundle) BUNDLE=1 ;; + --install) INSTALL=1 ;; + --validate-only) VALIDATE_ONLY=1 ;; + --out) OUT="$2"; shift ;; + -h|--help) + sed -n '4,15p' "$0" + exit 0 + ;; + *) + echo "unknown option: $1" >&2 + exit 2 + ;; + esac + shift +done + +echo "==> Validating Flatpak metadata..." + +# Validate desktop file +if command -v desktop-file-validate >/dev/null 2>&1; then + desktop-file-validate "$DESKTOP" + echo " ✓ Desktop file ($DESKTOP) is valid" +else + echo " ! desktop-file-validate not found, skipping desktop file check" +fi + +# Validate AppStream metainfo +if command -v appstreamcli >/dev/null 2>&1; then + appstreamcli validate --no-net "$METAINFO" + echo " ✓ AppStream metainfo ($METAINFO) is valid" +elif command -v appstream-util >/dev/null 2>&1; then + appstream-util validate-relax "$METAINFO" + echo " ✓ AppStream metainfo ($METAINFO) is valid" +else + echo " ! appstreamcli / appstream-util not found, skipping AppStream validation" +fi + +if [ "$VALIDATE_ONLY" = 1 ]; then + echo "Validation complete." + exit 0 +fi + +[ "$(uname -s)" = Linux ] || { echo "error: Flatpak builds must be run on Linux" >&2; exit 1; } + +command -v flatpak-builder >/dev/null 2>&1 || { + echo "error: flatpak-builder is required to build flatpak packages" >&2 + exit 1 +} + +VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -1) +ARCH=$(uname -m) +BUILD_DIR="$(mktemp -d)/flatpak-build" +REPO_DIR="$REPO/target/flatpak-repo" +mkdir -p "$OUT" "$REPO_DIR" +trap 'rm -rf "$BUILD_DIR"' EXIT + +echo "==> Building Flatpak for ${APP_ID} v${VERSION} (${ARCH})..." + +BUILD_ARGS=("--force-clean" "--repo=$REPO_DIR") +if [ "$INSTALL" = 1 ]; then + BUILD_ARGS+=("--user" "--install") +fi + +flatpak-builder "${BUILD_ARGS[@]}" "$BUILD_DIR" "$MANIFEST" + +if [ "$BUNDLE" = 1 ]; then + BUNDLE_FILE="$OUT/Hydra-${VERSION}-${ARCH}.flatpak" + echo "==> Creating bundle: $BUNDLE_FILE" + flatpak build-bundle "$REPO_DIR" "$BUNDLE_FILE" "$APP_ID" + echo "built: $BUNDLE_FILE" +fi + +echo "==> Flatpak build finished successfully."