tonhowtf/omniget · error

No quality available

Error message

No quality available

What it means

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.

Solutions

  1. Update yt-dlp to the latest version and re-run — old versions frequently miss Vimeo formats.
  2. Verify the video is public and fully processed (not a live/unfinished stream) by checking it in a browser.
  3. Run `yt-dlp -F <url>` manually to see what formats yt-dlp actually reports; supply credentials (cookies) if formats require auth.
  4. Inspect parse_video_info to ensure it isn't discarding valid formats, and add formats if the filter is too strict.
  5. Return a clearer upstream error (log the raw format list) so users can distinguish 'no formats' from 'access denied'.

Example fix

// before: empty quality list aborts
let first = info
    .available_qualities
    .first()
    .ok_or_else(|| anyhow!("No quality available"))?;

// after: fall back to a direct default-quality download request
let selected = if let Some(ref wanted) = opts.quality {
    info.available_qualities.iter().find(|q| q.label == *wanted)
        .or_else(|| info.available_qualities.first())
        .map(|q| q.clone())
        .unwrap_or_else(|| {
            tracing::warn!("[vimeo] no qualities parsed; using direct default download");
            MediaVideoQuality::default() // lets ytdlp pick best available format
        })
} else {
    info.available_qualities.first().cloned().unwrap_or_else(|| {
        tracing::warn!("[vimeo] no qualities parsed; using direct default download");
        MediaVideoQuality::default()
    })
};
Defensive patterns

Strategy: try-catch

Validate before calling

// Check that the Vimeo video exposes formats before attempting download
let probe = tokio::process::Command::new(&ytdlp_path)
    .args(["-J", "--no-download", url])
    .output().await?;
let json: serde_json::Value = serde_json::from_slice(&probe.stdout)?;
let format_count = json["formats"].as_array().map(|a| a.len()).unwrap_or(0);
if format_count == 0 {
    anyhow::bail!("video has no downloadable formats (private, DRM, live, or region-locked)");
}

Type guard

fn has_selectable_quality(info: &MediaInfo) -> bool {
    !info.available_qualities.is_empty()
}

Try / catch

if !has_selectable_quality(&info) {
    // decide policy before calling download: warn or abort
    anyhow::bail!("Vimeo video exposes no qualities; check access/DRM before download");
}
match vimeo.download(url, &opts).await {
    Ok(path) => use_file(path),
    Err(e) if e.to_string().contains("No quality available") => {
        update_ytdlp_and_retry(url).await // stale extractor is the usual cause
    }
    Err(e) => report(e),
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/257433f36df96664. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/vimeo.rs:150

        let json = ytdlp::get_video_info(&ytdlp_path, url, &[]).await?;
        Self::parse_video_info(&json)
    }

    async fn download(
        &self,
        info: &MediaInfo,
        opts: &DownloadOptions,
        progress: mpsc::Sender<ProgressUpdate>,
    ) -> anyhow::Result<DownloadResult> {
        let _ = progress.send(ProgressUpdate::percent(0.0)).await;

        let ytdlp_path = ytdlp::ensure_ytdlp().await?;

        let first = info
            .available_qualities
            .first()
            .ok_or_else(|| anyhow!("No quality available"))?;

        let selected = if let Some(ref wanted) = opts.quality {
            info.available_qualities
                .iter()
                .find(|q| q.label == *wanted)
                .unwrap_or(first)
        } else {
            first
        };

        let quality_height = Self::extract_quality_height(&selected.label);
        let video_url = &selected.url;

        ytdlp::download_video(
            &ytdlp_path,
            video_url,
            &opts.output_dir,
            quality_height,

View on GitHub (pinned to 8600b91f42)