diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index bac0132c..19d1d703 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -1,4 +1,4 @@ -from typing import List, Any, Union +from typing import List, Any from fastapi import APIRouter, Request, BackgroundTasks, Depends, HTTPException, Header from sqlalchemy.orm import Session @@ -35,8 +35,8 @@ async def read_subscribes( return Subscribe.list(db) -@router.get("/{mediaid}", summary="查询订阅", response_model=schemas.Subscribe) -async def subscribe_info_by_id( +@router.get("/media/{mediaid}", summary="查询订阅", response_model=schemas.Subscribe) +async def subscribe_mediaid( mediaid: str, season: int = None, db: Session = Depends(get_db), @@ -54,6 +54,17 @@ async def subscribe_info_by_id( return result if result else Subscribe() +@router.get("/{subscribe_id}", summary="订阅详情", response_model=schemas.Subscribe) +async def read_subscribe( + subscribe_id: int, + db: Session = Depends(get_db), + _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 根据订阅编号查询订阅信息 + """ + return Subscribe.get(db, subscribe_id) + + @router.post("/", summary="新增订阅", response_model=schemas.Response) async def create_subscribe( *, @@ -73,15 +84,17 @@ async def create_subscribe( title = subscribe_in.name else: title = None - result = SubscribeChain().add(mtype=mtype, - title=title, - year=subscribe_in.year, - tmdbid=subscribe_in.tmdbid, - season=subscribe_in.season, - doubanid=subscribe_in.doubanid, - username=current_user.name, - exist_ok=True) - return schemas.Response(success=True if result else False, message=result) + sid, message = SubscribeChain().add(mtype=mtype, + title=title, + year=subscribe_in.year, + tmdbid=subscribe_in.tmdbid, + season=subscribe_in.season, + doubanid=subscribe_in.doubanid, + username=current_user.name, + exist_ok=True) + return schemas.Response(success=True if sid else False, message=message, data={ + "id": sid + }) @router.put("/", summary="更新订阅", response_model=schemas.Subscribe) diff --git a/app/api/servarr.py b/app/api/servarr.py index 2cef834a..9b82aac0 100644 --- a/app/api/servarr.py +++ b/app/api/servarr.py @@ -311,11 +311,11 @@ async def arr_add_movie(apikey: str, movie: RadarrMovie) -> Any: status_code=403, detail="认证失败!", ) - sid = SubscribeChain().add(title=movie.title, - year=movie.year, - mtype=MediaType.MOVIE, - tmdbid=movie.tmdbId, - userid="Seerr") + sid, message = SubscribeChain().add(title=movie.title, + year=movie.year, + mtype=MediaType.MOVIE, + tmdbid=movie.tmdbId, + userid="Seerr") if sid: return { "id": sid @@ -323,7 +323,7 @@ async def arr_add_movie(apikey: str, movie: RadarrMovie) -> Any: else: raise HTTPException( status_code=500, - detail="添加订阅失败!" + detail=f"添加订阅失败:{message}" ) @@ -626,15 +626,16 @@ async def arr_add_series(apikey: str, tv: schemas.SonarrSeries) -> Any: detail="认证失败!", ) sid = 0 + message = "" for season in tv.seasons: if not season.get("monitored"): continue - sid = SubscribeChain().add(title=tv.title, - year=tv.year, - season=season.get("seasonNumber"), - tmdbid=tv.tmdbId, - mtype=MediaType.TV, - userid="Seerr") + sid, message = SubscribeChain().add(title=tv.title, + year=tv.year, + season=season.get("seasonNumber"), + tmdbid=tv.tmdbId, + mtype=MediaType.TV, + userid="Seerr") if sid: return { @@ -643,7 +644,7 @@ async def arr_add_series(apikey: str, tv: schemas.SonarrSeries) -> Any: else: raise HTTPException( status_code=500, - detail="添加订阅失败!" + detail=f"添加订阅失败:{message}" ) diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index fd970d50..61e3625c 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -1,5 +1,6 @@ import re -from typing import Dict, List, Optional, Union +from datetime import datetime +from typing import Dict, List, Optional, Union, Tuple from app.chain import ChainBase from app.chain.download import DownloadChain @@ -38,8 +39,9 @@ class SubscribeChain(ChainBase): season: int = None, userid: str = None, username: str = None, + message: bool = True, exist_ok: bool = False, - **kwargs) -> Optional[int]: + **kwargs) -> Tuple[Optional[int], str]: """ 识别媒体信息并添加订阅 """ @@ -61,7 +63,7 @@ class SubscribeChain(ChainBase): mediainfo: MediaInfo = self.recognize_media(meta=metainfo, mtype=mtype, tmdbid=tmdbid) if not mediainfo: logger.warn(f'未识别到媒体信息,标题:{title},tmdbid:{tmdbid}') - return 0 + return None, "未识别到媒体信息" # 更新媒体图片 self.obtain_images(mediainfo=mediainfo) # 总集数 @@ -76,14 +78,14 @@ class SubscribeChain(ChainBase): tmdbid=mediainfo.tmdb_id) if not mediainfo: logger.error(f"媒体信息识别失败!") - return 0 + return None, "媒体信息识别失败" if not mediainfo.seasons: logger.error(f"媒体信息中没有季集信息,标题:{title},tmdbid:{tmdbid}") - return 0 + return None, "媒体信息中没有季集信息" total_episode = len(mediainfo.seasons.get(season) or []) if not total_episode: logger.error(f'未获取到总集数,标题:{title},tmdbid:{tmdbid}') - return 0 + return None, "未获取到总集数" kwargs.update({ 'total_episode': total_episode }) @@ -96,21 +98,21 @@ class SubscribeChain(ChainBase): sid, err_msg = self.subscribehelper.add(mediainfo, doubanid=doubanid, season=season, **kwargs) if not sid: logger.error(f'{mediainfo.title_year} {err_msg}') - if not exist_ok: + if not exist_ok and message: # 发回原用户 self.post_message(title=f"{mediainfo.title_year}{metainfo.season} " f"添加订阅失败!", text=f"{err_msg}", image=mediainfo.get_message_image(), userid=userid) - else: + elif message: logger.info(f'{mediainfo.title_year}{metainfo.season} 添加订阅成功') # 广而告之 self.post_message(title=f"{mediainfo.title_year}{metainfo.season} 已添加订阅", text=f"评分:{mediainfo.vote_average},来自用户:{username or userid}", image=mediainfo.get_message_image()) # 返回结果 - return sid + return sid, "" def remote_refresh(self, userid: Union[str, int] = None): """ @@ -228,8 +230,10 @@ class SubscribeChain(ChainBase): else: # 未完成下载 logger.info(f'{mediainfo.title_year} 未下载未完整,继续订阅 ...') - # 更新订阅剩余集数 - self.__upate_lack_episodes(lefts=lefts, subscribe=subscribe, mediainfo=mediainfo) + # 更新订阅剩余集数和时间 + update_date = True if downloads else False + self.__upate_lack_episodes(lefts=lefts, subscribe=subscribe, + mediainfo=mediainfo, update_date=update_date) def refresh(self): """ @@ -352,15 +356,18 @@ class SubscribeChain(ChainBase): self.post_message(title=f'{mediainfo.title_year}{meta.season} 已完成订阅', image=mediainfo.get_message_image()) else: + update_date = True if downloads else False # 未完成下载,计算剩余集数 - self.__upate_lack_episodes(lefts=lefts, subscribe=subscribe, mediainfo=mediainfo) + self.__upate_lack_episodes(lefts=lefts, subscribe=subscribe, + mediainfo=mediainfo, update_date=update_date) else: # 未搜索到资源,但本地缺失可能有变化,更新订阅剩余集数 self.__upate_lack_episodes(lefts=no_exists, subscribe=subscribe, mediainfo=mediainfo) def __upate_lack_episodes(self, lefts: Dict[int, Dict[int, NotExistMediaInfo]], subscribe: Subscribe, - mediainfo: MediaInfo): + mediainfo: MediaInfo, + update_date: bool = False): """ 更新订阅剩余集数 """ @@ -371,9 +378,16 @@ class SubscribeChain(ChainBase): left_episodes = season_info.episodes logger.info(f'{mediainfo.title_year} 季 {season} 未搜索到资源,' f'更新缺失集数为{len(left_episodes)} ...') - self.subscribehelper.update(subscribe.id, { - "lack_episode": len(left_episodes) - }) + if update_date: + # 同时更新最后时间 + self.subscribehelper.update(subscribe.id, { + "lack_episode": len(left_episodes), + "last_update": datetime.now().strftime('%Y-%m-%d %H:%M:%S') + }) + else: + self.subscribehelper.update(subscribe.id, { + "lack_episode": len(left_episodes) + }) def remote_list(self, userid: Union[str, int] = None): """ diff --git a/app/db/models/subscribe.py b/app/db/models/subscribe.py index 6a7fe798..fddc058b 100644 --- a/app/db/models/subscribe.py +++ b/app/db/models/subscribe.py @@ -48,6 +48,8 @@ class Subscribe(Base): note = Column(String) # 状态:N-新建, R-订阅中 state = Column(String, nullable=False, index=True, default='N') + # 最后更新时间 + last_update = Column(String) @staticmethod def exists(db: Session, tmdbid: int, season: int = None): diff --git a/app/schemas/subscribe.py b/app/schemas/subscribe.py index 97791eb6..12408879 100644 --- a/app/schemas/subscribe.py +++ b/app/schemas/subscribe.py @@ -41,6 +41,8 @@ class Subscribe(BaseModel): note: Optional[str] = None # 状态:N-新建, R-订阅中 state: Optional[str] = None + # 最后更新时间 + last_update: Optional[str] = None class Config: orm_mode = True