Initial commit: MusicDownload con UI pywebview
- Frontend HTML/CSS/JS in webui/ (stile Spotify-like, hero gradient, modal blur) - API bridge JS<->Python in api/bridge.py - Core invariato: downloader yt-dlp, upgrader, spotify client, config - Build scripts macOS (WebKit) e Windows (EdgeChromium) - .gitignore esclude credenziali (config.json, cookies.txt) e artefatti build Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
commit
8c06a26ddb
25 files changed
+4316
No files matched your search
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Build script per creare MusicDownload.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 MusicDownload.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", "MusicDownload",
|
||||
"--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",
|
||||
# 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 / "MusicDownload" / "MusicDownload.exe"
|
||||
if exe_path.exists():
|
||||
log(f"Build completata: {exe_path}")
|
||||
else:
|
||||
log("ATTENZIONE: MusicDownload.exe non trovato, controlla l'output")
|
||||
|
||||
|
||||
# =========================================================================
|
||||
# Main
|
||||
# =========================================================================
|
||||
def main():
|
||||
print("=" * 50)
|
||||
print(" MusicDownload - 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 / "MusicDownload"
|
||||
if dist.exists():
|
||||
print(f"\n L'app e in: {dist}")
|
||||
print(f" Eseguibile: {dist / 'MusicDownload.exe'}")
|
||||
print(f"\n Per distribuirla, comprimi la cartella:")
|
||||
print(f" Comprimi {dist} -> MusicDownload_Windows.zip")
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user