Files
script.service.ultimate/lib/streaming_providers/providers/_template/models.py
T
2026-10-04 16:41:49 +02:00

143 lines
5.4 KiB
Python

# streaming_providers/providers/_template/models.py
"""
{TODO: Provider name} models.
What is needed:
* A custom AuthToken subclass -- MANDATORY for any provider with auth.
BaseAuthToken is an ABC with an abstract to_dict(), so it cannot be
instantiated directly. The minimal subclass below is live code, not
an example; auth.py imports it.
* A custom Channel subclass -- optional (extra per-channel fields; see
MoveTV's MoveTVChannel, Discovery's DiscoveryChannel, simpliTV's
SimpliTVChannel).
* A custom Credentials subclass -- optional (unusual login payload; see
HRTi's HRTiCredentials).
A provider WITHOUT auth can delete the AuthToken subclass. A provider that
uses plain Channel and UserPasswordCredentials needs nothing else here.
Rules
-----
* When overriding to_dict(), call super().to_dict() and add your fields.
Channel.to_dict() chains correctly. BaseAuthToken.to_dict() is abstract,
so an AuthToken subclass implements it in full.
* to_dict() keys on Channel subclasses are TitleCase, no underscores
("YourField"), matching the base serializer.
* Custom Channel subclasses are returned from ChannelManager.get_channels()
as-is; nothing in the base inspects the concrete type. Use the inherited
factories (create_live_channel / create_vod_channel / create_radio_channel);
they use cls(...) and therefore return your subclass.
* Custom AuthToken subclasses are returned from your Auth's
_perform_authentication(); the base never inspects their type beyond the
attributes it needs (access_token, expires_in, is_expired).
"""
from dataclasses import dataclass
from typing import Any, Dict
from ...base.auth.base_auth import BaseAuthToken
# from ...base.models import Channel
# from ...base.auth.credentials import UserPasswordCredentials
# ---------------------------------------------------------------------------
# AuthToken subclass (mandatory when the provider has auth)
# ---------------------------------------------------------------------------
@dataclass
class YourAuthToken(BaseAuthToken):
"""
Minimal concrete token.
Add provider-specific claims as new fields WITH DEFAULTS, after the
base fields, and include them in to_dict()/from_dict().
to_dict() must exist even if you never persist tokens (the ABC
requires it). Implement it for real so enabling persistence later
needs no follow-up edit.
VERIFY against base/auth/base_auth.py: the field list below mirrors
the README example. If BaseAuthToken has required fields not listed
here, add them to to_dict() and from_dict().
"""
def to_dict(self) -> Dict[str, Any]:
return {
"access_token": self.access_token,
"token_type": self.token_type,
"expires_in": self.expires_in,
"issued_at": self.issued_at,
"refresh_token": self.refresh_token,
"refresh_expires_in": self.refresh_expires_in,
"auth_level": self.auth_level.value,
"credential_type": self.credential_type,
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "YourAuthToken":
"""
Reconstruct from a persisted dict. Used by Auth._load_session().
Mirror to_dict(). auth_level is serialized via `.value`, so it
must be converted back to its enum here (see base_auth.py);
until you do, keep persistence off or let _load_session() return
None -- a failed load only costs one re-authentication.
"""
return cls(
access_token=data["access_token"],
token_type=data.get("token_type", "Bearer"),
expires_in=data.get("expires_in", 0),
issued_at=data.get("issued_at", 0),
refresh_token=data.get("refresh_token"),
refresh_expires_in=data.get("refresh_expires_in", 0),
# TODO: auth_level=..., credential_type=...
)
# ---------------------------------------------------------------------------
# Example: custom Channel subclass
# ---------------------------------------------------------------------------
# @dataclass
# class YourChannel(Channel):
# """
# Channel with provider-specific extra fields.
#
# Keep the base class's field names and defaults; add new fields after
# them so positional construction still works. Never remove or rename
# base fields -- downstream consumers read them.
# """
#
# codename: str = ""
# recording_id: str = ""
#
# def to_dict(self) -> Dict[str, Any]:
# result = super().to_dict()
# result["Codename"] = self.codename
# result["RecordingId"] = self.recording_id
# return result
# ---------------------------------------------------------------------------
# Example: custom Credentials subclass
# ---------------------------------------------------------------------------
# @dataclass
# class YourCredentials(UserPasswordCredentials):
# """
# Credentials with a provider-specific payload shape.
#
# Only needed when the login payload isn't the usual
# {username, password} (HRTi's grant_access takes
# {Username, Password, OperatorReferenceId}, for example).
# """
#
# operator_reference_id: str = "default"
#
# def to_auth_payload(self) -> Dict[str, Any]:
# return {
# "Username": self.username,
# "Password": self.password,
# "OperatorReferenceId": self.operator_reference_id,
# }