diff --git a/backend/src/module/api/bangumi.py b/backend/src/module/api/bangumi.py index a96c22c2..c912f978 100644 --- a/backend/src/module/api/bangumi.py +++ b/backend/src/module/api/bangumi.py @@ -75,6 +75,13 @@ async def enable_rule(bangumi_id: str): return u_response(resp) +@router.get(path="/refresh/poster/all", response_model=APIResponse, dependencies=[Depends(get_current_user)]) +async def refresh_poster(): + with TorrentManager() as manager: + resp = manager.refresh_poster() + return u_response(resp) + + @router.get("/reset/all", response_model=APIResponse, dependencies=[Depends(get_current_user)]) async def reset_all(): with TorrentManager() as manager: diff --git a/backend/src/module/manager/collector.py b/backend/src/module/manager/collector.py index f6f1dab8..22e11456 100644 --- a/backend/src/module/manager/collector.py +++ b/backend/src/module/manager/collector.py @@ -9,13 +9,13 @@ logger = logging.getLogger(__name__) class SeasonCollector(DownloadClient): - def collect_season(self, bangumi: Bangumi, link: str = None): + def collect_season(self, bangumi: Bangumi, link: str = None, limit: int = None): logger.info( f"Start collecting {bangumi.official_title} Season {bangumi.season}..." ) with SearchTorrent() as st: if not link: - torrents = st.search_season(bangumi) + torrents = st.search_season(bangumi, limit=limit) else: torrents = st.get_torrents(link, bangumi.filter.replace(",", "|")) if self.add_torrent(torrents, bangumi): @@ -23,6 +23,7 @@ class SeasonCollector(DownloadClient): bangumi.eps_collect = True with RSSEngine() as engine: engine.bangumi.update(bangumi) + engine.torrent.add_all(torrents) return ResponseModel( status=True, status_code=200, @@ -57,7 +58,7 @@ def eps_complete(): logger.info("Start collecting full season...") for data in datas: if not data.eps_collect: - with SeasonCollector() as sc: - sc.collect_season(data) + with SeasonCollector() as collector: + collector.collect_season(data) data.eps_collect = True engine.bangumi.update_all(datas) diff --git a/backend/src/module/manager/torrent.py b/backend/src/module/manager/torrent.py index 9a559fbc..58cbed69 100644 --- a/backend/src/module/manager/torrent.py +++ b/backend/src/module/manager/torrent.py @@ -1,10 +1,10 @@ import logging -from fastapi.responses import JSONResponse from module.database import Database from module.downloader import DownloadClient from module.models import Bangumi, BangumiUpdate, ResponseModel +from module.parser import TitleParser logger = logging.getLogger(__name__) @@ -133,6 +133,18 @@ class TorrentManager(Database): msg_zh=f"无法找到 id {bangumi_id} 的数据", ) + def refresh_poster(self): + bangumis = self.bangumi.search_all() + for bangumi in bangumis: + if not bangumi.poster_link: + TitleParser().tmdb_poster_parser(bangumi) + self.bangumi.update_all(bangumis) + return ResponseModel( + status_code=200, + status=True, + msg_en=f"Refresh poster link successfully.", + msg_zh=f"刷新海报链接成功。", + ) def search_all_bangumi(self): datas = self.bangumi.search_all() @@ -152,3 +164,7 @@ class TorrentManager(Database): ) else: return data + +if __name__ == '__main__': + with TorrentManager() as manager: + manager.refresh_poster() diff --git a/backend/src/module/network/request_contents.py b/backend/src/module/network/request_contents.py index bde907a4..869fce62 100644 --- a/backend/src/module/network/request_contents.py +++ b/backend/src/module/network/request_contents.py @@ -30,8 +30,9 @@ class RequestContent(RequestURL): torrents.append( Torrent(name=_title, url=torrent_url, homepage=homepage) ) - if len(torrents) >= limit: - break + if isinstance(limit, int): + if len(torrents) >= limit: + break return torrents else: logger.warning(f"[Network] Failed to get torrents: {_url}") diff --git a/backend/src/module/parser/title_parser.py b/backend/src/module/parser/title_parser.py index 89f0f2b2..1730ee31 100644 --- a/backend/src/module/parser/title_parser.py +++ b/backend/src/module/parser/title_parser.py @@ -36,6 +36,16 @@ class TitleParser: logger.warning("Please change bangumi info manually.") return title, season, None, None + @staticmethod + def tmdb_poster_parser(bangumi: Bangumi): + tmdb_info = tmdb_parser(bangumi.official_title, settings.rss_parser.language) + if tmdb_info: + logger.debug(f"TMDB Matched, official title is {tmdb_info.title}") + bangumi.poster_link = tmdb_info.poster_link + else: + logger.warning(f"Cannot match {bangumi.official_title} in TMDB. Use raw title instead.") + logger.warning("Please change bangumi info manually.") + @staticmethod def raw_parser(raw: str) -> Bangumi | None: language = settings.rss_parser.language diff --git a/backend/src/module/searcher/searcher.py b/backend/src/module/searcher/searcher.py index e38b7402..6bc8beb1 100644 --- a/backend/src/module/searcher/searcher.py +++ b/backend/src/module/searcher/searcher.py @@ -44,7 +44,7 @@ class SearchTorrent(RequestContent, RSSAnalyser): url = search_url(site, keywords) return url - def search_season(self, data: Bangumi, site: str = "mikan") -> list[Torrent]: + def search_season(self, data: Bangumi, site: str = "mikan", limit: int = None) -> list[Torrent]: rss_item = self.special_url(data, site) - torrents = self.search_torrents(rss_item) + torrents = self.search_torrents(rss_item, limit=limit) return [torrent for torrent in torrents if data.title_raw in torrent.name] \ No newline at end of file diff --git a/webui/src/api/bangumi.ts b/webui/src/api/bangumi.ts index 5f0d0c21..69ed9a21 100644 --- a/webui/src/api/bangumi.ts +++ b/webui/src/api/bangumi.ts @@ -1,129 +1,137 @@ -import type { BangumiAPI, BangumiRule } from '#/bangumi'; -import type { ApiSuccess } from '#/api'; +import type {BangumiAPI, BangumiRule} from '#/bangumi'; +import type {ApiSuccess} from '#/api'; export const apiBangumi = { - /** - * 获取所有 bangumi 数据 - * @returns 所有 bangumi 数据 - */ - async getAll() { - const { data } = await axios.get('api/v1/bangumi/get/all'); - const result: BangumiRule[] = data.map((bangumi) => ( - { - ...bangumi, - filter: bangumi.filter.split(','), - rss_link: bangumi.rss_link.split(','), + /** + * 获取所有 bangumi 数据 + * @returns 所有 bangumi 数据 + */ + async getAll() { + const {data} = await axios.get('api/v1/bangumi/get/all'); + const result: BangumiRule[] = data.map((bangumi) => ( + { + ...bangumi, + filter: bangumi.filter.split(','), + rss_link: bangumi.rss_link.split(','), + } + )); + return result; + }, + + /** + * 获取指定 bangumiId 的规则 + * @param bangumiId bangumi id + * @returns 指定 bangumi 的规则 + */ + async getRule(bangumiId: number) { + const {data} = await axios.get( + `api/v1/bangumi/get/${bangumiId}` + ); + const result: BangumiRule = { + ...data, + filter: data.filter.split(','), + rss_link: data.rss_link.split(','), } - )); - return result; - }, + return result; + }, - /** - * 获取指定 bangumiId 的规则 - * @param bangumiId bangumi id - * @returns 指定 bangumi 的规则 - */ - async getRule(bangumiId: number) { - const { data } = await axios.get( - `api/v1/bangumi/get/${bangumiId}` - ); - const result: BangumiRule = { - ...data, - filter: data.filter.split(','), - rss_link: data.rss_link.split(','), + /** + * 更新指定 bangumiId 的规则 + * @param bangumiId - 需要更新的 bangumi 的 id + * @param bangumiRule + * @returns axios 请求返回的数据 + */ + async updateRule(bangumiId: number, bangumiRule: BangumiRule) { + const rule: BangumiAPI = { + ...bangumiRule, + filter: bangumiRule.filter.join(','), + rss_link: bangumiRule.rss_link.join(','), + } + const post = omit(rule, ['id']) + const {data} = await axios.patch( + `api/v1/bangumi/update/${bangumiId}`, + post + ); + return data; + }, + + /** + * 删除指定 bangumiId 的数据库规则,会在重新匹配到后重建 + * @param bangumiId - 需要删除的 bangumi 的 id + * @param file - 是否同时删除关联文件。 + * @returns axios 请求返回的数据 + */ + async deleteRule(bangumiId: number | number[], file: boolean) { + let url = 'api/v1/bangumi/delete'; + let ids: undefined | number[]; + + if (typeof bangumiId === 'number') { + url = `${url}/${bangumiId}`; + } else { + url = `${url}/many`; + ids = bangumiId; + } + + const {data} = await axios.delete(url, { + data: ids, + params: { + file, + }, + }); + return data; + }, + + /** + * 删除指定 bangumiId 的规则。如果 file 为 true,则同时删除关联文件。 + * @param bangumiId - 需要删除规则的 bangumi 的 id。 + * @param file - 是否同时删除关联文件。 + * @returns axios 请求返回的数据 + */ + async disableRule(bangumiId: number | number[], file: boolean) { + let url = 'api/v1/bangumi/disable'; + let ids: undefined | number[]; + + if (typeof bangumiId === 'number') { + url = `${url}/${bangumiId}`; + } else { + url = `${url}/many`; + ids = bangumiId; + } + + const {data} = await axios.delete(url, { + data: ids, + params: { + file, + }, + }); + return data; + }, + + /** + * 启用指定 bangumiId 的规则 + * @param bangumiId - 需要启用的 bangumi 的 id + */ + async enableRule(bangumiId: number) { + const {data} = await axios.get( + `api/v1/bangumi/enable/${bangumiId}` + ); + return data; + }, + + /** + * 重置所有 bangumi 数据 + */ + async resetAll() { + const {data} = await axios.get('api/v1/bangumi/reset/all'); + return data; + }, + + /** + * 刷新所有没有海报的 bangumi 海报 + */ + async refreshPoster() { + const {data} = await axios.get('api/v1/bangumi/refresh/poster/all'); + return data; } - return result; - }, - - /** - * 更新指定 bangumiId 的规则 - * @param bangumiId - 需要更新的 bangumi 的 id - * @param bangumiRule - * @returns axios 请求返回的数据 - */ - async updateRule(bangumiId: number, bangumiRule: BangumiRule) { - const rule: BangumiAPI = { - ...bangumiRule, - filter: bangumiRule.filter.join(','), - rss_link: bangumiRule.rss_link.join(','), - } - const post = omit(rule, ['id']) - const { data } = await axios.patch< ApiSuccess >( - `api/v1/bangumi/update/${bangumiId}`, - post - ); - return data; - }, - - /** - * 删除指定 bangumiId 的数据库规则,会在重新匹配到后重建 - * @param bangumiId - 需要删除的 bangumi 的 id - * @param file - 是否同时删除关联文件。 - * @returns axios 请求返回的数据 - */ - async deleteRule(bangumiId: number | number[], file: boolean) { - let url = 'api/v1/bangumi/delete'; - let ids: undefined | number[]; - - if (typeof bangumiId === 'number') { - url = `${url}/${bangumiId}`; - } else { - url = `${url}/many`; - ids = bangumiId; - } - - const { data } = await axios.delete< ApiSuccess >(url, { - data: ids, - params: { - file, - }, - }); - return data; - }, - - /** - * 删除指定 bangumiId 的规则。如果 file 为 true,则同时删除关联文件。 - * @param bangumiId - 需要删除规则的 bangumi 的 id。 - * @param file - 是否同时删除关联文件。 - * @returns axios 请求返回的数据 - */ - async disableRule(bangumiId: number | number[], file: boolean) { - let url = 'api/v1/bangumi/disable'; - let ids: undefined | number[]; - - if (typeof bangumiId === 'number') { - url = `${url}/${bangumiId}`; - } else { - url = `${url}/many`; - ids = bangumiId; - } - - const { data } = await axios.delete< ApiSuccess >(url, { - data: ids, - params: { - file, - }, - }); - return data; - }, - - /** - * 启用指定 bangumiId 的规则 - * @param bangumiId - 需要启用的 bangumi 的 id - */ - async enableRule(bangumiId: number) { - const { data } = await axios.get< ApiSuccess >( - `api/v1/bangumi/enable/${bangumiId}` - ); - return data; - }, - - /** - * 重置所有 bangumi 数据 - */ - async resetAll() { - const { data } = await axios.get< ApiSuccess >('api/v1/bangumi/reset/all'); - return data; - }, }; diff --git a/webui/src/components/ab-status-bar.vue b/webui/src/components/ab-status-bar.vue index 6d5146f8..d0caa4e7 100644 --- a/webui/src/components/ab-status-bar.vue +++ b/webui/src/components/ab-status-bar.vue @@ -1,6 +1,6 @@