Last active
March 3, 2026 16:05
-
-
Save connorads/38488e1dde78f84cf73317f3369562a3 to your computer and use it in GitHub Desktop.
Download StarCraft 1 unit 'wht' (first-selected) voice lines from The Sounds Resource
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
| #!/usr/bin/env python3 | |
| # /// script | |
| # requires-python = ">=3.11" | |
| # dependencies = [ | |
| # "httpx", | |
| # ] | |
| # /// | |
| """Download StarCraft: Brood War unit 'wht' (What) sounds from The Sounds Resource. | |
| Covers all units from both the original StarCraft and the Brood War expansion. | |
| 'wht' files are the voice lines played when a unit is first selected: | |
| Marine: "You want a piece of me, boy?" | |
| Firebat: "Need a light?" | |
| Ghost: "Somebody call for an exterminator?" | |
| Zealot: "My life for Aiur!" | |
| Medic: "Where does it hurt?" (Brood War) | |
| Lurker: *growl* (Brood War) | |
| Downloads ZIPs for each unit, extracts only *wht*.wav files, | |
| and organises them into folders by unit name. | |
| Source: https://sounds.spriters-resource.com/pc_computer/starcraft/ | |
| Usage: | |
| uv run download_wht.py [--output-dir DIR] | |
| """ | |
| import argparse | |
| import io | |
| import time | |
| import zipfile | |
| from pathlib import Path | |
| import httpx | |
| BASE_URL = "https://sounds.spriters-resource.com" | |
| ZIP_PATH = "/media/assets/416/{asset_id}.zip" | |
| # {name: asset_id} — units only (no buildings, advisors, music, misc) | |
| # Includes both original StarCraft and Brood War expansion units. | |
| # Brood War additions marked with (BW). | |
| UNITS: dict[str, int] = { | |
| # --- Protoss --- | |
| "arbiter": 419545, | |
| "archon": 419547, | |
| "carrier": 419550, | |
| "corsair": 419551, # (BW) | |
| "dark-archon": 419553, # (BW) | |
| "dark-templar": 419554, # (BW) | |
| "dragoon": 419555, | |
| "high-templar": 419565, | |
| "interceptor": 419559, | |
| "probe": 419560, | |
| "reaver": 419567, | |
| "scout": 419561, | |
| "shuttle": 419562, | |
| "zealot": 419570, | |
| # --- Protoss heroes --- | |
| "artanis": 419548, # (BW) | |
| "fenix-dragoon": 419556, | |
| "fenix-zealot": 419557, | |
| "gantrithor": 419558, | |
| "tassadar": 419564, | |
| "witness-iv": 419569, | |
| "zeratul": 419572, | |
| # --- Terran --- | |
| "battlecruiser": 419575, | |
| "civilian": 419577, | |
| "dropship": 419578, | |
| "firebat": 419582, | |
| "ghost": 419584, | |
| "goliath": 419585, | |
| "marine": 419587, | |
| "medic": 419588, # (BW) | |
| "science-vessel": 419594, | |
| "scv": 419592, | |
| "siege-tank": 419593, | |
| "valkyrie": 419583, # (BW) listed as "Frigate" on source site | |
| "vulture": 419595, | |
| "wraith": 419589, | |
| # --- Terran heroes --- | |
| "edmund-duke-battlecruiser": 419579, | |
| "edmund-duke-siege-tank": 419580, | |
| "jim-raynor-marine": 419590, | |
| "jim-raynor-vulture": 419591, | |
| "samir-duran": 419581, # (BW) | |
| "sarah-kerrigan": 419586, | |
| # --- Zerg --- | |
| "broodling": 419599, | |
| "defiler": 419601, | |
| "devourer": 419602, # (BW) | |
| "guardian": 419604, | |
| "hydralisk": 419605, | |
| "infested-terran": 419600, | |
| "lurker": 419607, # (BW) | |
| "mutalisk": 419608, | |
| "overlord": 419609, | |
| "queen": 419610, | |
| "scourge": 419597, | |
| "ultralisk": 419611, | |
| "zergling": 419614, | |
| # --- Zerg heroes --- | |
| "samir-duran-infested": 419612, # (BW) | |
| "sarah-kerrigan-infested": 419613, | |
| } | |
| # Units unlikely to have voice lines (non-speaking units / creatures) | |
| SKIP_NO_VOICE = { | |
| "interceptor", | |
| "broodling", | |
| "egg", | |
| "larva", | |
| "scourge", | |
| } | |
| def download_wht_sounds(output_dir: Path, *, dry_run: bool = False) -> None: | |
| output_dir.mkdir(parents=True, exist_ok=True) | |
| headers = { | |
| "User-Agent": "Mozilla/5.0 (compatible; SC1SoundDL/1.0)", | |
| "Referer": f"{BASE_URL}/pc_computer/starcraft/", | |
| } | |
| total_files = 0 | |
| units_with_sounds = 0 | |
| units_without = [] | |
| with httpx.Client( | |
| base_url=BASE_URL, | |
| headers=headers, | |
| follow_redirects=True, | |
| timeout=30.0, | |
| ) as client: | |
| for name, asset_id in sorted(UNITS.items()): | |
| if name in SKIP_NO_VOICE: | |
| print(f" skip {name} (non-speaking)") | |
| continue | |
| url = ZIP_PATH.format(asset_id=asset_id) | |
| print(f" fetch {name} ({asset_id})...", end=" ", flush=True) | |
| if dry_run: | |
| print("(dry run)") | |
| continue | |
| try: | |
| resp = client.get(url) | |
| resp.raise_for_status() | |
| except httpx.HTTPError as e: | |
| print(f"FAILED: {e}") | |
| continue | |
| with zipfile.ZipFile(io.BytesIO(resp.content)) as zf: | |
| wht_files = [ | |
| f for f in zf.namelist() if "wht" in f.lower() and f.endswith(".wav") | |
| ] | |
| if not wht_files: | |
| print("no wht files") | |
| units_without.append(name) | |
| continue | |
| unit_dir = output_dir / name | |
| unit_dir.mkdir(exist_ok=True) | |
| for fname in sorted(wht_files): | |
| # Flatten nested paths — just use the filename | |
| out_path = unit_dir / Path(fname).name | |
| out_path.write_bytes(zf.read(fname)) | |
| count = len(wht_files) | |
| total_files += count | |
| units_with_sounds += 1 | |
| print(f"{count} wht files") | |
| # Be polite | |
| time.sleep(0.5) | |
| print() | |
| print(f"Done: {total_files} files from {units_with_sounds} units -> {output_dir}") | |
| if units_without: | |
| print(f"No wht files found for: {', '.join(units_without)}") | |
| def main() -> None: | |
| parser = argparse.ArgumentParser(description="Download StarCraft 1 unit 'wht' sounds") | |
| parser.add_argument( | |
| "--output-dir", | |
| "-o", | |
| type=Path, | |
| default=Path("sc1-wht-sounds"), | |
| help="Output directory (default: sc1-wht-sounds)", | |
| ) | |
| parser.add_argument( | |
| "--dry-run", | |
| "-n", | |
| action="store_true", | |
| help="Show what would be downloaded without downloading", | |
| ) | |
| args = parser.parse_args() | |
| print(f"Downloading StarCraft unit 'wht' sounds to {args.output_dir}/") | |
| print() | |
| download_wht_sounds(args.output_dir, dry_run=args.dry_run) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment