name: Build Windows Release on: push: tags: - 'v*' permissions: contents: write env: PYTHON_VERSION: '3.12.10' jobs: build-windows: runs-on: windows-latest defaults: run: shell: pwsh steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 - name: Derive version from tag id: version run: | $tag = "${{ github.ref_name }}" $ver = $tag -replace '^v','' echo "VERSION=$ver" >> $env:GITHUB_OUTPUT echo "VERSION=$ver" >> $env:GITHUB_ENV - name: Setup Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} - name: Create venv and install dependencies run: | $py = $env:pythonLocation & "$py\python.exe" -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 yt-dlp - name: Build Standard (portable ZIP) run: | .\venv\Scripts\Activate.ps1 $version = "$env:VERSION" Write-Host "Building Standard v$version" if (Test-Path "build") { Remove-Item "build" -Recurse -Force } if (Test-Path "dist") { Remove-Item "dist" -Recurse -Force } .\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze 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,playsound3,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" ` --include-files "assets" $stdScreenshots = "dist\YTSage\assets\branding\screenshots" if (Test-Path $stdScreenshots) { Remove-Item $stdScreenshots -Recurse -Force } - name: Download FFmpeg bundle run: | New-Item -ItemType Directory -Path "ffmpeg-temp" -Force | Out-Null $ffmpegUrl = "https://github.com/GyanD/codexffmpeg/releases/download/7.1.1/ffmpeg-7.1.1-full_build.zip" Invoke-WebRequest -Uri $ffmpegUrl -OutFile "ffmpeg-temp\ffmpeg.zip" Expand-Archive -Path "ffmpeg-temp\ffmpeg.zip" -DestinationPath "ffmpeg-temp" -Force $ffmpegDir = Get-ChildItem -Path "ffmpeg-temp" -Directory | Where-Object { $_.Name -like "ffmpeg-*" } | Select-Object -First 1 if (-not $ffmpegDir) { Write-Error "FFmpeg dir not found"; exit 1 } $ffmpegBin = Join-Path $ffmpegDir.FullName "bin" echo "FFMPEG_PATH=$ffmpegBin" >> $env:GITHUB_ENV - name: Build FFmpeg bundle (portable ZIP) run: | .\venv\Scripts\Activate.ps1 $version = "$env:VERSION" Write-Host "Building FFmpeg bundle v$version" if (Test-Path "build\exe.*") { Remove-Item "build\exe.*" -Recurse -Force } if (Test-Path "build\bdist.*") { Remove-Item "build\bdist.*" -Recurse -Force } .\venv\Scripts\python.exe -m cx_Freeze.cli cxfreeze 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,playsound3,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" ` --include-files "assets" if ($env:FFMPEG_PATH) { $dest = "dist\YTSage-FFmpeg" foreach ($n in 'ffmpeg.exe','ffprobe.exe','ffplay.exe') { $src = Join-Path $env:FFMPEG_PATH $n if (Test-Path $src) { Copy-Item $src -Destination (Join-Path $dest $n) -Force } } } $ffShots = "dist\YTSage-FFmpeg\assets\branding\screenshots" if (Test-Path $ffShots) { Remove-Item $ffShots -Recurse -Force } - name: Package artifacts run: | $version = "$env:VERSION" New-Item -ItemType Directory -Path "artifacts" -Force | Out-Null $std = "artifacts\YTSage-v$version-portable.zip" if (Test-Path "dist\YTSage") { if (Test-Path $std) { Remove-Item $std -Force } Compress-Archive -Path "dist\YTSage\*" -DestinationPath $std -CompressionLevel Optimal } $ff = "artifacts\YTSage-v$version-ffmpeg-portable.zip" if (Test-Path "dist\YTSage-FFmpeg") { if (Test-Path $ff) { Remove-Item $ff -Force } Compress-Archive -Path "dist\YTSage-FFmpeg\*" -DestinationPath $ff -CompressionLevel Optimal } Write-Host "Artifacts:" Get-ChildItem artifacts | ForEach-Object { Write-Host " - $($_.Name)" } - name: Create draft release uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.ref_name }} name: YTSage ${{ github.ref_name }} draft: true prerelease: false body: | ## Downloads - YTSage-v${{ steps.version.outputs.VERSION }}-portable.zip (portable) - YTSage-v${{ steps.version.outputs.VERSION }}-ffmpeg-portable.zip (portable with FFmpeg included) Extract and run the EXE. Console window is hidden. files: | artifacts/*