Files
musicdownload/build_windows.py
T
luzadevandClaude Opus 4.7 850d3f4b6c Rename: l'app si chiama MusicTools (non piu MusicDownload)
- main.py: titolo finestra pywebview -> 'MusicTools'
- core/config.py: APP_NAME='MusicTools', cartella config bundled
  ora ~/Library/Application Support/MusicTools/ e %APPDATA%/MusicTools/.
  Migrazione one-shot: se non esiste la nuova ma esiste la legacy
  'MusicDownload', il config.json viene copiato cosi' le impostazioni
  non vanno perse dopo l'aggiornamento
- core/recorder.py: messaggio errore permesso microfono
- api/bridge.py: User-Agent update check + guida Spotify menzionano
  MusicTools
- build_macos.py / build_windows.py / .github/workflows/build.yml:
  prodotti rinominati (MusicTools.app, MusicTools.exe,
  MusicTools-macOS.zip, MusicTools-Windows.zip, Release 'Build MusicTools')
- webui: <title>, brand-name sidebar, intestazioni commenti
- README: tutti i riferimenti aggiornati + nota che il repo Git
  conserva il nome storico 'musicdownload' (URL stabili per
  release gia' pubblicate)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 11:15:34 +02:00

181 lines
5.5 KiB
Python

"""
Build script per creare MusicTools.exe su Windows.
Uso:
python build_windows.py
Cosa fa:
1. Scarica il binario standalone di yt-dlp per Windows
2. Scarica ffmpeg static build per Windows
3. Esegue PyInstaller per creare MusicTools.exe
Requisiti:
pip install pyinstaller
pip install -r requirements.txt
"""
import io
import os
import shutil
import stat
import subprocess
import sys
import urllib.request
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parent
BUILD_DIR = ROOT / "build"
DIST_DIR = ROOT / "dist"
BUNDLE_DIR = ROOT / "bundle_bin"
YTDLP_URL = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe"
FFMPEG_URL = "https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-master-latest-win64-gpl.zip"
def log(msg):
print(f"\n>>> {msg}")
# =========================================================================
# 1. Scarica yt-dlp.exe
# =========================================================================
def download_ytdlp():
dest = BUNDLE_DIR / "yt-dlp.exe"
if dest.exists():
log(f"yt-dlp.exe gia presente: {dest}")
return dest
log(f"Scarico yt-dlp.exe ...")
BUNDLE_DIR.mkdir(parents=True, exist_ok=True)
urllib.request.urlretrieve(YTDLP_URL, dest)
log(f"yt-dlp.exe scaricato ({dest.stat().st_size // 1024 // 1024} MB)")
return dest
# =========================================================================
# 2. Scarica ffmpeg static build
# =========================================================================
def download_ffmpeg():
ffmpeg_exe = BUNDLE_DIR / "ffmpeg.exe"
ffprobe_exe = BUNDLE_DIR / "ffprobe.exe"
if ffmpeg_exe.exists() and ffprobe_exe.exists():
log("ffmpeg.exe e ffprobe.exe gia presenti")
return
log("Scarico ffmpeg static build per Windows ...")
BUNDLE_DIR.mkdir(parents=True, exist_ok=True)
# Scarica lo zip in memoria
response = urllib.request.urlopen(FFMPEG_URL)
zip_data = io.BytesIO(response.read())
log("Download completato, estraggo ffmpeg.exe e ffprobe.exe ...")
with zipfile.ZipFile(zip_data) as zf:
for member in zf.namelist():
basename = Path(member).name
if basename in ("ffmpeg.exe", "ffprobe.exe"):
# Estrai solo il file, senza sottocartelle
data = zf.read(member)
dest = BUNDLE_DIR / basename
dest.write_bytes(data)
log(f" Estratto: {basename} ({len(data) // 1024 // 1024} MB)")
if not ffmpeg_exe.exists():
print("ERRORE: ffmpeg.exe non trovato nello zip!")
sys.exit(1)
# =========================================================================
# 3. PyInstaller
# =========================================================================
def run_pyinstaller():
log("Eseguo PyInstaller...")
# Genera lista --add-binary per i binari bundled
add_binaries = []
for f in BUNDLE_DIR.iterdir():
if f.is_file() and f.suffix in (".exe", ""):
add_binaries.extend(["--add-binary", f"{f};."])
cmd = [
sys.executable, "-m", "PyInstaller",
"--name", "MusicTools",
"--windowed",
"--onedir",
"--noconfirm",
# Icona (se presente)
# "--icon", str(ROOT / "icon.ico"),
# Web UI assets (HTML/CSS/JS) — usati da pywebview
"--add-data", f"{ROOT / 'webui'};webui",
# Hidden imports per pywebview (EdgeChromium su Windows)
"--hidden-import", "webview",
"--hidden-import", "webview.platforms.edgechromium",
"--hidden-import", "clr",
"--hidden-import", "requests",
# Collegamenti completi alle DLL .NET di pywebview/pythonnet
"--collect-all", "webview",
"--collect-all", "mutagen",
# Binari bundled
*add_binaries,
# Entry point
str(ROOT / "main.py"),
]
log(f"Comando: {' '.join(cmd[:10])}...")
subprocess.run(cmd, check=True, cwd=str(ROOT))
exe_path = DIST_DIR / "MusicTools" / "MusicTools.exe"
if exe_path.exists():
log(f"Build completata: {exe_path}")
else:
log("ATTENZIONE: MusicTools.exe non trovato, controlla l'output")
# =========================================================================
# Main
# =========================================================================
def main():
print("=" * 50)
print(" MusicTools - Build Windows")
print("=" * 50)
if sys.platform != "win32":
print("\nATTENZIONE: Questo script va eseguito su Windows!")
print("PyInstaller non supporta cross-compilation.")
print("Copia il progetto su una macchina Windows e rilancia.")
sys.exit(1)
# Verifica pyinstaller
try:
import PyInstaller
log(f"PyInstaller {PyInstaller.__version__}")
except ImportError:
log("Installo PyInstaller...")
subprocess.run([sys.executable, "-m", "pip", "install", "pyinstaller"],
check=True)
# Pulisci bundle precedente
if BUNDLE_DIR.exists():
shutil.rmtree(BUNDLE_DIR)
download_ytdlp()
download_ffmpeg()
run_pyinstaller()
print("\n" + "=" * 50)
print(" DONE!")
print("=" * 50)
dist = DIST_DIR / "MusicTools"
if dist.exists():
print(f"\n L'app e in: {dist}")
print(f" Eseguibile: {dist / 'MusicTools.exe'}")
print(f"\n Per distribuirla, comprimi la cartella:")
print(f" Comprimi {dist} -> MusicTools_Windows.zip")
print()
if __name__ == "__main__":
main()