fix hdr playback, lint warnings

This commit is contained in:
2026-06-04 22:50:46 +03:00
parent 2fce5dfb59
commit 6756bf9d40
10 changed files with 144 additions and 72 deletions
+3 -2
View File
@@ -124,14 +124,15 @@ export default function EndOfVideoCard({
}
function RateAndLogRow({ itemId, itemName }: { itemId?: string; itemName: string }) {
if (!itemId || String(itemId).startsWith('tmdb-')) return null
const [hoverRating, setHoverRating] = useState(0)
const personal = usePersonalData(s => s.entries[itemId])
const personal = usePersonalData(s => itemId ? s.entries[itemId] : undefined)
const setRating = usePersonalData(s => s.setRating)
const addDiary = useDiary(s => s.add)
const current = personal?.rating || 0
const [logged, setLogged] = useState(false)
if (!itemId || String(itemId).startsWith('tmdb-')) return null
return (
<div className="flex items-center justify-center gap-3 mb-5">
<div className="flex items-center gap-1">
+25 -3
View File
@@ -13,6 +13,7 @@ import {
videoRangeLabel,
} from '../../lib/jellyfin-meta'
import { formatBitrate } from '../../lib/format'
import { isHdrDisplayActive } from '../../lib/device-profile'
interface Props {
item?: BaseItemDto | null
@@ -58,7 +59,7 @@ function readLiveStats(): LiveStats {
}
}
async function checkHwDecode(stream: { Codec?: string | null; Width?: number | null; Height?: number | null; BitRate?: number | null } | null): Promise<HwDecodeState> {
async function checkHwDecode(stream: { Codec?: string | null; Width?: number | null; Height?: number | null; BitRate?: number | null; VideoRangeType?: string | null; VideoRange?: string | null } | null): Promise<HwDecodeState> {
if (!stream || typeof navigator.mediaCapabilities?.decodingInfo !== 'function') {
return { supported: null, hwAccelerated: null }
}
@@ -66,9 +67,15 @@ async function checkHwDecode(stream: { Codec?: string | null; Width?: number | n
const w = stream.Width ?? 1920
const h = stream.Height ?? 1080
const br = stream.BitRate ?? 8000000
// Map common Jellyfin codec names to MIME codec strings
// Use the main10 profile codec string for HDR content so the
// MediaCapabilities query matches what the actual stream requires.
// The previous main (8-bit) string caused HDR sources to probe
// against the wrong profile.
const range = (stream.VideoRangeType || stream.VideoRange || '').toUpperCase()
const isHdr = range === 'HDR' || range === 'HDR10' || range === 'HLG' || range.startsWith('DOVI')
const mimeCodec =
codec === 'h264' ? 'avc1.640033'
: codec === 'hevc' && isHdr ? 'hev1.2.4.L153.B0'
: codec === 'hevc' ? 'hev1.1.6.L150.90'
: codec === 'av1' ? 'av01.0.05M.08'
: codec === 'vp9' ? 'vp09.00.50.08'
@@ -164,7 +171,7 @@ export default function StreamInfo({ item, visible, playMethod }: Props) {
if (!visible) return
const v = getVideoStream(item || {})
checkHwDecode(v).then(setHw)
}, [visible, item?.Id])
}, [visible, item])
if (!visible || !item) return null
const source = pickPrimarySource(item)
@@ -192,6 +199,21 @@ export default function StreamInfo({ item, visible, playMethod }: Props) {
const res = resolutionLabel(item)
const range = videoRangeLabel(item)
// HDR display status - tells the user whether their display is actually
// in HDR mode. When the source is HDR but the display is SDR, the browser
// tone-maps the content. If the profile was built with HDR display off,
// the server did FFmpeg tone-mapping instead.
const hdrDisplay = isHdrDisplayActive()
if (range && range !== 'SDR') {
rows.push({
label: 'HDR display',
value: hdrDisplay ? 'Active' : 'Off (tone-mapped)',
accent: hdrDisplay,
warn: !hdrDisplay,
})
}
if (res) rows.push({ label: 'Resolution', value: range ? `${res} · ${range}` : res })
if (v) {
rows.push({ label: 'Video', value: videoCodecLabel(v) })
+1 -1
View File
@@ -54,7 +54,7 @@ function parseVTT(raw: string): Cue[] {
// Accept both WebVTT dots and SRT commas for the milliseconds separator.
// Jellyfin sometimes serves SRT content even on the .vtt endpoint.
const m = lines[tsLine].match(
/(\d+:)?(\d+):(\d+)[\.,](\d+)\s+-->\s+(\d+:)?(\d+):(\d+)[\.,](\d+)/,
/(\d+:)?(\d+):(\d+)[.,](\d+)\s+-->\s+(\d+:)?(\d+):(\d+)[.,](\d+)/,
)
if (!m) continue
const start = parseTimeStamp(m[1], m[2], m[3], m[4])