mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-06-26 02:02:39 +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.user_oper import get_current_active_user_async
|
||||
from app.helper.server import MoviePilotServerHelper
|
||||
from app.log import logger
|
||||
from app.scheduler import Scheduler
|
||||
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])
|
||||
async def read_subscribes(
|
||||
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)
|
||||
if subscribe:
|
||||
delete_subscribes.append(subscribe)
|
||||
delete_events = []
|
||||
for subscribe in delete_subscribes:
|
||||
# 在删除之前获取订阅信息
|
||||
subscribe_info = subscribe.to_dict()
|
||||
subscribe_id = subscribe.id
|
||||
await Subscribe.async_delete(db, subscribe_id)
|
||||
# 发送事件
|
||||
await eventmanager.async_send_event(
|
||||
EventType.SubscribeDeleted,
|
||||
{"subscribe_id": subscribe_id, "subscribe_info": subscribe_info},
|
||||
)
|
||||
subscribe_info = build_subscribe_event_payload(subscribe)
|
||||
subscribe_id = subscribe_info.get("id")
|
||||
if not subscribe_id:
|
||||
continue
|
||||
delete_events.append((subscribe_id, subscribe_info))
|
||||
await db.delete(subscribe)
|
||||
try:
|
||||
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)
|
||||
|
||||
|
||||
@@ -726,8 +746,13 @@ async def delete_subscribe(
|
||||
subscribe = await Subscribe.async_get(db, subscribe_id)
|
||||
if subscribe:
|
||||
# 在删除之前获取订阅信息
|
||||
subscribe_info = subscribe.to_dict()
|
||||
await Subscribe.async_delete(db, subscribe_id)
|
||||
subscribe_info = build_subscribe_event_payload(subscribe)
|
||||
await db.delete(subscribe)
|
||||
try:
|
||||
await db.commit()
|
||||
except Exception:
|
||||
await db.rollback()
|
||||
raise
|
||||
# 发送事件
|
||||
await eventmanager.async_send_event(
|
||||
EventType.SubscribeDeleted,
|
||||
@@ -735,6 +760,6 @@ async def delete_subscribe(
|
||||
)
|
||||
# 统计订阅
|
||||
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)
|
||||
|
||||
@@ -582,8 +582,8 @@ class SubscribeChain(ChainBase):
|
||||
"include") else kwargs.get("include"),
|
||||
'exclude': self.__get_default_subscribe_config(mtype, "exclude") if not kwargs.get(
|
||||
"exclude") else kwargs.get("exclude"),
|
||||
'best_version': self.__get_default_subscribe_config(mtype, "best_version") if not kwargs.get(
|
||||
"best_version") else kwargs.get("best_version"),
|
||||
'best_version': self.__get_default_subscribe_config(mtype, "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")
|
||||
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(
|
||||
|
||||
@@ -62,9 +62,9 @@ class Subscribe(BaseModel):
|
||||
# 下载器
|
||||
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
|
||||
# 洗版时已下载剧集的优先级状态
|
||||
|
||||
@@ -372,6 +372,25 @@ class SubscribeChainTest(TestCase):
|
||||
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):
|
||||
"""订阅列表展示必须把 S0 当作合法季号,而不是回退到第 1 季。"""
|
||||
subscribe = self._build_subscribe(season=0, total_episode=5, lack_episode=2)
|
||||
|
||||
Reference in New Issue
Block a user