unshackle-services/MTSP/__init__.py

108 lines
3.1 KiB
Python
Raw Normal View History

2025-08-03 01:47:10 +02:00
from __future__ import annotations
from http.cookiejar import MozillaCookieJar
from typing import Any, Optional
import re
import click
from click import Context
from bs4 import BeautifulSoup
2025-10-10 05:17:43 +02:00
from unshackle.core.credential import Credential
from unshackle.core.service import Service
from unshackle.core.titles import Movie, Movies
from unshackle.core.tracks import Chapter, Tracks
from unshackle.core.manifests.dash import DASH
2025-08-03 01:47:10 +02:00
class MTSP(Service):
TITLE_RE = r"^(?:https?://(?:www\.)?magentasport\.de/event/[^/]+)?/[0-9]+/(?P<video_id>[0-9]+)"
@staticmethod
@click.command(name="MTSP", short_help="https://magentasport.de", help=__doc__)
@click.argument("title", type=str)
@click.pass_context
def cli(ctx: Context, **kwargs: Any) -> MTSP:
return MTSP(ctx, **kwargs)
def __init__(self, ctx: Context, title: str):
self.title = title
super().__init__(ctx)
def authenticate(self, cookies: Optional[MozillaCookieJar] = None, credential: Optional[Credential] = None) -> None:
cache = self.cache.get(f"session_{credential.sha1}")
if cache and not cache.expired:
self.session.cookies.update({
"session": cache.data,
"entitled": "1",
})
return
self.log.info("No cached session cookie, logging in...")
r = self.session.get(self.config["endpoints"]["login_form"])
r.raise_for_status()
tid, xsrf_name, xsrf_value = self.get_login_tid_xsrf(r.text)
data = {
"tid": tid,
xsrf_name: xsrf_value,
"pkc": "",
"webauthn_supported": "false",
"pw_usr": credential.username
}
r = self.session.post(self.config["endpoints"]["login_post"], data=data)
r.raise_for_status()
tid, xsrf_name, xsrf_value = self.get_login_tid_xsrf(r.text)
data = {
"tid": tid,
xsrf_name: xsrf_value,
"hidden_usr": credential.username,
"pw_pwd": credential.password,
"persist_session_displayed": "1",
"persist_session": "on"
}
r = self.session.post(self.config["endpoints"]["login_post"], data=data)
r.raise_for_status()
session = self.session.cookies.get_dict().get('session')
cache.set(session)
def get_titles(self) -> Movies:
video_id = re.match(self.TITLE_RE, self.title).group("video_id")
r = self.session.get(self.config["endpoints"]["video_config"].format(video_id=video_id))
config = r.json()
return Movies([Movie(
id_=video_id,
service=self.__class__,
name=config["title"],
language="de",
data=config,
)])
def get_tracks(self, title: Movie) -> Tracks:
r = self.session.post(title.data['streamAccess'])
access = r.json()
tracks = DASH.from_url(access["data"]["stream"]["dash"]).to_tracks(title.language)
2025-10-10 04:34:28 +02:00
# Audio tracks sometimes have 'random' language codes in the DASH manifest.
# Since there is only ever one language in practice anyway, force it to the correct one.
for track in tracks:
track.language = title.language
2025-08-03 01:47:10 +02:00
return tracks
def get_chapters(self, title: Movie) -> list[Chapter]:
return [
]
def get_login_tid_xsrf(self, html):
soup = BeautifulSoup(html, "html.parser")
form = soup.find("form", id="login")
xsrf = form.find("input", {"name": re.compile("^xsrf_")})
tid = form.find("input", {"name": "tid"})
return tid.get("value"), xsrf.get('name'), xsrf.get("value")