feat(iP): Add function to download single episodes directly
- Title regex has been updated to fit more scenarios. - Single episodes can now be downloaded directly by URL. - Use full title URL as input so that type can be captured from string
This commit is contained in:
parent
5b5ef7ec2f
commit
ef38c307c5
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
@ -34,10 +35,7 @@ class iP(Service):
|
|||||||
|
|
||||||
\b
|
\b
|
||||||
Tips:
|
Tips:
|
||||||
- Use title URL or id as input:
|
- Use full title URL as input for best results.
|
||||||
https://www.bbc.co.uk/iplayer/episodes/b007r3n8 OR b007r3n8
|
|
||||||
- Downloading by episode URL is currently not supported
|
|
||||||
|
|
||||||
\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:
|
||||||
@ -51,7 +49,7 @@ class iP(Service):
|
|||||||
|
|
||||||
ALIASES = ("bbciplayer", "bbc", "iplayer")
|
ALIASES = ("bbciplayer", "bbc", "iplayer")
|
||||||
GEOFENCE = ("gb",)
|
GEOFENCE = ("gb",)
|
||||||
TITLE_RE = r"^(?:https?://(?:www\.)?bbc\.co\.uk/iplayer/episodes?/)?(?P<id>[a-z0-9]+)"
|
TITLE_RE = r"^(?:https?://(?:www\.)?bbc\.co\.uk/(?:iplayer/(?P<kind>episode|episodes)/|programmes/))?(?P<id>[a-z0-9]+)(?:/.*)?$"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@click.command(name="iP", short_help="https://www.bbc.co.uk/iplayer", help=__doc__)
|
@click.command(name="iP", short_help="https://www.bbc.co.uk/iplayer", help=__doc__)
|
||||||
@ -94,14 +92,16 @@ class iP(Service):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_titles(self) -> Union[Movies, Series]:
|
def get_titles(self) -> Union[Movies, Series]:
|
||||||
pid = re.match(self.TITLE_RE, self.title).group("id")
|
kind, pid = (re.match(self.TITLE_RE, self.title).group(i) for i in ("kind", "id"))
|
||||||
if not pid:
|
if not pid:
|
||||||
self.log.error("Invalid input - is the URL or id correct?")
|
self.log.error("Unable to parse title ID - is the URL or id correct?")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
data = self.get_data(pid, slice_id=None)
|
data = self.get_data(pid, slice_id=None)
|
||||||
if data is None:
|
if data is None and kind == "episode":
|
||||||
self.log.error("Metadata was not found - is the URL or id correct?")
|
return self.get_single_episode(self.title)
|
||||||
|
elif data is None:
|
||||||
|
self.log.error("Metadata was not found - if %s is an episode, use full URL as input", pid)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if "Film" in data["labels"]["category"]:
|
if "Film" in data["labels"]["category"]:
|
||||||
@ -277,6 +277,44 @@ class iP(Service):
|
|||||||
name=ep_name,
|
name=ep_name,
|
||||||
language="en",
|
language="en",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_single_episode(self, url: str) -> Series:
|
||||||
|
r = self.session.get(url)
|
||||||
|
r.raise_for_status()
|
||||||
|
|
||||||
|
redux = re.search(
|
||||||
|
"window.__IPLAYER_REDUX_STATE__ = (.*?);</script>", r.text
|
||||||
|
).group(1)
|
||||||
|
data = json.loads(redux)
|
||||||
|
subtitle = data["episode"].get("subtitle")
|
||||||
|
|
||||||
|
if subtitle is not None:
|
||||||
|
season_match = re.search(r"Series (\d+):", subtitle)
|
||||||
|
season = int(season_match.group(1)) if season_match else 0
|
||||||
|
number_match = re.finditer(r"(\d+)\.|Episode (\d+)", subtitle)
|
||||||
|
number = int(next((m.group(1) or m.group(2) for m in number_match), 0))
|
||||||
|
name_match = re.search(r"\d+\. (.+)", subtitle)
|
||||||
|
name = (
|
||||||
|
name_match.group(1)
|
||||||
|
if name_match
|
||||||
|
else subtitle
|
||||||
|
if not re.search(r"Series (\d+): Episode (\d+)", subtitle)
|
||||||
|
else ""
|
||||||
|
)
|
||||||
|
|
||||||
|
return Series(
|
||||||
|
[
|
||||||
|
Episode(
|
||||||
|
id_=data["episode"]["id"],
|
||||||
|
service=self.__class__,
|
||||||
|
title=data["episode"]["title"],
|
||||||
|
season=season if subtitle else 0,
|
||||||
|
number=number if subtitle else 0,
|
||||||
|
name=name if subtitle else "",
|
||||||
|
language="en",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def find(self, pattern, string, group=None):
|
def find(self, pattern, string, group=None):
|
||||||
if group:
|
if group:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user