from __future__ import annotations from http.cookiejar import MozillaCookieJar from typing import Any, Optional, Union from functools import partial from pathlib import Path import sys import re import click import isodate from click import Context from devine.core.credential import Credential from devine.core.service import Service from devine.core.titles import Movie, Movies, Episode, Series from devine.core.tracks import Track, Chapter, Tracks, Video, Audio, Subtitle from devine.core.manifests.hls import HLS from devine.core.manifests.dash import DASH class NRK(Service): """ Service code for NRK TV (https://tv.nrk.no) \b Version: 1.0.0 Author: lambda Authorization: None Robustness: Unencrypted: 1080p, DD5.1 """ GEOFENCE = ("no",) TITLE_RE = r"^https://tv.nrk.no/serie/fengselseksperimentet/sesong/1/episode/(?P.+)$" @staticmethod @click.command(name="NRK", short_help="https://tv.nrk.no", help=__doc__) @click.argument("title", type=str) @click.pass_context def cli(ctx: Context, **kwargs: Any) -> NRK: return NRK(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: pass def get_titles(self) -> Union[Movies, Series]: match = re.match(self.TITLE_RE, self.title) if not match: return content_id = match.group("content_id") r = self.session.get(self.config["endpoints"]["content"].format(content_id=content_id)) item = r.json() episode, name = item["programInformation"]["titles"]["title"].split(". ", maxsplit=1) return Series([Episode( id_=content_id, service=self.__class__, language="nb", year=item["moreInformation"]["productionYear"], title=item["_links"]["seriesPage"]["title"], name=name, season=item["_links"]["season"]["name"], number=episode, )]) def get_tracks(self, title: Union[Episode, Movie]) -> Tracks: r = self.session.get(self.config["endpoints"]["manifest"].format(content_id=title.id)) manifest = r.json() tracks = Tracks() for asset in manifest["playable"]["assets"]: if asset["format"] == "HLS": tracks += Tracks(HLS.from_url(asset["url"], session=self.session).to_tracks("nb")) for sub in manifest["playable"]["subtitles"]: tracks.add(Subtitle( codec=Subtitle.Codec.WebVTT, language=sub["language"], url=sub["webVtt"], sdh=sub["type"] == "ttv", )) for track in tracks: track.needs_proxy = True # if isinstance(track, Audio) and track.channels == 6.0: # track.channels = 5.1 return tracks def get_chapters(self, title: Union[Episode, Movie]) -> list[Chapter]: r = self.session.get(self.config["endpoints"]["metadata"].format(content_id=title.id)) sdi = r.json()["skipDialogInfo"] chapters = [] if sdi["endIntroInSeconds"]: if sdi["startIntroInSeconds"]: chapters.append(Chapter(timestamp=0)) chapters |= [ Chapter(timestamp=sdi["startIntroInSeconds"], name="Intro"), Chapter(timestamp=sdi["endIntroInSeconds"]) ] if sdi["startCreditsInSeconds"]: if not chapters: chapters.append(Chapter(timestamp=0)) credits = isodate.parse_duration(sdi["startCredits"]) chapters.append(Chapter(credits.total_seconds(), name="Credits")) return chapters