mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
fix: respect explicit subscribe best version settings (#6001)
* fix(subscribe): respect explicit best version settings * fix(subscribe): isolate delete event failures
This commit is contained in:
@@ -19,6 +19,7 @@ from app.db.models.user import User
|
|||||||
from app.db.systemconfig_oper import SystemConfigOper
|
from app.db.systemconfig_oper import SystemConfigOper
|
||||||
from app.db.user_oper import get_current_active_user_async
|
from app.db.user_oper import get_current_active_user_async
|
||||||
from app.helper.server import MoviePilotServerHelper
|
from app.helper.server import MoviePilotServerHelper
|
||||||
|
from app.log import logger
|
||||||
from app.scheduler import Scheduler
|
from app.scheduler import Scheduler
|
||||||
from app.schemas.types import MediaType, EventType, SystemConfigKey
|
from app.schemas.types import MediaType, EventType, SystemConfigKey
|
||||||
|
|
||||||
@@ -41,6 +42,14 @@ def start_subscribe_add(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_subscribe_event_payload(subscribe: Subscribe) -> dict:
|
||||||
|
"""
|
||||||
|
从 ORM 已加载字段构造订阅事件快照,避免异步接口里属性懒加载触发隐式 IO。
|
||||||
|
"""
|
||||||
|
values = subscribe.__dict__
|
||||||
|
return {column.name: values.get(column.name) for column in subscribe.__table__.columns}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/", summary="查询所有订阅", response_model=List[schemas.Subscribe])
|
@router.get("/", summary="查询所有订阅", response_model=List[schemas.Subscribe])
|
||||||
async def read_subscribes(
|
async def read_subscribes(
|
||||||
db: AsyncSession = Depends(get_async_db),
|
db: AsyncSession = Depends(get_async_db),
|
||||||
@@ -349,16 +358,27 @@ async def delete_subscribe_by_mediaid(
|
|||||||
subscribe = await Subscribe.async_get_by_mediaid(db, mediaid)
|
subscribe = await Subscribe.async_get_by_mediaid(db, mediaid)
|
||||||
if subscribe:
|
if subscribe:
|
||||||
delete_subscribes.append(subscribe)
|
delete_subscribes.append(subscribe)
|
||||||
|
delete_events = []
|
||||||
for subscribe in delete_subscribes:
|
for subscribe in delete_subscribes:
|
||||||
# 在删除之前获取订阅信息
|
subscribe_info = build_subscribe_event_payload(subscribe)
|
||||||
subscribe_info = subscribe.to_dict()
|
subscribe_id = subscribe_info.get("id")
|
||||||
subscribe_id = subscribe.id
|
if not subscribe_id:
|
||||||
await Subscribe.async_delete(db, subscribe_id)
|
continue
|
||||||
# 发送事件
|
delete_events.append((subscribe_id, subscribe_info))
|
||||||
await eventmanager.async_send_event(
|
await db.delete(subscribe)
|
||||||
EventType.SubscribeDeleted,
|
try:
|
||||||
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
|
await db.commit()
|
||||||
)
|
except Exception:
|
||||||
|
await db.rollback()
|
||||||
|
raise
|
||||||
|
for subscribe_id, subscribe_info in delete_events:
|
||||||
|
try:
|
||||||
|
await eventmanager.async_send_event(
|
||||||
|
EventType.SubscribeDeleted,
|
||||||
|
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
|
||||||
|
)
|
||||||
|
except Exception as err:
|
||||||
|
logger.error(f"发送订阅删除事件失败:{subscribe_id} - {err}", exc_info=True)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
|
|
||||||
|
|
||||||
@@ -726,8 +746,13 @@ async def delete_subscribe(
|
|||||||
subscribe = await Subscribe.async_get(db, subscribe_id)
|
subscribe = await Subscribe.async_get(db, subscribe_id)
|
||||||
if subscribe:
|
if subscribe:
|
||||||
# 在删除之前获取订阅信息
|
# 在删除之前获取订阅信息
|
||||||
subscribe_info = subscribe.to_dict()
|
subscribe_info = build_subscribe_event_payload(subscribe)
|
||||||
await Subscribe.async_delete(db, subscribe_id)
|
await db.delete(subscribe)
|
||||||
|
try:
|
||||||
|
await db.commit()
|
||||||
|
except Exception:
|
||||||
|
await db.rollback()
|
||||||
|
raise
|
||||||
# 发送事件
|
# 发送事件
|
||||||
await eventmanager.async_send_event(
|
await eventmanager.async_send_event(
|
||||||
EventType.SubscribeDeleted,
|
EventType.SubscribeDeleted,
|
||||||
@@ -735,6 +760,6 @@ async def delete_subscribe(
|
|||||||
)
|
)
|
||||||
# 统计订阅
|
# 统计订阅
|
||||||
MoviePilotServerHelper.sub_done_async(
|
MoviePilotServerHelper.sub_done_async(
|
||||||
{"tmdbid": subscribe.tmdbid, "doubanid": subscribe.doubanid}
|
{"tmdbid": subscribe_info.get("tmdbid"), "doubanid": subscribe_info.get("doubanid")}
|
||||||
)
|
)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
|
|||||||
@@ -582,8 +582,8 @@ class SubscribeChain(ChainBase):
|
|||||||
"include") else kwargs.get("include"),
|
"include") else kwargs.get("include"),
|
||||||
'exclude': self.__get_default_subscribe_config(mtype, "exclude") if not kwargs.get(
|
'exclude': self.__get_default_subscribe_config(mtype, "exclude") if not kwargs.get(
|
||||||
"exclude") else kwargs.get("exclude"),
|
"exclude") else kwargs.get("exclude"),
|
||||||
'best_version': self.__get_default_subscribe_config(mtype, "best_version") if not kwargs.get(
|
'best_version': self.__get_default_subscribe_config(mtype, "best_version")
|
||||||
"best_version") else kwargs.get("best_version"),
|
if kwargs.get("best_version") is None else kwargs.get("best_version"),
|
||||||
'best_version_full': self.__get_default_subscribe_config(mtype, "best_version_full")
|
'best_version_full': self.__get_default_subscribe_config(mtype, "best_version_full")
|
||||||
if kwargs.get("best_version_full") is None else kwargs.get("best_version_full"),
|
if kwargs.get("best_version_full") is None else kwargs.get("best_version_full"),
|
||||||
'search_imdbid': self.__get_default_subscribe_config(mtype, "search_imdbid") if not kwargs.get(
|
'search_imdbid': self.__get_default_subscribe_config(mtype, "search_imdbid") if not kwargs.get(
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ class Subscribe(BaseModel):
|
|||||||
# 下载器
|
# 下载器
|
||||||
downloader: Optional[str] = None
|
downloader: Optional[str] = None
|
||||||
# 是否洗版
|
# 是否洗版
|
||||||
best_version: Optional[int] = 0
|
best_version: Optional[int] = None
|
||||||
# 是否只洗全集整包
|
# 是否只洗全集整包
|
||||||
best_version_full: Optional[int] = 0
|
best_version_full: Optional[int] = None
|
||||||
# 当前优先级
|
# 当前优先级
|
||||||
current_priority: Optional[int] = None
|
current_priority: Optional[int] = None
|
||||||
# 洗版时已下载剧集的优先级状态
|
# 洗版时已下载剧集的优先级状态
|
||||||
|
|||||||
@@ -372,6 +372,25 @@ class SubscribeChainTest(TestCase):
|
|||||||
media_info=SimpleNamespace(type=MediaType.TV, tmdb_id=1, douban_id=None),
|
media_info=SimpleNamespace(type=MediaType.TV, tmdb_id=1, douban_id=None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_default_kwargs_respects_explicit_zero_best_version(self):
|
||||||
|
"""显式关闭洗版时必须保留 0,仅未传值才应用默认订阅规则。"""
|
||||||
|
|
||||||
|
def _default_config(_mtype, key):
|
||||||
|
return 1 if key in {"best_version", "best_version_full"} else None
|
||||||
|
|
||||||
|
with patch.object(SubscribeChain, "_SubscribeChain__get_default_subscribe_config", side_effect=_default_config):
|
||||||
|
explicit = SubscribeChain()._SubscribeChain__get_default_kwargs(
|
||||||
|
MediaType.TV,
|
||||||
|
best_version=0,
|
||||||
|
best_version_full=0,
|
||||||
|
)
|
||||||
|
omitted = SubscribeChain()._SubscribeChain__get_default_kwargs(MediaType.TV)
|
||||||
|
|
||||||
|
self.assertEqual(explicit["best_version"], 0)
|
||||||
|
self.assertEqual(explicit["best_version_full"], 0)
|
||||||
|
self.assertEqual(omitted["best_version"], 1)
|
||||||
|
self.assertEqual(omitted["best_version_full"], 1)
|
||||||
|
|
||||||
def test_format_subscribe_progress_preserves_special_season_zero(self):
|
def test_format_subscribe_progress_preserves_special_season_zero(self):
|
||||||
"""订阅列表展示必须把 S0 当作合法季号,而不是回退到第 1 季。"""
|
"""订阅列表展示必须把 S0 当作合法季号,而不是回退到第 1 季。"""
|
||||||
subscribe = self._build_subscribe(season=0, total_episode=5, lack_episode=2)
|
subscribe = self._build_subscribe(season=0, total_episode=5, lack_episode=2)
|
||||||
|
|||||||
Reference in New Issue
Block a user