feat(mediaserver): add item count retrieval for various media servers

This commit is contained in:
jxxghp
2026-06-29 10:45:10 +08:00
parent 8bf826faa0
commit 9eb71c744b
17 changed files with 799 additions and 157 deletions

View File

@@ -243,6 +243,19 @@ class EmbyModule(_ModuleBase, _MediaServerBase[Emby]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: Emby = 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[schemas.MediaServerItem]:
"""
媒体库项目详情

View File

@@ -693,6 +693,33 @@ class Emby:
logger.error(f"连接/Users/{self.user}/Items/{itemid}出错:" + str(e))
return None
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库可同步的电影和剧集总数
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not parent or not self._host or not self._apikey:
return None
url = f"{self._host}emby/Users/{self.user}/Items"
params = {
"ParentId": parent,
"Recursive": "true",
"IncludeItemTypes": "Movie,Series",
"Limit": 0,
"api_key": self._apikey,
}
try:
res = RequestUtils().get_res(url, params)
if not res or res.status_code != 200:
return None
total_count = res.json().get("TotalRecordCount")
return int(total_count) if total_count is not None else None
except Exception as e:
logger.error(f"查询媒体库 {parent} 的媒体总数出错:{str(e)}")
return None
def get_items(self, parent: Union[str, int], start_index: Optional[int] = 0,
limit: Optional[int] = -1) -> Generator[MediaServerItem | None | Any, Any, None]:
"""

View File

@@ -242,6 +242,19 @@ class JellyfinModule(_ModuleBase, _MediaServerBase[Jellyfin]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: Jellyfin = 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[schemas.MediaServerItem]:
"""
媒体库项目详情

View File

@@ -808,6 +808,33 @@ class Jellyfin:
logger.error(f"连接Users/{self.user}/Items/{itemid}" + str(e))
return None
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库可同步的电影和剧集总数
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not parent or not self._host or not self._apikey or not self.user:
return None
url = f"{self._host}Users/{self.user}/Items"
params = {
"ParentId": parent,
"Recursive": "true",
"IncludeItemTypes": "Movie,Series",
"Limit": 0,
"api_key": self._apikey,
}
try:
res = RequestUtils().get_res(url, params)
if not res or res.status_code != 200:
return None
total_count = res.json().get("TotalRecordCount")
return int(total_count) if total_count is not None else None
except Exception as e:
logger.error(f"查询媒体库 {parent} 的媒体总数出错:{str(e)}")
return None
def get_items(self, parent: Union[str, int], start_index: Optional[int] = 0, limit: Optional[int] = -1) \
-> Generator[MediaServerItem | None | Any, Any, None]:
"""

View File

@@ -253,6 +253,19 @@ class PlexModule(_ModuleBase, _MediaServerBase[Plex]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: Plex = 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[schemas.MediaServerItem]:
"""
媒体库项目详情

View File

@@ -575,6 +575,22 @@ class Plex:
user_state=user_state,
)
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库可同步的媒体条目总数
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not parent or not self._plex:
return None
try:
section = self._plex.library.sectionByID(int(parent))
return int(section.totalSize) if section else None
except Exception as err:
logger.error(f"查询媒体库 {parent} 的媒体总数出错:{str(err)}")
return None
def get_items(self, parent: Union[str, int], start_index: Optional[int] = 0, limit: Optional[int] = -1) \
-> Generator[MediaServerItem | None, Any, None]:
"""

View File

@@ -281,6 +281,21 @@ class TrimeMediaModule(_ModuleBase, _MediaServerBase[TrimeMedia]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: Optional[TrimeMedia] = 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[schemas.MediaServerItem]:

View File

@@ -347,6 +347,32 @@ class Api:
return [self.__build_item(info) for info in res.data.get("list", [])]
return None
def item_count(self, guid: str, types=None) -> Optional[int]:
"""
获取指定媒体库的媒体条目总数
:param guid: 媒体库GUID
:param types: 需要统计的媒体类型
:return: 媒体条目总数查询失败时返回None
"""
if types is None:
types = [Type.MOVIE, Type.TV]
post = {
"ancestor_guid": guid,
"tags": {"type": types},
"exclude_grouped_video": 1,
"page": 1,
"page_size": 1,
}
if (res := self.request("/item/list", data=post)) and res.success:
if not res.data:
return 0
total_count = res.data.get("total")
if total_count is None:
total_count = res.data.get("total_count")
return int(total_count) if total_count is not None else None
return None
def search_list(self, keywords: str) -> Optional[list[Item]]:
"""
搜索影片、演员

View File

@@ -510,6 +510,20 @@ class TrimeMedia:
use_cookies=True,
)
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库可同步的媒体条目总数
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not self.is_authenticated():
return None
return self._api.item_count(
guid=str(parent),
types=[fnapi.Type.MOVIE, fnapi.Type.TV],
)
def get_items(
self,
parent: Union[str, int],

View File

@@ -253,6 +253,21 @@ class UgreenModule(_ModuleBase, _MediaServerBase[Ugreen]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: Optional[Ugreen] = 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[schemas.MediaServerItem]:

View File

@@ -66,15 +66,19 @@ class Ugreen:
@property
def api(self) -> Optional[Api]:
"""获取当前绿联影视 API 客户端"""
return self._api
def close(self):
"""关闭绿联影视连接"""
self.disconnect()
def is_configured(self) -> bool:
"""检查绿联影视连接配置是否完整"""
return bool(self._host and self._username and self._password)
def is_authenticated(self) -> bool:
"""检查绿联影视会话是否已认证"""
return (
self.is_configured()
and self._api is not None
@@ -83,6 +87,7 @@ class Ugreen:
)
def is_inactive(self) -> bool:
"""检查绿联影视会话是否已失效"""
if not self.is_authenticated():
return True
self._userinfo = self._api.current_user() if self._api else None
@@ -516,6 +521,12 @@ class Ugreen:
return paths
def get_librarys(self, hidden: Optional[bool] = False) -> List[schemas.MediaServerLibrary]:
"""
获取绿联影视媒体库列表
:param hidden: 是否过滤未启用同步的媒体库
:return: 媒体库列表
"""
if not self.is_authenticated() or not self._api:
return []
@@ -567,12 +578,14 @@ class Ugreen:
return libraries
def get_user_count(self) -> int:
"""获取绿联影视媒体库用户数量"""
if not self.is_authenticated() or not self._api:
return 0
users = self._api.media_lib_users()
return len(users)
def get_medias_count(self) -> schemas.Statistic:
"""获取绿联影视的电影和电视剧数量统计"""
if not self.is_authenticated() or not self._api:
return schemas.Statistic()
@@ -856,12 +869,36 @@ class Ugreen:
break
page += 1
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库可同步的媒体条目总数
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not self.is_authenticated() or not self._api:
return None
if not self._libraries:
self.get_librarys()
library = self._libraries.get(str(parent))
if not library:
return None
return int(library.get("video_count") or 0)
def get_items(
self,
parent: Union[str, int],
start_index: Optional[int] = 0,
limit: Optional[int] = -1,
) -> Generator[schemas.MediaServerItem | None | Any, Any, None]:
"""
获取指定绿联影视媒体库的可同步条目
:param parent: 媒体库ID
:param start_index: 起始条目索引
:param limit: 最大返回条目数,-1表示不限制
:return: 媒体条目生成器
"""
if not self.is_authenticated() or not self._api:
return None

View File

@@ -237,6 +237,19 @@ class ZSpaceModule(_ModuleBase, _MediaServerBase[ZSpace]):
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: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
server_obj: ZSpace = 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[schemas.MediaServerItem]:
"""
媒体库项目详情

View File

@@ -815,6 +815,32 @@ class ZSpace:
logger.error(f"连接/Users/{self.user}/Items/{itemid}出错:{e}")
return None
def get_items_count(self, parent: Union[str, int]) -> Optional[int]:
"""
获取指定媒体库的媒体条目总数
极影视当前兼容层会忽略条目类型过滤,因此以递归查询返回的
TotalRecordCount 作为同步进度的总数。
:param parent: 媒体库ID
:return: 媒体条目总数查询失败时返回None
"""
if not parent or not self._host or not self._apikey or not self.user:
return None
url = f"{self._host}emby/Users/{self.user}/Items"
try:
res = self.__request_utils().get_res(
url,
params={"ParentId": parent, "Recursive": "true", "Limit": 0},
)
if not res or res.status_code != 200:
return None
total_count = res.json().get("TotalRecordCount")
return int(total_count) if total_count is not None else None
except Exception as e:
logger.error(f"查询媒体库 {parent} 的媒体总数出错:{e}")
return None
def get_items(self, parent: Union[str, int], start_index: Optional[int] = 0,
limit: Optional[int] = -1) -> Generator[MediaServerItem | None | Any, Any, None]:
"""