feat: added export log, added batch panel, and more. #89
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Release | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| permissions: | |
| contents: write | |
| actions: read | |
| jobs: | |
| # Download ffmpeg & yt-dlp | |
| prepare-binaries: | |
| name: Download ffmpeg & yt-dlp | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Cache binaries | |
| id: cache-bins | |
| uses: actions/cache@v4 | |
| with: | |
| path: assets/binaries | |
| key: binaries-${{ hashFiles('scripts/prepare_binaries.py') }}-v1 | |
| - name: Download binaries | |
| if: steps.cache-bins.outputs.cache-hit != 'true' | |
| run: python3 scripts/prepare_binaries.py --all | |
| - name: Upload binary artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: binaries | |
| path: assets/binaries | |
| retention-days: 1 | |
| # Windows | |
| build-windows: | |
| name: Windows | |
| needs: prepare-binaries | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| path: assets/binaries | |
| - name: Set up Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: "3.x" | |
| channel: stable | |
| cache: true | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| # Installer | |
| - name: Build Windows (installer) | |
| run: flutter build windows --release --dart-define=PORTABLE=false | |
| - name: Inject binaries — installer | |
| shell: pwsh | |
| run: | | |
| $dest = "build\windows\x64\runner\Release\bin" | |
| New-Item -ItemType Directory -Force -Path $dest | Out-Null | |
| Copy-Item "assets\binaries\windows\ffmpeg.exe" $dest | |
| Copy-Item "assets\binaries\windows\yt-dlp.exe" $dest | |
| - name: Generate app_icon.ico from PNG | |
| shell: pwsh | |
| run: | | |
| magick convert ` | |
| assets/icons/icon_256.png ` | |
| -define icon:auto-resize=256,128,64,48,32,16 ` | |
| windows/runner/resources/app_icon.ico | |
| Write-Host "✓ app_icon.ico generated" | |
| - name: Create Inno Setup script | |
| shell: pwsh | |
| run: | | |
| $version = (Get-Content pubspec.yaml | | |
| Select-String "^version:" | | |
| ForEach-Object { ($_ -replace "version:\s*","") -replace "\+.*","" }).Trim() | |
| @" | |
| [Setup] | |
| AppName=Frame Extractor | |
| AppVersion=$version | |
| AppPublisher=nokarin | |
| AppPublisherURL=https://github.com/nokarin-dev/frameextractor | |
| AppSupportURL=https://github.com/nokarin-dev/frameextractor/issues | |
| DefaultDirName={localappdata}\FrameExtractor | |
| DefaultGroupName=FrameExtractor | |
| AllowNoIcons=yes | |
| OutputDir=. | |
| OutputBaseFilename=FrameExtractor-windows-installer | |
| Compression=lzma2/ultra64 | |
| SolidCompression=yes | |
| WizardStyle=modern | |
| PrivilegesRequired=lowest | |
| PrivilegesRequiredOverridesAllowed=dialog | |
| UninstallDisplayIcon={app}\frameextractor.exe | |
| SetupIconFile=windows\runner\resources\app_icon.ico | |
| [Languages] | |
| Name: "english"; MessagesFile: "compiler:Default.isl" | |
| [Tasks] | |
| Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}" | |
| [Files] | |
| Source: "build\windows\x64\runner\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs | |
| [Icons] | |
| Name: "{userprograms}\Frame Extractor"; Filename: "{app}\frameextractor.exe" | |
| Name: "{userprograms}\Uninstall Frame Extractor"; Filename: "{uninstallexe}" | |
| Name: "{userdesktop}\Frame Extractor"; Filename: "{app}\frameextractor.exe"; Tasks: desktopicon | |
| [Run] | |
| Filename: "{app}\frameextractor.exe"; Description: "{cm:LaunchProgram,Frame Extractor}"; Flags: nowait postinstall skipifsilent | |
| "@ | Out-File -FilePath "installer.iss" -Encoding UTF8 | |
| Write-Host "Inno Setup script created for v$version" | |
| - name: Compile installer | |
| shell: pwsh | |
| run: | | |
| & "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "installer.iss" | |
| if (!(Test-Path "FrameExtractor-windows-installer.exe")) { | |
| Write-Error "Installer .exe not produced" | |
| exit 1 | |
| } | |
| Write-Host "✓ Installer built: FrameExtractor-windows-installer.exe" | |
| # Portable | |
| - name: Build Windows (portable) | |
| run: flutter build windows --release --dart-define=PORTABLE=true | |
| - name: Inject binaries — portable | |
| shell: pwsh | |
| run: | | |
| $dest = "build\windows\x64\runner\Release\bin" | |
| New-Item -ItemType Directory -Force -Path $dest | Out-Null | |
| Copy-Item "assets\binaries\windows\ffmpeg.exe" $dest | |
| Copy-Item "assets\binaries\windows\yt-dlp.exe" $dest | |
| - name: Package Windows portable | |
| shell: pwsh | |
| run: | | |
| Compress-Archive -Path "build\windows\x64\runner\Release\*" ` | |
| -DestinationPath "FrameExtractor-windows-portable.zip" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-builds | |
| path: | | |
| FrameExtractor-windows-installer.exe | |
| FrameExtractor-windows-portable.zip | |
| # Linux | |
| build-linux: | |
| name: Linux | |
| needs: prepare-binaries | |
| runs-on: ubuntu-22.04 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install system dependencies | |
| run: | | |
| sudo apt-get update -y | |
| sudo apt-get install -y \ | |
| clang cmake ninja-build pkg-config \ | |
| libgtk-3-dev liblzma-dev libstdc++-12-dev \ | |
| dpkg-dev fakeroot | |
| - name: Download dummy binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| path: assets/binaries | |
| - name: Set up Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: "3.x" | |
| channel: stable | |
| cache: true | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| # Release build | |
| - name: Build Linux release | |
| run: flutter build linux --release --dart-define=PORTABLE=false | |
| - name: Inject binaries | |
| run: | | |
| mkdir -p build/linux/x64/release/bundle/bin | |
| cp assets/binaries/linux/ffmpeg build/linux/x64/release/bundle/bin/ | |
| cp assets/binaries/linux/yt-dlp build/linux/x64/release/bundle/bin/ | |
| chmod +x build/linux/x64/release/bundle/bin/* | |
| - name: Get version | |
| run: | | |
| echo "VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //;s/+.*//')" >> $GITHUB_ENV | |
| # deb package | |
| - name: Build .deb package | |
| run: | | |
| PKG="frameextractor_${{ env.VERSION }}_amd64" | |
| BUNDLE="build/linux/x64/release/bundle" | |
| mkdir -p "$PKG/DEBIAN" | |
| mkdir -p "$PKG/opt/frameextractor" | |
| mkdir -p "$PKG/usr/bin" | |
| mkdir -p "$PKG/usr/share/applications" | |
| mkdir -p "$PKG/usr/share/icons/hicolor/256x256/apps" | |
| cp -r "$BUNDLE/." "$PKG/opt/frameextractor/" | |
| EXE=$(find "$PKG/opt/frameextractor" -maxdepth 1 -type f ! -name "*.so" ! -name "*.desktop" | head -1) | |
| EXE_NAME=$(basename "$EXE") | |
| if [ "$EXE_NAME" != "frameextractor" ]; then | |
| mv "$EXE" "$PKG/opt/frameextractor/frameextractor" | |
| echo "Renamed $EXE_NAME → frameextractor" | |
| fi | |
| ln -s /opt/frameextractor/frameextractor "$PKG/usr/bin/frameextractor" | |
| cat > "$PKG/usr/share/applications/frameextractor.desktop" << 'DESKTOP' | |
| [Desktop Entry] | |
| Version=1.0 | |
| Type=Application | |
| Name=FrameExtractor | |
| GenericName=Video Frame Extractor | |
| Comment=Extract frames from video files using ffmpeg | |
| Exec=/opt/frameextractor/frameextractor | |
| Icon=frameextractor | |
| Categories=Video;AudioVideo;FrameExtraction;FrameExtractor; | |
| Terminal=false | |
| StartupWMClass=frameextractor | |
| DESKTOP | |
| if [ -f "assets/icons/icon_256.png" ]; then | |
| cp "assets/icons/icon_256.png" "$PKG/usr/share/icons/hicolor/256x256/apps/frameextractor.png" | |
| fi | |
| cat > "$PKG/DEBIAN/control" << EOF | |
| Package: frameextractor | |
| Version: ${{ env.VERSION }} | |
| Architecture: amd64 | |
| Maintainer: nokarin <contact@nokarin.my.id> | |
| Description: FrameExtractor is a modern, cross-platform video frame extractor with a clean UI built with Flutter, powered by ffmpeg and yt-dlp. Supports local video files and direct YouTube URL extraction. | |
| Depends: libgtk-3-0, libblkid1, liblzma5 | |
| Section: video | |
| Priority: optional | |
| Homepage: https://github.com/nokarin-dev/frameextractor | |
| EOF | |
| cat > "$PKG/DEBIAN/postinst" << 'POSTINST' | |
| #!/bin/sh | |
| chmod +x /opt/frameextractor/frameextractor 2>/dev/null || true | |
| chmod +x /opt/frameextractor/bin/ffmpeg 2>/dev/null || true | |
| chmod +x /opt/frameextractor/bin/yt-dlp 2>/dev/null || true | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| exit 0 | |
| POSTINST | |
| chmod 755 "$PKG/DEBIAN/postinst" | |
| cat > "$PKG/DEBIAN/postrm" << 'POSTRM' | |
| #!/bin/sh | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| exit 0 | |
| POSTRM | |
| chmod 755 "$PKG/DEBIAN/postrm" | |
| fakeroot dpkg-deb --build "$PKG" | |
| mv "$PKG.deb" "FrameExtractor-linux-installer.deb" | |
| echo "✓ .deb built" | |
| dpkg-deb --info "FrameExtractor-linux-installer.deb" | |
| # AppImage | |
| - name: Build .AppImage | |
| run: | | |
| BUNDLE="build/linux/x64/release/bundle" | |
| wget -q "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" \ | |
| -O /tmp/appimagetool.AppImage | |
| chmod +x /tmp/appimagetool.AppImage | |
| cd /tmp && ./appimagetool.AppImage --appimage-extract > /dev/null 2>&1 | |
| APPIMAGETOOL="/tmp/squashfs-root/AppRun" | |
| cd "$GITHUB_WORKSPACE" | |
| mkdir -p AppDir/app | |
| mkdir -p AppDir/usr/share/applications | |
| mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps | |
| cp -r "$BUNDLE/." AppDir/app/ | |
| chmod +x AppDir/app/bin/ffmpeg AppDir/app/bin/yt-dlp | |
| if [ -f "assets/icons/icon_256.png" ]; then | |
| cp "assets/icons/icon_256.png" AppDir/usr/share/icons/hicolor/256x256/apps/frameextractor.png | |
| cp "assets/icons/icon_64.png" AppDir/frameextractor.png | |
| else | |
| printf 'PNG' > AppDir/frameextractor.png | |
| fi | |
| cat > AppDir/frameextractor.desktop << 'DESKTOP' | |
| [Desktop Entry] | |
| Version=1.0 | |
| Type=Application | |
| Name=FrameExtractor | |
| GenericName=Video Frame Extractor | |
| Comment=Extract frames from video files using ffmpeg | |
| Exec=frameextractor | |
| Icon=frameextractor | |
| Categories=Video;AudioVideo;Development;Utility; | |
| Terminal=false | |
| DESKTOP | |
| # AppRun | |
| cat > AppDir/AppRun << 'APPRUN' | |
| #!/bin/sh | |
| APPDIR="$(dirname "$(readlink -f "$0")")" | |
| export PATH="$APPDIR/app/bin:$PATH" | |
| export LD_LIBRARY_PATH="$APPDIR/app/lib:$LD_LIBRARY_PATH" | |
| EXE_NAME=$(find "$APPDIR/app" -maxdepth 1 -type f ! -name "*.so" ! -name "*.desktop" \ | |
| | xargs -I{} basename {} | head -1) | |
| if [ -z "$EXE_NAME" ]; then | |
| echo "FrameExtractor: could not find executable in $APPDIR/app/" >&2 | |
| exit 1 | |
| fi | |
| cd "$APPDIR/app" | |
| exec "./$EXE_NAME" "$@" | |
| APPRUN | |
| chmod +x AppDir/AppRun | |
| ARCH=x86_64 "$APPIMAGETOOL" --no-appstream AppDir "FrameExtractor-linux.AppImage" | |
| chmod +x "FrameExtractor-linux.AppImage" | |
| echo "✓ AppImage built" | |
| # Portable tarball | |
| - name: Build Linux (portable) | |
| run: flutter build linux --release --dart-define=PORTABLE=true | |
| - name: Inject binaries (portable) | |
| run: | | |
| mkdir -p build/linux/x64/release/bundle/bin | |
| cp assets/binaries/linux/ffmpeg build/linux/x64/release/bundle/bin/ | |
| cp assets/binaries/linux/yt-dlp build/linux/x64/release/bundle/bin/ | |
| chmod +x build/linux/x64/release/bundle/bin/* | |
| - name: Package portable (.tar.gz) | |
| run: | | |
| tar -czf FrameExtractor-linux-portable.tar.gz \ | |
| -C build/linux/x64/release/bundle . | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-builds | |
| path: | | |
| FrameExtractor-linux-installer.deb | |
| FrameExtractor-linux-installer.rpm | |
| FrameExtractor-linux.AppImage | |
| FrameExtractor-linux-portable.tar.gz | |
| # Linux (RPM) | |
| build-linux-rpm: | |
| name: Linux (RPM) | |
| needs: prepare-binaries | |
| runs-on: ubuntu-latest | |
| container: | |
| image: fedora:latest | |
| env: | |
| GIT_CONFIG_GLOBAL: /root/.gitconfig-ci | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install build dependencies | |
| run: | | |
| sudo dnf install -y @development-tools \ | |
| clang cmake ninja-build pkg-config \ | |
| gtk3-devel xz-devel libstdc++-devel \ | |
| rpm-build desktop-file-utils hicolor-icon-theme jq which | |
| - name: Configure Git safe directory | |
| run: | | |
| git config --global --add safe.directory '*' | |
| cat /root/.gitconfig-ci | |
| - name: Set up Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: "3.x" | |
| channel: stable | |
| cache: true | |
| - name: Download binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| path: assets/binaries | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Get version | |
| run: | | |
| echo "VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //;s/+.*//')" >> $GITHUB_ENV | |
| - name: Build Linux (release) | |
| run: flutter build linux --release --dart-define=PORTABLE=false | |
| - name: Inject binaries | |
| run: | | |
| mkdir -p build/linux/x64/release/bundle/bin | |
| cp assets/binaries/linux/ffmpeg build/linux/x64/release/bundle/bin/ | |
| cp assets/binaries/linux/yt-dlp build/linux/x64/release/bundle/bin/ | |
| chmod +x build/linux/x64/release/bundle/bin/* | |
| # rpm package | |
| - name: Build .rpm package | |
| run: | | |
| VERSION="${{ env.VERSION }}" | |
| BUNDLE_DIR="$GITHUB_WORKSPACE/build/linux/x64/release/bundle" | |
| RPMROOT="$GITHUB_WORKSPACE/rpmbuild" | |
| mkdir -p "$RPMROOT"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} | |
| echo "$BUNDLE_DIR" > "$RPMROOT/SOURCES/bundle_path.txt" | |
| mkdir -p "$RPMROOT/SOURCES" | |
| cat > "$RPMROOT/SOURCES/frameextractor.desktop" << 'DESKTOP' | |
| [Desktop Entry] | |
| Version=1.0 | |
| Type=Application | |
| Name=FrameExtractor | |
| Exec=/opt/frameextractor/frameextractor | |
| Icon=frameextractor | |
| Categories=Video;AudioVideo;Development;Utility; | |
| Terminal=false | |
| StartupWMClass=frameextractor | |
| DESKTOP | |
| if [ -f "$GITHUB_WORKSPACE/assets/icons/icon_256.png" ]; then | |
| cp "$GITHUB_WORKSPACE/assets/icons/icon_256.png" \ | |
| "$RPMROOT/SOURCES/frameextractor.png" | |
| fi | |
| cat > "$RPMROOT/SPECS/frameextractor.spec" << SPEC | |
| Name: FrameExtractor | |
| Version: ${VERSION} | |
| Release: 1%{?dist} | |
| Summary: Effortless video frame extraction | |
| License: GPLv3 | |
| URL: https://github.com/nokarin-dev/frameextractor | |
| BuildArch: x86_64 | |
| Requires: gtk3, xz-libs | |
| %description | |
| FrameExtractor is a modern, cross-platform video frame extractor | |
| with a clean UI built with Flutter, powered by ffmpeg and yt-dlp. | |
| Supports local video files and direct YouTube URL extraction. | |
| %install | |
| BUNDLE=\$(cat $RPMROOT/SOURCES/bundle_path.txt) | |
| mkdir -p %{buildroot}/opt/frameextractor | |
| mkdir -p %{buildroot}/usr/bin | |
| mkdir -p %{buildroot}/usr/share/applications | |
| mkdir -p %{buildroot}/usr/share/icons/hicolor/256x256/apps | |
| cp -r \$BUNDLE/. %{buildroot}/opt/frameextractor/ | |
| EXE=\$(find %{buildroot}/opt/frameextractor -maxdepth 1 -type f ! -name "*.so" ! -name "*.desktop" | head -1) | |
| EXE_NAME=\$(basename \$EXE) | |
| if [ "\$EXE_NAME" != "frameextractor" ]; then | |
| mv "\$EXE" %{buildroot}/opt/frameextractor/frameextractor | |
| fi | |
| chmod +x %{buildroot}/opt/frameextractor/frameextractor | |
| chmod +x %{buildroot}/opt/frameextractor/bin/ffmpeg 2>/dev/null || true | |
| chmod +x %{buildroot}/opt/frameextractor/bin/yt-dlp 2>/dev/null || true | |
| ln -s /opt/frameextractor/frameextractor %{buildroot}/usr/bin/frameextractor | |
| cp $RPMROOT/SOURCES/frameextractor.desktop \ | |
| %{buildroot}/usr/share/applications/frameextractor.desktop | |
| if [ -f "$RPMROOT/SOURCES/frameextractor.png" ]; then | |
| cp $RPMROOT/SOURCES/frameextractor.png \ | |
| %{buildroot}/usr/share/icons/hicolor/256x256/apps/frameextractor.png | |
| fi | |
| %post | |
| chmod +x /opt/frameextractor/frameextractor 2>/dev/null || true | |
| chmod +x /opt/frameextractor/bin/ffmpeg 2>/dev/null || true | |
| chmod +x /opt/frameextractor/bin/yt-dlp 2>/dev/null || true | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| /usr/bin/gtk-update-icon-cache /usr/share/icons/hicolor 2>/dev/null || true | |
| exit 0 | |
| %postun | |
| update-desktop-database /usr/share/applications 2>/dev/null || true | |
| /usr/bin/gtk-update-icon-cache /usr/share/icons/hicolor 2>/dev/null || true | |
| exit 0 | |
| %files | |
| %attr(0755, root, root) /usr/bin/frameextractor | |
| /opt/frameextractor/* | |
| /usr/share/applications/frameextractor.desktop | |
| /usr/share/icons/hicolor/256x256/apps/frameextractor.png | |
| SPEC | |
| cat "$RPMROOT/SPECS/frameextractor.spec" | |
| rpmbuild -bb \ | |
| --define "_topdir $RPMROOT" \ | |
| --define "_builddir $RPMROOT/BUILD" \ | |
| --define "__os_install_post %{nil}" \ | |
| "$RPMROOT/SPECS/frameextractor.spec" | |
| RPM_FILE=$(find "$RPMROOT/RPMS/x86_64" -name "*.rpm" | head -1) | |
| [ -z "$RPM_FILE" ] && echo "✗ RPM not found!" && exit 1 | |
| cp "$RPM_FILE" "$GITHUB_WORKSPACE/FrameExtractor-linux-installer.rpm" | |
| echo "✓ RPM built: FrameExtractor-linux-installer.rpm" | |
| rpm -qip "$GITHUB_WORKSPACE/FrameExtractor-linux-installer.rpm" | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-rpm-build | |
| path: FrameExtractor-linux-installer.rpm | |
| # Android | |
| build-android: | |
| name: Android | |
| needs: prepare-binaries | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download binaries | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: binaries | |
| path: assets/binaries | |
| - name: Set up Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: "17" | |
| - name: Set up Flutter | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version: "3.x" | |
| channel: stable | |
| cache: true | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Setup release signing | |
| env: | |
| KEYSTORE_B64: ${{ secrets.KEYSTORE_BASE64 }} | |
| if: env.KEYSTORE_B64 != '' | |
| run: | | |
| echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > android/app/keystore.jks | |
| cat > android/key.properties << EOF | |
| storePassword=${{ secrets.STORE_PASSWORD }} | |
| keyPassword=${{ secrets.KEY_PASSWORD }} | |
| keyAlias=${{ secrets.KEY_ALIAS }} | |
| storeFile=keystore.jks | |
| EOF | |
| echo "SIGNING_CONFIGURED=true" >> $GITHUB_ENV | |
| - name: Build split APKs | |
| run: | | |
| flutter build apk --release --dart-define=PORTABLE=false --split-per-abi | |
| - name: Verify APK signing | |
| if: env.SIGNING_CONFIGURED == 'true' | |
| run: | | |
| for apk in \ | |
| build/app/outputs/flutter-apk/app-arm64-v8a-release.apk \ | |
| build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk \ | |
| build/app/outputs/flutter-apk/app-x86_64-release.apk; do | |
| echo "Verifying $apk..." | |
| $ANDROID_HOME/build-tools/$(ls $ANDROID_HOME/build-tools | tail -1)/apksigner \ | |
| verify --verbose "$apk" 2>&1 | grep -E "Verified|error" || true | |
| done | |
| echo "✓ APK signing verified" | |
| - name: Rename APKs | |
| run: | | |
| cp build/app/outputs/flutter-apk/app-arm64-v8a-release.apk FrameExtractor-android-arm64.apk | |
| cp build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk FrameExtractor-android-arm32.apk | |
| cp build/app/outputs/flutter-apk/app-x86_64-release.apk FrameExtractor-android-x86_64.apk | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-build | |
| path: | | |
| FrameExtractor-android-arm64.apk | |
| FrameExtractor-android-arm32.apk | |
| FrameExtractor-android-x86_64.apk | |
| # Create GitHub Release | |
| release: | |
| name: Create Release | |
| needs: [build-windows, build-linux, build-linux-rpm, build-android] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| pattern: "*-build*" | |
| merge-multiple: true | |
| - name: Generate changelog | |
| run: | | |
| VERSION="${{ github.ref_name }}" | |
| VERSION_BARE="${VERSION#v}" | |
| SECTION=$(awk \ | |
| "/^## \[$VERSION_BARE\]/{found=1; next} found && /^## \[/{exit} found{print}" \ | |
| changelog.md) | |
| if [ -z "$SECTION" ]; then | |
| PREV_TAG=$(git tag --sort=-version:refname | sed -n '2p') | |
| if [ -z "$PREV_TAG" ]; then | |
| SECTION=$(git log --oneline --no-decorate | head -20) | |
| else | |
| SECTION=$(git log --oneline --no-decorate ${PREV_TAG}..HEAD) | |
| fi | |
| fi | |
| echo "CHANGELOG<<EOF" >> $GITHUB_ENV | |
| echo "$SECTION" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| name: "Frame Extractor ${{ github.ref_name }}" | |
| body: | | |
| <img width="1536" height="100" alt="Changelog" src="https://github.com/user-attachments/assets/9e9b3837-cfbc-4b17-8dfd-03deae7c67c5" /> | |
| ${{ env.CHANGELOG }} | |
| --- | |
| <img width="1536" height="100" alt="Downloads" src="https://github.com/user-attachments/assets/fdc58dff-3ba3-4b2a-98aa-d2992f02c1c0" /> | |
| ## Downloads | |
| ### Windows | |
| [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-windows-installer.exe) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-windows-portable.zip) | |
| ### Linux | |
| [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-linux-installer.deb) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-linux-installer.rpm) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-linux.AppImage) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-linux-portable.tar.gz) | |
| ### Android | |
| [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-android-arm64.apk) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-android-arm32.apk) [](https://github.com/nokarin-dev/FrameExtractor/releases/download/${{ github.ref_name }}/FrameExtractor-android-x86_64.apk) | |
| draft: false | |
| prerelease: ${{ contains(github.ref_name, '-') }} | |
| files: | | |
| dist/FrameExtractor-windows-installer.exe | |
| dist/FrameExtractor-windows-portable.zip | |
| dist/FrameExtractor-linux-installer.deb | |
| dist/FrameExtractor-linux-installer.rpm | |
| dist/FrameExtractor-linux.AppImage | |
| dist/FrameExtractor-linux-portable.tar.gz | |
| dist/FrameExtractor-android-arm64.apk | |
| dist/FrameExtractor-android-arm32.apk | |
| dist/FrameExtractor-android-x86_64.apk | |
| fail_on_unmatched_files: false | |
| token: ${{ secrets.GITHUB_TOKEN }} |