feat(video): detect interlaced scan type from MPD manifests

This commit is contained in:
Andy
2026-01-31 23:51:57 -07:00
parent d4328f0eb7
commit d0d8044fb3
4 changed files with 26 additions and 2 deletions

View File

@@ -151,6 +151,11 @@ class DASH:
if not track_fps and segment_base is not None:
track_fps = segment_base.get("timescale")
scan_type = None
scan_type_str = get("scanType")
if scan_type_str and scan_type_str.lower() == "interlaced":
scan_type = Video.ScanType.INTERLACED
track_args = dict(
range_=self.get_video_range(
codecs, findall("SupplementalProperty"), findall("EssentialProperty")
@@ -159,6 +164,7 @@ class DASH:
width=get("width") or 0,
height=get("height") or 0,
fps=track_fps or None,
scan_type=scan_type,
)
elif content_type == "audio":
track_type = Audio

View File

@@ -153,7 +153,12 @@ class Episode(Title):
# likely a movie or HD source, so it's most likely widescreen so
# 16:9 canvas makes the most sense.
resolution = int(primary_video_track.width * (9 / 16))
name += f" {resolution}p"
# Determine scan type suffix - default to "p", use "i" only if explicitly interlaced
scan_suffix = "p"
scan_type = getattr(primary_video_track, 'scan_type', None)
if scan_type and str(scan_type).lower() == "interlaced":
scan_suffix = "i"
name += f" {resolution}{scan_suffix}"
# Service (use track source if available)
if show_service:

View File

@@ -88,7 +88,12 @@ class Movie(Title):
# likely a movie or HD source, so it's most likely widescreen so
# 16:9 canvas makes the most sense.
resolution = int(primary_video_track.width * (9 / 16))
name += f" {resolution}p"
# Determine scan type suffix - default to "p", use "i" only if explicitly interlaced
scan_suffix = "p"
scan_type = getattr(primary_video_track, 'scan_type', None)
if scan_type and str(scan_type).lower() == "interlaced":
scan_suffix = "i"
name += f" {resolution}{scan_suffix}"
# Service (use track source if available)
if show_service:

View File

@@ -186,6 +186,10 @@ class Video(Track):
# for some reason there's no Dolby Vision info tag
raise ValueError(f"The M3U Range Tag '{tag}' is not a supported Video Range")
class ScanType(str, Enum):
PROGRESSIVE = "progressive"
INTERLACED = "interlaced"
def __init__(
self,
*args: Any,
@@ -195,6 +199,7 @@ class Video(Track):
width: Optional[int] = None,
height: Optional[int] = None,
fps: Optional[Union[str, int, float]] = None,
scan_type: Optional[Video.ScanType] = None,
**kwargs: Any,
) -> None:
"""
@@ -232,6 +237,8 @@ class Video(Track):
raise TypeError(f"Expected height to be a {int}, not {height!r}")
if not isinstance(fps, (str, int, float, type(None))):
raise TypeError(f"Expected fps to be a {str}, {int}, or {float}, not {fps!r}")
if not isinstance(scan_type, (Video.ScanType, type(None))):
raise TypeError(f"Expected scan_type to be a {Video.ScanType}, not {scan_type!r}")
self.codec = codec
self.range = range_ or Video.Range.SDR
@@ -256,6 +263,7 @@ class Video(Track):
except Exception as e:
raise ValueError("Expected fps to be a number, float, or a string as numerator/denominator form, " + str(e))
self.scan_type = scan_type
self.needs_duration_fix = False
def __str__(self) -> str: