diff --git a/app/api/endpoints/login.py b/app/api/endpoints/login.py index e34da0de..5307cc12 100644 --- a/app/api/endpoints/login.py +++ b/app/api/endpoints/login.py @@ -38,7 +38,12 @@ async def login_access_token( raise HTTPException(status_code=401, detail="用户名或密码不正确") else: logger.info(f"辅助认证成功,用户信息: {token}") - user = schemas.User(id=-1, name=form_data.username, is_active=True, is_superuser=False) + # 加入用户信息表 + user = User.get_by_name(db=db, name=form_data.username) + if not user: + logger.info(f"用户不存在,创建用户: {form_data.username}") + user = User(name=form_data.username, is_active=True, is_superuser=False) + user.create(db) elif not user.is_active: raise HTTPException(status_code=403, detail="用户未启用") access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES) diff --git a/app/api/endpoints/search.py b/app/api/endpoints/search.py index 2930ee64..e754274c 100644 --- a/app/api/endpoints/search.py +++ b/app/api/endpoints/search.py @@ -1,33 +1,47 @@ from typing import List, Any -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, HTTPException from app import schemas +from app.chain.douban import DoubanChain from app.chain.search import SearchChain +from app.core.context import Context from app.core.security import verify_token from app.schemas.types import MediaType router = APIRouter() -@router.get("/tmdbid", summary="精确搜索资源", response_model=List[schemas.Context]) -async def search_by_tmdbid(tmdbid: int, +@router.get("/media/{mediaid}", summary="精确搜索资源", response_model=List[schemas.Context]) +async def search_by_tmdbid(mediaid: str, mtype: str = None, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ - 根据TMDBID精确搜索站点资源 + 根据TMDBID/豆瓣ID精确搜索站点资源 tmdb:/douban:/ """ - if mtype: - mtype = MediaType.TV if mtype == MediaType.TV.value else MediaType.MOVIE - torrents = SearchChain().search_by_tmdbid(tmdbid=tmdbid, mtype=mtype) + if mediaid.startswith("tmdb:"): + tmdbid = int(mediaid.replace("tmdb:", "")) + if mtype: + mtype = MediaType(mtype) + torrents = SearchChain().search_by_tmdbid(tmdbid=tmdbid, mtype=mtype) + elif mediaid.startswith("douban:"): + doubanid = mediaid.replace("douban:", "") + # 识别豆瓣信息 + context = DoubanChain().recognize_by_doubanid(doubanid) + if not context or not context.media_info or not context.media_info.tmdb_id: + raise HTTPException(status_code=404, detail="无法识别TMDB媒体信息!") + torrents = SearchChain().search_by_tmdbid(tmdbid=context.media_info.tmdb_id, + mtype=context.media_info.type) + else: + return [] return [torrent.to_dict() for torrent in torrents] -@router.get("/title", summary="模糊搜索资源", response_model=List[schemas.TorrentInfo]) -async def search_by_title(title: str, +@router.get("/title/{keyword}", summary="模糊搜索资源", response_model=List[schemas.Context]) +async def search_by_title(keyword: str, _: schemas.TokenPayload = Depends(verify_token)) -> Any: """ 根据名称模糊搜索站点资源 """ - torrents = SearchChain().search_by_title(title=title) - return [torrent.to_dict() for torrent in torrents] + torrents = SearchChain().search_by_title(title=keyword) + return [Context(torrent_info=torrent).to_dict() for torrent in torrents] diff --git a/app/chain/search.py b/app/chain/search.py index f449491c..d3c8b6d2 100644 --- a/app/chain/search.py +++ b/app/chain/search.py @@ -26,7 +26,7 @@ class SearchChain(ChainBase): self.siteshelper = SitesHelper() self.progress = ProgressHelper() - def search_by_tmdbid(self, tmdbid: int, mtype: str = None) -> Optional[List[Context]]: + def search_by_tmdbid(self, tmdbid: int, mtype: MediaType = None) -> Optional[List[Context]]: """ 根据TMDB ID搜索资源,精确匹配,但不不过滤本地存在的资源 :param tmdbid: TMDB ID diff --git a/app/core/meta/metabase.py b/app/core/meta/metabase.py index 3f07cfea..c8fade6b 100644 --- a/app/core/meta/metabase.py +++ b/app/core/meta/metabase.py @@ -1,3 +1,4 @@ +from dataclasses import dataclass from typing import Union, Optional, List import cn2an @@ -7,6 +8,7 @@ from app.utils.string import StringUtils from app.schemas.types import MediaType +@dataclass class MetaBase(object): """ 媒体信息基类 diff --git a/app/schemas/context.py b/app/schemas/context.py index 849edc86..db3db914 100644 --- a/app/schemas/context.py +++ b/app/schemas/context.py @@ -83,42 +83,42 @@ class MediaInfo(BaseModel): # 海报图片 poster_path: Optional[str] = None # 评分 - vote_average: int = 0 + vote_average: Optional[int] = 0 # 描述 overview: Optional[str] = None # 二级分类 - category: str = "" + category: Optional[str] = "" # 季季集清单 seasons: Dict[int, list] = {} # 季详情 season_info: List[dict] = [] # 别名和译名 - names: list = [] + names: Optional[list] = [] # 演员 - actors: list = [] + actors: Optional[list] = [] # 导演 - directors: list = [] + directors: Optional[list] = [] # 其它TMDB属性 - adult: bool = False - created_by: list = [] - episode_run_time: list = [] - genres: list = [] + adult: Optional[bool] = False + created_by: Optional[list] = [] + episode_run_time: Optional[list] = [] + genres: Optional[list] = [] first_air_date: Optional[str] = None homepage: Optional[str] = None - languages: list = [] + languages: Optional[list] = [] last_air_date: Optional[str] = None - networks: list = [] - number_of_episodes: int = 0 - number_of_seasons: int = 0 - origin_country: list = [] + networks: Optional[list] = [] + number_of_episodes: Optional[int] = 0 + number_of_seasons: Optional[int] = 0 + origin_country: Optional[list] = [] original_name: Optional[str] = None - production_companies: list = [] - production_countries: list = [] - spoken_languages: list = [] + production_companies: Optional[list] = [] + production_countries: Optional[list] = [] + spoken_languages: Optional[list] = [] status: Optional[str] = None tagline: Optional[str] = None - vote_count: int = 0 - popularity: int = 0 + vote_count: Optional[int] = 0 + popularity: Optional[int] = 0 runtime: Optional[int] = None next_episode_to_air: Optional[str] = None @@ -136,27 +136,27 @@ class TorrentInfo(BaseModel): # 站点UA site_ua: Optional[str] = None # 站点是否使用代理 - site_proxy: bool = False + site_proxy: Optional[bool] = False # 站点优先级 - site_order: int = 0 + site_order: Optional[int] = 0 # 种子名称 title: Optional[str] = None # 种子副标题 description: Optional[str] = None # IMDB ID - imdbid: str = None + imdbid: Optional[str] = None # 种子链接 enclosure: Optional[str] = None # 详情页面 page_url: Optional[str] = None # 种子大小 - size: float = 0 + size: Optional[float] = 0 # 做种者 - seeders: int = 0 + seeders: Optional[int] = 0 # 下载者 - peers: int = 0 + peers: Optional[int] = 0 # 完成者 - grabs: int = 0 + grabs: Optional[int] = 0 # 发布时间 pubdate: Optional[str] = None # 已过时间 @@ -166,11 +166,11 @@ class TorrentInfo(BaseModel): # 下载因子 downloadvolumefactor: Optional[float] = None # HR - hit_and_run: bool = False + hit_and_run: Optional[bool] = False # 种子标签 labels: Optional[list] = [] # 种子优先级 - pri_order: int = 0 + pri_order: Optional[int] = 0 class Context(BaseModel): @@ -178,11 +178,11 @@ class Context(BaseModel): 上下文 """ # 元数据 - meta_info: Optional[MetaInfo] + meta_info: Optional[MetaInfo] = None # 媒体信息 - media_info: Optional[MediaInfo] + media_info: Optional[MediaInfo] = None # 种子信息 - torrent_info: Optional[TorrentInfo] + torrent_info: Optional[TorrentInfo] = None class TransferTorrent(BaseModel): @@ -221,11 +221,11 @@ class TransferInfo(BaseModel): # 转移后路径 target_path: Optional[Path] = None # 处理文件数 - file_count: int = 0 + file_count: Optional[int] = 0 # 总文件大小 - total_size: float = 0 + total_size: Optional[float] = 0 # 失败清单 - fail_list: list = [] + fail_list: Optional[list] = [] # 错误信息 message: Optional[str] = None @@ -235,9 +235,9 @@ class ExistMediaInfo(BaseModel): 媒体服务器存在媒体信息 """ # 类型 电影、电视剧 - type: MediaType + type: Optional[MediaType] # 季 - seasons: Dict[int, list] = {} + seasons: Optional[Dict[int, list]] = {} class NotExistMediaInfo(BaseModel): @@ -259,7 +259,7 @@ class RefreshMediaItem(BaseModel): 媒体库刷新信息 """ # 标题 - title: str + title: Optional[str] = None # 年份 year: Optional[str] = None # 类型