diff --git a/src/module/core/sub_thread.py b/src/module/core/sub_thread.py index 9544b464..4ad355eb 100644 --- a/src/module/core/sub_thread.py +++ b/src/module/core/sub_thread.py @@ -4,6 +4,7 @@ from .status import ProgramStatus from module.rss import analyser, add_rules from module.manager import Renamer, eps_complete +from module.network import PostNotification from module.conf import settings @@ -48,7 +49,7 @@ class RenameThread(ProgramStatus): def rename_loop(self): while not self.stop_event.is_set(): with Renamer() as renamer: - renamer.rename() + renamed_info = renamer.rename() self.stop_event.wait(settings.program.rename_time) def rename_start(self): diff --git a/src/module/downloader/path.py b/src/module/downloader/path.py index b6359ecb..7161501d 100644 --- a/src/module/downloader/path.py +++ b/src/module/downloader/path.py @@ -47,7 +47,6 @@ class TorrentPath: def _file_depth(path): return len(path.split(path.sep)) - @staticmethod def is_ep(self, path): return self._file_depth(path) <= 2 diff --git a/src/module/manager/renamer.py b/src/module/manager/renamer.py index 7e2cbcf8..01e6d188 100644 --- a/src/module/manager/renamer.py +++ b/src/module/manager/renamer.py @@ -3,10 +3,10 @@ import logging from module.downloader import DownloadClient from module.parser import TitleParser -from module.network import PostNotification +# from module.network import PostNotification from module.models import SubtitleFile, EpisodeFile, Notification from module.conf import settings -from module.database import BangumiDatabase +# from module.database import BangumiDatabase logger = logging.getLogger(__name__) @@ -47,23 +47,23 @@ class Renamer(DownloadClient): logger.error(f"[Renamer] Unknown rename method: {method}") return file_info.media_path - @staticmethod - def send_notification(bangumi_name, ep: EpisodeFile): - with BangumiDatabase() as db: - poster_path = db.match_poster(bangumi_name) - poster_link = "https://mikanani.me" + poster_path - n = Notification( - official_title=bangumi_name, - season=ep.season, - episode=ep.episode, - poster_link=poster_link, - ) - with PostNotification() as notificator: - status = notificator.send_msg(n) - if status: - logger.info(f"[Renamer] Notification sent: {ep.title} S{ep.season}E{ep.episode}") - else: - logger.warning(f"[Renamer] Notification failed: {ep.title} S{ep.season}E{ep.episode}") + # @staticmethod + # def send_notification(bangumi_name, ep: EpisodeFile): + # with BangumiDatabase() as db: + # poster_path = db.match_poster(bangumi_name) + # poster_link = "https://mikanani.me" + poster_path + # n = Notification( + # official_title=bangumi_name, + # season=ep.season, + # episode=ep.episode, + # poster_link=poster_link, + # ) + # with PostNotification() as notificator: + # status = notificator.send_msg(n) + # if status: + # logger.info(f"[Renamer] Notification sent: {ep.title} S{ep.season}E{ep.episode}") + # else: + # logger.warning(f"[Renamer] Notification failed: {ep.title} S{ep.season}E{ep.episode}") def rename_file( self, @@ -87,12 +87,14 @@ class Renamer(DownloadClient): _hash=_hash, old_path=media_path, new_path=new_path ) if renamed: - if settings.notification.enable: - self.send_notification(bangumi_name, ep) - return True - logger.warning(f"[Renamer] {media_path} parse failed") - if settings.bangumi_manage.remove_bad_torrent: - self.delete_torrent(hashes=_hash) + # if settings.notification.enable: + # self.send_notification(bangumi_name, ep) + return ep + else: + logger.warning(f"[Renamer] {media_path} parse failed") + if settings.bangumi_manage.remove_bad_torrent: + self.delete_torrent(hashes=_hash) + return None def rename_collection( self, @@ -154,6 +156,7 @@ class Renamer(DownloadClient): logger.debug("[Renamer] Start rename process.") rename_method = settings.bangumi_manage.rename_method torrents_info = self.get_torrent_info() + renamed_info = [] for info in torrents_info: media_list, subtitle_list = self.check_files(info) bangumi_name, season = self._path_to_bangumi(info.save_path) @@ -166,7 +169,9 @@ class Renamer(DownloadClient): } # Rename single media file if len(media_list) == 1: - self.rename_file(media_path=media_list[0], **kwargs) + ep_info = self.rename_file(media_path=media_list[0], **kwargs) + if ep_info: + renamed_info.append(ep_info) # Rename subtitle file if len(subtitle_list) > 0: self.rename_subtitles(subtitle_list=subtitle_list, **kwargs) @@ -180,11 +185,11 @@ class Renamer(DownloadClient): else: logger.warning(f"[Renamer] {info.name} has no media file") logger.debug("[Renamer] Rename process finished.") + return renamed_info if __name__ == "__main__": from module.conf import setup_logger - settings.log.debug_enable = True setup_logger() with Renamer() as renamer: diff --git a/src/module/manager/torrent.py b/src/module/manager/torrent.py index e35dc5f3..9325f259 100644 --- a/src/module/manager/torrent.py +++ b/src/module/manager/torrent.py @@ -13,62 +13,77 @@ class TorrentManager(BangumiDatabase): def __match_torrents_list(data: BangumiData) -> list: with DownloadClient() as client: torrents = client.get_torrent_info() - matched_list = [] - for torrent in torrents: - if data.save_path == torrent.save_path: - matched_list.append(torrent.hash) - return matched_list + return [torrent.hash for torrent in torrents if torrent.save_path == data.save_path] - def delete_torrents(self, _id: int | str): - data = self.search_one(int(_id)) - if isinstance(data, BangumiData): - hash_list = self.__match_torrents_list(data) - with DownloadClient() as client: - client.delete_torrent(hash_list) + def delete_torrents(self, data: BangumiData, client: DownloadClient): + hash_list = self.__match_torrents_list(data) + if hash_list: + client.delete_torrent(hash_list) + logger.info(f"Delete rule and torrents for {data.official_title}") return { "status": "success", "msg": f"Delete torrents for {data.official_title}", } else: - return data + return { + "status": "error", + "msg": f"Can't find torrents for {data.official_title}", + } def delete_rule(self, _id: int | str, file: bool = False): data = self.search_id(int(_id)) if isinstance(data, BangumiData): - self.delete_one(int(_id)) - if file: - self.delete_torrents(data.id) - logger.info(f"Delete {data.official_title} and torrents.") + with DownloadClient() as client: + client.remove_rule(data.rule_name) + self.delete_one(int(_id)) + if file: + self.delete_torrents(data, client) + return { + "status": "success", + "msg": f"Delete rule and torrents for {data.official_title}", + } + logger.info(f"Delete rule for {data.official_title}") return { "status": "success", - "msg": f"Delete {data.official_title} and torrents.", + "msg": f"Delete rule for {data.official_title}", } - logger.info(f"Delete {data.official_title}") - return {"status": "success", "msg": f"Delete {data.official_title}"} else: - return data + return {"status": "error", "msg": f"Can't find id {_id}"} + # data = self.search_id(int(_id)) + # if isinstance(data, BangumiData): + # self.delete_one(int(_id)) + # if file: + # self.delete_torrents(data) + # logger.info(f"Delete {data.official_title} and torrents.") + # return { + # "status": "success", + # "msg": f"Delete {data.official_title} and torrents.", + # } + # logger.info(f"Delete {data.official_title}") + # return {"status": "success", "msg": f"Delete {data.official_title}"} + # else: + # return data def disable_rule(self, _id: str | int, file: bool = False): data = self.search_id(int(_id)) if isinstance(data, BangumiData): with DownloadClient() as client: client.remove_rule(data.rule_name) - data.deleted = True - self.update_one(data) - if file: - self.delete_torrents(data.id) - logger.info(f"Delete rule and torrents for {data.official_title}") + data.deleted = True + self.update_one(data) + if file: + self.delete_torrents(data, client) + return { + "status": "success", + "msg": f"Disable rule and delete torrents for {data.official_title}", + } + logger.info(f"Disable rule for {data.official_title}") return { "status": "success", - "msg": f"Disable rule and delete torrents for {data.official_title}", + "msg": f"Disable rule for {data.official_title}", } - logger.info(f"Disable rule for {data.official_title}") - return { - "status": "success", - "msg": f"Disable rule for {data.official_title}", - } else: - return data + return {"status": "error", "msg": f"Can't find data with id {_id}"} def enable_rule(self, _id: str | int): data = self.search_id(int(_id)) diff --git a/src/module/network/__init__.py b/src/module/network/__init__.py index 2da0ea13..aca7b27b 100644 --- a/src/module/network/__init__.py +++ b/src/module/network/__init__.py @@ -1,2 +1 @@ from .request_contents import RequestContent, TorrentInfo -from .notification import PostNotification diff --git a/src/module/network/notification.py b/src/module/network/notification.py deleted file mode 100644 index c351fe35..00000000 --- a/src/module/network/notification.py +++ /dev/null @@ -1,96 +0,0 @@ -import logging - -from module.network.request_contents import RequestContent -from module.models import Notification -from module.conf import settings - - -logger = logging.getLogger(__name__) - -type = settings.notification.type -token = settings.notification.token -chat_id = settings.notification.chat_id - - -class TelegramNotification(RequestContent): - def __init__(self): - super().__init__() - self.notification_url = f"https://api.telegram.org/bot{token}/sendMessage" - self.chat_id = chat_id - - def post_msg(self, text: str) -> bool: - data = { - "chat_id": self.chat_id, - "text": text, - "disable_notification": True, - } - resp = self.post_data(self.notification_url, data) - logger.debug(f"Telegram notification: {resp.status_code}") - return resp.status_code == 200 - - -class ServerChanNotification(RequestContent): - """Server酱推送""" - - def __init__(self): - super().__init__() - self.notification_url = f"https://sctapi.ftqq.com/{token}.send" - - def post_msg(self, text: str) -> bool: - data = { - "title": "AutoBangumi 番剧更新", - "desp": text, - } - resp = self.post_data(self.notification_url, data) - logger.debug(f"ServerChan notification: {resp.status_code}") - return resp.status_code == 200 - - -class BarkNotification(RequestContent): - def __init__(self): - super().__init__() - self.token = token - self.notification_url = "https://api.day.app/push" - - def post_msg(self, text) -> bool: - data = {"title": "AutoBangumi 番剧更新", "body": text, "device_key": self.token} - resp = self.post_data(self.notification_url, data) - logger.debug(f"Bark notification: {resp.status_code}") - return resp.status_code == 200 - - -def getClient(): - if type.lower() == "telegram": - return TelegramNotification - elif type.lower() == "server-chan": - return ServerChanNotification - elif type.lower() == "bark": - return BarkNotification - else: - return None - - -class PostNotification(getClient()): - def send_msg(self, info: Notification) -> bool: - text = ( - f"番剧名称:{info.official_title}\n" - f"季度: 第{info.season}季\n" - f"更新集数: 第{info.episode}集\n" - f"{info.poster_link}\n" - ) - try: - return self.post_msg(text) - except Exception as e: - logger.warning(f"Failed to send notification: {e}") - return False - - -if __name__ == "__main__": - info = Notification( - official_title="魔法纪录 魔法少女小圆外传", - season=2, - episode=1, - poster_link="https://mikanani.me/images/Bangumi/202107/3788b33f.jpg", - ) - with PostNotification() as client: - client.send_msg(info) diff --git a/src/module/notification/__init__.py b/src/module/notification/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/module/notification/notification.py b/src/module/notification/notification.py new file mode 100644 index 00000000..059b2699 --- /dev/null +++ b/src/module/notification/notification.py @@ -0,0 +1,50 @@ +import logging + +from .plugin import * + +from module.models import Notification +from module.conf import settings + + +logger = logging.getLogger(__name__) + +type = settings.notification.type +token = settings.notification.token +chat_id = settings.notification.chat_id + + +def getClient(): + if type.lower() == "telegram": + return TelegramNotification + elif type.lower() == "server-chan": + return ServerChanNotification + elif type.lower() == "bark": + return BarkNotification + else: + return None + + +class PostNotification(getClient()): + def send_msg(self, info: Notification) -> bool: + text = ( + f"番剧名称:{info.official_title}\n" + f"季度: 第{info.season}季\n" + f"更新集数: 第{info.episode}集\n" + f"{info.poster_link}\n" + ) + try: + return self.post_msg(text) + except Exception as e: + logger.warning(f"Failed to send notification: {e}") + return False + + +if __name__ == "__main__": + info = Notification( + official_title="魔法纪录 魔法少女小圆外传", + season=2, + episode=1, + poster_link="https://mikanani.me/images/Bangumi/202107/3788b33f.jpg", + ) + with PostNotification() as client: + client.send_msg(info) diff --git a/src/module/notification/plugin/__init__.py b/src/module/notification/plugin/__init__.py new file mode 100644 index 00000000..106087f6 --- /dev/null +++ b/src/module/notification/plugin/__init__.py @@ -0,0 +1,3 @@ +from .bark import BarkNotification +from .server_chan import ServerChanNotification +from .telegram import TelegramNotification \ No newline at end of file diff --git a/src/module/notification/plugin/bark.py b/src/module/notification/plugin/bark.py new file mode 100644 index 00000000..bafa3c93 --- /dev/null +++ b/src/module/notification/plugin/bark.py @@ -0,0 +1,11 @@ +class BarkNotification(RequestContent): + def __init__(self): + super().__init__() + self.token = token + self.notification_url = "https://api.day.app/push" + + def post_msg(self, text) -> bool: + data = {"title": "AutoBangumi 番剧更新", "body": text, "device_key": self.token} + resp = self.post_data(self.notification_url, data) + logger.debug(f"Bark notification: {resp.status_code}") + return resp.status_code == 200 \ No newline at end of file diff --git a/src/module/notification/plugin/server_chan.py b/src/module/notification/plugin/server_chan.py new file mode 100644 index 00000000..cc6f12f8 --- /dev/null +++ b/src/module/notification/plugin/server_chan.py @@ -0,0 +1,15 @@ +class ServerChanNotification(RequestContent): + """Server酱推送""" + + def __init__(self): + super().__init__() + self.notification_url = f"https://sctapi.ftqq.com/{token}.send" + + def post_msg(self, text: str) -> bool: + data = { + "title": "AutoBangumi 番剧更新", + "desp": text, + } + resp = self.post_data(self.notification_url, data) + logger.debug(f"ServerChan notification: {resp.status_code}") + return resp.status_code == 200 \ No newline at end of file diff --git a/src/module/notification/plugin/slack.py b/src/module/notification/plugin/slack.py new file mode 100644 index 00000000..bafa3c93 --- /dev/null +++ b/src/module/notification/plugin/slack.py @@ -0,0 +1,11 @@ +class BarkNotification(RequestContent): + def __init__(self): + super().__init__() + self.token = token + self.notification_url = "https://api.day.app/push" + + def post_msg(self, text) -> bool: + data = {"title": "AutoBangumi 番剧更新", "body": text, "device_key": self.token} + resp = self.post_data(self.notification_url, data) + logger.debug(f"Bark notification: {resp.status_code}") + return resp.status_code == 200 \ No newline at end of file diff --git a/src/module/notification/plugin/telegram.py b/src/module/notification/plugin/telegram.py new file mode 100644 index 00000000..c5a691e5 --- /dev/null +++ b/src/module/notification/plugin/telegram.py @@ -0,0 +1,17 @@ +from module.network.request_contents import RequestContent + +class TelegramNotification(RequestContent): + def __init__(self): + super().__init__() + self.notification_url = f"https://api.telegram.org/bot{token}/sendMessage" + self.chat_id = chat_id + + def post_msg(self, text: str) -> bool: + data = { + "chat_id": self.chat_id, + "text": text, + "disable_notification": True, + } + resp = self.post_data(self.notification_url, data) + logger.debug(f"Telegram notification: {resp.status_code}") + return resp.status_code == 200 \ No newline at end of file diff --git a/src/module/rss/analyser.py b/src/module/rss/analyser.py index a0230eab..6b50af1b 100644 --- a/src/module/rss/analyser.py +++ b/src/module/rss/analyser.py @@ -75,24 +75,13 @@ class RSSAnalyser: self.official_title_parser(data, mikan_title) return data - def rss_to_data(self, rss_link: str, full_parse: bool = True) -> list[BangumiData]: + def rss_to_data(self, rss_link: str, database: BangumiDatabase, full_parse: bool = True) -> list[BangumiData]: rss_torrents = self.get_rss_torrents(rss_link, full_parse) - with BangumiDatabase() as database: - torrents_to_add = database.match_list(rss_torrents, rss_link) - if not torrents_to_add: - logger.debug("[RSS] No new title has been found.") - return [] - # New List - new_data = self.torrents_to_data(torrents_to_add, rss_link, full_parse) - if new_data: - if full_parse: - database.insert_list(new_data) - return new_data - - def run(self, rss_link: str = settings.rss_link): - logger.info("[RSS] Start collecting RSS info.") - try: - self.rss_to_data(rss_link) - except Exception as e: - logger.debug(f"[RSS] {e}") - logger.error("[RSS] Failed to collect RSS info.") + torrents_to_add = database.match_list(rss_torrents, rss_link) + if not torrents_to_add: + logger.debug("[RSS] No new title has been found.") + return [] + # New List + new_data = self.torrents_to_data(torrents_to_add, rss_link, full_parse) + if new_data: + return new_data diff --git a/src/module/security/api.py b/src/module/security/api.py index a9cfa3cd..13069b72 100644 --- a/src/module/security/api.py +++ b/src/module/security/api.py @@ -1,7 +1,7 @@ from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer -from .jwt import decode_token +from .jwt import verify_token from module.database.user import AuthDB from module.models.user import User @@ -15,7 +15,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token" ) - payload = decode_token(token) + payload = verify_token(token) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token" @@ -31,7 +31,7 @@ async def get_current_user(token: str = Depends(oauth2_scheme)): async def get_token_data(token: str = Depends(oauth2_scheme)): - payload = decode_token(token) + payload = verify_token(token) if not payload: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="invalid token" diff --git a/src/module/security/jwt.py b/src/module/security/jwt.py index aaeb04b0..50586af8 100644 --- a/src/module/security/jwt.py +++ b/src/module/security/jwt.py @@ -30,8 +30,8 @@ def decode_token(token: str): if username is None: return None return payload - except JWTError as e: - raise e + except JWTError: + return None def verify_token(token: str):