Files
MoviePilot/app/modules/vyo/module.py
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

293 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Vyo 模块,沿用 mediavault 配置身份提供媒体服务器统一接口。
"""
from typing import Any, Dict, Generator, List, Optional, Tuple, Union
from app.modules._base.mediaserver import _MediaServerModuleBase
from app.modules.vyo.vyo import Vyo
from app.runtime.log import logger
from app.schemas.dashboard import Statistic as _SchemaStatistic
from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
from app.schemas.mediaserver import MediaServerSeasonInfo as _SchemaMediaServerSeasonInfo
from app.schemas.types import MediaServerType, ModuleType
class VyoModule(_MediaServerModuleBase[Vyo]):
"""
Vyo 媒体服务模块。
"""
# 媒体库标识(ExistMediaInfo.server_type)
# 配置类型值沿用 mediavault,兼容已保存的媒体服务器配置
_server_type_value = "mediavault"
def init_module(self) -> None:
"""
初始化模块。
"""
# 展示名更改为 Vyo,持久化配置类型仍为 mediavault,不能从类名推导。
super().init_service(
service_name=self._server_type_value,
service_type=lambda conf: Vyo(
**conf.config, sync_libraries=conf.sync_libraries
),
)
@staticmethod
def get_name() -> str:
"""
返回媒体服务器模块名称。
"""
return "Vyo"
@staticmethod
def get_type() -> ModuleType:
"""
获取模块类型。
"""
return ModuleType.MediaServer
@staticmethod
def get_subtype() -> MediaServerType:
"""
获取模块子类型。
"""
return MediaServerType.Vyo
@staticmethod
def get_priority() -> int:
"""
获取模块优先级,数字越小优先级越高,只有同一接口下优先级才生效。
"""
return 7
def init_setting(self) -> Optional[Tuple[str, Union[str, bool]]]:
"""
本模块不使用开关设置。
"""
return None
def _is_inactive(self, server: Vyo) -> bool:
"""
未配置的实例不参与定时重连。
"""
return server.is_configured() and server.is_inactive()
def stop(self) -> None:
"""
逐个断开 Vyo 实例,单个失败不阻止其他实例关闭。
"""
for server in self.get_instances().values():
try:
server.disconnect()
except Exception as err:
logger.error(f"停止 Vyo 模块实例失败:{err}")
def _test_server(self, server: Vyo, name: Optional[str]) -> Optional[str]:
"""
用配置完整性与 API Key 探测结果判断连接状态。
"""
if not server.is_configured():
return f"{self.get_name()}配置不完整:{name}"
if server.is_inactive() and not server.reconnect():
return f"无法连接{self.get_name()}:{name}"
return None
def media_statistic(
self, server: Optional[str] = None
) -> Optional[List[_SchemaStatistic]]:
"""
汇总指定实例或全部 Vyo 实例的媒体与用户数量。
:param server: 指定实例名称,省略时汇总全部实例
:return: 统计列表;无实例或全部统计请求失败时返回空列表
"""
if server:
server_obj: Optional[Vyo] = self.get_instance(server)
servers = [server_obj] if server_obj else []
else:
servers = list(self.get_instances().values())
statistics = []
for s in servers:
statistic = s.get_medias_count()
if not statistic:
continue
statistic.user_count = s.get_user_count()
statistics.append(statistic)
return statistics
def mediaserver_librarys(
self, server: Optional[str] = None, hidden: Optional[bool] = False, **kwargs: Any
) -> Optional[List[_SchemaMediaServerLibrary]]:
"""
读取实例媒体库列表。
:param server: 媒体服务器配置名称,空值使用默认实例
:param hidden: 是否隐藏同步范围外的库
:param kwargs: 兼容统一媒体服务器接口的额外参数,当前不使用
:return: 统一媒体库列表;实例不存在或查询失败时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if server_obj:
return server_obj.get_librarys(hidden=hidden)
return None
def mediaserver_items(
self,
server: str,
library_id: Union[str, int],
start_index: Optional[int] = 0,
limit: Optional[int] = -1,
) -> Optional[Generator[Optional[_SchemaMediaServerItem], Any, None]]:
"""
返回电影和电视剧同步条目的迭代器。
:param server: 媒体服务器配置名称,空值使用默认实例
:param library_id: 媒体库标识
:param start_index: 零基条目偏移,负值按 0 处理
:param limit: 最多检查的条目数量,None 或 -1 表示不限
:return: 统一条目生成器;实例不存在时返回 None,底层请求失败可能提前结束迭代
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if server_obj:
return server_obj.get_items(library_id, start_index, limit)
return None
def mediaserver_items_count(
self, server: str, library_id: Union[str, int]
) -> Optional[int]:
"""
查询媒体库中的电影和电视剧总数。
:param server: 媒体服务器配置名称,空值使用默认实例
:param library_id: 媒体库标识
:return: 条目总数;实例不存在或查询失败时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if server_obj:
return server_obj.get_items_count(library_id)
return None
def mediaserver_iteminfo(
self, server: str, item_id: str
) -> Optional[_SchemaMediaServerItem]:
"""
查询单个媒体条目详情。
:param server: 媒体服务器配置名称,空值使用默认实例
:param item_id: Vyo 条目标识
:return: 统一媒体模型;实例不存在或查询失败时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if server_obj:
return server_obj.get_iteminfo(str(item_id))
return None
def mediaserver_tv_episodes(
self, server: str, item_id: Union[str, int]
) -> Optional[List[_SchemaMediaServerSeasonInfo]]:
"""
查询电视剧已入库的季集编号。
:param server: 媒体服务器配置名称,空值使用默认实例
:param item_id: Vyo 条目标识
:return: 季集列表;实例不存在时返回 None,无可用数据时返回空列表
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return None
_, seasoninfo = server_obj.get_tv_episodes(item_id=str(item_id))
if not seasoninfo:
return []
return [
_SchemaMediaServerSeasonInfo(season=season, episodes=episodes)
for season, episodes in seasoninfo.items()
]
def mediaserver_season_episode_ids(
self, server: str, item_id: Union[str, int], season: int
) -> Optional[Dict[int, str]]:
"""
查询指定季中集号对应的条目标识。
:param server: 媒体服务器配置名称,空值使用默认实例
:param item_id: Vyo 条目标识
:param season: 目标季号
:return: 集号到条目标识的映射;实例不存在时返回 None,客户端中途失败可能返回部分映射
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return None
return server_obj.get_season_episode_ids(str(item_id), season)
def mediaserver_playing(
self, server: str, count: Optional[int] = 20, **kwargs: Any
) -> Optional[List[_SchemaMediaServerPlayItem]]:
"""
查询当前账号的继续观看列表。
:param server: 媒体服务器配置名称,空值使用默认实例
:param count: 目标数量,最终限制为 1 至 100,空值或 0 使用 20
:param kwargs: 兼容统一媒体服务器接口的额外参数,当前不使用
:return: 播放条目列表;实例不存在或查询失败时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return None
return server_obj.get_resume(num=count)
def mediaserver_play_url(
self, server: str, item_id: Union[str, int]
) -> Optional[str]:
"""
构造媒体条目的网页播放链接。
:param server: 媒体服务器配置名称,空值使用默认实例
:param item_id: Vyo 条目标识
:return: 网页 URL;实例或必要配置不可用时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return None
return server_obj.get_play_url(str(item_id))
def mediaserver_latest(
self, server: Optional[str] = None, count: Optional[int] = 20, **kwargs: Any
) -> Optional[List[_SchemaMediaServerPlayItem]]:
"""
查询最近入库的电影和电视剧。
:param server: 媒体服务器配置名称,空值使用默认实例
:param count: 目标数量,最终限制为 1 至 100,空值或 0 使用 20
:param kwargs: 兼容统一媒体服务器接口的额外参数,当前不使用
:return: 最新媒体列表;实例不存在或查询失败时返回 None
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return None
return server_obj.get_latest(num=count)
def mediaserver_latest_images(
self,
server: Optional[str] = None,
count: Optional[int] = 20,
remote: Optional[bool] = False,
**kwargs: Any,
) -> List[str]:
"""
获取最新电影和电视剧的公开背景图。
:param server: 媒体服务器配置名称,空值使用默认实例
:param count: 目标图片数量,最终限制为 1 至 100
:param remote: 是否优先使用外网图片根地址
:param kwargs: 兼容统一媒体服务器接口的额外参数,当前不使用
:return: 无管理密钥的图片 URL 列表;实例不可用或没有图片时返回空列表
"""
server_obj: Optional[Vyo] = self.get_instance(server)
if not server_obj:
return []
return server_obj.get_latest_backdrops(num=count, remote=remote) or []