refactor(iP): Check all available versions instead of just the default

Some titles don't store the HD/FHD/UHD manifests in the default location, so it will now loop through all versions and pick the best one.
This commit is contained in:
stabbedbybrick 2024-04-10 09:07:13 +02:00
parent 03f5a881ee
commit 255a832039

View File

@ -36,7 +36,6 @@ class iP(Service):
\b \b
Tips: Tips:
- Use full title URL as input for best results. - Use full title URL as input for best results.
- See which titles are available in UHD: https://www.bbc.co.uk/programmes/p0dh39s7
\b \b
- An SSL certificate (PEM) is required for accessing the UHD endpoint. - An SSL certificate (PEM) is required for accessing the UHD endpoint.
Specify its path using the service configuration data in the root config: Specify its path using the service configuration data in the root config:
@ -46,6 +45,8 @@ class iP(Service):
cert: path/to/cert cert: path/to/cert
\b \b
- Use -v H.265 to request UHD tracks - Use -v H.265 to request UHD tracks
- See which titles are available in UHD:
https://www.bbc.co.uk/iplayer/help/questions/programme-availability/uhd-content
""" """
ALIASES = ("bbciplayer", "bbc", "iplayer") ALIASES = ("bbciplayer", "bbc", "iplayer")
@ -124,37 +125,26 @@ class iP(Service):
return Series(episodes) return Series(episodes)
def get_tracks(self, title: Union[Movie, Episode]) -> Tracks: def get_tracks(self, title: Union[Movie, Episode]) -> Tracks:
playlist = self.session.get(url=self.config["endpoints"]["playlist"].format(pid=title.id)).json() r = self.session.get(url=self.config["endpoints"]["playlist"].format(pid=title.id))
if not playlist["defaultAvailableVersion"]: r.raise_for_status()
self.log.error(" - Title is unavailable")
sys.exit(1)
if self.config.get("cert"): quality = [
url = self.config["endpoints"]["manifest_"].format( connection.get("height")
vpid=playlist["defaultAvailableVersion"]["smpConfig"]["items"][0]["vpid"], for i in (
mediaset="iptv-uhd" if self.vcodec == "H.265" else "iptv-all", self.check_all_versions(version)
for version in (x.get("pid") for x in r.json()["allAvailableVersions"])
) )
for connection in i
if connection.get("height")
]
max_quality = max((h for h in quality if h < "1080"), default=None)
session = self.session media = next((i for i in (self.check_all_versions(version)
session.mount("https://", SSLCiphers()) for version in (x.get("pid") for x in r.json()["allAvailableVersions"]))
session.mount("http://", SSLCiphers()) if any(connection.get("height") == max_quality for connection in i)), None)
manifest = session.get(
url, headers={"user-agent": self.config["user_agent"]}, cert=self.config["cert"]
).json()
if "result" in manifest:
self.log.error(f" - Failed to get manifest [{manifest['result']}]")
sys.exit(1)
else:
url = self.config["endpoints"]["manifest"].format(
vpid=playlist["defaultAvailableVersion"]["smpConfig"]["items"][0]["vpid"],
mediaset="iptv-all",
)
manifest = self.session.get(url).json()
connection = {} connection = {}
for video in [x for x in manifest["media"] if x["kind"] == "video"]: for video in [x for x in media if x["kind"] == "video"]:
connections = sorted(video["connection"], key=lambda x: x["priority"]) connections = sorted(video["connection"], key=lambda x: x["priority"])
if self.vcodec == "H.265": if self.vcodec == "H.265":
connection = connections[0] connection = connections[0]
@ -211,7 +201,7 @@ class iP(Service):
video.codec = Video.Codec.from_codecs(video.data["hls"]["playlist"].stream_info.codecs) video.codec = Video.Codec.from_codecs(video.data["hls"]["playlist"].stream_info.codecs)
video.bitrate = int(self.find(r"-video=(\d+)", as_list(video.url)[0]) or 0) video.bitrate = int(self.find(r"-video=(\d+)", as_list(video.url)[0]) or 0)
for caption in [x for x in manifest["media"] if x["kind"] == "captions"]: for caption in [x for x in media if x["kind"] == "captions"]:
connection = sorted(caption["connection"], key=lambda x: x["priority"])[0] connection = sorted(caption["connection"], key=lambda x: x["priority"])[0]
tracks.add( tracks.add(
Subtitle( Subtitle(
@ -255,6 +245,35 @@ class iP(Service):
return r.json()["data"]["programme"] return r.json()["data"]["programme"]
def check_all_versions(self, vpid: str) -> list:
if self.config.get("cert"):
url = self.config["endpoints"]["manifest_"].format(
vpid=vpid,
mediaset="iptv-uhd" if self.vcodec == "H.265" else "iptv-all",
)
session = self.session
session.mount("https://", SSLCiphers())
session.mount("http://", SSLCiphers())
manifest = session.get(
url, headers={"user-agent": self.config["user_agent"]}, cert=self.config["cert"]
).json()
if "result" in manifest:
return {}
else:
url = self.config["endpoints"]["manifest"].format(
vpid=vpid,
mediaset="iptv-all",
)
manifest = self.session.get(url).json()
if "result" in manifest:
return {}
return manifest["media"]
def create_episode(self, episode): def create_episode(self, episode):
title = episode["episode"]["title"]["default"].strip() title = episode["episode"]["title"]["default"].strip()
subtitle = episode["episode"]["subtitle"] subtitle = episode["episode"]["subtitle"]
@ -283,9 +302,7 @@ class iP(Service):
r = self.session.get(url) r = self.session.get(url)
r.raise_for_status() r.raise_for_status()
redux = re.search( redux = re.search("window.__IPLAYER_REDUX_STATE__ = (.*?);</script>", r.text).group(1)
"window.__IPLAYER_REDUX_STATE__ = (.*?);</script>", r.text
).group(1)
data = json.loads(redux) data = json.loads(redux)
subtitle = data["episode"].get("subtitle") subtitle = data["episode"].get("subtitle")