{"record":{"id":"257433f36df96664","repo":"tonhowtf/omniget","slug":"no-quality-available-vimeo","errorCode":null,"errorMessage":"No quality available","messagePattern":"No quality available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/platforms/vimeo.rs","lineNumber":150,"sourceCode":"\n        let json = ytdlp::get_video_info(&ytdlp_path, url, &[]).await?;\n        Self::parse_video_info(&json)\n    }\n\n    async fn download(\n        &self,\n        info: &MediaInfo,\n        opts: &DownloadOptions,\n        progress: mpsc::Sender<ProgressUpdate>,\n    ) -> anyhow::Result<DownloadResult> {\n        let _ = progress.send(ProgressUpdate::percent(0.0)).await;\n\n        let ytdlp_path = ytdlp::ensure_ytdlp().await?;\n\n        let first = info\n            .available_qualities\n            .first()\n            .ok_or_else(|| anyhow!(\"No quality available\"))?;\n\n        let selected = if let Some(ref wanted) = opts.quality {\n            info.available_qualities\n                .iter()\n                .find(|q| q.label == *wanted)\n                .unwrap_or(first)\n        } else {\n            first\n        };\n\n        let quality_height = Self::extract_quality_height(&selected.label);\n        let video_url = &selected.url;\n\n        ytdlp::download_video(\n            &ytdlp_path,\n            video_url,\n            &opts.output_dir,\n            quality_height,","sourceCodeStart":132,"sourceCodeEnd":168,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/platforms/vimeo.rs#L132-L168","documentation":"In Vimeo's `download` (src-tauri/omniget-core/src/platforms/vimeo.rs:150), after `get_media_info` succeeds, the code takes `available_qualities.first()` and throws 'No quality available' if the list is empty. It guards against yt-dlp returning video info JSON with no usable downloadable formats — i.e., metadata exists but nothing is selectable to download.","triggerScenarios":"Calling download with VimeoDownloadOptions for a video whose parsed info has an empty available_qualities list: yt-dlp returned no matching video formats, the video is DRM-protected/private/live-only, or parse_video_info filtered out all formats (e.g., audio-only or unsupported codecs).","commonSituations":"Private, paywalled, or password-protected Vimeo videos where yt-dlp sees no accessible formats, livestream VODs still processing, yt-dlp version too old to parse Vimeo's current format list, or region-locked videos returning metadata but zero formats.","solutions":["Update yt-dlp to the latest version and re-run — old versions frequently miss Vimeo formats.","Verify the video is public and fully processed (not a live/unfinished stream) by checking it in a browser.","Run `yt-dlp -F <url>` manually to see what formats yt-dlp actually reports; supply credentials (cookies) if formats require auth.","Inspect parse_video_info to ensure it isn't discarding valid formats, and add formats if the filter is too strict.","Return a clearer upstream error (log the raw format list) so users can distinguish 'no formats' from 'access denied'."],"exampleFix":"// before: empty quality list aborts\nlet first = info\n    .available_qualities\n    .first()\n    .ok_or_else(|| anyhow!(\"No quality available\"))?;\n\n// after: fall back to a direct default-quality download request\nlet selected = if let Some(ref wanted) = opts.quality {\n    info.available_qualities.iter().find(|q| q.label == *wanted)\n        .or_else(|| info.available_qualities.first())\n        .map(|q| q.clone())\n        .unwrap_or_else(|| {\n            tracing::warn!(\"[vimeo] no qualities parsed; using direct default download\");\n            MediaVideoQuality::default() // lets ytdlp pick best available format\n        })\n} else {\n    info.available_qualities.first().cloned().unwrap_or_else(|| {\n        tracing::warn!(\"[vimeo] no qualities parsed; using direct default download\");\n        MediaVideoQuality::default()\n    })\n};","handlingStrategy":"try-catch","validationCode":"// Check that the Vimeo video exposes formats before attempting download\nlet probe = tokio::process::Command::new(&ytdlp_path)\n    .args([\"-J\", \"--no-download\", url])\n    .output().await?;\nlet json: serde_json::Value = serde_json::from_slice(&probe.stdout)?;\nlet format_count = json[\"formats\"].as_array().map(|a| a.len()).unwrap_or(0);\nif format_count == 0 {\n    anyhow::bail!(\"video has no downloadable formats (private, DRM, live, or region-locked)\");\n}","typeGuard":"fn has_selectable_quality(info: &MediaInfo) -> bool {\n    !info.available_qualities.is_empty()\n}","tryCatchPattern":"if !has_selectable_quality(&info) {\n    // decide policy before calling download: warn or abort\n    anyhow::bail!(\"Vimeo video exposes no qualities; check access/DRM before download\");\n}\nmatch vimeo.download(url, &opts).await {\n    Ok(path) => use_file(path),\n    Err(e) if e.to_string().contains(\"No quality available\") => {\n        update_ytdlp_and_retry(url).await // stale extractor is the usual cause\n    }\n    Err(e) => report(e),\n}","preventionTips":["Run `yt-dlp -F <vimeo-url>` first to confirm the video actually exposes formats.","Check the video is public and fully processed (not live or paywalled) before downloading.","Keep yt-dlp current so Vimeo's format list parses correctly.","Supply cookies/credentials for videos that need authentication.","Handle empty quality lists in your caller with a fallback to default-quality download."],"tags":["empty-result","vimeo","yt-dlp","quality-selection"],"backgroundTag":"empty-result-set","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}