diff --git a/app/api/endpoints/subscribe.py b/app/api/endpoints/subscribe.py index 8f69a5b2..7bc97f71 100644 --- a/app/api/endpoints/subscribe.py +++ b/app/api/endpoints/subscribe.py @@ -1,4 +1,4 @@ -from typing import List, Any +from typing import List, Any, Union from fastapi import APIRouter, Request, BackgroundTasks, Depends, HTTPException, Header from sqlalchemy.orm import Session @@ -33,6 +33,25 @@ async def read_subscribes( return Subscribe.list(db) +@router.get("/{mediaid}", summary="查询订阅", response_model=schemas.Subscribe) +async def subscribe_info_by_id( + mediaid: str, + season: int, + db: Session = Depends(get_db), + _: schemas.TokenPayload = Depends(verify_token)) -> Any: + """ + 根据TMDBID或豆瓣ID查询订阅 tmdb:/douban: + """ + if mediaid.startswith("tmdb:"): + result = Subscribe.exists(db, int(mediaid[5:]), season) + elif mediaid.startswith("douban:"): + result = Subscribe.get_by_doubanid(db, mediaid[7:]) + else: + result = None + + return result if result else Subscribe() + + @router.post("/", summary="新增订阅", response_model=schemas.Response) async def create_subscribe( *, @@ -42,8 +61,24 @@ async def create_subscribe( """ 新增订阅 """ - result = SubscribeChain().add(**subscribe_in.dict()) - return schemas.Response(success=result) + # 类型转换 + if subscribe_in.type: + mtype = MediaType.TV if subscribe_in.type == "电视剧" else MediaType.MOVIE + else: + mtype = None + # 标题转换 + if subscribe_in.name: + 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, + exist_ok=True) + return schemas.Response(success=True if result else False, message=result) @router.put("/", summary="更新订阅", response_model=schemas.Subscribe) @@ -79,6 +114,24 @@ async def delete_subscribe( return schemas.Response(success=True) +@router.delete("/{mediaid}", summary="删除订阅", response_model=schemas.Response) +async def delete_subscribe_by_id( + mediaid: str, + season: int, + db: Session = Depends(get_db), + _: schemas.TokenPayload = Depends(verify_token) +) -> Any: + """ + 根据TMDBID或豆瓣ID删除订阅 tmdb:/douban: + """ + if mediaid.startswith("tmdb:"): + Subscribe().delete_by_tmdbid(db, int(mediaid[5:]), season) + elif mediaid.startswith("douban:"): + Subscribe().delete_by_doubanid(db, mediaid[7:]) + + return schemas.Response(success=True) + + @router.post("/seerr", summary="OverSeerr/JellySeerr通知订阅", response_model=schemas.Response) async def seerr_subscribe(request: Request, background_tasks: BackgroundTasks, authorization: str = Header(None)) -> Any: diff --git a/app/chain/subscribe.py b/app/chain/subscribe.py index 96caee5a..fd970d50 100644 --- a/app/chain/subscribe.py +++ b/app/chain/subscribe.py @@ -34,6 +34,7 @@ class SubscribeChain(ChainBase): def add(self, title: str, year: str, mtype: MediaType = None, tmdbid: int = None, + doubanid: str = None, season: int = None, userid: str = None, username: str = None, @@ -60,7 +61,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 False + return 0 # 更新媒体图片 self.obtain_images(mediainfo=mediainfo) # 总集数 @@ -75,14 +76,14 @@ class SubscribeChain(ChainBase): tmdbid=mediainfo.tmdb_id) if not mediainfo: logger.error(f"媒体信息识别失败!") - return False + return 0 if not mediainfo.seasons: logger.error(f"媒体信息中没有季集信息,标题:{title},tmdbid:{tmdbid}") - return False + return 0 total_episode = len(mediainfo.seasons.get(season) or []) if not total_episode: logger.error(f'未获取到总集数,标题:{title},tmdbid:{tmdbid}') - return False + return 0 kwargs.update({ 'total_episode': total_episode }) @@ -92,7 +93,7 @@ class SubscribeChain(ChainBase): 'lack_episode': kwargs.get('total_episode') }) # 添加订阅 - sid, err_msg = self.subscribehelper.add(mediainfo, season=season, **kwargs) + 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: diff --git a/app/core/context.py b/app/core/context.py index 6e17bf59..b45af685 100644 --- a/app/core/context.py +++ b/app/core/context.py @@ -109,6 +109,8 @@ class MediaInfo: title: Optional[str] = None # 年份 year: Optional[str] = None + # 季 + season: Optional[int] = None # TMDB ID tmdb_id: Optional[int] = None # IMDB ID @@ -334,7 +336,10 @@ class MediaInfo: self.type = MediaType.MOVIE if info.get("type") == "movie" else MediaType.TV # 标题 if not self.title: - self.title = MetaInfo(info.get("title")).name + self.title = info.get("title") + # 识别标题中的季 + meta = MetaInfo(self.title) + self.season = meta.begin_season # 原语种标题 if not self.original_title: self.original_title = info.get("original_title") diff --git a/app/db/models/subscribe.py b/app/db/models/subscribe.py index 6baee87c..6a7fe798 100644 --- a/app/db/models/subscribe.py +++ b/app/db/models/subscribe.py @@ -2,6 +2,7 @@ from sqlalchemy import Column, Integer, String, Sequence from sqlalchemy.orm import Session from app.db.models import Base +from app.schemas import MediaType class Subscribe(Base): @@ -19,8 +20,8 @@ class Subscribe(Base): keyword = Column(String) tmdbid = Column(Integer, index=True) imdbid = Column(String) - tvdbid = Column(Integer, index=True) - doubanid = Column(String) + tvdbid = Column(Integer) + doubanid = Column(String, index=True) # 季号 season = Column(Integer) # 海报 @@ -60,7 +61,10 @@ class Subscribe(Base): return db.query(Subscribe).filter(Subscribe.state == state).all() @staticmethod - def get_by_tmdbid(db: Session, tmdbid: int): + def get_by_tmdbid(db: Session, tmdbid: int, season: int = None): + if season: + return db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid, + Subscribe.season == season).all() return db.query(Subscribe).filter(Subscribe.tmdbid == tmdbid).all() @staticmethod @@ -68,5 +72,17 @@ class Subscribe(Base): return db.query(Subscribe).filter(Subscribe.name == title).first() @staticmethod - def get_by_tvdbid(db: Session, tvdbid: int): - return db.query(Subscribe).filter(Subscribe.tvdbid == tvdbid).first() + def get_by_doubanid(db: Session, doubanid: str): + return db.query(Subscribe).filter(Subscribe.doubanid == doubanid).first() + + def delete_by_tmdbid(self, db: Session, tmdbid: int, season: int): + subscrbies = self.get_by_tmdbid(db, tmdbid, season) + for subscrbie in subscrbies: + subscrbie.delete(db, subscrbie.id) + return True + + def delete_by_doubanid(self, db: Session, doubanid: str): + subscribe = self.get_by_doubanid(db, doubanid) + if subscribe: + subscribe.delete(db, subscribe.id) + return True diff --git a/app/db/models/systemconfig.py b/app/db/models/systemconfig.py index 99b38eb9..9fe299bb 100644 --- a/app/db/models/systemconfig.py +++ b/app/db/models/systemconfig.py @@ -18,7 +18,8 @@ class SystemConfig(Base): def get_by_key(db: Session, key: str): return db.query(SystemConfig).filter(SystemConfig.key == key).first() - @staticmethod - def delete_by_key(db: Session, key: str): - db.query(SystemConfig).filter(SystemConfig.key == key).delete() - db.commit() + def delete_by_key(self, db: Session, key: str): + systemconfig = self.get_by_key(db, key) + if systemconfig: + systemconfig.delete(db, systemconfig.id) + return True diff --git a/app/db/models/user.py b/app/db/models/user.py index 98764be8..ce258508 100644 --- a/app/db/models/user.py +++ b/app/db/models/user.py @@ -37,6 +37,8 @@ class User(Base): def get_by_name(db: Session, name: str): return db.query(User).filter(User.name == name).first() - @staticmethod - def delete_by_name(db: Session, name: str): - return db.query(User).filter(User.name == name).delete() + def delete_by_name(self, db: Session, name: str): + user = self.get_by_name(db, name) + if user: + user.delete(db, user.id) + return True diff --git a/app/db/subscribe_oper.py b/app/db/subscribe_oper.py index c3467662..a11610cb 100644 --- a/app/db/subscribe_oper.py +++ b/app/db/subscribe_oper.py @@ -14,22 +14,23 @@ class SubscribeOper(DbOper): """ 新增订阅 """ - subscribe = Subscribe(name=mediainfo.title, - year=mediainfo.year, - type=mediainfo.type.value, - tmdbid=mediainfo.tmdb_id, - imdbid=mediainfo.imdb_id, - tvdbid=mediainfo.tvdb_id, - poster=mediainfo.get_poster_image(), - backdrop=mediainfo.get_backdrop_image(), - vote=mediainfo.vote_average, - description=mediainfo.overview, - **kwargs) - if not subscribe.exists(self._db, tmdbid=mediainfo.tmdb_id, season=kwargs.get('season')): + subscribe = Subscribe.exists(self._db, tmdbid=mediainfo.tmdb_id, season=kwargs.get('season')) + if not subscribe: + subscribe = Subscribe(name=mediainfo.title, + year=mediainfo.year, + type=mediainfo.type.value, + tmdbid=mediainfo.tmdb_id, + imdbid=mediainfo.imdb_id, + tvdbid=mediainfo.tvdb_id, + poster=mediainfo.get_poster_image(), + backdrop=mediainfo.get_backdrop_image(), + vote=mediainfo.vote_average, + description=mediainfo.overview, + **kwargs) subscribe.create(self._db) return subscribe.id, "新增订阅成功" else: - return 0, "订阅已存在" + return subscribe.id, "订阅已存在" def get(self, sid: int) -> Subscribe: """ diff --git a/app/modules/themoviedb/__init__.py b/app/modules/themoviedb/__init__.py index a604dee6..a8e6361f 100644 --- a/app/modules/themoviedb/__init__.py +++ b/app/modules/themoviedb/__init__.py @@ -583,6 +583,8 @@ class TheMovieDbModule(_ModuleBase): images = self.tmdb.get_tv_images(mediainfo.tmdb_id, season=1) if not images: return mediainfo + if isinstance(images, list): + images = images[0] # 背景图 if not mediainfo.backdrop_path: backdrops = images.get("backdrops") diff --git a/app/scheduler.py b/app/scheduler.py index c60a2598..dc998098 100644 --- a/app/scheduler.py +++ b/app/scheduler.py @@ -37,7 +37,6 @@ class Scheduler(metaclass=Singleton): }) def __init__(self): - return # CookieCloud定时同步 if settings.COOKIECLOUD_INTERVAL: self._scheduler.add_job(CookieCloudChain().process, diff --git a/app/schemas/context.py b/app/schemas/context.py index 2b581b9d..c1ec23fc 100644 --- a/app/schemas/context.py +++ b/app/schemas/context.py @@ -56,6 +56,8 @@ class MediaInfo(BaseModel): title: Optional[str] = None # 年份 year: Optional[str] = None + # 季 + season: Optional[int] = None # TMDB ID tmdb_id: Optional[int] = None # IMDB ID diff --git a/app/schemas/subscribe.py b/app/schemas/subscribe.py index bebd37c0..97791eb6 100644 --- a/app/schemas/subscribe.py +++ b/app/schemas/subscribe.py @@ -4,43 +4,43 @@ from pydantic import BaseModel class Subscribe(BaseModel): - id: Optional[int] + id: Optional[int] = None # 订阅名称 - name: Optional[str] + name: Optional[str] = None # 订阅年份 - year: Optional[str] + year: Optional[str] = None # 订阅类型 电影/电视剧 - type: Optional[str] + type: Optional[str] = None # 搜索关键字 - keyword: Optional[str] - tmdbid: Optional[int] - doubanid: Optional[str] + keyword: Optional[str] = None + tmdbid: Optional[int] = None + doubanid: Optional[str] = None # 季号 - season: Optional[int] + season: Optional[int] = None # 海报 - poster: Optional[str] + poster: Optional[str] = None # 背景图 - backdrop: Optional[str] + backdrop: Optional[str] = None # 评分 - vote: Optional[int] + vote: Optional[int] = 0 # 描述 - description: Optional[str] + description: Optional[str] = None # 过滤规则 - filter: Optional[str] + filter: Optional[str] = None # 包含 - include: Optional[str] + include: Optional[str] = None # 排除 - exclude: Optional[str] + exclude: Optional[str] = None # 总集数 - total_episode: Optional[int] + total_episode: Optional[int] = 0 # 开始集数 - start_episode: Optional[int] + start_episode: Optional[int] = 0 # 缺失集数 - lack_episode: Optional[int] + lack_episode: Optional[int] = 0 # 附加信息 - note: Optional[str] + note: Optional[str] = None # 状态:N-新建, R-订阅中 - state: Optional[str] + state: Optional[str] = None class Config: orm_mode = True