forked from kenzuya/unshackle
refactor(netflix): support multiple video ranges and improve profile handling
- Add support for processing multiple video ranges in parallel - Handle HYBRID range by fetching HDR10 and DV profiles separately - Introduce get_profiles_for_range method to retrieve profiles for given range - Refactor profile fetching logic with detailed logging and error handling - Validate all requested video ranges are supported by the current codec - Allow H.264 codec only with SDR range and enforce checks for multiple ranges - Improve track hydration logic to avoid duplicates across ranges and profiles - Add logging for multi-range processing and profile fetching details
This commit is contained in:
@@ -207,15 +207,20 @@ class Netflix(Service):
|
||||
except Exception as e:
|
||||
self.log.error(e)
|
||||
else:
|
||||
if self.range[0] == Video.Range.HYBRID:
|
||||
# Handle HYBRID mode by getting HDR10 and DV profiles separately
|
||||
# Handle multiple video ranges
|
||||
for range_index, video_range in enumerate(self.range):
|
||||
try:
|
||||
# Only hydrate tracks on the first range to avoid duplicates
|
||||
should_hydrate = self.hydrate_track and range_index == 0
|
||||
|
||||
if video_range == Video.Range.HYBRID:
|
||||
# Handle HYBRID mode by getting HDR10 and DV profiles separately
|
||||
# Get HDR10 profiles for the current codec
|
||||
hdr10_profiles = self.config["profiles"]["video"][self.vcodec.extension.upper()].get("HDR10", [])
|
||||
if hdr10_profiles:
|
||||
self.log.info("Fetching HDR10 tracks for hybrid processing")
|
||||
self.log.info(f"Fetching HDR10 tracks for HYBRID processing (range {range_index + 1}/{len(self.range)})")
|
||||
hdr10_manifest = self.get_manifest(title, hdr10_profiles)
|
||||
hdr10_tracks = self.manifest_as_tracks(hdr10_manifest, title, self.hydrate_track)
|
||||
hdr10_tracks = self.manifest_as_tracks(hdr10_manifest, title, should_hydrate)
|
||||
tracks.add(hdr10_tracks)
|
||||
else:
|
||||
self.log.warning(f"No HDR10 profiles found for codec {self.vcodec.extension.upper()}")
|
||||
@@ -223,33 +228,45 @@ class Netflix(Service):
|
||||
# Get DV profiles for the current codec
|
||||
dv_profiles = self.config["profiles"]["video"][self.vcodec.extension.upper()].get("DV", [])
|
||||
if dv_profiles:
|
||||
self.log.info("Fetching DV tracks for hybrid processing")
|
||||
self.log.info(f"Fetching DV tracks for HYBRID processing (range {range_index + 1}/{len(self.range)})")
|
||||
dv_manifest = self.get_manifest(title, dv_profiles)
|
||||
dv_tracks = self.manifest_as_tracks(dv_manifest, title, False) # Don't hydrate again
|
||||
dv_tracks = self.manifest_as_tracks(dv_manifest, title, False) # Don't hydrate DV tracks
|
||||
tracks.add(dv_tracks.videos)
|
||||
else:
|
||||
self.log.warning(f"No DV profiles found for codec {self.vcodec.extension.upper()}")
|
||||
|
||||
except Exception as e:
|
||||
self.log.error(f"Error in HYBRID mode processing: {e}")
|
||||
elif self.high_bitrate:
|
||||
splitted_profiles = self.split_profiles(self.profiles)
|
||||
for index, profile_list in enumerate(splitted_profiles):
|
||||
# Get profiles for the current range
|
||||
range_profiles = self.get_profiles_for_range(video_range)
|
||||
if not range_profiles:
|
||||
self.log.warning(f"No profiles found for range {video_range.name}")
|
||||
continue
|
||||
|
||||
splitted_profiles = self.split_profiles(range_profiles)
|
||||
for profile_index, profile_list in enumerate(splitted_profiles):
|
||||
try:
|
||||
self.log.debug(f"Index: {index}. Getting profiles: {profile_list}")
|
||||
self.log.debug(f"Range {range_index + 1}/{len(self.range)} ({video_range.name}), Profile Index: {profile_index}. Getting profiles: {profile_list}")
|
||||
manifest = self.get_manifest(title, profile_list)
|
||||
manifest_tracks = self.manifest_as_tracks(manifest, title, self.hydrate_track if index == 0 else False)
|
||||
tracks.add(manifest_tracks if index == 0 else manifest_tracks.videos)
|
||||
manifest_tracks = self.manifest_as_tracks(manifest, title, should_hydrate and profile_index == 0)
|
||||
tracks.add(manifest_tracks if should_hydrate and profile_index == 0 else manifest_tracks.videos)
|
||||
except Exception:
|
||||
self.log.error(f"Error getting profile: {profile_list}. Skipping")
|
||||
self.log.error(f"Error getting profile: {profile_list} for range {video_range.name}. Skipping")
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
manifest = self.get_manifest(title, self.profiles)
|
||||
manifest_tracks = self.manifest_as_tracks(manifest, title, self.hydrate_track)
|
||||
tracks.add(manifest_tracks)
|
||||
# Get profiles for the current range
|
||||
range_profiles = self.get_profiles_for_range(video_range)
|
||||
if not range_profiles:
|
||||
self.log.warning(f"No profiles found for range {video_range.name}")
|
||||
continue
|
||||
|
||||
self.log.info(f"Processing range {range_index + 1}/{len(self.range)}: {video_range.name}")
|
||||
manifest = self.get_manifest(title, range_profiles)
|
||||
manifest_tracks = self.manifest_as_tracks(manifest, title, should_hydrate)
|
||||
tracks.add(manifest_tracks if should_hydrate else manifest_tracks.videos)
|
||||
|
||||
except Exception as e:
|
||||
self.log.error(e)
|
||||
self.log.error(f"Error processing range {video_range.name}: {e}")
|
||||
continue
|
||||
|
||||
|
||||
|
||||
@@ -400,15 +417,25 @@ class Netflix(Service):
|
||||
self.log.error(f"Video range {self.range[0].name} is not supported by Video Codec: {self.vcodec}")
|
||||
sys.exit(1)
|
||||
|
||||
if len(self.range) > 1:
|
||||
self.log.error(f"Multiple video range is not supported right now.")
|
||||
# Validate all ranges are supported
|
||||
for video_range in self.range:
|
||||
if video_range.name not in list(self.config["profiles"]["video"][self.vcodec.extension.upper()].keys()) and video_range != Video.Range.HYBRID and self.vcodec != Video.Codec.AVC and self.vcodec != Video.Codec.VP9:
|
||||
self.log.error(f"Video range {video_range.name} is not supported by Video Codec: {self.vcodec}")
|
||||
sys.exit(1)
|
||||
|
||||
if self.vcodec == Video.Codec.AVC and self.range[0] != Video.Range.SDR:
|
||||
self.log.error(f"H.264 Video Codec only supports SDR")
|
||||
if self.vcodec == Video.Codec.AVC:
|
||||
for video_range in self.range:
|
||||
if video_range != Video.Range.SDR:
|
||||
self.log.error(f"H.264 Video Codec only supports SDR, but {video_range.name} was requested")
|
||||
sys.exit(1)
|
||||
|
||||
self.profiles = self.get_profiles()
|
||||
|
||||
# Log information about video ranges being processed
|
||||
if len(self.range) > 1:
|
||||
range_names = [r.name for r in self.range]
|
||||
self.log.info(f"Processing multiple video ranges: {', '.join(range_names)}")
|
||||
|
||||
self.log.info("Intializing a MSL client")
|
||||
self.get_esn()
|
||||
# if self.cdm.security_level == 1:
|
||||
@@ -472,6 +499,40 @@ class Netflix(Service):
|
||||
self.log.debug(f"Result_profiles: {result_profiles}")
|
||||
return result_profiles
|
||||
|
||||
def get_profiles_for_range(self, video_range: Video.Range) -> List[str]:
|
||||
"""
|
||||
Get profiles for a specific video range.
|
||||
|
||||
Args:
|
||||
video_range: The video range to get profiles for
|
||||
|
||||
Returns:
|
||||
List of profile strings for the specified range
|
||||
"""
|
||||
result_profiles = []
|
||||
|
||||
# Handle case for codec VP9
|
||||
if self.vcodec == Video.Codec.VP9 and video_range != Video.Range.HDR10:
|
||||
result_profiles.extend(self.config["profiles"]["video"][self.vcodec.extension.upper()].values())
|
||||
return result_profiles
|
||||
|
||||
# Get profiles for the specific range
|
||||
codec_profiles = self.config["profiles"]["video"][self.vcodec.extension.upper()]
|
||||
|
||||
if video_range.name in codec_profiles:
|
||||
result_profiles.extend(codec_profiles[video_range.name])
|
||||
elif video_range == Video.Range.HYBRID:
|
||||
# For hybrid, use HDR10 profiles
|
||||
if "HDR10" in codec_profiles:
|
||||
result_profiles.extend(codec_profiles["HDR10"])
|
||||
else:
|
||||
self.log.warning(f"No HDR10 profiles found for HYBRID range in codec {self.vcodec.extension.upper()}")
|
||||
else:
|
||||
self.log.warning(f"Range {video_range.name} not found in codec {self.vcodec.extension.upper()} profiles")
|
||||
|
||||
self.log.debug(f"Profiles for range {video_range.name}: {result_profiles}")
|
||||
return result_profiles
|
||||
|
||||
def get_esn(self):
|
||||
if self.cdm.device_type == DeviceTypes.ANDROID:
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user