diff --git a/app/api/endpoints/douban.py b/app/api/endpoints/douban.py index f2d6ebfe..c8e45db4 100644 --- a/app/api/endpoints/douban.py +++ b/app/api/endpoints/douban.py @@ -32,7 +32,7 @@ async def sync_douban( return schemas.Response(success=True, message="任务已启动") -@router.get("/doubanid", response_model=schemas.Context) +@router.get("/id", response_model=schemas.Context) async def recognize_doubanid(doubanid: str, _: User = Depends(get_current_active_user)) -> Any: """ @@ -43,7 +43,7 @@ async def recognize_doubanid(doubanid: str, return context.to_dict() -@router.get("/doubaninfo", response_model=schemas.MediaInfo) +@router.get("/info", response_model=schemas.MediaInfo) async def douban_info(doubanid: str) -> Any: """ 根据豆瓣ID查询豆瓣媒体信息 @@ -55,7 +55,7 @@ async def douban_info(doubanid: str) -> Any: return schemas.MediaInfo() -@router.get("/doubanmovies", response_model=List[schemas.MediaInfo]) +@router.get("/movies", response_model=List[schemas.MediaInfo]) async def douban_movies(sort: str = "R", tags: str = "", start: int = 0, @@ -71,7 +71,7 @@ async def douban_movies(sort: str = "R", return [MediaInfo(douban_info=movie).to_dict() for movie in movies] -@router.get("/doubantvs", response_model=List[schemas.MediaInfo]) +@router.get("/tvs", response_model=List[schemas.MediaInfo]) async def douban_tvs(sort: str = "R", tags: str = "", start: int = 0, @@ -87,7 +87,7 @@ async def douban_tvs(sort: str = "R", return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] -@router.get("/top250", response_model=List[schemas.MediaInfo]) +@router.get("/movie_top250", response_model=List[schemas.MediaInfo]) async def movie_top250(page: int = 1, count: int = 30, _: User = Depends(get_current_active_user)) -> Any: @@ -96,3 +96,25 @@ async def movie_top250(page: int = 1, """ movies = DoubanChain().movie_top250(page=page, count=count) return [MediaInfo(douban_info=movie).to_dict() for movie in movies] + + +@router.get("/tv_weekly_chinese", response_model=List[schemas.MediaInfo]) +async def tv_weekly_chinese(page: int = 1, + count: int = 30, + _: User = Depends(get_current_active_user)) -> Any: + """ + 中国每周剧集口碑榜 + """ + tvs = DoubanChain().tv_weekly_chinese(page=page, count=count) + return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] + + +@router.get("/tv_weekly_global", response_model=List[schemas.MediaInfo]) +async def tv_weekly_global(page: int = 1, + count: int = 30, + _: User = Depends(get_current_active_user)) -> Any: + """ + 全球每周剧集口碑榜 + """ + tvs = DoubanChain().tv_weekly_global(page=page, count=count) + return [MediaInfo(douban_info=tv).to_dict() for tv in tvs] diff --git a/app/api/endpoints/tmdb.py b/app/api/endpoints/tmdb.py index 7994ffed..dde14298 100644 --- a/app/api/endpoints/tmdb.py +++ b/app/api/endpoints/tmdb.py @@ -12,7 +12,7 @@ from app.schemas.types import MediaType router = APIRouter() -@router.get("/tmdbinfo", response_model=schemas.MediaInfo) +@router.get("/info", response_model=schemas.MediaInfo) async def tmdb_info(tmdbid: int, type_name: str) -> Any: """ 根据TMDBID查询themoviedb媒体信息 @@ -25,7 +25,7 @@ async def tmdb_info(tmdbid: int, type_name: str) -> Any: return MediaInfo(tmdb_info=tmdbinfo).to_dict() -@router.get("/tmdbmovies", response_model=List[schemas.MediaInfo]) +@router.get("/movies", response_model=List[schemas.MediaInfo]) async def tmdb_movies(sort_by: str = "popularity.desc", with_genres: str = "", with_original_language: str = "", @@ -44,7 +44,7 @@ async def tmdb_movies(sort_by: str = "popularity.desc", return [MediaInfo(tmdb_info=movie).to_dict() for movie in movies] -@router.get("/tmdbtvs", response_model=List[schemas.MediaInfo]) +@router.get("/tvs", response_model=List[schemas.MediaInfo]) async def tmdb_tvs(sort_by: str = "popularity.desc", with_genres: str = "", with_original_language: str = "", @@ -61,3 +61,15 @@ async def tmdb_tvs(sort_by: str = "popularity.desc", if not tvs: return [] return [MediaInfo(tmdb_info=tv).to_dict() for tv in tvs] + + +@router.get("/trending", response_model=List[schemas.MediaInfo]) +async def tmdb_trending(page: int = 1, + _: User = Depends(get_current_active_user)) -> Any: + """ + 浏览TMDB剧集信息 + """ + infos = TmdbChain().tmdb_trending(page=page) + if not infos: + return [] + return [MediaInfo(tmdb_info=info).to_dict() for info in infos] diff --git a/app/chain/__init__.py b/app/chain/__init__.py index 04169889..ebc4199f 100644 --- a/app/chain/__init__.py +++ b/app/chain/__init__.py @@ -27,7 +27,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): self.modulemanager = ModuleManager() self.eventmanager = EventManager() - def __run_module(self, method: str, *args, **kwargs) -> Any: + def run_module(self, method: str, *args, **kwargs) -> Any: """ 运行包含该方法的所有模块,然后返回结果 """ @@ -70,7 +70,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param subtitle: 副标题 :return: 处理后的标题、副标题,该方法可被多个模块同时处理 """ - return self.__run_module("prepare_recognize", title=title, subtitle=subtitle) + return self.run_module("prepare_recognize", title=title, subtitle=subtitle) def recognize_media(self, meta: MetaBase = None, mtype: MediaType = None, @@ -82,7 +82,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param tmdbid: tmdbid :return: 识别的媒体信息,包括剧集信息 """ - return self.__run_module("recognize_media", meta=meta, mtype=mtype, tmdbid=tmdbid) + return self.run_module("recognize_media", meta=meta, mtype=mtype, tmdbid=tmdbid) def obtain_image(self, mediainfo: MediaInfo) -> Optional[MediaInfo]: """ @@ -90,7 +90,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param mediainfo: 识别的媒体信息 :return: 更新后的媒体信息 """ - return self.__run_module("obtain_image", mediainfo=mediainfo) + return self.run_module("obtain_image", mediainfo=mediainfo) def douban_info(self, doubanid: str) -> Optional[dict]: """ @@ -98,7 +98,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param doubanid: 豆瓣ID :return: 豆瓣信息 """ - return self.__run_module("douban_info", doubanid=doubanid) + return self.run_module("douban_info", doubanid=doubanid) def tvdb_info(self, tvdbid: int) -> Optional[dict]: """ @@ -106,7 +106,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param tvdbid: int :return: TVDB信息 """ - return self.__run_module("tvdb_info", tvdbid=tvdbid) + return self.run_module("tvdb_info", tvdbid=tvdbid) def tmdb_info(self, tmdbid: int, mtype: MediaType) -> Optional[dict]: """ @@ -115,7 +115,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param mtype: 媒体类型 :return: TVDB信息 """ - return self.__run_module("tmdb_info", tmdbid=tmdbid, mtype=mtype) + return self.run_module("tmdb_info", tmdbid=tmdbid, mtype=mtype) def message_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]: """ @@ -128,7 +128,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param args: 参数 :return: 消息内容、用户ID """ - return self.__run_module("message_parser", body=body, form=form, args=args) + return self.run_module("message_parser", body=body, form=form, args=args) def webhook_parser(self, body: Any, form: Any, args: Any) -> Optional[dict]: """ @@ -138,7 +138,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param args: 请求参数 :return: 字典,解析为消息时需要包含:title、text、image """ - return self.__run_module("webhook_parser", body=body, form=form, args=args) + return self.run_module("webhook_parser", body=body, form=form, args=args) def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]: """ @@ -146,7 +146,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param meta: 识别的元数据 :reutrn: 媒体信息列表 """ - return self.__run_module("search_medias", meta=meta) + return self.run_module("search_medias", meta=meta) def search_torrents(self, mediainfo: Optional[MediaInfo], sites: List[CommentedMap], keyword: str = None) -> Optional[List[TorrentInfo]]: @@ -157,7 +157,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param keyword: 搜索关键词,如有按关键词搜索,否则按媒体信息名称搜索 :reutrn: 资源列表 """ - return self.__run_module("search_torrents", mediainfo=mediainfo, sites=sites, keyword=keyword) + return self.run_module("search_torrents", mediainfo=mediainfo, sites=sites, keyword=keyword) def refresh_torrents(self, sites: List[CommentedMap]) -> Optional[List[TorrentInfo]]: """ @@ -165,7 +165,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param sites: 站点列表 :reutrn: 种子资源列表 """ - return self.__run_module("refresh_torrents", sites=sites) + return self.run_module("refresh_torrents", sites=sites) def filter_torrents(self, torrent_list: List[TorrentInfo], season_episodes: Dict[int, list] = None) -> List[TorrentInfo]: @@ -175,7 +175,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param season_episodes: 季集数过滤 {season:[episodes]} :return: 过滤后的资源列表,添加资源优先级 """ - return self.__run_module("filter_torrents", torrent_list=torrent_list, season_episodes=season_episodes) + return self.run_module("filter_torrents", torrent_list=torrent_list, season_episodes=season_episodes) def download(self, torrent_path: Path, cookie: str, episodes: Set[int] = None) -> Optional[Tuple[Optional[str], str]]: @@ -186,7 +186,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param episodes: 需要下载的集数 :return: 种子Hash,错误信息 """ - return self.__run_module("download", torrent_path=torrent_path, cookie=cookie, episodes=episodes) + return self.run_module("download", torrent_path=torrent_path, cookie=cookie, episodes=episodes) def download_added(self, context: Context, torrent_path: Path) -> None: """ @@ -195,7 +195,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param torrent_path: 种子文件地址 :return: None,该方法可被多个模块同时处理 """ - return self.__run_module("download_added", context=context, torrent_path=torrent_path) + return self.run_module("download_added", context=context, torrent_path=torrent_path) def list_torrents(self, status: TorrentStatus = None, hashs: Union[list, str] = None) -> Optional[List[Union[TransferTorrent, DownloadingTorrent]]]: @@ -205,7 +205,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param hashs: 种子Hash :return: 下载器中符合状态的种子列表 """ - return self.__run_module("list_torrents", status=status, hashs=hashs) + return self.run_module("list_torrents", status=status, hashs=hashs) def transfer(self, path: Path, mediainfo: MediaInfo) -> Optional[TransferInfo]: """ @@ -214,7 +214,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param mediainfo: 识别的媒体信息 :return: {path, target_path, message} """ - return self.__run_module("transfer", path=path, mediainfo=mediainfo) + return self.run_module("transfer", path=path, mediainfo=mediainfo) def transfer_completed(self, hashs: Union[str, list], transinfo: TransferInfo) -> None: """ @@ -222,7 +222,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param hashs: 种子Hash :param transinfo: 转移信息 """ - return self.__run_module("transfer_completed", hashs=hashs, transinfo=transinfo) + return self.run_module("transfer_completed", hashs=hashs, transinfo=transinfo) def remove_torrents(self, hashs: Union[str, list]) -> bool: """ @@ -230,7 +230,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param hashs: 种子Hash :return: bool """ - return self.__run_module("remove_torrents", hashs=hashs) + return self.run_module("remove_torrents", hashs=hashs) def media_exists(self, mediainfo: MediaInfo) -> Optional[ExistMediaInfo]: """ @@ -238,7 +238,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param mediainfo: 识别的媒体信息 :return: 如不存在返回None,存在时返回信息,包括每季已存在所有集{type: movie/tv, seasons: {season: [episodes]}} """ - return self.__run_module("media_exists", mediainfo=mediainfo) + return self.run_module("media_exists", mediainfo=mediainfo) def refresh_mediaserver(self, mediainfo: MediaInfo, file_path: Path) -> Optional[bool]: """ @@ -247,7 +247,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param file_path: 文件路径 :return: 成功或失败 """ - return self.__run_module("refresh_mediaserver", mediainfo=mediainfo, file_path=file_path) + return self.run_module("refresh_mediaserver", mediainfo=mediainfo, file_path=file_path) def post_message(self, title: str, text: str = None, image: str = None, userid: Union[str, int] = None) -> Optional[bool]: @@ -259,7 +259,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param userid: 用户ID :return: 成功或失败 """ - return self.__run_module("post_message", title=title, text=text, image=image, userid=userid) + return self.run_module("post_message", title=title, text=text, image=image, userid=userid) def post_medias_message(self, title: str, items: List[MediaInfo], userid: Union[str, int] = None) -> Optional[bool]: @@ -270,7 +270,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param userid: 用户ID :return: 成功或失败 """ - return self.__run_module("post_medias_message", title=title, items=items, userid=userid) + return self.run_module("post_medias_message", title=title, items=items, userid=userid) def post_torrents_message(self, title: str, items: List[Context], mediainfo: MediaInfo, @@ -283,7 +283,7 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param userid: 用户ID :return: 成功或失败 """ - return self.__run_module("post_torrents_message", title=title, mediainfo=mediainfo, + return self.run_module("post_torrents_message", title=title, mediainfo=mediainfo, items=items, userid=userid) def scrape_metadata(self, path: Path, mediainfo: MediaInfo) -> None: @@ -293,47 +293,10 @@ class ChainBase(AbstractSingleton, metaclass=Singleton): :param mediainfo: 识别的媒体信息 :return: 成功或失败 """ - return self.__run_module("scrape_metadata", path=path, mediainfo=mediainfo) + return self.run_module("scrape_metadata", path=path, mediainfo=mediainfo) def register_commands(self, commands: dict) -> None: """ 注册菜单命令 """ - return self.__run_module("register_commands", commands=commands) - - def douban_discover(self, mtype: MediaType, sort: str, tags: str, - start: int = 0, count: int = 30) -> Optional[List[dict]]: - """ - 发现豆瓣电影、剧集 - :param mtype: 媒体类型 - :param sort: 排序方式 - :param tags: 标签 - :param start: 起始位置 - :param count: 数量 - :return: 媒体信息列表 - """ - return self.__run_module("douban_discover", mtype=mtype, sort=sort, tags=tags, - start=start, count=count) - - def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str, - with_original_language: str, page: int = 1) -> Optional[List[dict]]: - """ - :param mtype: 媒体类型 - :param sort_by: 排序方式 - :param with_genres: 类型 - :param with_original_language: 语言 - :param page: 页码 - :return: 媒体信息列表 - """ - return self.__run_module("tmdb_discover", mtype=mtype, - sort_by=sort_by, with_genres=with_genres, - with_original_language=with_original_language, - page=page) - - def movie_top250(self, page: int = 1, count: int = 30) -> List[dict]: - """ - 获取豆瓣电影TOP250 - :param page: 页码 - :param count: 每页数量 - """ - return self.__run_module("movie_top250", page=page, count=count) + return self.run_module("register_commands", commands=commands) diff --git a/app/chain/douban.py b/app/chain/douban.py index 14a7ac5b..17484d04 100644 --- a/app/chain/douban.py +++ b/app/chain/douban.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Optional +from typing import Optional, List from typing import Union from app.chain import ChainBase @@ -12,6 +12,7 @@ from app.core.context import MediaInfo from app.core.metainfo import MetaInfo from app.helper.rss import RssHelper from app.log import logger +from app.schemas import MediaType class DoubanChain(ChainBase): @@ -50,6 +51,46 @@ class DoubanChain(ChainBase): mediainfo.set_douban_info(doubaninfo) return Context(meta=meta, mediainfo=mediainfo) + def movie_top250(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取豆瓣电影TOP250 + :param page: 页码 + :param count: 每页数量 + """ + return self.run_module("movie_top250", page=page, count=count) + + def movie_showing(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取正在上映的电影 + """ + return self.run_module("movie_showing", page=page, count=count) + + def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取本周中国剧集榜 + """ + return self.run_module("tv_weekly_chinese", page=page, count=count) + + def tv_weekly_global(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取本周全球剧集榜 + """ + return self.run_module("tv_weekly_global", page=page, count=count) + + def douban_discover(self, mtype: MediaType, sort: str, tags: str, + start: int = 0, count: int = 30) -> Optional[List[dict]]: + """ + 发现豆瓣电影、剧集 + :param mtype: 媒体类型 + :param sort: 排序方式 + :param tags: 标签 + :param start: 起始位置 + :param count: 数量 + :return: 媒体信息列表 + """ + return self.run_module("douban_discover", mtype=mtype, sort=sort, tags=tags, + start=start, count=count) + def remote_sync(self, userid: Union[int, str]): """ 同步豆瓣想看数据,发送消息 diff --git a/app/chain/tmdb.py b/app/chain/tmdb.py index de13356b..7ae1c2e9 100644 --- a/app/chain/tmdb.py +++ b/app/chain/tmdb.py @@ -1,8 +1,33 @@ +from typing import Optional, List + from app.chain import ChainBase +from app.schemas import MediaType class TmdbChain(ChainBase): """ TheMovieDB处理链 """ - pass + + def tmdb_discover(self, mtype: MediaType, sort_by: str, with_genres: str, + with_original_language: str, page: int = 1) -> Optional[List[dict]]: + """ + :param mtype: 媒体类型 + :param sort_by: 排序方式 + :param with_genres: 类型 + :param with_original_language: 语言 + :param page: 页码 + :return: 媒体信息列表 + """ + return self.run_module("tmdb_discover", mtype=mtype, + sort_by=sort_by, with_genres=with_genres, + with_original_language=with_original_language, + page=page) + + def tmdb_trending(self, page: int = 1) -> List[dict]: + """ + TMDB流行趋势 + :param page: 第几页 + :return: TMDB信息列表 + """ + return self.run_module("tmdb_trending", page=page) diff --git a/app/modules/douban/__init__.py b/app/modules/douban/__init__.py index 0467af1d..d0b34597 100644 --- a/app/modules/douban/__init__.py +++ b/app/modules/douban/__init__.py @@ -74,6 +74,36 @@ class DoubanModule(_ModuleBase): return [] return infos.get("items") or [] + def movie_showing(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取正在上映的电影 + """ + infos = self.doubanapi.movie_showing(start=(page - 1) * count, + count=count) + if not infos: + return [] + return infos.get("subject_collection_items") + + def tv_weekly_chinese(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取豆瓣本周口碑国产剧 + """ + infos = self.doubanapi.tv_chinese_best_weekly(start=(page - 1) * count, + count=count) + if not infos: + return [] + return infos.get("subject_collection_items") + + def tv_weekly_global(self, page: int = 1, count: int = 30) -> List[dict]: + """ + 获取豆瓣本周口碑外国剧 + """ + infos = self.doubanapi.tv_global_best_weekly(start=(page - 1) * count, + count=count) + if not infos: + return [] + return infos.get("subject_collection_items") + def search_medias(self, meta: MetaBase) -> Optional[List[MediaInfo]]: """ 搜索媒体信息 diff --git a/app/modules/themoviedb/__init__.py b/app/modules/themoviedb/__init__.py index b3255cd5..3bdce7c7 100644 --- a/app/modules/themoviedb/__init__.py +++ b/app/modules/themoviedb/__init__.py @@ -221,6 +221,14 @@ class TheMovieDbModule(_ModuleBase): else: return None + def tmdb_trending(self, page: int = 1) -> List[dict]: + """ + TMDB流行趋势 + :param page: 第几页 + :return: TMDB信息列表 + """ + return self.tmdb.trending.all_week(page=page) + def gen_scraper_files(self, mediainfo: MediaInfo, file_path: Path): """ 生成刮削文件 diff --git a/app/modules/themoviedb/tmdb.py b/app/modules/themoviedb/tmdb.py index 6db712b7..044a89f8 100644 --- a/app/modules/themoviedb/tmdb.py +++ b/app/modules/themoviedb/tmdb.py @@ -5,7 +5,7 @@ from urllib.parse import quote import zhconv from lxml import etree -from tmdbv3api import TMDb, Search, Movie, TV, Season, Episode, Discover +from tmdbv3api import TMDb, Search, Movie, TV, Season, Episode, Discover, Trending from tmdbv3api.exceptions import TMDbException from app.core.config import settings @@ -42,13 +42,14 @@ class TmdbHelper: self.tmdb.proxies = settings.PROXY # 调试模式 self.tmdb.debug = False - # 查询对象 + # TMDB查询对象 self.search = Search() self.movie = Movie() self.tv = TV() self.season = Season() self.episode = Episode() self.discover = Discover() + self.trending = Trending() def search_multiis(self, title: str) -> List[dict]: """