{"record":{"id":"cb8691b4b2feda03","repo":"calesthio/OpenMontage","slug":"no-preview-url-available-for-sound-sound-get-id","errorCode":null,"errorMessage":"No preview URL available for sound {sound.get('id')} ({sound.get('name')})","messagePattern":"No preview URL available for sound (.+?) \\((.+?)\\)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"tools/audio/freesound_music.py","lineNumber":210,"sourceCode":"        request = urllib.request.Request(\n            url,\n            headers={\"User-Agent\": \"OpenMontage/0.1 (music acquisition tool)\"},\n        )\n\n        with urllib.request.urlopen(request, timeout=30) as response:\n            data = json.loads(response.read().decode(\"utf-8\"))\n\n        results = data.get(\"results\", [])\n        return results\n\n    def _download(self, sound: dict, inputs: dict[str, Any], api_key: str) -> Path:\n        \"\"\"Download the HQ MP3 preview of a Freesound sound.\"\"\"\n        previews = sound.get(\"previews\", {})\n        # Prefer the HQ MP3 preview; fall back to LQ MP3\n        audio_url = previews.get(\"preview-hq-mp3\") or previews.get(\"preview-lq-mp3\")\n\n        if not audio_url:\n            raise RuntimeError(\n                f\"No preview URL available for sound {sound.get('id')} ({sound.get('name')})\"\n            )\n\n        # Build output path\n        sound_name = sound.get(\"name\", f\"freesound_{sound.get('id', 'unknown')}\")\n        safe_name = \"\".join(c if c.isalnum() or c in \"._- \" else \"_\" for c in sound_name)\n        default_filename = f\"freesound_{sound.get('id')}_{safe_name}.mp3\"\n        output_path = Path(inputs.get(\"output_path\", default_filename))\n        output_path.parent.mkdir(parents=True, exist_ok=True)\n\n        request = urllib.request.Request(\n            audio_url,\n            headers={\"User-Agent\": \"OpenMontage/0.1 (music acquisition tool)\"},\n        )\n\n        with urllib.request.urlopen(request, timeout=60) as response:\n            output_path.write_bytes(response.read())\n","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/calesthio/OpenMontage/blob/95e1c3d0ab93482159818560f6a8c8e866b9139f/tools/audio/freesound_music.py#L192-L228","documentation":"Raised by freesound_music._download when a search result's previews object contains neither preview-hq-mp3 nor preview-lq-mp3. Freesound download URLs only exist for sounds whose license/premium state permits previews; the tool downloads the HQ MP3 preview rather than the original file, so a result without preview URLs is undownloadable.","triggerScenarios":"Search returns a premium-only or delisted sound whose previews dict is empty; result objects from a stale/paginated search response missing the previews field.","commonSituations":"Picking the first search hit without checking its previews; sounds uploaded with original-only licensing; API response shape differences between search endpoints.","solutions":["Filter search results client-side: prefer items where result['previews'].get('preview-hq-mp3') is truthy","Fall back to the next search result instead of failing the whole job","Request additional fields in the search query so previews are populated"],"exampleFix":"// before\nsound = results[0]  # may lack previews\n\n// after\nsound = next(r for r in results if r.get(\"previews\", {}).get(\"preview-hq-mp3\") or r.get(\"previews\", {}).get(\"preview-lq-mp3\"))","handlingStrategy":"validation","validationCode":"def downloadable(sound: dict) -> bool:\n    p = sound.get(\"previews\") or {}\n    return bool(p.get(\"preview-hq-mp3\") or p.get(\"preview-lq-mp3\"))\n\nresults = [r for r in results if downloadable(r)]","typeGuard":"def has_preview(sound: dict) -> bool:\n    previews = sound.get(\"previews\")\n    return isinstance(previews, dict) and bool(\n        previews.get(\"preview-hq-mp3\") or previews.get(\"preview-lq-mp3\")\n    )","tryCatchPattern":null,"preventionTips":["Filter search results on preview presence before selection","Iterate candidates with fallback rather than taking results[0]","Request the previews field explicitly in search queries"],"tags":["freesound","music","download","api-contract"],"backgroundTag":null,"analyzedSha":"95e1c3d0ab93482159818560f6a8c8e866b9139f","analyzedAt":"2026-08-15T06:31:20.014Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}