tonhowtf/omniget · error

No quality available

Error message

No quality available

What it means

In GenericYtdlpPlatform::download, the candidate stream is taken from the first element of info.available_qualities. If that vector is empty there is no format to select and the function aborts with 'No quality available'.

Solutions

  1. Run `yt-dlp -F <url>` manually to see whether real formats exist; if none, the content is restricted.
  2. Update yt-dlp (yt-dlp -U) to pick up extractor fixes for the site.
  3. Check parse_video_info's format filtering logic against the raw JSON to see why formats were dropped.
  4. Validate available_qualities is non-empty in the caller before invoking download.

Example fix

// before
let first = info.available_qualities.first()
    .ok_or_else(|| anyhow!("No quality available"))?;
// after (caller-side guard)
if info.available_qualities.is_empty() {
    eprintln!("no formats extracted; refreshing MediaInfo / updating yt-dlp first");
    info = platform.get_media_info(&url).await?;
}
let first = info.available_qualities.first().context("No quality available")?;
Defensive patterns

Strategy: validation

Validate before calling

if info.available_qualities.is_empty() {
    eprintln!("no formats extracted for {url}; run `yt-dlp -F {url}` to inspect");
    return Err(anyhow!("refusing download: MediaInfo has zero qualities"));
}

Type guard

fn pick_quality<'a>(info: &'a MediaInfo, height: Option<u32>) -> Option<&'a Quality> {
    match height {
        Some(h) => info.available_qualities.iter().find(|q| q.height == Some(h)),
        None => info.available_qualities.first(),
    }
}

Try / catch

match platform.download(opts).await {
    Err(e) if e.to_string().contains("No quality available") => {
        let info = platform.get_media_info(url).await?;
        if info.available_qualities.is_empty() {
            Err(anyhow!("source exposes no downloadable formats (DRM/members-only?)"))
        } else {
            platform.download(opts).await
        }
    }
    other => other,
}

Prevention

When it happens

Trigger: download(opts) called with a MediaInfo built from parse_video_info where available_qualities ended up empty — e.g. yt-dlp JSON had no formats matching the filter, or the caller hand-built/passed an empty MediaInfo.

Common situations: Pages with only storyboard/m3u8 formats the parser skips; DRM or members-only content where yt-dlp returns no downloadable formats; outdated yt-dlp extractor returning empty formats; passing get_media_info output from a different platform into download.

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/3a58e9864a46c9ba. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/platforms/generic_ytdlp.rs:268

            .map_err(|e| anyhow!("yt-dlp unavailable: {}", e))?;

        let extra = platform_extra_flags(url);
        let json = ytdlp::get_video_info(&ytdlp_path, url, &extra).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 first = info
            .available_qualities
            .first()
            .ok_or_else(|| anyhow!("No quality available"))?;

        let requested_height = opts
            .quality
            .as_deref()
            .and_then(Self::extract_quality_height);

        let selected = if let Some(h) = requested_height {
            info.available_qualities
                .iter()
                .filter(|q| q.height > 0 && q.height <= h)
                .max_by_key(|q| q.height)
                .or_else(|| {
                    opts.quality
                        .as_deref()
                        .and_then(|w| info.available_qualities.iter().find(|q| q.label == *w))
                })
                .unwrap_or(first)
        } else if let Some(ref wanted) = opts.quality {

View on GitHub (pinned to 8600b91f42)