mirror of
https://github.com/nirvana-7777/script.service.ultimate.git
synced 2026-10-06 16:02:47 +02:00
110 lines
3.3 KiB
Python
110 lines
3.3 KiB
Python
# streaming_providers/base/vod.py
|
|
"""
|
|
Shared VOD return type.
|
|
|
|
Prior to this module, providers returned VOD category children in three
|
|
different shapes (dict-with-entries, bare list, ...). This module defines
|
|
one canonical shape.
|
|
|
|
Pagination rule: `next_cursor is None` is the authoritative end-of-list
|
|
signal (exposed as `has_more`). `total` may be missing; do not use it to
|
|
decide whether to keep paging.
|
|
|
|
Truthiness: bool(page) is driven by __len__, so an empty page is falsy and
|
|
a page with entries is truthy -- matching list semantics. An empty page
|
|
with a next_cursor is falsy; check `page.has_more` when the caller means
|
|
"are there more pages?".
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Iterator, List, Optional, Union
|
|
|
|
from .models.vod import VodCategory, VodItem
|
|
|
|
|
|
VodEntry = Union[VodCategory, VodItem]
|
|
|
|
|
|
@dataclass
|
|
class VodPage:
|
|
"""
|
|
One page of VOD results.
|
|
|
|
Attributes:
|
|
entries: Mixed list of VodCategory and VodItem.
|
|
next_cursor: Opaque continuation token; None = no next page.
|
|
total: Optional total count; None = unknown.
|
|
|
|
Truthiness follows list semantics via __len__: an empty page is falsy.
|
|
For pagination, use `has_more` -- NOT bool(page) -- because a page can
|
|
legitimately have zero entries and a non-None cursor.
|
|
"""
|
|
|
|
entries: List[VodEntry] = field(default_factory=list)
|
|
next_cursor: Optional[str] = None
|
|
total: Optional[int] = None
|
|
|
|
@property
|
|
def has_more(self) -> bool:
|
|
"""True when the provider indicated a next page exists.
|
|
|
|
This is the correct pagination check. `bool(page)` answers
|
|
"are there entries to display?" -- a different question.
|
|
"""
|
|
return self.next_cursor is not None
|
|
|
|
def __len__(self) -> int:
|
|
# Drives both len(page) and bool(page) via Python's default
|
|
# truthiness rule for objects with __len__. Matches list semantics.
|
|
return len(self.entries)
|
|
|
|
def __iter__(self) -> Iterator[VodEntry]:
|
|
return iter(self.entries)
|
|
|
|
def to_dict(self) -> dict:
|
|
return {
|
|
"entries": [
|
|
e.to_dict() if hasattr(e, "to_dict") else e
|
|
for e in self.entries
|
|
],
|
|
"next_cursor": self.next_cursor,
|
|
"total": self.total,
|
|
}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def empty_page() -> VodPage:
|
|
"""Return a fresh empty VodPage with no pagination."""
|
|
return VodPage()
|
|
|
|
|
|
def normalize_vod_result(result) -> VodPage:
|
|
"""
|
|
Coerce a legacy return value into a VodPage.
|
|
|
|
Migration bridge. Once every provider returns VodPage directly, this
|
|
becomes redundant.
|
|
"""
|
|
if isinstance(result, VodPage):
|
|
return result
|
|
if result is None:
|
|
return VodPage()
|
|
if isinstance(result, dict):
|
|
return VodPage(
|
|
entries=result.get("entries") or [],
|
|
next_cursor=result.get("next_cursor"),
|
|
total=result.get("total"),
|
|
)
|
|
if isinstance(result, list):
|
|
return VodPage(entries=result)
|
|
import logging
|
|
logging.getLogger(__name__).warning(
|
|
f"normalize_vod_result: unexpected type {type(result).__name__}; "
|
|
f"returning empty page"
|
|
)
|
|
return VodPage() |