Detect and badge HDR10+ (SMPTE ST 2094-40) via ffmpeg showinfo

This commit is contained in:
2026-05-31 02:05:10 +00:00
parent 1a6a3f0cf2
commit e1a961d21f
2 changed files with 49 additions and 0 deletions
@@ -19,6 +19,7 @@
<div class="version-badges">
<span v-if="torrent.resolution" class="v-badge res">{{ torrent.resolution }}</span>
<span v-if="showHdrBadge" class="v-badge hdr">HDR</span>
<span v-if="showHdr10PlusBadge" class="v-badge hdr10plus">HDR10+</span>
<span v-if="displayCodecBadge" class="v-badge codec">{{ displayCodecBadge }}</span>
<span v-if="displayQualityBadge" class="v-badge qual">{{ displayQualityBadge }}</span>
<span v-if="displayAudioBadge" class="v-badge audio">{{ displayAudioBadge }}</span>
@@ -317,6 +318,18 @@ const showHdrBadge = computed(() => {
)
})
const hdr10PlusPattern = /hdr10\+|hdr10plus/i
const hasHdr10Plus = computed(() => {
if (props.torrent.hdr10plus) return true
const text = [props.torrent.title, props.torrent.quality, props.torrent.codec, props.torrent.audio]
.filter(Boolean)
.join(" ")
return hdr10PlusPattern.test(text)
})
const showHdr10PlusBadge = computed(() => hasHdr10Plus.value && !hasDolbyVision.value)
const isSelectable = computed(() => {
if (props.selectable !== undefined) return props.selectable
return Boolean(props.torrent.playable_file)
@@ -525,6 +538,11 @@ html.mouse-active .version-row.version-best:hover {
color: #1c1917;
}
.v-badge.hdr10plus {
background: #b8860b;
color: #fff8e1;
}
.v-badge.group {
background: #1c54a1;
color: #f8fafc;
+31
View File
@@ -432,6 +432,8 @@ _dovi_profile_re = re.compile(
)
_audio_stream_re = re.compile(r"Stream #\d+:\d+(?:\(([^)]+)\))?:\s+Audio:")
_subtitle_stream_re = re.compile(r"Stream #\d+:\d+(?:\(([^)]+)\))?:\s+Subtitle:")
# showinfo emits e.g. "side data - HDR Dynamic Metadata SMPTE2094-40 (HDR10+)"
_showinfo_hdr10plus_re = re.compile(r"SMPTE2094-40|HDR Dynamic Metadata", re.IGNORECASE)
def _lang_code(raw: str | None) -> str | None:
@@ -492,6 +494,35 @@ async def probe_media_info(video_path: str) -> MediaProbeInfo:
or "dynamic hdr" in lower_text
)
# `ffmpeg -i` only reads container headers, not frame-level side data.
# Run a tiny showinfo decode (5 frames) to detect HDR10+ dynamic metadata
# (SMPTE ST 2094-40) when the header scan didn't already confirm it.
if info.hdr and not info.hdr10plus and not info.dovi:
showinfo_cmd = [
"ffmpeg",
"-hide_banner",
"-ss",
"0",
"-i",
video_path,
"-vf",
"showinfo",
"-frames:v",
"5",
"-an",
"-f",
"null",
"-",
]
showinfo_result = await _run_ffmpeg(
showinfo_cmd, timeout_seconds=15, allow_nonzero_exit=True
)
if showinfo_result is not None:
si_stdout, si_stderr = showinfo_result
si_text = (si_stderr + si_stdout).decode("utf-8", errors="replace")
if _showinfo_hdr10plus_re.search(si_text):
info.hdr10plus = True
dovi_match = _dovi_profile_re.search(text)
if dovi_match:
info.dovi_profile = int(dovi_match.group(1))