Skip to content

Commit 6642747

Browse files
committed
fix: fix quality for protected
1 parent 1cb2da6 commit 6642747

File tree

1 file changed

+103
-74
lines changed

1 file changed

+103
-74
lines changed

ofscraper/classes/of/media.py

Lines changed: 103 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -578,74 +578,117 @@ def get_text(self):
578578
text = f"{date_str} {self.text or self.filename}"
579579
return text
580580

581-
async def mpd_video_helper(self, mpd_obj=None):
582-
mpd = mpd_obj or await self.parse_mpd
581+
582+
@async_cached_property
583+
async def _video_adaptation_set(self):
584+
"""Finds and caches the first video/mp4 AdaptationSet."""
585+
mpd = await self.parse_mpd
583586
if not mpd:
584587
return None
585-
allowed = quality.get_allowed_qualities()
586588
for period in mpd.periods:
587589
for adapt_set in filter(
588590
lambda x: x.mime_type == "video/mp4", period.adaptation_sets
589591
):
590-
kId = None
591-
for prot in adapt_set.content_protections:
592-
if prot.value is None:
593-
kId = prot.pssh[0].pssh
594-
break
595-
596-
representations = sorted(
597-
adapt_set.representations, key=lambda x: x.height, reverse=True
598-
)
599-
selected_repr = None
600-
for quality_val in allowed:
601-
if quality_val.lower() == "source":
602-
selected_repr = representations[0]
603-
break
604-
for r in representations:
605-
if str(r.height) == quality_val:
606-
selected_repr = r
607-
break
608-
if selected_repr:
609-
break
610-
611-
selected_repr = (
612-
selected_repr or representations[0]
613-
) # Fallback to highest
614-
615-
origname = f"{selected_repr.base_urls[0].base_url_value}"
616-
return {
617-
"origname": origname,
618-
"pssh": kId,
619-
"type": "video",
620-
"name": f"tempvid_{self.id}_{self.post_id}",
621-
"ext": "mp4",
622-
}
592+
return adapt_set
593+
return None
623594

624-
async def mpd_audio_helper(self, mpd_obj=None):
625-
mpd = mpd_obj or await self.parse_mpd
595+
@async_cached_property
596+
async def _audio_adaptation_set(self):
597+
"""Finds and caches the first audio/mp4 AdaptationSet."""
598+
mpd = await self.parse_mpd
626599
if not mpd:
627600
return None
628601
for period in mpd.periods:
629602
for adapt_set in filter(
630603
lambda x: x.mime_type == "audio/mp4", period.adaptation_sets
631604
):
632-
kId = None
633-
for prot in adapt_set.content_protections:
634-
if prot.value is None:
635-
kId = prot.pssh[0].pssh
636-
sensitive.add_sensitive_pattern(kId, "pssh_code")
637-
break
638-
639-
# Typically there's only one audio representation
640-
repr = adapt_set.representations[0]
641-
origname = f"{repr.base_urls[0].base_url_value}"
642-
return {
643-
"origname": origname,
644-
"pssh": kId,
645-
"type": "audio",
646-
"name": f"tempaudio_{self.id}_{self.post_id}",
647-
"ext": "mp4",
648-
}
605+
return adapt_set
606+
return None
607+
608+
@async_cached_property
609+
async def selected_video_representation(self):
610+
"""
611+
Finds and caches the specific video Representation object
612+
based on user's quality settings.
613+
"""
614+
adapt_set = await self._video_adaptation_set
615+
if not adapt_set:
616+
return None
617+
618+
representations = sorted(
619+
adapt_set.representations, key=lambda x: x.height, reverse=True
620+
)
621+
if not representations:
622+
return None
623+
624+
allowed = quality.get_allowed_qualities()
625+
# Build a map of height -> representation
626+
# e.g., {'1080': <Repr obj>, '720': <Repr obj>}
627+
available_map = {str(r.height): r for r in representations}
628+
629+
# Loop from LOWEST to HIGHEST allowed
630+
for qual in allowed:
631+
if qual in available_map:
632+
return available_map[qual] # Return first (lowest) match
633+
634+
# Fallback: return highest available
635+
return representations[0]
636+
637+
@async_cached_property
638+
async def selected_audio_representation(self):
639+
"""Finds and caches the specific audio Representation object."""
640+
adapt_set = await self._audio_adaptation_set
641+
if not adapt_set or not adapt_set.representations:
642+
return None
643+
# Audio typically only has one representation
644+
return adapt_set.representations[0]
645+
646+
async def mpd_video_helper(self, mpd_obj=None):
647+
# We ignore mpd_obj because our helper properties are cached
648+
adapt_set = await self._video_adaptation_set
649+
selected_repr = await self.selected_video_representation
650+
651+
if not adapt_set or not selected_repr:
652+
return None
653+
654+
kId = None
655+
for prot in adapt_set.content_protections:
656+
if prot.value is None:
657+
kId = prot.pssh[0].pssh
658+
break
659+
660+
origname = f"{selected_repr.base_urls[0].base_url_value}"
661+
return {
662+
"origname": origname,
663+
"pssh": kId,
664+
"type": "video",
665+
"name": f"tempvid_{self.id}_{self.post_id}",
666+
"ext": "mp4",
667+
}
668+
669+
async def mpd_audio_helper(self, mpd_obj=None):
670+
# We ignore mpd_obj because our helper properties are cached
671+
adapt_set = await self._audio_adaptation_set
672+
selected_repr = await self.selected_audio_representation
673+
674+
if not adapt_set or not selected_repr:
675+
return None
676+
677+
kId = None
678+
for prot in adapt_set.content_protections:
679+
if prot.value is None:
680+
kId = prot.pssh[0].pssh
681+
sensitive.add_sensitive_pattern(kId, "pssh_code")
682+
break
683+
684+
origname = f"{selected_repr.base_urls[0].base_url_value}"
685+
return {
686+
"origname": origname,
687+
"pssh": kId,
688+
"type": "audio",
689+
"name": f"tempaudio_{self.id}_{self.post_id}",
690+
"ext": "mp4",
691+
}
649692

650693
def normal_quality_helper(self):
651694
if self.mediatype != "videos":
@@ -664,25 +707,11 @@ def normal_quality_helper(self):
664707
return "source"
665708

666709
async def alt_quality_helper(self, mpd_obj=None):
667-
mpd = mpd_obj or await self.parse_mpd
668-
if not mpd:
710+
# We ignore mpd_obj because our helper properties are cached
711+
selected_repr = await self.selected_video_representation
712+
if not selected_repr:
669713
return None
670-
671-
allowed = quality.get_allowed_qualities()
672-
for period in mpd.periods:
673-
for adapt_set in filter(
674-
lambda x: x.mime_type == "video/mp4", period.adaptation_sets
675-
):
676-
representations = sorted(
677-
adapt_set.representations, key=lambda x: x.height, reverse=True
678-
)
679-
for qual in allowed:
680-
if qual.lower() == "source":
681-
return str(representations[0].height)
682-
for r in representations:
683-
if str(r.height) == qual:
684-
return str(r.height)
685-
return str(representations[0].height) # Fallback to best
714+
return str(selected_repr.height)
686715

687716
async def _get_final_filename_async(self):
688717
filename = self.filename or str(self.id)
@@ -697,4 +726,4 @@ async def _get_final_filename_async(self):
697726
except Exception as e:
698727
log.error(f"Error creating final filename: {e}")
699728

700-
return filename
729+
return filename

0 commit comments

Comments
 (0)