mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
feat(agent): complete music workflow support
This commit is contained in:
@@ -9,7 +9,8 @@ from app.agent.tools.base import MoviePilotTool
|
||||
from app.agent.tools.tags import ToolTag
|
||||
from app.db.subscribehistory_oper import SubscribeHistoryOper
|
||||
from app.log import logger
|
||||
from app.schemas.types import media_type_to_agent
|
||||
from app.schemas.types import MediaType, media_type_to_agent
|
||||
from ._music_utils import normalize_music_type
|
||||
|
||||
PAGE_SIZE = 20
|
||||
|
||||
@@ -18,7 +19,11 @@ class QuerySubscribeHistoryInput(BaseModel):
|
||||
"""查询订阅历史工具的输入参数模型"""
|
||||
|
||||
media_type: Optional[str] = Field(
|
||||
"all", description="Allowed values: movie, tv, all"
|
||||
"all", description="Allowed values: movie, tv, music, all"
|
||||
)
|
||||
music_type: Optional[str] = Field(
|
||||
None,
|
||||
description="Optional music history filter: recording or album",
|
||||
)
|
||||
name: Optional[str] = Field(
|
||||
None, description="Filter by media name (partial match, optional)"
|
||||
@@ -30,6 +35,8 @@ class QuerySubscribeHistoryInput(BaseModel):
|
||||
|
||||
|
||||
class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
"""查询已完成的影视、单曲与整张专辑订阅历史。"""
|
||||
|
||||
name: str = "query_subscribe_history"
|
||||
tags: list[str] = [
|
||||
ToolTag.Read,
|
||||
@@ -58,38 +65,63 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
async def run(
|
||||
self,
|
||||
media_type: Optional[str] = "all",
|
||||
music_type: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
page: Optional[int] = 1,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""按规范化数据库类型查询并合并订阅历史。"""
|
||||
page = max(1, page or 1)
|
||||
logger.info(
|
||||
f"执行工具: {self.name}, 参数: media_type={media_type}, name={name}, page={page}"
|
||||
)
|
||||
|
||||
try:
|
||||
if media_type not in ["all", "movie", "tv"]:
|
||||
return f"错误:无效的媒体类型 '{media_type}',支持的类型:'movie', 'tv', 'all'"
|
||||
if media_type == "all":
|
||||
requested_types = [
|
||||
MediaType.MOVIE.value,
|
||||
MediaType.TV.value,
|
||||
MediaType.MUSIC.value,
|
||||
]
|
||||
else:
|
||||
media_type_enum = MediaType.from_agent(media_type)
|
||||
if not media_type_enum:
|
||||
return (
|
||||
f"错误:无效的媒体类型 '{media_type}',"
|
||||
"支持的类型:'movie', 'tv', 'music', 'all'"
|
||||
)
|
||||
requested_types = [media_type_enum.value]
|
||||
|
||||
normalized_music_type = None
|
||||
if music_type:
|
||||
normalized_music_type = normalize_music_type(
|
||||
music_type,
|
||||
allow_artist=False,
|
||||
)
|
||||
if not normalized_music_type:
|
||||
return (
|
||||
f"错误:无效的音乐实体类型 '{music_type}',"
|
||||
"支持的类型:'recording', 'album'"
|
||||
)
|
||||
if MediaType.MUSIC.value not in requested_types:
|
||||
return "错误:music_type 仅能与 media_type='music' 或 'all' 一起使用"
|
||||
|
||||
subscribe_history_oper = SubscribeHistoryOper()
|
||||
if name:
|
||||
# 有名称过滤时,获取足够多的记录在内存中过滤,不分页
|
||||
fetch_count = 500
|
||||
if media_type == "all":
|
||||
movie_history = await subscribe_history_oper.async_list_by_type(
|
||||
mtype="movie", page=1, count=fetch_count
|
||||
)
|
||||
tv_history = await subscribe_history_oper.async_list_by_type(
|
||||
mtype="tv", page=1, count=fetch_count
|
||||
)
|
||||
all_history = list(movie_history) + list(tv_history)
|
||||
all_history.sort(key=lambda x: x.date or "", reverse=True)
|
||||
else:
|
||||
all_history = list(
|
||||
await subscribe_history_oper.async_list_by_type(
|
||||
mtype=media_type, page=1, count=fetch_count
|
||||
)
|
||||
history_groups = [
|
||||
await subscribe_history_oper.async_list_by_type(
|
||||
mtype=requested_type,
|
||||
page=1,
|
||||
count=fetch_count,
|
||||
)
|
||||
for requested_type in requested_types
|
||||
]
|
||||
all_history = [
|
||||
record for group in history_groups for record in group
|
||||
]
|
||||
all_history.sort(key=lambda x: x.date or "", reverse=True)
|
||||
|
||||
# 按名称过滤
|
||||
name_lower = name.lower()
|
||||
@@ -97,6 +129,7 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
record
|
||||
for record in all_history
|
||||
if record.name and name_lower in record.name.lower()
|
||||
and self._matches_music_type(record, normalized_music_type)
|
||||
]
|
||||
|
||||
if not filtered_history:
|
||||
@@ -110,22 +143,23 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
return result_json
|
||||
else:
|
||||
# 无名称过滤时,直接利用数据库分页
|
||||
if media_type == "all":
|
||||
movie_history = await subscribe_history_oper.async_list_by_type(
|
||||
mtype="movie", page=1, count=page * PAGE_SIZE
|
||||
)
|
||||
tv_history = await subscribe_history_oper.async_list_by_type(
|
||||
mtype="tv", page=1, count=page * PAGE_SIZE
|
||||
)
|
||||
all_history = list(movie_history) + list(tv_history)
|
||||
all_history.sort(key=lambda x: x.date or "", reverse=True)
|
||||
filtered_history = all_history
|
||||
else:
|
||||
filtered_history = list(
|
||||
await subscribe_history_oper.async_list_by_type(
|
||||
mtype=media_type, page=1, count=page * PAGE_SIZE
|
||||
)
|
||||
history_groups = [
|
||||
await subscribe_history_oper.async_list_by_type(
|
||||
mtype=requested_type,
|
||||
page=1,
|
||||
count=page * PAGE_SIZE,
|
||||
)
|
||||
for requested_type in requested_types
|
||||
]
|
||||
all_history = [
|
||||
record for group in history_groups for record in group
|
||||
]
|
||||
all_history.sort(key=lambda x: x.date or "", reverse=True)
|
||||
filtered_history = [
|
||||
record
|
||||
for record in all_history
|
||||
if self._matches_music_type(record, normalized_music_type)
|
||||
]
|
||||
|
||||
if not filtered_history:
|
||||
return "未找到相关订阅历史记录"
|
||||
@@ -156,6 +190,15 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
logger.error(f"查询订阅历史失败: {e}", exc_info=True)
|
||||
return f"查询订阅历史时发生错误: {str(e)}"
|
||||
|
||||
@staticmethod
|
||||
def _matches_music_type(record, music_type: Optional[str]) -> bool:
|
||||
"""匹配音乐实体类型,旧空值历史按单曲兼容。"""
|
||||
if not music_type:
|
||||
return True
|
||||
if media_type_to_agent(getattr(record, "type", None)) != "music":
|
||||
return False
|
||||
return (getattr(record, "music_type", None) or "recording") == music_type
|
||||
|
||||
@staticmethod
|
||||
def _simplify_records(records) -> list:
|
||||
"""转换为字典格式,只保留关键信息"""
|
||||
@@ -173,6 +216,10 @@ class QuerySubscribeHistoryTool(MoviePilotTool):
|
||||
"anilistid": record.anilistid,
|
||||
"media_source": record.media_source,
|
||||
"media_id": record.media_id,
|
||||
"music_type": getattr(record, "music_type", None) or (
|
||||
"recording" if media_type_to_agent(record.type) == "music" else None
|
||||
),
|
||||
"total_tracks": getattr(record, "total_tracks", None),
|
||||
"poster": record.poster,
|
||||
"vote": record.vote,
|
||||
"total_episode": record.total_episode,
|
||||
|
||||
Reference in New Issue
Block a user