Randoloot/tools/audio/fetch_surface_sources.py
2026-10-18 16:22:39 +02:00

55 lines
2.6 KiB
Python

"""Fetch licensed nature field recordings; no login or API key required."""
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import re
import subprocess
import urllib.parse
import urllib.request
ROOT = Path(__file__).resolve().parents[2]
DEST = ROOT / "tmp/surface_audio_sources"
SOURCES = {
"wind.wav": ("https://opengameart.org/content/park-ambiences", "park_ambience_wind.wav"),
"leaves.7z": ("https://opengameart.org/content/20-rustles-dry-leaves", "qubodup-rustle.7z"),
"birds.ogg": ("https://opengameart.org/content/ambient-bird-sounds", "birds-isaiah658.ogg"),
"fauna.7z": ("https://opengameart.org/content/ambient-bird-cricket-and-frog", "amb_bird_cricket_frog.7z"),
}
def get(url):
request = urllib.request.Request(url,headers={"User-Agent":"Randoloot nature audio preparation"})
with urllib.request.urlopen(request,timeout=120) as response: return response.read()
def fetch(entry):
name,(page,filename) = entry
path = DEST/name
if not path.exists():
html = get(page).decode("utf-8")
links = re.findall(r'href=[\"\x27]([^\"\x27]+)[\"\x27]',html)
matches = [urllib.parse.urljoin(page,url) for url in links if urllib.parse.unquote(url).endswith(filename)]
if name == "birds.ogg" and not matches:
matches = [urllib.parse.urljoin(page,url) for url in links if "birds-isaiah658" in url and url.endswith(".ogg")]
if not matches: raise ValueError("Missing published file: "+filename)
path.write_bytes(get(matches[0]))
if name.endswith(".7z"):
folder = DEST/path.stem
folder.mkdir(exist_ok=True)
names = subprocess.check_output(["tar","-tf",str(path)],text=True).splitlines()
for member in names:
if not (folder/member).resolve().is_relative_to(folder.resolve()): raise ValueError("Unsafe archive path")
subprocess.run(["tar","-xf",str(path),"-C",str(folder)],check=True)
return name,names
return name,path.stat().st_size
if __name__ == "__main__":
DEST.mkdir(parents=True,exist_ok=True)
(DEST/".gdignore").write_text("")
with ThreadPoolExecutor(max_workers=4) as pool:
for result in pool.map(fetch,SOURCES.items()): print(result)
wood = DEST/"wood_creak.mp3"
if not wood.exists():
page = get("https://freesound.org/people/Rudmer_Rotteveel/sounds/506662/").decode()
previews = re.findall(r'https://cdn\.freesound\.org/previews/[^\s\"<>]+?-hq\.mp3',page)
if not previews: raise ValueError("Missing public wood-creak preview")
wood.write_bytes(get(previews[0]))
print("wood_creak.mp3",wood.stat().st_size)