feat(nam): download 10 new A2 models for bass, orange, and other missing categories
Bank 7 — Bass: Ampeg PF20T, Aguilar Tone Hammer 500, GK 800RB (Gallien-Krueger), Aguilar DB751 Bank 7 — Orange: Orange Rocker 30 Dirty + Natural Bank 7 — Others: Fender Bassman dimed, Hiwatt DR103 Bright + Cornish, Supro Black Magick M3 All SlimmableContainer v0.7.0, ~287-295 KB, verified JSON. Also fixed Bank 2 Tone3000 storage keys to include .nam extension (Tone3000 changed URL format). Inventory updated: 54 → 64 total models.
This commit is contained in:
@@ -0,0 +1,508 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Download NAM WaveNet models from community collection + cabinet IRs.
|
||||
|
||||
Downloads real NAM amp captures from the pelennor2170/NAM_models GitHub
|
||||
repository and free cabinet IR packs from Archive.org.
|
||||
|
||||
Usage:
|
||||
python3 scripts/download_nam_and_irs.py # Download everything
|
||||
python3 scripts/download_nam_and_irs.py --list # List models only
|
||||
python3 scripts/download_nam_and_irs.py --models # Models only
|
||||
python3 scripts/download_nam_and_irs.py --irs # IRs only
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
# ── Paths ────────────────────────────────────────────────────────────────
|
||||
NAM_DIR = Path.home() / ".pedal" / "models"
|
||||
IR_DIR = Path.home() / ".pedal" / "irs"
|
||||
BASE_URL = "https://raw.githubusercontent.com/pelennor2170/NAM_models/main"
|
||||
|
||||
# ── Model definitions ────────────────────────────────────────────────────
|
||||
# (local_name, remote_name, description, architecture, size_kb)
|
||||
# URL for Tone3000 (ToneHunt) Supabase storage downloads
|
||||
TONE3000_STORAGE = "https://api.tone3000.com/storage/v1/object/public/models"
|
||||
|
||||
MODELS = [
|
||||
# ── Bank 0: Clean & Edge ──────────────────────────────────────────
|
||||
("Fender_TwinVerb_Clean.nam",
|
||||
"Tim R Fender TwinVerb Norm Bright.nam",
|
||||
"Fender Twin Reverb Normal Bright — clean jazz/fingerstyle",
|
||||
"WaveNet", 400),
|
||||
("Fender_TwinVerb_Vibrato.nam",
|
||||
"Tim R Fender TwinVerb Vibrato Bright.nam",
|
||||
"Fender Twin Reverb Vibrato Bright — sparkling clean",
|
||||
"WaveNet", 400),
|
||||
("Vox_AC15_TopBoost.nam",
|
||||
"Phillipe P VOXAC15-TopBoost.nam",
|
||||
"Vox AC15 Top Boost — chimey edge-of-breakup",
|
||||
"WaveNet", 400),
|
||||
("Magnatone_Super59_Pushed.nam",
|
||||
"Tim R Magnatone Super 59 Mkii Pushed.nam",
|
||||
"Magnatone Super 59 pushed — bluesy breakup",
|
||||
"WaveNet", 400),
|
||||
("JCM2000_Clean.nam",
|
||||
"Tim R JCM2000 Clean.nam",
|
||||
"Marshall JCM2000 clean — crisp rhythm",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Bank 1: Drive & Lead ──────────────────────────────────────────
|
||||
("Orange_Rockerverb.nam",
|
||||
"Tom C Axe FX 2 Orange Rockerverb.nam",
|
||||
"Orange Rockerverb — classic rock crunch",
|
||||
"WaveNet", 400),
|
||||
("JCM2000_Crunch.nam",
|
||||
"Tim R JCM2000 Crunch.nam",
|
||||
"Marshall JCM2000 crunch — hard rock rhythm",
|
||||
"WaveNet", 400),
|
||||
("JCM900_Lead.nam",
|
||||
"Tim R JCM90050WDualVerbCHAG6.nam",
|
||||
"Marshall JCM900 Dual Verb ch.A gain6 — lead",
|
||||
"WaveNet", 400),
|
||||
("5150_BlockLetter_Boosted.nam",
|
||||
"Helga B 5150 BlockLetter - Boosted.nam",
|
||||
"EVH 5150 Block Letter boosted — high-gain lead",
|
||||
"WaveNet", 400),
|
||||
("6505_Red_NoBoost.nam",
|
||||
"Helga B 6505+ Red ch - NoBoost.nam",
|
||||
"Peavey 6505+ Red ch no boost — metal rhythm",
|
||||
"WaveNet", 400),
|
||||
("Engl_Savage.nam",
|
||||
"Tom C Axe FX 2 Engl Savage.nam",
|
||||
"ENGL Savage 120 — modern high-gain lead",
|
||||
"WaveNet", 400),
|
||||
("Splawn_ProMod_3rd.nam",
|
||||
"Tim R Splawn Pro Mod 3rd Gear - G12.nam",
|
||||
"Splawn Pro Mod 3rd gear — tight high-gain",
|
||||
"WaveNet", 400),
|
||||
("5152_Lead_TS9.nam",
|
||||
"Tim R 5152 Lead + TS9.nam",
|
||||
"5152 Lead with Tube Screamer — boosted solo",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Pedal captures ───────────────────────────────────────────────
|
||||
("Klon_Centaur_High.nam",
|
||||
"Keith B Klone_plus_BB_highGain.nam",
|
||||
"Klon Centaur-style OD high gain — boost/overdrive",
|
||||
"WaveNet", 400),
|
||||
("TubeScreamer_Maxed.nam",
|
||||
"Tudor N Ibanez Tube Screamer Mini Tone@10_Level@10_Overdrive@0 ESR_0.0006_lite.nam",
|
||||
"Ibanez TS808 Mini maxed — clean boost",
|
||||
"WaveNet", 200),
|
||||
("Mesa_MarkIV_Lead.nam",
|
||||
"Roman A LT_MESA_MARKIV_1.nam",
|
||||
"Mesa Boogie Mark IV lead — singing sustain",
|
||||
"WaveNet", 400),
|
||||
("Bogner_Ecstasy.nam",
|
||||
"Tudor N Ceriatone Molecular 50.nam",
|
||||
"Bogner Ecstasy-style — versatile OD range",
|
||||
"WaveNet", 400),
|
||||
|
||||
# ── Feather models (93 KB, ultra-low CPU) ────────────────────────
|
||||
("BOSS_SD1_Feather.nam",
|
||||
"Phillipe P BOSS-SD1-Feather-ESR0,001.nam",
|
||||
"BOSS SD-1 overdrive feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("MXR_M77_Feather.nam",
|
||||
"Phillipe P MXR-M77-Feather-ESR0,001.nam",
|
||||
"MXR M77 Custom Badass OD feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("TS808_Feather.nam",
|
||||
"Phillipe P Maxon-OD808-Feather-ESR0,001.nam",
|
||||
"Maxon OD808 Tube Screamer feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("Precision_Drive_Feather.nam",
|
||||
"Phillipe P Precision-Drive-Feather-ESR0,001.nam",
|
||||
"Precision Drive overdrive feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
("Friedman_PowerAmp_Feather.nam",
|
||||
"Sascha S FriedmanDSM_PowerAmpEL84_MV10_feather.nam",
|
||||
"Friedman DSM power amp feather — 93 KB",
|
||||
"WaveNet", 93),
|
||||
|
||||
# ── Bank 2: Blues (Tone3000 captures — clean, edge-of-breakup, pushed) ─
|
||||
# Downloaded from ToneHunt / Tone3000 Supabase storage. These are real
|
||||
# amp captures of classic blues tones: Fender Princeton, Tweed Deluxe,
|
||||
# Twin Reverb, Dumble ODS, Two-Rock, Marshall Bluesbreaker.
|
||||
# Format: (local_name, tone3000_storage_key, description, architecture, size_kb)
|
||||
("Fender_Princeton_Clone_Clean.nam",
|
||||
"4f81e2b088149c04.nam",
|
||||
"Fender Hand-wired Princeton Clone Clean — blues clean (WaveNet A1, 289 KB)",
|
||||
"WaveNet", 289),
|
||||
("Fender_Princeton_Clone_EOB.nam",
|
||||
"68a5f3484e1759ed_a2.nam",
|
||||
"Fender Hand-wired Princeton Clone Edge-of-Breakup — blues edge (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Fender_Tweed_Dlx_Edge.nam",
|
||||
"ga5rj9sxlnk.nam",
|
||||
"Fender Tweed Deluxe The Edge (Celestion Blue) — pushed clean (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Fender_Twin_Reverb_Clean_A2.nam",
|
||||
"tg81uadutjo_a2.nam",
|
||||
"Fender Twin Reverb Clean — sparkling blues clean (Slimmable A2, 290 KB)",
|
||||
"SlimmableContainer", 290),
|
||||
("TwoRock_JTM_Signature.nam",
|
||||
"qj3hkgbcwb9.nam",
|
||||
"Two-Rock John Mayer Signature Prototype — clean/edge bloom (WaveNet A1, 276 KB)",
|
||||
"WaveNet", 276),
|
||||
("Dumble_ODS_102_Rock_A1.nam",
|
||||
"5bac98f136812852.nam",
|
||||
"Dumble ODS #102 Clone Rock setting — pushed blues overdrive (WaveNet A1, 87 KB feather)",
|
||||
"WaveNet", 87),
|
||||
("Dumble_ODS_102_Rock_A2.nam",
|
||||
"5bac98f136812852_a2.nam",
|
||||
"Dumble ODS #102 Clone Rock setting — pushed blues OD (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Marshall_Bluesbreaker_1962_A2.nam",
|
||||
"bbc41ecccdc730fb_a2.nam",
|
||||
"Marshall Bluesbreaker 1962 — classic blues rock (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Marshall_Bluesbreaker_Pedal.nam",
|
||||
"31aeae1256b36839.nam",
|
||||
"Marshall Bluesbreaker Pedal (Original) Setting 1 — pedal OD (WaveNet A1, 290 KB)",
|
||||
"WaveNet", 290),
|
||||
|
||||
|
||||
# ── Bank 7: Bass, Orange & Others (Tone3000 A2 — SlimmableContainer) ────
|
||||
# Downloaded from Tone3000 Supabase storage. Covers missing categories:
|
||||
# Bass (Ampeg, Aguilar, GK), Orange Rocker 30, Fender Bassman,
|
||||
# Hiwatt DR103, Supro Black Magick.
|
||||
("Ampeg_PF20T_A2.nam",
|
||||
"db2c43e99cfc346d_a2.nam",
|
||||
"Ampeg PF20T — clean bass tube amp (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Aguilar_Tone_Hammer_500_A2.nam",
|
||||
"38rdhwzk3hp_a2.nam",
|
||||
"Aguilar Tone Hammer 500 — modern bass preamp (Slimmable A2, 293 KB)",
|
||||
"SlimmableContainer", 293),
|
||||
("GK_800RB_Line_A2.nam",
|
||||
"ce68ccd373a10054_a2.nam",
|
||||
"Gallien-Krueger 800RB — classic bass solid-state (Slimmable A2, 295 KB)",
|
||||
"SlimmableContainer", 295),
|
||||
("Aguilar_DB751_Cab_A2.nam",
|
||||
"lage0x4ybk_a2.nam",
|
||||
"Aguilar DB751 — bass amp DI (Slimmable A2, 292 KB)",
|
||||
"SlimmableContainer", 292),
|
||||
("Orange_Rocker30_Dirty_A2.nam",
|
||||
"8491c70c3771de1e_a2.nam",
|
||||
"Orange Rocker 30 Dirty — classic rock crunch (Slimmable A2, 295 KB)",
|
||||
"SlimmableContainer", 295),
|
||||
("Orange_Rocker30_Natural_A2.nam",
|
||||
"a60ed1a1aa0c2c0f_a2.nam",
|
||||
"Orange Rocker 30 Natural — clean edge (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Fender_Bassman_Dimed_A2.nam",
|
||||
"0a9223a9f1c879d5_a2.nam",
|
||||
"Fender Bassman dimed — blues/rock breakup (Slimmable A2, 289 KB)",
|
||||
"SlimmableContainer", 289),
|
||||
("Hiwatt_DR103_Bright_A2.nam",
|
||||
"0c46e5a7db1cce48_a2.nam",
|
||||
"Hiwatt DR-103 Bright Normal — prog/classic rock (Slimmable A2, 287 KB)",
|
||||
"SlimmableContainer", 287),
|
||||
("Hiwatt_DR103_Cornish_A2.nam",
|
||||
"ynk0h2e2tph.nam",
|
||||
"Hiwatt DR103 + Cornish PGS — boosted prog rock (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
("Supro_Black_Magick_M3_A2.nam",
|
||||
"c90a6d8ee0c0a689_a2.nam",
|
||||
"Supro Black Magick M3 — garage/blues (Slimmable A2, 288 KB)",
|
||||
"SlimmableContainer", 288),
|
||||
]
|
||||
|
||||
# ── IR Pack definitions ──────────────────────────────────────────────────
|
||||
IR_PACKS = [
|
||||
("God's Cab",
|
||||
"https://archive.org/download/godscab-ir-pack/GodsCab_IR_Pack.zip",
|
||||
"godscab.zip",
|
||||
"100+ cabinet IRs (412, 1960, V30, Greenback, etc.) — CC0 / Public Domain"),
|
||||
("Seacow Cabs",
|
||||
"https://archive.org/download/seacow-cabs-free-ir-2023/Seacow_Cabs_Free_IRs_2023.zip",
|
||||
"seacow.zip",
|
||||
"Boutique cab IR pack, 15+ cabs — Free for personal use"),
|
||||
]
|
||||
|
||||
|
||||
def info(msg): print(f"\033[0;36m[INFO]\033[0m {msg}")
|
||||
def ok(msg): print(f"\033[0;32m[OK]\033[0m {msg}")
|
||||
def warn(msg): print(f"\033[1;33m[WARN]\033[0m {msg}")
|
||||
def err(msg): print(f"\033[0;31m[ERR]\033[0m {msg}", file=sys.stderr)
|
||||
|
||||
|
||||
def download_file(url: str, dest: Path, max_size_mb: int = 50) -> bool:
|
||||
"""Download a file from url to dest. Returns True on success."""
|
||||
try:
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "pedal-downloader/1.0"})
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
size_mb = len(data) / (1024 * 1024)
|
||||
if size_mb > max_size_mb:
|
||||
err(f"File too large ({size_mb:.1f} MB, max {max_size_mb} MB)")
|
||||
return False
|
||||
if len(data) < 1000:
|
||||
err(f"File too small ({len(data)} bytes)")
|
||||
return False
|
||||
dest.write_bytes(data)
|
||||
return True
|
||||
except Exception as e:
|
||||
err(f"Download failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def download_model(local_name: str, remote_name: str, desc: str, arch: str, size: int) -> bool:
|
||||
"""Download a single NAM model file from GitHub."""
|
||||
outfile = NAM_DIR / local_name
|
||||
|
||||
if outfile.exists() and outfile.stat().st_size > 1000:
|
||||
ok(f"SKIP {local_name} (already exists, {outfile.stat().st_size // 1024} KB)")
|
||||
return True
|
||||
|
||||
# URL-encode the remote filename
|
||||
encoded = urllib.parse.quote(remote_name)
|
||||
url = f"{BASE_URL}/{encoded}"
|
||||
|
||||
info(f"DL {local_name} ({desc})")
|
||||
if download_file(url, outfile):
|
||||
# Verify it's valid JSON (.nam format)
|
||||
try:
|
||||
data = json.loads(outfile.read_text())
|
||||
actual_arch = data.get("architecture", "?")
|
||||
actual_ver = data.get("version", "?")
|
||||
ok(f"Downloaded {local_name} ({outfile.stat().st_size // 1024} KB, {actual_arch} v{actual_ver})")
|
||||
return True
|
||||
except json.JSONDecodeError as e:
|
||||
err(f"Invalid .nam file (not valid JSON): {e}")
|
||||
outfile.unlink(missing_ok=True)
|
||||
return False
|
||||
else:
|
||||
err(f"Failed to download {local_name}")
|
||||
return False
|
||||
|
||||
|
||||
def _discover_tone3000_key() -> str:
|
||||
"""Discover the Supabase anon key from Tone3000 website JS bundles."""
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
"https://tone3000.com",
|
||||
headers={"User-Agent": "Mozilla/5.0 (compatible; pedal-downloader/1.0)"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=15) as resp:
|
||||
html = resp.read().decode("utf-8", errors="replace")
|
||||
import re
|
||||
chunk_urls = set()
|
||||
for m in re.finditer(r'src="([^"]*\.js[^"]*)"', html):
|
||||
url = m.group(1)
|
||||
if url.startswith("/"):
|
||||
url = "https://tone3000.com" + url
|
||||
chunk_urls.add(url)
|
||||
for chunk_url in sorted(chunk_urls)[:15]:
|
||||
try:
|
||||
req2 = urllib.request.Request(chunk_url, headers={"User-Agent": "Mozilla/5.0"})
|
||||
with urllib.request.urlopen(req2, timeout=10) as resp2:
|
||||
js = resp2.read().decode("utf-8", errors="replace")
|
||||
m = re.search(
|
||||
r'createBrowserClient\s*\)\s*\(\s*["\']https?://api\.tone3000\.com["\']\s*,\s*["\']([^"\']+)["\']',
|
||||
js,
|
||||
)
|
||||
if m:
|
||||
key = m.group(1)
|
||||
if key.startswith("eyJ") and key.count(".") >= 2:
|
||||
return key
|
||||
for m2 in re.finditer(
|
||||
r'["\'](eyJ[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{10,})["\']', js
|
||||
):
|
||||
key = m2.group(1)
|
||||
if key.count(".") >= 2:
|
||||
return key
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
def download_tone3000_model(local_name: str, storage_key: str, desc: str, arch: str, size: int) -> bool:
|
||||
"""Download a single NAM model file from Tone3000 Supabase storage."""
|
||||
outfile = NAM_DIR / local_name
|
||||
|
||||
if outfile.exists() and outfile.stat().st_size > 1000:
|
||||
ok(f"SKIP {local_name} (already exists, {outfile.stat().st_size // 1024} KB)")
|
||||
return True
|
||||
|
||||
url = f"{TONE3000_STORAGE}/{storage_key}"
|
||||
|
||||
info(f"DL {local_name} ({desc})")
|
||||
req = urllib.request.Request(url, headers={"User-Agent": "pedal-downloader/1.0"})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
except Exception as e:
|
||||
err(f"Download failed: {e}")
|
||||
# Try with anon key (some storage buckets require authentication)
|
||||
try:
|
||||
anon_key = _discover_tone3000_key()
|
||||
if anon_key:
|
||||
req = urllib.request.Request(url, headers={
|
||||
"User-Agent": "pedal-downloader/1.0",
|
||||
"Authorization": f"Bearer {anon_key}",
|
||||
})
|
||||
with urllib.request.urlopen(req, timeout=30) as src:
|
||||
data = src.read()
|
||||
else:
|
||||
return False
|
||||
except Exception as e2:
|
||||
err(f"Download failed even with auth: {e2}")
|
||||
return False
|
||||
|
||||
size_mb = len(data) / (1024 * 1024)
|
||||
if size_mb > 50:
|
||||
err(f"File too large ({size_mb:.1f} MB, max 50 MB)")
|
||||
return False
|
||||
if len(data) < 1000:
|
||||
err(f"File too small ({len(data)} bytes)")
|
||||
return False
|
||||
outfile.write_bytes(data)
|
||||
|
||||
# Verify
|
||||
try:
|
||||
model_data = json.loads(data)
|
||||
actual_arch = model_data.get("architecture", "?")
|
||||
actual_ver = model_data.get("version", "?")
|
||||
ok(f"Downloaded {local_name} ({outfile.stat().st_size // 1024} KB, {actual_arch} v{actual_ver})")
|
||||
return True
|
||||
except json.JSONDecodeError as e:
|
||||
err(f"Invalid .nam file (not valid JSON): {e}")
|
||||
outfile.unlink(missing_ok=True)
|
||||
return False
|
||||
|
||||
|
||||
def download_models():
|
||||
"""Download all NAM models (GitHub + Tone3000)."""
|
||||
print(f"\n\033[0;36mDownloading {len(MODELS)} NAM models to {NAM_DIR}\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
NAM_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
|
||||
for local_name, remote_name, desc, arch, size in MODELS:
|
||||
# Detect Tone3000 storage key (short, no dots, no spaces)
|
||||
if " " not in remote_name and "." not in remote_name and len(remote_name) < 64:
|
||||
if download_tone3000_model(local_name, remote_name, desc, arch, size):
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
else:
|
||||
if download_model(local_name, remote_name, desc, arch, size):
|
||||
success += 1
|
||||
else:
|
||||
fail += 1
|
||||
|
||||
print(f"\n Models: {success} successful, {fail} failed")
|
||||
return fail
|
||||
|
||||
|
||||
def download_irs():
|
||||
"""Download IR packs from Archive.org."""
|
||||
print(f"\n\033[0;36mDownloading IR packs to {IR_DIR}\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
IR_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
|
||||
for name, url, fname, desc in IR_PACKS:
|
||||
# Check if already extracted
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
if wav_count > 10:
|
||||
ok(f"SKIP {name} ({wav_count} .wav files already present)")
|
||||
success += 1
|
||||
continue
|
||||
|
||||
dest = IR_DIR / fname
|
||||
info(f"Downloading {name}...")
|
||||
info(f" Source: {url}")
|
||||
|
||||
if download_file(url, dest, max_size_mb=200):
|
||||
size_mb = dest.stat().st_size / (1024 * 1024)
|
||||
ok(f"Downloaded {fname} ({size_mb:.1f} MB)")
|
||||
|
||||
# Extract ZIP
|
||||
import zipfile
|
||||
try:
|
||||
with zipfile.ZipFile(dest, "r") as zf:
|
||||
zf.extractall(IR_DIR)
|
||||
dest.unlink()
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
ok(f"Extracted to {IR_DIR}/ ({wav_count} .wav files)")
|
||||
success += 1
|
||||
except zipfile.BadZipFile:
|
||||
warn(f"Bad ZIP — saved at {dest}")
|
||||
fail += 1
|
||||
else:
|
||||
err(f"Failed to download {name}")
|
||||
fail += 1
|
||||
|
||||
print(f"\n IR packs: {success} downloaded, {fail} failed")
|
||||
wav_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
info(f"Total .wav IR files: {wav_count}")
|
||||
return fail
|
||||
|
||||
|
||||
def list_models():
|
||||
"""Print model listing."""
|
||||
print(f"\n\033[0;36mNAM Models by Genre:\033[0m")
|
||||
print(f"\033[0;36m{'━' * 60}\033[0m")
|
||||
for local_name, remote_name, desc, arch, size in MODELS:
|
||||
print(f" {local_name:35s} {desc}")
|
||||
print(f" {'':35s} ({arch}, ~{size} KB)")
|
||||
print()
|
||||
|
||||
print(f"\nTotal: {len(MODELS)} models")
|
||||
|
||||
# Count existing files
|
||||
nam_count = len(list(NAM_DIR.glob("*.nam")))
|
||||
ir_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
print(f"\n Models in {NAM_DIR}: {nam_count}")
|
||||
print(f" IRs in {IR_DIR}: {ir_count}")
|
||||
|
||||
|
||||
def main():
|
||||
# Also check ~/.pedal/nam/ legacy location
|
||||
NAM_DIR.mkdir(parents=True, exist_ok=True)
|
||||
IR_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
arg = sys.argv[1] if len(sys.argv) > 1 else "--all"
|
||||
|
||||
if arg == "--list":
|
||||
list_models()
|
||||
elif arg == "--models":
|
||||
exit(download_models())
|
||||
elif arg == "--irs":
|
||||
exit(download_irs())
|
||||
elif arg == "--all":
|
||||
fail = download_models()
|
||||
download_irs()
|
||||
|
||||
# Summary
|
||||
nam_count = len(list(NAM_DIR.glob("*.nam")))
|
||||
ir_count = len(list(IR_DIR.rglob("*.wav")))
|
||||
print(f"\n\033[0;32m{'╔' + '═' * 56 + '╗'}\033[0m")
|
||||
print(f"\033[0;32m║ Download complete!{' ' * 36}║\033[0m")
|
||||
print(f"\033[0;32m{'╚' + '═' * 56 + '╝'}\033[0m")
|
||||
print()
|
||||
print(f" Models: {nam_count} .nam files in {NAM_DIR}")
|
||||
print(f" IRs: {ir_count} .wav files in {IR_DIR}")
|
||||
exit(fail)
|
||||
else:
|
||||
print(f"Usage: {sys.argv[0]} [--list|--models|--irs|--all]")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user