agalwood/Motrix · error · MediaParseError

unsupported-master

unsupported-master

Error message

no video representation

What it means

MediaParseError with code 'unsupported-master', thrown by parseDash at line 40 when zero AdaptationSets in the Period are classified as video (by contentType or mimeType starting with 'video'). The parser requires at least one video AdaptationSet to produce a SegmentPlan.

Source

Thrown at src/core/media/dash-parser.ts:40

  const periodMatch = /<Period\b([^>]*)>([\s\S]*?)<\/Period>/i.exec(xml)
  const periodAttrs = periodMatch?.[1] ?? ''
  const periodBody = periodMatch?.[2] ?? xml

  const mpdDuration = parseDuration(
    attrVal(/<MPD\b[^>]*>/i.exec(xml)?.[0] ?? '', 'mediaPresentationDuration')
  )
  const periodDuration =
    parseDuration(attrVal(periodAttrs, 'duration')) ?? mpdDuration
  const periodBaseUrl = resolveLevelBaseUrl(periodBody, mpdBaseUrl)

  const adaptationSets = extractAdaptationSets(periodBody)

  const videoSets = adaptationSets.filter((a) => isMediaType(a.attrs, 'video'))
  const audioSets = adaptationSets.filter((a) => isMediaType(a.attrs, 'audio'))

  if (videoSets.length === 0) {
    throw new MediaParseError('unsupported-master', 'no video representation')
  }

  const video = pickBestPlan(videoSets, periodBaseUrl, periodDuration, mpdUrl)
  if (video === null) {
    throw new MediaParseError('unsupported-master', 'no video representation')
  }

  let audio: SegmentPlan | undefined
  if (audioSets.length > 0) {
    const audioPlan = pickBestPlan(
      audioSets,
      periodBaseUrl,
      periodDuration,
      mpdUrl
    )
    if (audioPlan !== null) {
      audio = audioPlan
    }

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Inspect the manifest and confirm at least one AdaptationSet has contentType="video" or mimeType starting with "video".
  2. If the asset is audio-only, route it to an audio-only pipeline rather than parseDash's video expectation.
  3. Pre-check the manifest: const hasVideo = /<AdaptationSet[^>]*\bcontentType\s*=\s*"video"/i.test(xml).
  4. If the manifest uses codec-based typing, patch isMediaType or pre-process the XML to add contentType attributes.

Example fix

// before
const { video } = parseDash(xml, mpdUrl)
// after — verify a video set exists and is classifiable
const hasVideoSet = /<AdaptationSet\b[^>]*\b(?:contentType|mimeType)\s*=\s*"video/i.test(xml)
if (!hasVideoSet) throw new UserFacingError('No video track in this DASH manifest.')
const { video } = parseDash(xml, mpdUrl)
Defensive patterns

Strategy: validation

Validate before calling

function dashHasVideoSet(xml: string): boolean {
  return /<AdaptationSet\b[^>]*\b(?:contentType|mimeType)\s*=\s*"video/i.test(xml)
}
if (!dashHasVideoSet(xml)) {
  throw new Error('No video AdaptationSet found in this DASH manifest.')
}

Type guard

function dashHasVideoSet(xml: string): boolean {
  return /<AdaptationSet\b[^>]*\b(?:contentType|mimeType)\s*=\s*"video/i.test(xml)
}

Try / catch

try {
  const { video } = parseDash(xml, mpdUrl)
} catch (e) {
  if (e instanceof MediaParseError && e.code === 'unsupported-master') {
    // classify as 'no playable video track' for the user
  } else throw e
}

Prevention

When it happens

Trigger: Calling parseDash on a manifest whose AdaptationSets are all audio-only (e.g. a pure-audio DASH pack), whose video sets lack contentType/mimeType attributes (so the regex classifier misses them), or whose Period body failed to match the <Period>...</Period> regex entirely.

Common situations: Audio-only podcast assets; manifests that rely on <Codecs> attributes instead of explicit contentType/mimeType; deeply nested or malformed Periods where the greedy regex returns empty; misidentified media type because the AdaptationSet uses mimeType="video/mp4" vs contentType that the parser does not see.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/d0ec5d8668098556. Report an issue: GitHub.