diff --git a/.github/workflows/build-and-make.yaml b/.github/workflows/build-and-make.yaml index 00a2f5d8d..e5438965c 100644 --- a/.github/workflows/build-and-make.yaml +++ b/.github/workflows/build-and-make.yaml @@ -15,12 +15,24 @@ on: jobs: build: - name: Build on ${{ matrix.os }} - runs-on: ${{ matrix.os }} + name: Build on ${{ matrix.os }} ${{ matrix.arch }} + runs-on: ${{ matrix.runner }} timeout-minutes: 60 strategy: matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + include: + # macOS builds - separate runners to avoid native module conflicts + - os: macos + runner: macos-15-intel + arch: x64 + - os: macos + runner: macos-latest + arch: arm64 + # Linux and Windows + - os: linux + runner: ubuntu-latest + - os: windows + runner: windows-latest steps: - name: Checkout code @@ -38,18 +50,18 @@ jobs: cache: 'pnpm' - name: Install Linux system dependencies - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'linux' run: | - sudo apt-get update - sudo apt-get install --no-install-recommends -y rpm libarchive-tools flatpak flatpak-builder - - # Configure Flatpak - # 1. Add the Flathub repository (source of runtimes) - flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo - - # 2. Install the standard Freedesktop Platform and SDK (required by electron-builder) - # We install version 24.08 as a safe default, electron-builder might pick what it needs - flatpak install --user -y flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 + sudo apt-get update + sudo apt-get install --no-install-recommends -y rpm libarchive-tools flatpak flatpak-builder + + # Configure Flatpak + # 1. Add the Flathub repository (source of runtimes) + flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo + + # 2. Install the standard Freedesktop Platform and SDK (required by electron-builder) + # We install version 24.08 as a safe default, electron-builder might pick what it needs + flatpak install --user -y flathub org.freedesktop.Platform//24.08 org.freedesktop.Sdk//24.08 - name: Install dependencies run: pnpm install --no-frozen-lockfile @@ -60,21 +72,32 @@ jobs: - name: Build backend run: npm run build:backend + - name: Override macOS arch in electron-builder.json + if: matrix.os == 'macos' + run: | + # Replace the mac arch array with just the target architecture + node -e " + const fs = require('fs'); + const pkg = JSON.parse(fs.readFileSync('electron-builder.json', 'utf8')); + pkg.mac.target.arch = ['${{ matrix.arch }}']; + fs.writeFileSync('electron-builder.json', JSON.stringify(pkg, null, 4)); + " + - name: Make Electron app run: npm run make:app - name: Upload artifacts (macOS) - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos' uses: actions/upload-artifact@v4 with: - name: macos-artifacts + name: macos-${{ matrix.arch }}-artifacts path: | dist/executables/**/*.dmg dist/executables/**/*.zip retention-days: 7 - name: Upload artifacts (Linux) - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'linux' uses: actions/upload-artifact@v4 with: name: linux-artifacts @@ -89,7 +112,7 @@ jobs: retention-days: 7 - name: Upload artifacts (Windows) - if: matrix.os == 'windows-latest' + if: matrix.os == 'windows' uses: actions/upload-artifact@v4 with: name: windows-artifacts @@ -131,8 +154,10 @@ jobs: tag_name: ${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || format('test-{0}', github.sha) }} generate_release_notes: true files: | - artifacts/macos-artifacts/*.dmg - artifacts/macos-artifacts/*.zip + artifacts/macos-x64-artifacts/*-x64.dmg + artifacts/macos-x64-artifacts/*-x64.zip + artifacts/macos-arm64-artifacts/*-arm64.dmg + artifacts/macos-arm64-artifacts/*-arm64.zip artifacts/linux-artifacts/*.AppImage artifacts/linux-artifacts/*.deb artifacts/linux-artifacts/*.rpm @@ -145,3 +170,31 @@ jobs: artifacts/windows-artifacts/*.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-snap: + name: Publish to Snapcraft Store + needs: build + runs-on: ubuntu-latest + # if: startsWith(github.ref, 'refs/tags/v') # Only publish on version tags + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.snapcraft_token }} + + steps: + - name: Download snap artifact + uses: actions/download-artifact@v4 + with: + name: linux-artifacts + path: artifacts + + - name: Setup Snapcraft + uses: samuelmeuli/action-snapcraft@v3 + + - name: Publish all snaps to edge channel + run: | + # Find and publish all snap files + for SNAP_FILE in artifacts/*.snap; do + if [ -f "$SNAP_FILE" ]; then + echo "Publishing: $SNAP_FILE" + snapcraft upload --release=edge "$SNAP_FILE" + fi + done diff --git a/README.md b/README.md index f8c6c395e..decbf3e32 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,63 @@ sudo emerge iptvnator-bin Buy Me A Coffee +## Troubleshooting + +### macOS: "App is damaged and can't be opened" + +Due to Apple's Gatekeeper security and code signing requirements, you may need to remove the quarantine flag from the downloaded application: + +```bash +xattr -c /Applications/IPTVnator.app +``` + +Alternatively, if the app is located in a different directory: + +```bash +xattr -c ~/Downloads/IPTVnator.app +``` + +### Linux: chrome-sandbox Issues + +If you encounter the following error when launching IPTVnator: + +``` +The SUID sandbox helper binary was found, but is not configured correctly. +Rather than run without sandboxing I'm aborting now. +You need to make sure that chrome-sandbox is owned by root and has mode 4755. +``` + +**Solution 1: Fix chrome-sandbox permissions (Recommended for .deb/.rpm installations)** + +Navigate to the IPTVnator installation directory and run: + +```bash +sudo chown root:root chrome-sandbox +sudo chmod 4755 chrome-sandbox +``` + +**Solution 2: Launch with --no-sandbox flag** + +Edit the desktop launcher file to add the `--no-sandbox` flag: + +1. Find your desktop file location: + - **Ubuntu/Debian**: `~/.local/share/applications/iptvnator.desktop` + - **System-wide**: `/usr/share/applications/iptvnator.desktop` + +2. Edit the file and modify the `Exec` line: + + ``` + Exec=iptvnator --no-sandbox %U + ``` + +3. Save the file and relaunch the application from your application menu. + +Alternatively, you can launch IPTVnator from the terminal with the flag: + +```bash +iptvnator --no-sandbox +``` + ## How to Build and Develop Requirements: diff --git a/electron-builder.json b/electron-builder.json new file mode 100644 index 000000000..4fca4325c --- /dev/null +++ b/electron-builder.json @@ -0,0 +1,91 @@ +{ + "appId": "com.fourgray.iptvnator", + "productName": "IPTVnator", + "artifactName": "${name}-${version}-${os}-${arch}.${ext}", + "directories": { + "buildResources": "dist/apps", + "output": "dist/executables" + }, + "files": [ + { + "from": "dist/apps/remote-control-web", + "to": "remote-control-web", + "filter": ["**/*"] + }, + "electron-backend/**/*", + "web/**/*", + "!**/*.map" + ], + "asarUnpack": ["**/better-sqlite3/**"], + "extraMetadata": { + "main": "electron-backend/main.js" + }, + "extraResources": ["dist/apps/electron-backend/workers"], + "mac": { + "target": { + "target": "dmg", + "arch": ["x64", "arm64"] + }, + "category": "public.app-category.video", + "identity": null, + "gatekeeperAssess": false, + "hardenedRuntime": false, + "entitlements": null, + "entitlementsInherit": null, + "icon": "apps/web/src/assets/icons/favicon.icns" + }, + "linux": { + "category": "Video", + "target": [ + { + "target": "AppImage", + "arch": ["x64", "armv7l", "arm64"] + }, + { + "target": "deb", + "arch": ["x64", "armv7l", "arm64"] + }, + { + "target": "Snap", + "arch": ["x64", "armv7l"] + }, + { + "target": "rpm", + "arch": ["x64"] + }, + { + "target": "pacman", + "arch": ["x64"] + }, + { + "target": "flatpak", + "arch": ["x64"] + } + ], + "artifactName": "${name}-${version}-${os}-${arch}.${ext}", + "icon": "apps/web/src/assets/icons" + }, + "afterPack": "electron-builder-sandbox-fix", + "snap": { + "confinement": "strict", + "grade": "stable", + "summary": "IPTV application for M3U playlists, Xtream Codes API, and Stalker portals", + "environment": { + "DISABLE_WAYLAND": "1" + } + }, + "win": { + "compression": "maximum", + "target": [ + { + "target": "nsis", + "arch": ["x64"] + } + ], + "artifactName": "${name}-${version}-windows-${arch}-setup.${ext}", + "icon": "apps/web/src/assets/icons/icon.png" + }, + "nsis": { + "artifactName": "${name}-${version}-windows-${arch}-setup.${ext}" + } +} diff --git a/package-lock.json b/package-lock.json index bb3aff4f2..d6635c17e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "iptvnator", - "version": "0.17.0", + "version": "0.17.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "iptvnator", - "version": "0.17.0", + "version": "0.17.1", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -38,6 +38,7 @@ "better-sqlite3": "^12.5.0", "date-fns": "4.1.0", "drizzle-orm": "0.44.6", + "electron-builder-sandbox-fix": "1.1.1", "electron-conf": "1.3.0", "epg-parser": "0.4.0", "fix-path": "5.0.0", @@ -113,6 +114,7 @@ "drizzle-kit": "0.31.5", "electron": "^39.0.0", "electron-builder": "^26.0.12", + "electron-playwright-helpers": "1.8.2", "esbuild": "0.27.0", "eslint": "^9.8.0", "eslint-config-prettier": "^10.1.8", @@ -16277,7 +16279,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -19212,6 +19213,15 @@ "node": ">=14.0.0" } }, + "node_modules/electron-builder-sandbox-fix": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/electron-builder-sandbox-fix/-/electron-builder-sandbox-fix-1.1.1.tgz", + "integrity": "sha512-+gSiLZ7KZDRr3ATm89eud96QwURauUXMzB2lbzZffkAYMC4yyF2fqq0W2xoTg38eDVFCJThcoyW1p8Ph/unxoA==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + } + }, "node_modules/electron-builder/node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -22547,7 +22557,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -37193,7 +37202,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" diff --git a/package.json b/package.json index beee534dc..eacf990b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iptvnator", - "version": "0.17.0", + "version": "0.17.1", "license": "MIT", "description": "IPTV player application.", "homepage": "https://github.com/4gray/iptvnator", @@ -19,97 +19,6 @@ "video", "tv" ], - "build": { - "appId": "com.fourgray.iptvnator", - "productName": "IPTVnator", - "artifactName": "${name}-${version}-${os}-${arch}.${ext}", - "directories": { - "buildResources": "dist/apps", - "output": "dist/executables" - }, - "files": [ - { - "from": "dist/apps/remote-control-web", - "to": "remote-control-web", - "filter": ["**/*"] - }, - "electron-backend/**/*", - "web/**/*", - "!**/*.map" - ], - "asarUnpack": [ - "**/better-sqlite3/**" - ], - "extraMetadata": { - "main": "electron-backend/main.js" - }, - "extraResources": [ - "dist/apps/electron-backend/workers" - ], - "mac": { - "target": { - "target": "dmg", - "arch": [ - "x64", - "arm64" - ] - }, - "category": "public.app-category.video", - "identity": null, - "gatekeeperAssess": false, - "hardenedRuntime": false, - "entitlements": null, - "entitlementsInherit": null, - "icon": "apps/web/src/assets/icons/favicon.icns" - }, - "linux": { - "category": "Video", - "target": [ - { - "target": "AppImage", - "arch": ["x64", "armv7l", "arm64"] - }, - { - "target": "deb", - "arch": ["x64", "armv7l", "arm64"] - }, - { - "target": "Snap", - "arch": ["x64"] - }, - { - "target": "rpm", - "arch": ["x64"] - }, - { - "target": "pacman", - "arch": ["x64"] - }, - { - "target": "flatpak", - "arch": ["x64"] - } - ], - "artifactName": "${name}-${version}-${os}-${arch}.${ext}", - "icon": "apps/web/src/assets/icons" - }, - "win": { - "compression": "maximum", - "target": [ - { - "target": "nsis", - "arch": [ - "x64" - ] - } - ], - "artifactName": "${name}-${version}-windows-${arch}-setup.${ext}", - "icon": "apps/web/src/assets/icons/icon.png" - }, - "nsis": { - "artifactName": "${name}-${version}-windows-${arch}-setup.${ext}" - } - }, "scripts": { "postinstall": "electron-builder install-app-deps", "build:frontend": "nx build web", @@ -153,6 +62,7 @@ "better-sqlite3": "^12.5.0", "date-fns": "4.1.0", "drizzle-orm": "0.44.6", + "electron-builder-sandbox-fix": "1.1.1", "electron-conf": "1.3.0", "epg-parser": "0.4.0", "fix-path": "5.0.0",