mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-10-22 07:42:35 +02:00
work towards provider v2
This commit is contained in:
@@ -8,6 +8,8 @@ methods (headers, DRM defaults, search no-ops) come for free.
|
||||
|
||||
Design rules
|
||||
------------
|
||||
* All managers share one constructor contract, implemented once in the
|
||||
private ManagerBase (managers/_base.py).
|
||||
* Constructors take four required collaborators -- http_manager, auth,
|
||||
country, config -- plus keyword-only extras (caches, collaborators).
|
||||
* Managers never hold a reference to the provider. Shared state is passed
|
||||
@@ -30,12 +32,17 @@ conventions documented in the template.
|
||||
|
||||
Manager list
|
||||
------------
|
||||
Three required capabilities:
|
||||
ChannelManager
|
||||
VodManager (providers without a browseable catalogue return None)
|
||||
EpgManager (providers without EPG return None)
|
||||
All seven managers are OPTIONAL (see providers/_template/README.md): a
|
||||
provider wires the ones its service offers and returns None from the other
|
||||
_build_*() factories. A VOD-only provider has no ChannelManager; a
|
||||
metadata-only provider may have just an EpgManager.
|
||||
|
||||
Four optional capabilities -- providers implement the ones they support:
|
||||
Core capabilities:
|
||||
ChannelManager (live channels, channel manifest / DRM)
|
||||
VodManager (browseable catalogue)
|
||||
EpgManager (guide data)
|
||||
|
||||
Four further capabilities -- providers implement the ones they support:
|
||||
RecordingsManager (cloud / network PVR)
|
||||
FavoritesManager (user bookmarks on programs / channels)
|
||||
BookmarksManager (resume position)
|
||||
|
||||
@@ -16,14 +16,19 @@ Return-value conventions
|
||||
------------------------
|
||||
get_bookmarks returns [] when the user has no bookmarks. Not an error.
|
||||
|
||||
update_bookmark raises RuntimeError if the provider rejects the write
|
||||
update_bookmark raises OperationFailedError if the provider rejects the write
|
||||
(e.g. content inaccessible, backend error). It is called on every
|
||||
playback stop / pause, so providers should tolerate a write that
|
||||
overwrites the same position with a no-op rather than failing.
|
||||
|
||||
delete_bookmark raises KeyError if no bookmark exists for content_id,
|
||||
so callers can distinguish "already gone" from "successfully deleted".
|
||||
The base ProviderBookmarksMixin documents the same rule.
|
||||
delete_bookmark raises ItemNotFoundError if no bookmark exists for
|
||||
content_id, so callers can distinguish "already gone" from "successfully
|
||||
deleted". The base ProviderBookmarksMixin documents the same rule (as
|
||||
KeyError; ItemNotFoundError is a KeyError subclass).
|
||||
|
||||
ItemNotFoundError is both a NotFoundError (ProviderError) and a KeyError, and
|
||||
OperationFailedError is both a ProviderError and a RuntimeError, so handlers
|
||||
written against the old KeyError / RuntimeError convention keep working.
|
||||
|
||||
Caller guidance
|
||||
---------------
|
||||
@@ -35,36 +40,16 @@ they are given.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from ..models.bookmark import Bookmark, ContentType
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class BookmarksManager(ABC):
|
||||
class BookmarksManager(ManagerBase):
|
||||
"""Abstract base for provider bookmarks managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Abstract
|
||||
# ------------------------------------------------------------------
|
||||
@@ -94,7 +79,8 @@ class BookmarksManager(ABC):
|
||||
Called on playback stop / pause, so should be tolerant of
|
||||
repeated writes to the same position (a no-op write is fine).
|
||||
|
||||
Raises RuntimeError if the provider rejects the write.
|
||||
Raises OperationFailedError (also a RuntimeError) if the provider
|
||||
rejects the write.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -104,7 +90,8 @@ class BookmarksManager(ABC):
|
||||
Delete a bookmark.
|
||||
|
||||
Raises:
|
||||
KeyError: if no bookmark exists for content_id.
|
||||
RuntimeError: on backend failure.
|
||||
ItemNotFoundError: if no bookmark exists for content_id
|
||||
(also a KeyError).
|
||||
OperationFailedError: on backend failure (also a RuntimeError).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
@@ -9,6 +9,7 @@ Public interface
|
||||
get_catchup_manifest(content_id, start_time, end_time=None, ...)
|
||||
-> Optional[str] [abstract]
|
||||
get_catchup_manifest_headers(...) -> Dict[str,str] [concrete]
|
||||
get_catchup_segment_headers(...) -> Dict[str,str] [concrete]
|
||||
get_catchup_drm(...) -> List[DRMConfig] [concrete]
|
||||
|
||||
Constructor contract
|
||||
@@ -30,8 +31,8 @@ get_catchup_drm returns [] when catchup shares DRM with live (the
|
||||
common case), or a provider-specific list when catchup uses different
|
||||
DRM.
|
||||
|
||||
Return the manifest of the live stream
|
||||
--------------------------------------
|
||||
What a catchup manifest is (and is not)
|
||||
----------------------------------------
|
||||
The catchup manifest is a *modified* live manifest URL (with time
|
||||
parameters) for providers like Magenta and MoveTV, or a distinct URL
|
||||
for providers whose catchup is served from a different origin. This
|
||||
@@ -60,36 +61,16 @@ when the ABC accepts None. Pass None.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ..models import DRMConfig
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class CatchupManager(ABC):
|
||||
class CatchupManager(ManagerBase):
|
||||
"""Abstract base for provider catchup managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Capability
|
||||
# ------------------------------------------------------------------
|
||||
@@ -165,6 +146,25 @@ class CatchupManager(ABC):
|
||||
"""
|
||||
return self.auth.build_headers()
|
||||
|
||||
def get_catchup_segment_headers(
|
||||
self,
|
||||
content_id: str,
|
||||
start_time: int,
|
||||
end_time: Optional[int] = None,
|
||||
epg_id: Optional[str] = None,
|
||||
**kw: Any,
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Headers for catchup segment requests.
|
||||
|
||||
Default: the catchup manifest headers. Mirrors
|
||||
ChannelManager.get_segment_headers; override for providers whose
|
||||
catchup segments need different or token-bound headers.
|
||||
"""
|
||||
return self.get_catchup_manifest_headers(
|
||||
content_id, start_time, end_time, epg_id, **kw
|
||||
)
|
||||
|
||||
def get_catchup_drm(
|
||||
self,
|
||||
content_id: str,
|
||||
|
||||
@@ -13,10 +13,10 @@ Public interface
|
||||
|
||||
Constructor contract
|
||||
--------------------
|
||||
Four required keyword-only collaborators. Subclasses that need extra state
|
||||
declare additional keyword-only args and store them on self AFTER calling
|
||||
super().__init__. The base does NOT accept **kwargs -- a typo at a call
|
||||
site becomes an immediate TypeError, which is what you want.
|
||||
Four required keyword-only collaborators (see ManagerBase). Subclasses that
|
||||
need extra state declare additional keyword-only args and store them on
|
||||
self AFTER calling super().__init__. The base does NOT accept **kwargs -- a
|
||||
typo at a call site becomes an immediate TypeError, which is what you want.
|
||||
|
||||
Return-value conventions
|
||||
------------------------
|
||||
@@ -27,39 +27,16 @@ failures propagate as exceptions from base.errors.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ..models import Channel, DRMConfig
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class ChannelManager(ABC):
|
||||
class ChannelManager(ManagerBase):
|
||||
"""Abstract base for provider channel managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
# Sanity check the Auth collaborator against the runtime_checkable
|
||||
# protocol. isinstance on a runtime_checkable Protocol only verifies
|
||||
# method presence, not signatures -- that's the intended check here.
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Routing
|
||||
# ------------------------------------------------------------------
|
||||
@@ -71,7 +48,8 @@ class ChannelManager(ABC):
|
||||
Default: True. Override in providers whose channel content_ids have
|
||||
a distinguishable grammar (e.g. numeric-only for live channels).
|
||||
The orchestrator uses this to route get_manifest / get_drm without
|
||||
a wasted request to the wrong manager.
|
||||
a wasted request to the wrong manager. It is a cheap, I/O-free
|
||||
PRE-FILTER: False means the manager is never asked.
|
||||
"""
|
||||
return True
|
||||
|
||||
|
||||
@@ -22,37 +22,17 @@ epg_window returns (past_days, future_days). (0, 0) means no EPG support.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
from ..models.epg_models import EPGEntry, EPGProgramDetails
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class EpgManager(ABC):
|
||||
class EpgManager(ManagerBase):
|
||||
"""Abstract base for provider EPG managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Capability
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@@ -18,13 +18,17 @@ Return-value conventions
|
||||
get_favorites returns [] when the user has no favorites or the provider
|
||||
does not support favorites. Not an error.
|
||||
|
||||
add_favorite returns the created Favorite. Raises RuntimeError on
|
||||
add_favorite returns the created Favorite. Raises OperationFailedError on
|
||||
rejection (e.g. provider's backend refuses the operation).
|
||||
|
||||
remove_favorite raises KeyError if the content_id is not currently
|
||||
favorited, and RuntimeError on backend failure. Deleting a
|
||||
non-existent favorite is a KeyError -- consistent with
|
||||
FavoritesManager's counterpart in ProviderFavoritesMixin.
|
||||
remove_favorite raises ItemNotFoundError if the content_id is not currently
|
||||
favorited, and OperationFailedError on backend failure. Deleting a
|
||||
non-existent favorite is an ItemNotFoundError -- which is also a KeyError,
|
||||
consistent with ProviderFavoritesMixin.
|
||||
|
||||
ItemNotFoundError is both a NotFoundError (ProviderError) and a KeyError, and
|
||||
OperationFailedError is both a ProviderError and a RuntimeError, so handlers
|
||||
written against the old KeyError / RuntimeError convention keep working.
|
||||
|
||||
Provider guidance
|
||||
-----------------
|
||||
@@ -36,36 +40,16 @@ them (PROGRAM, CHANNEL, CLIP, LIVE, EVENT).
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from ..models.favorite import Favorite, FavoriteType
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class FavoritesManager(ABC):
|
||||
class FavoritesManager(ManagerBase):
|
||||
"""Abstract base for provider favorites managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Abstract
|
||||
# ------------------------------------------------------------------
|
||||
@@ -91,7 +75,8 @@ class FavoritesManager(ABC):
|
||||
"""
|
||||
Add a favorite.
|
||||
|
||||
Raises RuntimeError if the provider refuses the operation.
|
||||
Raises OperationFailedError (also a RuntimeError) if the provider
|
||||
refuses the operation.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -101,8 +86,9 @@ class FavoritesManager(ABC):
|
||||
Remove a favorite.
|
||||
|
||||
Raises:
|
||||
KeyError: if content_id is not currently favorited.
|
||||
RuntimeError: on backend failure.
|
||||
ItemNotFoundError: if content_id is not currently favorited
|
||||
(also a KeyError).
|
||||
OperationFailedError: on backend failure (also a RuntimeError).
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -20,13 +20,14 @@ Return-value conventions
|
||||
get_recordings returns [] when the provider has no recordings. Callers
|
||||
should treat an empty list as "no recordings", not as an error.
|
||||
|
||||
delete_recording raises KeyError when the recording does not exist, and
|
||||
ProviderError subclasses (usually ServerError / EntitlementError) on
|
||||
transport or permission failures. Returning silently on a failed delete
|
||||
would hide real errors.
|
||||
delete_recording raises ItemNotFoundError (also a KeyError) when the
|
||||
recording does not exist, and ProviderError subclasses (usually ServerError
|
||||
/ EntitlementError / OperationFailedError) on transport or permission
|
||||
failures. Returning silently on a failed delete would hide real errors.
|
||||
|
||||
schedule_recording is optional -- the ABC provides a default that raises
|
||||
NotImplementedYetError. Providers that support scheduling override it.
|
||||
UnsupportedOperationError, and supports_scheduling is False. Providers that
|
||||
support scheduling override both.
|
||||
|
||||
Recording identity
|
||||
------------------
|
||||
@@ -38,16 +39,15 @@ that distinction is preserved.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, List
|
||||
|
||||
from ..errors import NotImplementedYetError
|
||||
from ..errors import UnsupportedOperationError
|
||||
from ..models import Channel
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class RecordingsManager(ABC):
|
||||
class RecordingsManager(ManagerBase):
|
||||
"""
|
||||
Abstract base for provider recordings managers.
|
||||
|
||||
@@ -72,29 +72,10 @@ class RecordingsManager(ABC):
|
||||
|
||||
Providers that need a recording's manifest should implement the
|
||||
routing in their provider's get_manifest, not by adding a
|
||||
get_manifest method here. See providers/simpli/provider.py for
|
||||
the pattern.
|
||||
get_manifest method here. See the simpliTV provider's provider.py
|
||||
for the pattern.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Routing
|
||||
# ------------------------------------------------------------------
|
||||
@@ -143,8 +124,9 @@ class RecordingsManager(ABC):
|
||||
Delete a recording.
|
||||
|
||||
Raises:
|
||||
KeyError: if the recording does not exist.
|
||||
ProviderError: on transport / permission / server failure.
|
||||
ItemNotFoundError: if the recording does not exist (also a
|
||||
KeyError).
|
||||
ProviderError: on transport / permission / server failure.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -152,6 +134,17 @@ class RecordingsManager(ABC):
|
||||
# Concrete -- optional
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def supports_scheduling(self) -> bool:
|
||||
"""
|
||||
True when schedule_recording() is implemented. Default False.
|
||||
|
||||
Providers that override schedule_recording MUST also override this
|
||||
to return True, so callers can gate the UI instead of catching
|
||||
UnsupportedOperationError.
|
||||
"""
|
||||
return False
|
||||
|
||||
def schedule_recording(self, content_id: str, **kw: Any) -> Any:
|
||||
"""
|
||||
Schedule a recording of content_id.
|
||||
@@ -161,10 +154,11 @@ class RecordingsManager(ABC):
|
||||
indicating success. The ABC does not constrain the shape
|
||||
because there is no shared one across providers.
|
||||
|
||||
Default raises NotImplementedYetError. Providers that support
|
||||
scheduling override this.
|
||||
Default raises UnsupportedOperationError (a NotImplementedYetError
|
||||
subclass, so old handlers still match). "Not supported by this
|
||||
provider" is not the same as "planned, not done yet". Providers
|
||||
that support scheduling override this AND supports_scheduling.
|
||||
"""
|
||||
raise NotImplementedYetError(
|
||||
f"{self.__class__.__name__}.schedule_recording is not "
|
||||
f"implemented"
|
||||
raise UnsupportedOperationError(
|
||||
f"{self.__class__.__name__} does not support schedule_recording"
|
||||
)
|
||||
@@ -10,6 +10,7 @@ Public interface
|
||||
get_vod_manifest(content_id, **kw) -> Optional[str] [abstract]
|
||||
search_vod(query, cursor=None, page_size=24, **kw) -> VodPage [concrete]
|
||||
get_vod_manifest_headers(content_id, **kw) -> Dict[str, str] [concrete]
|
||||
get_segment_headers(content_id, **kw) -> Dict[str, str] [concrete]
|
||||
get_vod_drm(content_id, **kw) -> List[DRMConfig] [concrete]
|
||||
|
||||
content_id grammar
|
||||
@@ -24,9 +25,9 @@ documents it here. The base class does not parse content_id. Examples:
|
||||
|
||||
Constructor contract
|
||||
--------------------
|
||||
Four required keyword-only collaborators. No **kwargs. Subclasses accept
|
||||
extra keyword-only args explicitly and call super().__init__ with only
|
||||
the four required. A typo at a call site becomes an immediate TypeError.
|
||||
Four required keyword-only collaborators (see ManagerBase). No **kwargs.
|
||||
Subclasses accept extra keyword-only args explicitly and call
|
||||
super().__init__ with only the four required.
|
||||
|
||||
Return-value conventions
|
||||
------------------------
|
||||
@@ -37,37 +38,17 @@ propagate as exceptions from base.errors.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from abc import abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from ..models import DRMConfig
|
||||
from ..protocols import AuthProtocol
|
||||
from ..utils.logger import logger
|
||||
from ..vod import VodPage
|
||||
from ._base import ManagerBase
|
||||
|
||||
|
||||
class VodManager(ABC):
|
||||
class VodManager(ManagerBase):
|
||||
"""Abstract base for provider VOD managers."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
http_manager: Any,
|
||||
auth: AuthProtocol,
|
||||
country: str,
|
||||
config: Any,
|
||||
) -> None:
|
||||
if not isinstance(auth, AuthProtocol):
|
||||
logger.warning(
|
||||
f"{self.__class__.__name__}: auth does not match AuthProtocol "
|
||||
f"(missing one of get_access_token / build_headers / "
|
||||
f"invalidate). Got {type(auth).__name__}."
|
||||
)
|
||||
self.http_manager = http_manager
|
||||
self.auth = auth
|
||||
self.country = country
|
||||
self.config = config
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Routing
|
||||
# ------------------------------------------------------------------
|
||||
@@ -78,6 +59,7 @@ class VodManager(ABC):
|
||||
|
||||
Default: True. Override in providers whose VOD content_ids have a
|
||||
distinguishable grammar (e.g. start with "details_" or "clip_").
|
||||
Cheap, I/O-free PRE-FILTER: False means the manager is never asked.
|
||||
"""
|
||||
return True
|
||||
|
||||
@@ -123,6 +105,18 @@ class VodManager(ABC):
|
||||
"""Headers for the manifest request. Default: auth headers."""
|
||||
return self.auth.build_headers()
|
||||
|
||||
def get_segment_headers(
|
||||
self, content_id: str, **kw: Any
|
||||
) -> Dict[str, str]:
|
||||
"""
|
||||
Headers for segment requests. Default: manifest headers.
|
||||
|
||||
Mirrors ChannelManager.get_segment_headers so the orchestrator can
|
||||
ask any routed manager for segment headers. Override for providers
|
||||
with token-bound segment URLs.
|
||||
"""
|
||||
return self.get_vod_manifest_headers(content_id, **kw)
|
||||
|
||||
def get_vod_drm(
|
||||
self, content_id: str, **kw: Any
|
||||
) -> List[DRMConfig]:
|
||||
|
||||
Reference in New Issue
Block a user