name: Build Windows Release on: push: tags: - 'v*' permissions: contents: write env: PYTHON_VERSION: '3.13.6' jobs: build-windows: runs-on: windows-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Extract version from tag id: get_version shell: powershell run: | $tag = "${{ github.ref_name }}" $version = $tag -replace '^v', '' Write-Host "Extracted version: $version" echo "VERSION=$version" >> $env:GITHUB_OUTPUT - name: Setup Python uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} - name: Cache Python dependencies uses: actions/cache@v3 with: path: | venv ~\AppData\Local\pip\Cache key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip- - name: Create virtual environment and install dependencies shell: powershell run: | python -m venv venv .\venv\Scripts\Activate.ps1 python -m pip install --upgrade pip pip install --no-cache-dir -r requirements.txt pip install --no-cache-dir cx_Freeze pip install --no-cache-dir yt-dlp - name: Prepare build variables shell: powershell run: | $version = "${{ steps.get_version.outputs.VERSION }}" echo "VERSION=$version" >> $env:GITHUB_ENV Write-Host "Prepared build variables for version: $version" - name: Build Standard Version (ZIP) shell: powershell run: | .\venv\Scripts\Activate.ps1 $version = "$env:VERSION" Write-Host "Building Standard Version v$version ..." # Clean previous build artifacts if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force } if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force } if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force } # Build executable using cx_Freeze CLI (use python -m to ensure proper base handling) python -m cx_Freeze main.py ` --target-dir "dist\YTSage" ` --base-name Win32GUI ` --icon "assets\branding\icons\YTSage.ico" ` --target-name "YTSage-v$version.exe" ` --optimize 2 ` --packages "PySide6.QtCore,PySide6.QtGui,PySide6.QtWidgets,requests,PIL,packaging,markdown,pyglet,loguru,setuptools" ` --excludes "PySide6.QtBluetooth,PySide6.QtNetwork,PySide6.QtOpenGL,PySide6.QtPrintSupport,PySide6.QtSvg,PySide6.QtTest,PySide6.QtXml,PySide6.QtSql,PySide6.QtHelp,PySide6.QtMultimedia,PySide6.QtQml,PySide6.QtQuick,PySide6.QtWebEngineCore,PIL.ImageDraw,PIL.ImageFont,numpy,scipy,wx,pandas,tkinter,yt_dlp,unittest,test,tests" ` --include-files "src" ` # Use colon for source:destination mapping ( '=' is treated as literal path by cx_Freeze CLI ) --include-files "assets:lib/assets" # Remove screenshots folder to reduce build size if (Test-Path "dist\YTSage\lib\assets\branding\screenshots") { Remove-Item "dist\YTSage\lib\assets\branding\screenshots" -Recurse -Force Write-Host "Removed screenshots folder from standard build" } - name: Setup FFmpeg for bundle shell: powershell run: | Write-Host "Setting up FFmpeg for bundled version..." # Create ffmpeg directory New-Item -ItemType Directory -Path "ffmpeg-temp" -Force # Download FFmpeg $ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip" Write-Host "Downloading FFmpeg from: $ffmpegUrl" Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip" # Extract FFmpeg Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force # Find the extracted directory (GyanD ffmpeg has specific naming) $ffmpegDir = Get-ChildItem -Path "ffmpeg-temp" -Directory | Where-Object { $_.Name -like "ffmpeg-*" } | Select-Object -First 1 if ($ffmpegDir) { # Set environment variable for the build $ffmpegBinPath = Join-Path $ffmpegDir.FullName "bin" Write-Host "FFmpeg binaries found at: $ffmpegBinPath" echo "FFMPEG_PATH=$ffmpegBinPath" >> $env:GITHUB_ENV # Verify files exist $ffmpegExe = Join-Path $ffmpegBinPath "ffmpeg.exe" $ffprobeExe = Join-Path $ffmpegBinPath "ffprobe.exe" $ffplayExe = Join-Path $ffmpegBinPath "ffplay.exe" if (Test-Path $ffmpegExe) { Write-Host "[OK] ffmpeg.exe found" } else { Write-Host "[ERROR] ffmpeg.exe missing" } if (Test-Path $ffprobeExe) { Write-Host "[OK] ffprobe.exe found" } else { Write-Host "[ERROR] ffprobe.exe missing" } if (Test-Path $ffplayExe) { Write-Host "[OK] ffplay.exe found" } else { Write-Host "[ERROR] ffplay.exe missing" } } else { Write-Host "Error: Could not find FFmpeg directory after extraction" exit 1 } - name: Build FFmpeg Version (ZIP) shell: powershell run: | .\venv\Scripts\Activate.ps1 $version = "$env:VERSION" Write-Host "Building FFmpeg Version v$version ..." # Clean previous build artifacts if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force } if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force } # Build executable with FFmpeg using cx_Freeze CLI (use python -m to ensure proper base handling) python -m cx_Freeze main.py ` --target-dir "dist\YTSage-FFmpeg" ` --base-name Win32GUI ` --icon "assets\branding\icons\YTSage.ico" ` --target-name "YTSage-v$version-ffmpeg.exe" ` --optimize 2 ` --packages "PySide6.QtCore,PySide6.QtGui,PySide6.QtWidgets,requests,PIL,packaging,markdown,pyglet,loguru,setuptools" ` --excludes "PySide6.QtBluetooth,PySide6.QtNetwork,PySide6.QtOpenGL,PySide6.QtPrintSupport,PySide6.QtSvg,PySide6.QtTest,PySide6.QtXml,PySide6.QtSql,PySide6.QtHelp,PySide6.QtMultimedia,PySide6.QtQml,PySide6.QtQuick,PySide6.QtWebEngineCore,PIL.ImageDraw,PIL.ImageFont,numpy,scipy,wx,pandas,tkinter,yt_dlp,unittest,test,tests" ` --include-files "src" ` # Use colon for source:destination mapping --include-files "assets:lib/assets" # Remove screenshots folder to reduce build size if (Test-Path "dist\YTSage-FFmpeg\lib\assets\branding\screenshots") { Remove-Item "dist\YTSage-FFmpeg\lib\assets\branding\screenshots" -Recurse -Force Write-Host "Removed screenshots folder from FFmpeg build" } # Copy FFmpeg binaries into dist folder post-build (more reliable than CLI include) if ($env:FFMPEG_PATH) { $destDir = "dist\YTSage-FFmpeg" $ffmpegExe = Join-Path $env:FFMPEG_PATH "ffmpeg.exe" $ffprobeExe = Join-Path $env:FFMPEG_PATH "ffprobe.exe" $ffplayExe = Join-Path $env:FFMPEG_PATH "ffplay.exe" foreach ($file in @($ffmpegExe, $ffprobeExe, $ffplayExe)) { if (Test-Path $file) { $name = Split-Path $file -Leaf Copy-Item $file -Destination (Join-Path $destDir $name) -Force Write-Host "Copied $name into $destDir" } else { Write-Host "Warning: FFmpeg binary not found: $file" } } } - name: Package and prepare release artifacts (ZIPs) shell: powershell run: | $version = "${{ steps.get_version.outputs.VERSION }}" Write-Host "Preparing ZIP artifacts for version: $version" New-Item -ItemType Directory -Path "artifacts" -Force # List dist directories Write-Host "Files in dist directory:" if (Test-Path "dist") { Get-ChildItem -Path "dist" -Recurse | ForEach-Object { Write-Host " $($_.FullName)" } } else { Write-Host " No dist directory found!" exit 1 } # Create Standard ZIP $standardDist = Join-Path (Get-Location) "dist\YTSage" if (Test-Path $standardDist) { $stdZip = "artifacts\YTSage-v$version-portable.zip" if (Test-Path $stdZip) { Remove-Item $stdZip -Force } Compress-Archive -Path "$standardDist\*" -DestinationPath $stdZip -CompressionLevel Optimal Write-Host "Created: $(Resolve-Path $stdZip)" } else { Write-Host "Warning: Standard dist folder not found at $standardDist" } # Create FFmpeg ZIP $ffmpegDist = Join-Path (Get-Location) "dist\YTSage-FFmpeg" if (Test-Path $ffmpegDist) { $ffZip = "artifacts\YTSage-v$version-ffmpeg-portable.zip" if (Test-Path $ffZip) { Remove-Item $ffZip -Force } Compress-Archive -Path "$ffmpegDist\*" -DestinationPath $ffZip -CompressionLevel Optimal Write-Host "Created: $(Resolve-Path $ffZip)" } else { Write-Host "Warning: FFmpeg dist folder not found at $ffmpegDist" } # List all artifacts Write-Host "Final artifacts:" if (Test-Path "artifacts") { $artifactFiles = Get-ChildItem artifacts if ($artifactFiles) { $artifactFiles | ForEach-Object { Write-Host " $($_.Name)" } } else { Write-Host " No artifacts created!" } } else { Write-Host " Artifacts directory not found!" } - name: Create draft release uses: softprops/action-gh-release@v1 with: tag_name: ${{ github.ref_name }} name: YTSage ${{ github.ref_name }} draft: true prerelease: false body: | # YTSage ${{ github.ref_name }} **Release Date**: ${{ github.event.head_commit.timestamp }} **Full Changelog**: https://github.com/${{ github.repository }}/compare/${{ github.ref_name }} files: | artifacts/* env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}