refactor: unify emby family item formatting

This commit is contained in:
jxxghp
2026-08-24 06:08:04 +08:00
parent 073ff7ff81
commit 1047bfa821
8 changed files with 115 additions and 157 deletions
+58
View File
@@ -1,9 +1,12 @@
import re
from collections.abc import Iterable, Mapping
from datetime import datetime
from typing import Any, Optional, Protocol
from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
from app.schemas.mediaserver import MediaServerItemUserState as _SchemaMediaServerItemUserState
from app.domain.context import MusicInfo
from app.runtime.log import logger
from app.schemas.media import normalize_media_source, resolve_media_identity
from app.application.service import ServiceBaseHelper
from app.schemas.system import MediaServerConf
@@ -284,6 +287,61 @@ class MusicMediaServerHelper:
)
def format_emby_family_item(
item: Mapping[str, Any],
*,
server: str,
include_server_id: bool = False,
) -> Optional[_SchemaMediaServerItem]:
"""把 Emby 系服务条目转换为统一媒体服务器模型,并保留各服务既有字段差异。"""
try:
user_data = item.get("UserData") or {}
if not user_data:
user_state = None
else:
resume = (
user_data.get("PlaybackPositionTicks")
and user_data.get("PlaybackPositionTicks") > 0
)
last_played_date = user_data.get("LastPlayedDate")
if last_played_date is not None and "." in last_played_date:
last_played_date = last_played_date.split(".")[0]
user_state = _SchemaMediaServerItemUserState(
played=user_data.get("Played"),
resume=resume,
last_played_date=datetime.strptime(
last_played_date,
"%Y-%m-%dT%H:%M:%S",
).strftime("%Y-%m-%d %H:%M:%S") if last_played_date else None,
play_count=user_data.get("PlayCount"),
percentage=user_data.get("PlayedPercentage"),
)
media_source, media_id = MediaServerIdentityHelper.from_provider_ids(
item.get("ProviderIds")
)
fields = {
"server": server,
"library": item.get("ParentId"),
"item_id": item.get("Id"),
"item_type": item.get("Type"),
"title": item.get("Name"),
"original_title": item.get("OriginalTitle"),
"year": item.get("ProductionYear"),
"media_source": media_source,
"media_id": media_id,
"path": item.get("Path"),
"note": MusicMediaServerHelper.build_note(item)
if item.get("Type") in {"MusicAlbum", "Audio"} else None,
"user_state": user_state,
}
if include_server_id:
fields["server_id"] = item.get("ServerId")
return _SchemaMediaServerItem(**fields)
except Exception as error:
logger.error(error)
return None
class MediaServerHelper(ServiceBaseHelper[MediaServerConf]):
"""管理媒体服务器配置,并按类型发现已启用的服务实例。"""
+7 -47
View File
@@ -1,7 +1,6 @@
import json
import re
import traceback
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Union, Dict, Generator, Tuple, Any
@@ -9,7 +8,6 @@ from requests import Response
from app.schemas.dashboard import Statistic as _SchemaStatistic
from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
from app.schemas.mediaserver import MediaServerItemUserState as _SchemaMediaServerItemUserState
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
from app.schemas.mediaserver import RefreshMediaItem as _SchemaRefreshMediaItem
@@ -17,7 +15,7 @@ from app.schemas.mediaserver import WebhookEventInfo as _SchemaWebhookEventInfo
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.mediaserver import MediaServerIdentityHelper, MusicMediaServerHelper
from app.application.mediaserver import MediaServerIdentityHelper, format_emby_family_item
from app.runtime.log import logger
from app.schemas.mediaserver import MediaServerItem
from app.schemas.types import MediaSource, MediaType
@@ -731,50 +729,12 @@ class Emby:
@staticmethod
def __format_item_info(item) -> Optional[_SchemaMediaServerItem]:
"""
格式化item
"""
try:
user_data = item.get("UserData", {})
if not user_data:
user_state = None
else:
resume = item.get("UserData", {}).get("PlaybackPositionTicks") and item.get("UserData", {}).get(
"PlaybackPositionTicks") > 0
last_played_date = item.get("UserData", {}).get("LastPlayedDate")
if last_played_date is not None and "." in last_played_date:
last_played_date = last_played_date.split(".")[0]
user_state = _SchemaMediaServerItemUserState(
played=item.get("UserData", {}).get("Played"),
resume=resume,
last_played_date=datetime.strptime(last_played_date, "%Y-%m-%dT%H:%M:%S").strftime(
"%Y-%m-%d %H:%M:%S") if last_played_date else None,
play_count=item.get("UserData", {}).get("PlayCount"),
percentage=item.get("UserData", {}).get("PlayedPercentage"),
)
media_source, media_id = MediaServerIdentityHelper.from_provider_ids(
item.get("ProviderIds")
)
return _SchemaMediaServerItem(
server="emby",
library=item.get("ParentId"),
server_id=item.get("ServerId"),
item_id=item.get("Id"),
item_type=item.get("Type"),
title=item.get("Name"),
original_title=item.get("OriginalTitle"),
year=item.get("ProductionYear"),
media_source=media_source,
media_id=media_id,
path=item.get("Path"),
note=MusicMediaServerHelper.build_note(item)
if item.get("Type") in {"MusicAlbum", "Audio"} else None,
user_state=user_state
)
except Exception as e:
logger.error(e)
return None
"""通过统一 Emby 系转换契约生成条目,并保留 Emby 的服务器 ID。"""
return format_emby_family_item(
item,
server="emby",
include_server_id=True,
)
def get_iteminfo(self, itemid: str) -> Optional[_SchemaMediaServerItem]:
"""
+3 -46
View File
@@ -1,20 +1,18 @@
import json
import posixpath
from datetime import datetime
from typing import List, Union, Optional, Dict, Generator, Tuple, Any
from requests import Response
from app.schemas.dashboard import Statistic as _SchemaStatistic
from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
from app.schemas.mediaserver import MediaServerItemUserState as _SchemaMediaServerItemUserState
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
from app.schemas.mediaserver import WebhookEventInfo as _SchemaWebhookEventInfo
from app.runtime.settings import RuntimeSettingsCompat
settings = RuntimeSettingsCompat()
from app.application.mediaserver import MediaServerIdentityHelper, MusicMediaServerHelper
from app.application.mediaserver import MediaServerIdentityHelper, format_emby_family_item
from app.runtime.log import logger
from app.schemas.types import MediaType
from app.schemas.types import MediaSource
@@ -889,49 +887,8 @@ class Jellyfin:
@staticmethod
def __format_item_info(item) -> Optional[_SchemaMediaServerItem]:
"""
格式化item
"""
try:
user_data = item.get("UserData", {})
if not user_data:
user_state = None
else:
resume = item.get("UserData", {}).get("PlaybackPositionTicks") and item.get("UserData", {}).get(
"PlaybackPositionTicks") > 0
last_played_date = item.get("UserData", {}).get("LastPlayedDate")
if last_played_date is not None and "." in last_played_date:
last_played_date = last_played_date.split(".")[0]
user_state = _SchemaMediaServerItemUserState(
played=item.get("UserData", {}).get("Played"),
resume=resume,
last_played_date=datetime.strptime(last_played_date, "%Y-%m-%dT%H:%M:%S").strftime(
"%Y-%m-%d %H:%M:%S") if last_played_date else None,
play_count=item.get("UserData", {}).get("PlayCount"),
percentage=item.get("UserData", {}).get("PlayedPercentage"),
)
media_source, media_id = MediaServerIdentityHelper.from_provider_ids(
item.get("ProviderIds")
)
return _SchemaMediaServerItem(
server="jellyfin",
library=item.get("ParentId"),
item_id=item.get("Id"),
item_type=item.get("Type"),
title=item.get("Name"),
original_title=item.get("OriginalTitle"),
year=item.get("ProductionYear"),
media_source=media_source,
media_id=media_id,
path=item.get("Path"),
note=MusicMediaServerHelper.build_note(item)
if item.get("Type") in {"MusicAlbum", "Audio"} else None,
user_state=user_state
)
except Exception as e:
logger.error(e)
return None
"""通过统一 Emby 系转换契约生成 Jellyfin 条目。"""
return format_emby_family_item(item, server="jellyfin")
def get_iteminfo(self, itemid: str) -> Optional[_SchemaMediaServerItem]:
"""
+3 -46
View File
@@ -1,7 +1,6 @@
import json
import re
import traceback
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Union, Dict, Generator, Tuple, Any
@@ -9,12 +8,11 @@ from requests import Response
from app.schemas.dashboard import Statistic as _SchemaStatistic
from app.schemas.mediaserver import MediaServerItem as _SchemaMediaServerItem
from app.schemas.mediaserver import MediaServerItemUserState as _SchemaMediaServerItemUserState
from app.schemas.mediaserver import MediaServerLibrary as _SchemaMediaServerLibrary
from app.schemas.mediaserver import MediaServerPlayItem as _SchemaMediaServerPlayItem
from app.schemas.mediaserver import RefreshMediaItem as _SchemaRefreshMediaItem
from app.schemas.mediaserver import WebhookEventInfo as _SchemaWebhookEventInfo
from app.application.mediaserver import MediaServerIdentityHelper, MusicMediaServerHelper
from app.application.mediaserver import MediaServerIdentityHelper, format_emby_family_item
from app.runtime.log import logger
from app.schemas.mediaserver import MediaServerItem
from app.schemas.types import MediaSource, MediaType
@@ -813,49 +811,8 @@ class ZSpace:
@staticmethod
def __format_item_info(item) -> Optional[_SchemaMediaServerItem]:
"""
格式化item
"""
try:
user_data = item.get("UserData", {})
if not user_data:
user_state = None
else:
resume = item.get("UserData", {}).get("PlaybackPositionTicks") and item.get("UserData", {}).get(
"PlaybackPositionTicks") > 0
last_played_date = item.get("UserData", {}).get("LastPlayedDate")
if last_played_date is not None and "." in last_played_date:
last_played_date = last_played_date.split(".")[0]
user_state = _SchemaMediaServerItemUserState(
played=item.get("UserData", {}).get("Played"),
resume=resume,
last_played_date=datetime.strptime(last_played_date, "%Y-%m-%dT%H:%M:%S").strftime(
"%Y-%m-%d %H:%M:%S") if last_played_date else None,
play_count=item.get("UserData", {}).get("PlayCount"),
percentage=item.get("UserData", {}).get("PlayedPercentage"),
)
media_source, media_id = MediaServerIdentityHelper.from_provider_ids(
item.get("ProviderIds")
)
return _SchemaMediaServerItem(
server="zspace",
library=item.get("ParentId"),
item_id=item.get("Id"),
item_type=item.get("Type"),
title=item.get("Name"),
original_title=item.get("OriginalTitle"),
year=item.get("ProductionYear"),
media_source=media_source,
media_id=media_id,
path=item.get("Path"),
note=MusicMediaServerHelper.build_note(item)
if item.get("Type") in {"MusicAlbum", "Audio"} else None,
user_state=user_state
)
except Exception as e:
logger.error(e)
return None
"""通过统一 Emby 系转换契约生成极影视条目。"""
return format_emby_family_item(item, server="zspace")
def get_iteminfo(self, itemid: str) -> Optional[_SchemaMediaServerItem]:
"""