Make Huntarr auto-start and open browser window when launched on Windows #25
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 Huntarr Windows Standalone Application | |
| on: | |
| push: | |
| tags: | |
| - 'v*.*.*' # This will trigger on version tags like v1.1.1 | |
| workflow_dispatch: # Allow manual triggering | |
| # Define permissions needed for release creation | |
| permissions: | |
| contents: write | |
| jobs: | |
| build-windows: | |
| runs-on: windows-latest | |
| steps: | |
| # Checkout code with full depth | |
| - name: Checkout code | |
| uses: actions/checkout@v3 | |
| with: | |
| fetch-depth: 0 | |
| # Set up Python environment | |
| - name: Set up Python 3.9 | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.9' | |
| # Install NSIS using Chocolatey (more reliable) | |
| - name: Install NSIS | |
| run: | | |
| # Install Chocolatey if not already installed | |
| if (!(Test-Path "C:\ProgramData\chocolatey\choco.exe")) { | |
| Set-ExecutionPolicy Bypass -Scope Process -Force | |
| [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072 | |
| Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) | |
| } | |
| # Install NSIS using Chocolatey | |
| choco install nsis -y --no-progress | |
| # Check if NSIS is installed | |
| if (Test-Path "C:\Program Files (x86)\NSIS\makensis.exe") { | |
| Write-Output "NSIS is available at C:\Program Files (x86)\NSIS\makensis.exe" | |
| } else { | |
| Write-Error "NSIS installation failed!" | |
| exit 1 | |
| } | |
| shell: pwsh | |
| # Install dependencies | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install pyinstaller pywin32 | |
| # Get version from tag or set to v1.1.1 if not available | |
| - name: Extract version | |
| id: get_version | |
| run: | | |
| if ("${{ github.ref }}" -like "refs/tags/v*") { | |
| $version = "${{ github.ref }}".Substring(11) | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| } else { | |
| $version = "1.1.1" | |
| echo "VERSION=$version" >> $env:GITHUB_OUTPUT | |
| echo "BUILD_VERSION=$version" >> $env:GITHUB_ENV | |
| } | |
| # Create necessary directories and prepare icon | |
| - name: Create required directories and copy icon | |
| run: | | |
| # Create static directory if it doesn't exist | |
| if (-not (Test-Path "static")) { | |
| New-Item -Path "static" -ItemType Directory -Force | |
| echo "Created static directory" | |
| } | |
| # Create target directory for icon if it doesn't exist | |
| if (-not (Test-Path "frontend/static/img")) { | |
| New-Item -Path "frontend/static/img" -ItemType Directory -Force | |
| echo "Created frontend/static/img directory" | |
| } | |
| # Check if we have the icon and copy it to the expected location | |
| if (Test-Path "frontend/static/img/logo/huntarr.ico") { | |
| Copy-Item -Path "frontend/static/img/logo/huntarr.ico" -Destination "frontend/static/img/logo.ico" -Force | |
| echo "Copied huntarr.ico to the expected location" | |
| } else { | |
| echo "WARNING: Could not find huntarr.ico at expected location. Creating a placeholder." | |
| # Create a minimal ICO file as placeholder | |
| $minimumIconPath = "frontend/static/img/logo.ico" | |
| if (-not (Test-Path $minimumIconPath)) { | |
| # Download a generic icon if we can | |
| try { | |
| Invoke-WebRequest -Uri "https://www.google.com/favicon.ico" -OutFile $minimumIconPath | |
| echo "Downloaded a placeholder icon" | |
| } catch { | |
| echo "Could not download placeholder icon. Build may fail." | |
| } | |
| } | |
| } | |
| # Verify Windows config file exists | |
| - name: Verify Windows config file | |
| run: | | |
| # Verify windows_config.py exists | |
| if (Test-Path src/primary/windows_config.py) { | |
| echo "Windows config file exists." | |
| } else { | |
| echo "ERROR: windows_config.py not found. Creating emergency version..." | |
| $windowsConfigContent = @" | |
| # Windows patch for config paths | |
| import os | |
| import sys | |
| import pathlib | |
| import logging | |
| import shutil | |
| # Setup a logger for this module | |
| logger = logging.getLogger("windows_config") | |
| # Patch function to ensure config path works on Windows | |
| def get_config_dir(): | |
| """ | |
| Get the appropriate config directory for Windows. | |
| When running as an executable, this will be next to the exe. | |
| When running as a script, this will be in the project root. | |
| """ | |
| if getattr(sys, 'frozen', False): | |
| # Running as compiled exe | |
| base_dir = os.path.dirname(sys.executable) | |
| logger.info(f"Running as frozen application from {base_dir}") | |
| else: | |
| # Running as script | |
| base_dir = os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) | |
| logger.info(f"Running as script from {base_dir}") | |
| config_dir = os.path.join(base_dir, 'config') | |
| os.makedirs(config_dir, exist_ok=True) | |
| logger.info(f"Using config directory: {config_dir}") | |
| return config_dir | |
| # Create directories for config | |
| def ensure_config_dirs(): | |
| """ | |
| Ensure all required config directories exist. | |
| Returns the base config directory path. | |
| """ | |
| config_dir = get_config_dir() | |
| data_dir = os.path.join(config_dir, 'data') | |
| logs_dir = os.path.join(config_dir, 'logs') | |
| settings_dir = os.path.join(config_dir, 'settings') | |
| # Create each directory | |
| os.makedirs(data_dir, exist_ok=True) | |
| os.makedirs(logs_dir, exist_ok=True) | |
| os.makedirs(settings_dir, exist_ok=True) | |
| return config_dir | |
| "@ | |
| New-Item -Path src/primary -Name windows_config.py -ItemType File -Force | |
| Set-Content -Path src/primary/windows_config.py -Value $windowsConfigContent | |
| echo "Created emergency windows_config.py file." | |
| } | |
| # Patch the settings_manager.py file to handle Windows paths | |
| - name: Patch settings_manager for Windows | |
| run: | | |
| # Create a patch file | |
| $patchContent = @" | |
| # Path patch for settings_manager.py | |
| import re | |
| import os | |
| # Read the file | |
| with open('src/primary/settings_manager.py', 'r') as f: | |
| content = f.read() | |
| # Create the replacement for the SETTINGS_DIR declaration | |
| pathPatch = ''' | |
| # Settings directory setup - Dynamically determine based on environment | |
| import os | |
| import sys | |
| # Import Windows-specific config handling for the executable | |
| try: | |
| from primary.windows_config import get_config_dir, ensure_config_dirs | |
| # On Windows executable, use the special config directory | |
| if sys.platform == 'win32' and getattr(sys, 'frozen', False): | |
| config_dir = ensure_config_dirs() | |
| SETTINGS_DIR = pathlib.Path(os.path.join(config_dir, 'settings')) | |
| settings_logger.info(f"Windows executable mode: Using config at {SETTINGS_DIR}") | |
| else: | |
| # Default Docker/Linux behavior | |
| SETTINGS_DIR = pathlib.Path("/config") | |
| except ImportError: | |
| # Fallback to default behavior | |
| SETTINGS_DIR = pathlib.Path("/config") | |
| SETTINGS_DIR.mkdir(parents=True, exist_ok=True) | |
| ''' | |
| # Define the pattern to replace | |
| pattern = r'# Settings directory setup.*?SETTINGS_DIR\.mkdir\(parents=True, exist_ok=True\)' | |
| # Replace using regex with DOTALL flag to match across multiple lines | |
| newContent = re.sub(pattern, pathPatch, content, flags=re.DOTALL) | |
| # Write the modified content back | |
| with open('src/primary/settings_manager.py', 'w') as f: | |
| f.write(newContent) | |
| print("Successfully patched settings_manager.py for Windows compatibility") | |
| "@ | |
| Set-Content -Path patch_settings_manager.py -Value $patchContent | |
| # Run the patch script | |
| python patch_settings_manager.py | |
| # Create config directory and default settings | |
| - name: Set up default config structure | |
| run: | | |
| # Create config directory structure | |
| mkdir -p config\data | |
| mkdir -p config\logs | |
| mkdir -p config\settings | |
| # Copy default configs to the settings directory | |
| Copy-Item -Path src\primary\default_configs\*.json -Destination config\settings\ -Force | |
| # Create a launcher.bat file for better user experience | |
| $launcherContent = @" | |
| @echo off | |
| echo ====================================== | |
| echo Welcome to Huntarr v${{ steps.get_version.outputs.VERSION }} | |
| echo ====================================== | |
| echo Starting Huntarr server... | |
| echo. | |
| echo Access the web interface at: http://localhost:9705 | |
| echo. | |
| echo New features in this version: | |
| echo * Improved API handling with consistent timeout settings | |
| echo * Modern UI with animated app icons | |
| echo * Enhanced community links via GitHub | |
| echo. | |
| echo Press Ctrl+C in the Huntarr console window to quit | |
| echo. | |
| start http://localhost:9705 | |
| start Huntarr.exe | |
| "@ | |
| Set-Content -Path launcher.bat -Value $launcherContent | |
| # Create a Windows-specific README | |
| - name: Create Windows README | |
| run: | | |
| $readmeContent = @" | |
| # Huntarr for Windows v${{ steps.get_version.outputs.VERSION }} | |
| ## Quick Start | |
| 1. Double-click on the `launcher.bat` file to start Huntarr | |
| 2. Your default browser will open to http://localhost:9705 | |
| 3. Configure your Arr applications in the Settings page | |
| ## New Features | |
| - **Bazarr Integration**: Manage your subtitle needs directly from Huntarr | |
| - **Improved API Handling**: Consistent API timeouts and User-Agent (Huntarr/1.0) across all apps | |
| - **Enhanced UI**: Modern interface with animated app icons and improved tooltips | |
| - **GitHub Community**: All community links now point to GitHub resources instead of plex.one | |
| ## Configuration | |
| - All configuration files are stored in the `config` folder next to the executable | |
| - Logs are stored in the `config/logs` folder | |
| - App settings are in `config/settings` | |
| - Default API timeout is 120 seconds (configurable in general.json) | |
| ## Troubleshooting | |
| - If you encounter Error 500, check the logs in the `config/logs` folder | |
| - Ensure you have proper permissions on the `config` directory | |
| - For support, visit our [GitHub Discussions](https://github.com/Admin9705/Huntarr.io/discussions) | |
| - Report bugs at [GitHub Issues](https://github.com/Admin9705/Huntarr.io/issues) | |
| - Join our community on [Discord](https://discord.com/invite/PGJJjR5Cww) | |
| "@ | |
| Set-Content -Path Huntarr-Windows-README.md -Value $readmeContent | |
| # Create PyInstaller spec file | |
| - name: Create PyInstaller spec file | |
| run: | | |
| $specContent = @" | |
| # -*- mode: python ; coding: utf-8 -*- | |
| block_cipher = None | |
| a = Analysis( | |
| ['main.py'], | |
| pathex=[], | |
| binaries=[], | |
| datas=[ | |
| ('frontend', 'frontend'), | |
| ('config', 'config'), | |
| ('src/primary/default_configs', 'src/primary/default_configs'), | |
| ('static', 'static'), | |
| ('Huntarr-Windows-README.md', '.'), | |
| ('launcher.bat', '.'), | |
| ], | |
| hiddenimports=[ | |
| 'win32timezone', | |
| 'waitress', | |
| 'src.primary.windows_config', | |
| 'win32api', | |
| 'win32con', | |
| 'win32service', | |
| 'win32serviceutil', | |
| ], | |
| hookspath=[], | |
| hooksconfig={}, | |
| runtime_hooks=[], | |
| excludes=[], | |
| win_no_prefer_redirects=False, | |
| win_private_assemblies=False, | |
| cipher=block_cipher, | |
| noarchive=False, | |
| ) | |
| pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) | |
| exe = EXE( | |
| pyz, | |
| a.scripts, | |
| a.binaries, | |
| a.zipfiles, | |
| a.datas, | |
| [], | |
| name='Huntarr', | |
| debug=False, | |
| bootloader_ignore_signals=False, | |
| strip=False, | |
| upx=True, | |
| upx_exclude=[], | |
| runtime_tmpdir=None, | |
| console=False, | |
| disable_windowed_traceback=False, | |
| argv_emulation=False, | |
| target_arch=None, | |
| codesign_identity=None, | |
| entitlements_file=None, | |
| icon='frontend/static/img/logo.ico', | |
| ) | |
| "@ | |
| Set-Content -Path Huntarr.spec -Value $specContent | |
| # Build executable with version information | |
| - name: Build executable with version information | |
| run: | | |
| # Get version from previous step | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| # Add version info to executable | |
| $versionInfo = @" | |
| VSVersionInfo( | |
| ffi=FixedFileInfo( | |
| filevers=($version.Split('.') + @(0) * 4)[0..3], | |
| prodvers=($version.Split('.') + @(0) * 4)[0..3], | |
| mask=0x3f, | |
| flags=0x0, | |
| OS=0x40004, | |
| fileType=0x1, | |
| subtype=0x0, | |
| date=(0, 0) | |
| ), | |
| kids=[ | |
| StringFileInfo( | |
| [ | |
| StringTable( | |
| '040904B0', | |
| [StringStruct('CompanyName', 'Huntarr.io'), | |
| StringStruct('FileDescription', 'Huntarr - Arr App Manager'), | |
| StringStruct('FileVersion', '$version'), | |
| StringStruct('InternalName', 'Huntarr'), | |
| StringStruct('LegalCopyright', 'Copyright (c) 2025'), | |
| StringStruct('OriginalFilename', 'Huntarr.exe'), | |
| StringStruct('ProductName', 'Huntarr'), | |
| StringStruct('ProductVersion', '$version')] | |
| ) | |
| ] | |
| ), | |
| VarFileInfo([VarStruct('Translation', [1033, 1200])]) | |
| ] | |
| ) | |
| "@ | |
| Set-Content -Path file_version_info.txt -Value $versionInfo | |
| # Build with the spec file - can't use --version-file with a spec file | |
| pyinstaller Huntarr.spec | |
| # Create Windows package | |
| - name: Create package zip | |
| run: | | |
| # Get version from previous step | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| # Create a zip package with everything needed | |
| Compress-Archive -Path "./dist/Huntarr.exe", "./Huntarr-Windows-README.md", "./launcher.bat" -DestinationPath "./Huntarr-Windows-v$version.zip" | |
| # List files in the ZIP | |
| Write-Output "\nCreated package: Huntarr-Windows-v$version.zip" | |
| Write-Output "ZIP contents:" | |
| Get-ChildItem "./Huntarr-Windows-v$version.zip" | Select-Object Name, Length | |
| shell: pwsh | |
| # Create Windows installer with NSIS | |
| - name: Create Windows installer | |
| run: | | |
| # Get version from previous step | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| # Update version in installer script | |
| (Get-Content -Path installer.nsi) -replace '!define PRODUCT_VERSION ".*"', "!define PRODUCT_VERSION `"$version`"" | Set-Content -Path installer.nsi | |
| # Run NSIS to create installer | |
| & "C:\Program Files (x86)\NSIS\makensis.exe" installer.nsi | |
| # Verify installer was created | |
| if (Test-Path -Path "Huntarr-Setup-$version.exe") { | |
| Write-Output "Successfully created installer: Huntarr-Setup-$version.exe" | |
| } else { | |
| Write-Error "Failed to create installer" | |
| exit 1 | |
| } | |
| shell: pwsh | |
| # Test the executable | |
| - name: Test executable setup | |
| run: | | |
| echo "Verifying build artifacts" | |
| dir dist | |
| # List and upload build artifacts | |
| - name: List build artifacts | |
| run: | | |
| echo "Build completed. Here are the artifacts:" | |
| dir | |
| echo "Windows build available at: ./Huntarr-Windows-v${{ steps.get_version.outputs.VERSION }}.zip" | |
| # Create a release using the github CLI | |
| - name: Create GitHub Release | |
| run: | | |
| # Get version | |
| $version = "${{ steps.get_version.outputs.VERSION }}" | |
| $releaseTag = "v$version" | |
| echo "Creating release for $releaseTag..." | |
| # Copy the standalone executable to a more accessible location | |
| Copy-Item -Path ./dist/Huntarr.exe -Destination ./Huntarr.exe -Force | |
| # Create release notes file | |
| $releaseNotes = @" | |
| # Huntarr Windows $releaseTag | |
| Windows standalone executable for Huntarr | |
| ## Improvements | |
| - Improved API handling with consistent timeout settings | |
| - Modern UI with animated app icons | |
| - GitHub community integrations | |
| - Windows path handling to prevent configuration errors | |
| ## Files | |
| - **Huntarr.exe**: Standalone executable | |
| - **Huntarr-Windows-v$version.zip**: Complete package with launcher and config | |
| "@ | |
| Set-Content -Path release-notes.md -Value $releaseNotes | |
| # Attempt to create release | |
| try { | |
| # Try creating a new release with both files | |
| New-Item -Path release -ItemType Directory -Force | Out-Null | |
| Copy-Item -Path ./Huntarr.exe -Destination ./release/ -Force | |
| Copy-Item -Path ./Huntarr-Windows-v$version.zip -Destination ./release/ -Force | |
| Write-Host "Files are ready for release in ./release/ directory" | |
| Get-ChildItem ./release/ | |
| } catch { | |
| Write-Error "Error preparing release files: $_" | |
| } | |
| # Create GitHub Release with proper permissions | |
| - name: Create GitHub Release | |
| uses: ncipollo/release-action@v1 | |
| with: | |
| artifacts: "./dist/Huntarr.exe,./Huntarr-Windows-v${{ steps.get_version.outputs.VERSION }}.zip,./Huntarr-Setup-${{ steps.get_version.outputs.VERSION }}.exe" | |
| artifactContentType: application/zip | |
| body: | | |
| # Huntarr Windows Build v${{ steps.get_version.outputs.VERSION }} | |
| Windows standalone executable and installer for Huntarr.io | |
| ## New in this Build | |
| - Improved API handling with consistent timeout settings | |
| - Modern UI with animated app icons | |
| - GitHub community integrations | |
| - Windows path handling to prevent configuration errors | |
| - New professional installer with setup wizard | |
| ## Installation Options | |
| ### Option 1: Windows Installer (Recommended) | |
| - Download and run **Huntarr-Setup-${{ steps.get_version.outputs.VERSION }}.exe** | |
| - Follows standard Windows installation flow | |
| - Creates desktop and start menu shortcuts | |
| - Can be uninstalled from Control Panel | |
| ### Option 2: Standalone Executable | |
| - Download **Huntarr.exe** for a portable version | |
| - No installation required | |
| ### Option 3: Complete Package | |
| - Download **Huntarr-Windows-v${{ steps.get_version.outputs.VERSION }}.zip** | |
| - Contains the executable and additional files | |
| draft: false | |
| prerelease: true | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| tag: v${{ steps.get_version.outputs.VERSION }} | |
| name: Huntarr Windows v${{ steps.get_version.outputs.VERSION }} | |
| # Provide download information | |
| - name: Build Summary | |
| run: | | |
| echo "========================================" | |
| echo "Windows build completed successfully!" | |
| echo "Version: v${{ steps.get_version.outputs.VERSION }}" | |
| echo "========================================" | |
| echo "Download from GitHub Releases:" | |
| echo "https://github.com/Admin9705/Huntarr.io/releases/tag/v${{ steps.get_version.outputs.VERSION }}" | |
| echo "========================================" | |
| shell: pwsh |