video-master-adapt/build.py
nickviljoen 891c36bbfb Add standalone desktop application with web interface
Major Features:
- 🖥️ Standalone desktop app (VideoMatcher.app) - double-click to run
- 🎨 Black & gold branded UI (Montserrat font, #FFC407 accent)
- 📁 Local file browser for master/adaptation folders
-  Fast mode processing (10-20x faster, disables AKAZE/AI Vision)
- 🤖 Smart AI Vision fallback (auto-retry when no matches found)
- 📊 Real-time progress bars (fingerprinting & matching)
- 💾 Local processing (no cloud, no authentication)
- 📤 CSV export with master filenames

Web Application (Enterprise):
- 🌐 Flask web app with Azure AD authentication
- 📦 Box.com integration for cloud storage
- 🐳 Docker support for deployment
- 🔐 JWT validation with httpOnly cookies
- 🎯 REST API endpoints

Enhancements:
- Fixed master filename lookup (was showing "Unknown")
- Automatic fingerprint recovery (detects missing files)
- Improved CSV format (master file next to adaptation)
- Port conflict handling (auto-finds available port)
- Environment variable fixes for standalone mode

Documentation:
- Updated README with standalone app section
- Added 10+ guide documents (UI improvements, fingerprint recovery, etc.)
- Build instructions with PyInstaller
- Comprehensive troubleshooting guide

Technical:
- PyInstaller build configuration (video_matcher.spec)
- Launcher with environment setup (launcher.py)
- Mock authentication for standalone mode
- Video matcher service layer
- Metadata parser and AKAZE video matching

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 09:49:04 +02:00

227 lines
6.3 KiB
Python
Executable file

#!/usr/bin/env python3
"""
Build script for Video Matcher Standalone Application
Creates a distributable executable using PyInstaller
"""
import os
import sys
import subprocess
import shutil
from pathlib import Path
def check_dependencies():
"""Check if required build dependencies are installed."""
print("Checking build dependencies...")
try:
import PyInstaller
print(f"✓ PyInstaller {PyInstaller.__version__} installed")
except ImportError:
print("✗ PyInstaller not found")
print("\nPlease install PyInstaller:")
print(" pip install pyinstaller")
return False
# Check if FFmpeg is available
try:
result = subprocess.run(['ffmpeg', '-version'],
capture_output=True,
text=True,
timeout=5)
if result.returncode == 0:
print("✓ FFmpeg is available")
else:
print("⚠ FFmpeg check returned non-zero exit code")
except (subprocess.TimeoutExpired, FileNotFoundError):
print("⚠ FFmpeg not found in PATH")
print(" The standalone app will require FFmpeg to be installed on target systems")
return True
def clean_build():
"""Clean previous build artifacts."""
print("\nCleaning previous build artifacts...")
dirs_to_clean = ['build', 'dist']
files_to_clean = ['*.spec~']
for dir_name in dirs_to_clean:
if os.path.exists(dir_name):
print(f" Removing {dir_name}/")
shutil.rmtree(dir_name)
print("✓ Clean complete")
def build_application():
"""Build the application using PyInstaller."""
print("\nBuilding application...")
print("=" * 60)
try:
# Run PyInstaller with the spec file
result = subprocess.run(
['pyinstaller', 'video_matcher.spec', '--clean'],
check=True
)
if result.returncode == 0:
print("=" * 60)
print("✓ Build successful!")
return True
else:
print("✗ Build failed")
return False
except subprocess.CalledProcessError as e:
print(f"✗ Build failed with error: {e}")
return False
except FileNotFoundError:
print("✗ PyInstaller not found")
return False
def create_distribution():
"""Create distribution package with data directories."""
print("\nCreating distribution package...")
dist_dir = Path('dist/VideoMatcher')
if not dist_dir.exists():
print("✗ Distribution directory not found")
return False
# Create data directory structure
data_dir = dist_dir / 'data'
data_dir.mkdir(exist_ok=True)
(data_dir / 'fingerprints').mkdir(exist_ok=True)
(data_dir / 'jobs').mkdir(exist_ok=True)
# Create empty masters.json
masters_file = data_dir / 'masters.json'
if not masters_file.exists():
with open(masters_file, 'w') as f:
f.write('[]')
# Create tmp directory
tmp_dir = dist_dir / 'tmp' / 'video_downloads'
tmp_dir.mkdir(parents=True, exist_ok=True)
# Create README
readme_content = """# Video Matcher - Standalone Application
## How to Run
1. **macOS**: Double-click `VideoMatcher` (or `VideoMatcher.app`)
2. **Windows**: Double-click `VideoMatcher.exe`
3. **Linux**: Run `./VideoMatcher` in terminal
The application will:
- Start a local web server
- Automatically open your browser
- Prompt you to select master and adaptation folders
- Process videos and show results
## Requirements
- **FFmpeg**: Must be installed on your system
- macOS: `brew install ffmpeg`
- Windows: Download from https://ffmpeg.org/download.html
- Linux: `sudo apt-get install ffmpeg`
## Data Storage
All data is stored locally in the `data/` folder:
- `data/masters.json` - Master video registry
- `data/fingerprints/` - Video fingerprints cache
- `data/jobs/` - Job history (if any)
## Troubleshooting
### Application won't start
- Check that FFmpeg is installed: `ffmpeg -version`
- Check console output for error messages
### Port already in use
- The app will automatically find an available port
- If issues persist, close other applications using ports 5000-5010
### Permission errors
- On macOS, you may need to allow the app in System Preferences > Security
- On Windows, you may need to allow through Windows Defender
## Support
For issues or questions, contact your system administrator.
"""
readme_file = dist_dir / 'README.txt'
with open(readme_file, 'w') as f:
f.write(readme_content)
print("✓ Distribution package created")
print(f"\nApplication location: {dist_dir}")
return True
def show_next_steps():
"""Show instructions for next steps."""
print("\n" + "=" * 60)
print("BUILD COMPLETE!")
print("=" * 60)
print("\nYour standalone application is ready:")
print(" Location: dist/VideoMatcher/")
print("\nTo distribute:")
print(" 1. Zip the entire 'VideoMatcher' folder")
print(" 2. Share the zip file with users")
print(" 3. Users extract and run the VideoMatcher executable")
print("\nTo test locally:")
print(" cd dist/VideoMatcher")
if sys.platform == 'darwin':
print(" ./VideoMatcher")
print(" or: open VideoMatcher.app")
elif sys.platform == 'win32':
print(" VideoMatcher.exe")
else:
print(" ./VideoMatcher")
print("\n" + "=" * 60)
def main():
"""Main build process."""
print("=" * 60)
print(" VIDEO MATCHER - Standalone Build Script")
print("=" * 60)
# Check dependencies
if not check_dependencies():
print("\n✗ Build cancelled: Missing dependencies")
sys.exit(1)
# Clean previous builds
clean_build()
# Build application
if not build_application():
print("\n✗ Build cancelled: Build failed")
sys.exit(1)
# Create distribution
if not create_distribution():
print("\n✗ Build cancelled: Distribution creation failed")
sys.exit(1)
# Show next steps
show_next_steps()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print("\n\n✗ Build cancelled by user")
sys.exit(1)
except Exception as e:
print(f"\n✗ Build failed with error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)