diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6888de4c --- /dev/null +++ b/.gitignore @@ -0,0 +1,163 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# Custom +/AutoBangumi/config/bangumi.json \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore index 26d33521..d16bbbe5 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,77 @@ -# Default ignored files -/shelf/ -/workspace.xml +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..21b889b4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // 使用 IntelliSense 了解相关属性。 + // 悬停以查看现有属性的描述。 + // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: docker_main", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/AutoBangumi/app/docker_main.py", + "cwd": "${workspaceFolder}/AutoBangumi/app", + "console": "integratedTerminal", + "justMyCode": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..de288e1e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.formatting.provider": "black" +} \ No newline at end of file diff --git a/AutoBangumi/app/RSSFilter.py b/AutoBangumi/app/RSSFilter.py index 222aac43..e3c4825e 100644 --- a/AutoBangumi/app/RSSFilter.py +++ b/AutoBangumi/app/RSSFilter.py @@ -4,6 +4,20 @@ import zhconv import logging from RssFilter.fliter_base import * +logger = logging.getLogger(__name__) +handler = logging.FileHandler( + filename="RssFilter/rename_log.txt", + mode="w", + encoding="utf-8" +) +handler.setFormatter( + logging.Formatter( + "%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s" + ) +) +logger.level = logging.WARNING +logger.addHandler(handler) + class RSSInfoCleaner: class Name: @@ -32,20 +46,72 @@ class RSSInfoCleaner: self.file_name = file_name self.Name.raw = file_name # 接收文件名参数 self.clean() # 清理广告等杂质 - # 加载日志,匹配特征等 - logging.basicConfig(level=logging.WARN, - filename='RssFilter/rename_log.txt', - filemode='w', - format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') - self.group_character = ['字幕社', '字幕组', '字幕屋', '发布组', "连载组", '动漫', '国漫', '汉化', 'raw', 'works', '工作室', '压制', '合成', - '制作', '搬运', '委员会', '家族', '译制', '动画', '研究所', 'sub', '翻译', '联盟', 'dream', '-rip', 'neo', - 'team', "百合组", "慕留人", "行动组"] - self.group_char = ['dmhy', '澄空学园', 'c.c动漫', "vcb", 'amor', 'moozzi2', 'skytree', 'sweetsub', 'pcsub', 'ahu-sub', - 'f宅', 'captions', 'dragsterps', 'onestar', "lolihouse", "天空树", "妇联奶子", "不够热", "烤肉同好", '卡通', - '时雨初空', 'nyaa', 'ddd', 'koten', 'reinforce', '届恋对邦小队', 'cxraw', "witex.io"] - with open("../config/clean_rule.json", encoding='utf-8') as file_obj: + # 匹配特征等 + self.group_character = [ + "字幕社", + "字幕组", + "字幕屋", + "发布组", + "连载组", + "动漫", + "国漫", + "汉化", + "raw", + "works", + "工作室", + "压制", + "合成", + "制作", + "搬运", + "委员会", + "家族", + "译制", + "动画", + "研究所", + "sub", + "翻译", + "联盟", + "dream", + "-rip", + "neo", + "team", + "百合组", + "慕留人", + "行动组", + ] + self.group_char = [ + "dmhy", + "澄空学园", + "c.c动漫", + "vcb", + "amor", + "moozzi2", + "skytree", + "sweetsub", + "pcsub", + "ahu-sub", + "f宅", + "captions", + "dragsterps", + "onestar", + "lolihouse", + "天空树", + "妇联奶子", + "不够热", + "烤肉同好", + "卡通", + "时雨初空", + "nyaa", + "ddd", + "koten", + "reinforce", + "届恋对邦小队", + "cxraw", + "witex.io", + ] + with open("../config/clean_rule.json", encoding="utf-8") as file_obj: rule_json = json.load(file_obj)[0]["group_name"] - self.group_rule = [zhconv.convert(x, 'zh-cn') for x in rule_json] + self.group_rule = [zhconv.convert(x, "zh-cn") for x in rule_json] self.file_info = {} self.pre_analyse = None @@ -70,40 +136,82 @@ class RSSInfoCleaner: # 清理原链接(中文字符替换为英文) def clean(self): - file_name = zhconv.convert(self.Name.raw, 'zh-cn') + file_name = zhconv.convert(self.Name.raw, "zh-cn") # 去广告 - file_name = re.sub("[((\[【]?(字幕)?[\u4e00-\u9fa5、]{0,3}(新人|招募?新?)[\u4e00-\u9fa5、]{0,8}[))\]】]?", "", file_name) + file_name = re.sub( + "[((\[【]?(字幕)?[\u4e00-\u9fa5、]{0,3}(新人|招募?新?)[\u4e00-\u9fa5、]{0,8}[))\]】]?", + "", + file_name, + ) # 除杂 - file_name = re.sub("[((\[【]?★?((网飞)?\d{4}年[春夏秋冬]?)?[\d一二三四五六七八九十]{1,2}月新?番?★?[))\]】]?", "", file_name) + file_name = re.sub( + "[((\[【]?★?((网飞)?\d{4}年[春夏秋冬]?)?[\d一二三四五六七八九十]{1,2}月新?番?★?[))\]】]?", + "", + file_name, + ) # 除杂x2 file_name = re.sub("[((\[【 ](2\d{3})[))\]】 ]", " ", file_name) # 除杂x3 - file_name = re.sub("[((\[【]?((网飞)?2(\d{3}[年.][春夏秋冬]?)\d{1,2}\.?\d{1,2})[))\]】]?", "", file_name) + file_name = re.sub( + "[((\[【]?((网飞)?2(\d{3}[年.][春夏秋冬]?)\d{1,2}\.?\d{1,2})[))\]】]?", "", file_name + ) # 除杂x4 file_name = re.sub("[((\[【]检索.*[))\]】]?", "", file_name) - strip = ["特效歌词", "复制磁连", "兼容", "配音", "网盘", "\u200b", "[PSV&PC]", "Rv40", "R10", "Fin]", "Fin ", "[mkv]", "[]", - "★", "☆"] + strip = [ + "特效歌词", + "复制磁连", + "兼容", + "配音", + "网盘", + "\u200b", + "[PSV&PC]", + "Rv40", + "R10", + "Fin]", + "Fin ", + "[mkv]", + "[]", + "★", + "☆", + ] file_name = del_rules(file_name, strip) # xx_xx_xx f_res = re.search("]?(([a-zA-Z:.。,,!!]{1,10})[_\[ ]){2,}", file_name) if f_res is not None: - file_name = file_name.replace(f_res.group(), "%s/" % f_res.group().replace("_"," ")) + file_name = file_name.replace( + f_res.group(), "%s/" % f_res.group().replace("_", " ") + ) # 中文_英文名_ f_res = re.search("_[a-zA-Z_ \-·、.。,!!]*[_))\]】]", file_name) # !!!重要 if f_res is not None: - file_name = file_name.replace(f_res.group(), "/%s/" % f_res.group().strip("_")) + file_name = file_name.replace( + f_res.group(), "/%s/" % f_res.group().strip("_") + ) # 日文.英文名 - f_res = re.search("([\u4e00-\u9fa5\u3040-\u31ff\d:\-·、.。,!!]{1,20}\.)([a-zA-Z\d:\-.。,,!!]{1,20} ?){2,}", - file_name) + f_res = re.search( + "([\u4e00-\u9fa5\u3040-\u31ff\d:\-·、.。,!!]{1,20}\.)([a-zA-Z\d:\-.。,,!!]{1,20} ?){2,}", + file_name, + ) if f_res is not None: - file_name = file_name.replace(f_res.group(1), "%s/" % f_res.group(1).strip(".")) + file_name = file_name.replace( + f_res.group(1), "%s/" % f_res.group(1).strip(".") + ) - - - self.Name.raw = str(file_name).replace(':', ':').replace('【', '[').replace('】', ']').replace('-', '-') \ - .replace('(', '(').replace(')', ')').replace("&", "&").replace("X", "x").replace("×", "x") \ - .replace("Ⅹ", "x").replace("__", "/") + self.Name.raw = ( + str(file_name) + .replace(":", ":") + .replace("【", "[") + .replace("】", "]") + .replace("-", "-") + .replace("(", "(") + .replace(")", ")") + .replace("&", "&") + .replace("X", "x") + .replace("×", "x") + .replace("Ⅹ", "x") + .replace("__", "/") + ) # 检索字幕组特征 def recognize_group(self): @@ -115,8 +223,14 @@ class RSSInfoCleaner: # !强规则,人工录入标准名,区分大小写,优先匹配 for char in rule: if ("&" + char) in self.file_name or (char + "&") in self.file_name: - self.pre_analyse = re.search("[((\[【]?(.*?(&%s|%s&).*?)[))\]】]?" % (char, char), self.file_name).group( - 1).lower() + self.pre_analyse = ( + re.search( + "[((\[【]?(.*?(&%s|%s&).*?)[))\]】]?" % (char, char), + self.file_name, + ) + .group(1) + .lower() + ) return "enforce" else: if char in self.file_name: @@ -127,13 +241,19 @@ class RSSInfoCleaner: str_split = self.Name.raw.lower().split("]") # 检索特征值是否位于文件名第1、2、最后一段 for char in character: - if char in str_split[0] or char in str_split[1] or char in str_split[-1]: + if ( + char in str_split[0] + or char in str_split[1] + or char in str_split[-1] + ): self.pre_analyse = char return "success" # 文件名是否为 [字幕组名&字幕组名&字幕组名] ,求求了,一集的工作量真的需要三个组一起做吗 if "&" in str_split[0]: # 限制匹配长度,防止出bug - self.pre_analyse = str_split[0][1:] if len(str_split[0][1:]) < 15 else None + self.pre_analyse = ( + str_split[0][1:] if len(str_split[0][1:]) < 15 else None + ) return "special" # 再匹配不上我就麻了 self.pre_analyse = None @@ -176,7 +296,10 @@ class RSSInfoCleaner: gp = get_gp(res_char, self.Name.raw.lower()) return gp except Exception as e: - logging.warning("bug -- res_char:%s,%s,%s" % (res_char, self.Name.raw.lower(), e)) + logger.warning( + "bug -- res_char:%s,%s,%s" + % (res_char, self.Name.raw.lower(), e) + ) else: return res_char # 再见 @@ -185,11 +308,46 @@ class RSSInfoCleaner: # 扒了6W数据,硬找的参数,没啥说的 def get_dpi(self): file_name = self.Name.raw - dpi_list = ["4k", "2160p", "1440p", "1080p", "1036p", "816p", "810p", "720p", "576p", "544P", "540p", "480p", - "1080i", "1080+", "360p", - "3840x2160", "1920x1080", "1920x1036", "1920x804", "1920x800", "1536x864", "1452x1080", "1440x1080", - "1280x720", "1272x720", "1255x940", "1024x768", "1024X576", "960x720", "948x720", "896x672", - "872x480", "848X480", "832x624", "704x528", "640x480", "mp4_1080", "mp4_720"] + dpi_list = [ + "4k", + "2160p", + "1440p", + "1080p", + "1036p", + "816p", + "810p", + "720p", + "576p", + "544P", + "540p", + "480p", + "1080i", + "1080+", + "360p", + "3840x2160", + "1920x1080", + "1920x1036", + "1920x804", + "1920x800", + "1536x864", + "1452x1080", + "1440x1080", + "1280x720", + "1272x720", + "1255x940", + "1024x768", + "1024X576", + "960x720", + "948x720", + "896x672", + "872x480", + "848X480", + "832x624", + "704x528", + "640x480", + "mp4_1080", + "mp4_720", + ] for i in dpi_list: dpi = str(file_name).lower().find(i) if dpi > 0: @@ -203,23 +361,32 @@ class RSSInfoCleaner: # 中文标示 try: lang.append( - re.search("[((\[【 ]((tvb)?([粤简繁英俄][日中文体&/]?[_&]?){1,5})[))\]】]?", str(file_name)).group( - 1).strip(" ")) + re.search( + "[((\[【 ]((tvb)?([粤简繁英俄][日中文体&/]?[_&]?){1,5})[))\]】]?", + str(file_name), + ) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) # 中文标示 try: lang.append( - re.search("[((\[【]?[粤中简繁英俄日文体](双?(语|字幕))[))\]】]?", str(file_name)).group( - 1).strip(" ")) + re.search("[((\[【]?[粤中简繁英俄日文体](双?(语|字幕))[))\]】]?", str(file_name)) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) # 英文标示 try: - lang = lang + re.search("[((\[【]?(((G?BIG5|CHT|CHS|GB|JPN?|CN)[/ _]?){1,3})[))\]】]?", str(file_name)).group( - 1).lower().strip(" ").split(" ") + lang = lang + re.search( + "[((\[【]?(((G?BIG5|CHT|CHS|GB|JPN?|CN)[/ _]?){1,3})[))\]】]?", + str(file_name), + ).group(1).lower().strip(" ").split(" ") except Exception as e: - logging.info(e) + logger.info(e) if lang: return lang else: @@ -231,10 +398,16 @@ class RSSInfoCleaner: type_list = [] # 英文标示 try: - type_list.append(re.search("[((\[【]?(((mp4|mkv|mp3)[ -]?){1,3})[))\]】]?", - str(file_name).lower()).group(1).strip(" ")) + type_list.append( + re.search( + "[((\[【]?(((mp4|mkv|mp3)[ -]?){1,3})[))\]】]?", + str(file_name).lower(), + ) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) if type_list: return type_list else: @@ -248,23 +421,25 @@ class RSSInfoCleaner: try: code = code + re.search( "[((\[【]?([ _-]?([xh]26[45]|hevc|avc)){1,5}[ ))\]】]?", - str(file_name).lower()).group(1).split(" ") + str(file_name).lower(), + ).group(1).split(" ") except Exception as e: - logging.info(e) + logger.info(e) # 位深 try: code = code + re.search( - "[((\[【]?[ _-]?((10|8)[ -]?bit)[ ))\]】]?", - str(file_name).lower()).group(1).split(" ") + "[((\[【]?[ _-]?((10|8)[ -]?bit)[ ))\]】]?", str(file_name).lower() + ).group(1).split(" ") except Exception as e: - logging.info(e) + logger.info(e) # 音频编码 try: code = code + re.search( "[((\[【]?(([ _-]?((flac(x\d)?|aac|mp3|opus)(x\d)?)){1,5})[ ))\]】]?", - str(file_name).lower()).group(3).split(" ") + str(file_name).lower(), + ).group(3).split(" ") except Exception as e: - logging.info(e) + logger.info(e) if code: return code else: @@ -277,13 +452,19 @@ class RSSInfoCleaner: # 英文标示 for _ in range(3): try: - res = re.search( - "[((\[【]?((bd|dvd|hd|remux|(viu)?tvb?|ani-one|bilibili|网飞(动漫)|b-?global|baha|web[ /-]?(dl|rip))[ -]?(b[o0]x|iso|mut|rip)?)[))\]】]?", - file_name).group(1).lower().strip(" ") + res = ( + re.search( + "[((\[【]?((bd|dvd|hd|remux|(viu)?tvb?|ani-one|bilibili|网飞(动漫)|b-?global|baha|web[ /-]?(dl|rip))[ -]?(b[o0]x|iso|mut|rip)?)[))\]】]?", + file_name, + ) + .group(1) + .lower() + .strip(" ") + ) if res not in type_list: type_list.append(res) except Exception as e: - logging.info(e) + logger.info(e) for res in type_list: file_name = file_name.replace(res, "") if type_list: @@ -297,15 +478,25 @@ class RSSInfoCleaner: season = [] # 中文标示 try: - season.append(re.search(" ?(第?(\d{1,2}|[一二三])(部|季|季度|丁目))", str(file_name)).group(1).strip(" ")) + season.append( + re.search(" ?(第?(\d{1,2}|[一二三])(部|季|季度|丁目))", str(file_name)) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) # 英文标示 try: season.append( - re.search("((final ?)?(season|[ \[]s) ?\d{1,2}|\d{1,2}-?choume)", str(file_name)).group(1).strip(" ")) + re.search( + "((final ?)?(season|[ \[]s) ?\d{1,2}|\d{1,2}-?choume)", + str(file_name), + ) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) if season: return season else: @@ -318,39 +509,55 @@ class RSSInfoCleaner: # _集,国漫 try: episode.append( - re.search("(_((\d+集-)?\d+集)|[ (\[第]\d+-\d+ ?)", str(file_name)).group(1)) + re.search("(_((\d+集-)?\d+集)|[ (\[第]\d+-\d+ ?)", str(file_name)).group(1) + ) return episode except Exception as e: - logging.info(e) + logger.info(e) # [10 11]集点名批评这种命名方法,几个国漫的组 try: episode.append( - re.search("[\[( ](\d{1,3}[- &_]\d{1,3}) ?(fin| Fin|\(全集\))?[ )\]]", str(file_name)).group(1)) + re.search( + "[\[( ](\d{1,3}[- &_]\d{1,3}) ?(fin| Fin|\(全集\))?[ )\]]", + str(file_name), + ).group(1) + ) return episode except Exception as e: - logging.info(e) + logger.info(e) # 这里匹配ova 剧场版 不带集数的合集 之类的 try: episode.append( - re.search("[\[ 第](_\d{1,3}集|ova|剧场版|全|OVA ?\d{0,2}|合|[一二三四五六七八九十])[集话章 \]\[]", str(file_name)).group(1)) + re.search( + "[\[ 第](_\d{1,3}集|ova|剧场版|全|OVA ?\d{0,2}|合|[一二三四五六七八九十])[集话章 \]\[]", + str(file_name), + ).group(1) + ) return episode except Exception as e: - logging.info(e) + logger.info(e) # 标准单集 sp单集 try: episode.append( - re.search("[\[ 第e]((sp|(数码)?重映)?(1?\d{1,3}(\.\d)?|1?\d{1,3}\(1?\d{1,3}\)))(v\d)?[集话章 \]\[]", - str(file_name)).group(1)) + re.search( + "[\[ 第e]((sp|(数码)?重映)?(1?\d{1,3}(\.\d)?|1?\d{1,3}\(1?\d{1,3}\)))(v\d)?[集话章 \]\[]", + str(file_name), + ).group(1) + ) return episode except Exception as e: - logging.info(e) + logger.info(e) # xx-xx集 try: episode.append( - re.search("[\[ 第(]((合集)?\\\)?(\d{1,3}[ &]\d{1,3})(话| |]|\(全集\)|全集|fin)", str(file_name)).group(1)) + re.search( + "[\[ 第(]((合集)?\\\)?(\d{1,3}[ &]\d{1,3})(话| |]|\(全集\)|全集|fin)", + str(file_name), + ).group(1) + ) return episode except Exception as e: - logging.info(e) + logger.info(e) return None # 获取版本 @@ -360,22 +567,28 @@ class RSSInfoCleaner: # 中文 try: vision.append( - re.search("[((\[【]?(([\u4e00-\u9fa5]{0,5}|v\d)((版本?|修[复正]|WEB限定)|片源?|内详|(特别篇))(话|版|合?集?))[))\]】]?", - str(file_name)).group(1)) + re.search( + "[((\[【]?(([\u4e00-\u9fa5]{0,5}|v\d)((版本?|修[复正]|WEB限定)|片源?|内详|(特别篇))(话|版|合?集?))[))\]】]?", + str(file_name), + ).group(1) + ) except Exception as e: - logging.info(e) + logger.info(e) # 英文 try: vision.append( - re.search("[((\[【 ]\d{1,2}((v\d)((版本?|修复?正?版)|片源?|内详)?)[))\]】]", str(file_name)).group(1)) + re.search( + "[((\[【 ]\d{1,2}((v\d)((版本?|修复?正?版)|片源?|内详)?)[))\]】]", + str(file_name), + ).group(1) + ) except Exception as e: - logging.info(e) + logger.info(e) # [v2] try: - vision.append( - re.search("[((\[【 ](v\d)[))\]】]", str(file_name)).group(1)) + vision.append(re.search("[((\[【 ](v\d)[))\]】]", str(file_name)).group(1)) except Exception as e: - logging.info(e) + logger.info(e) if vision: return vision else: @@ -388,15 +601,24 @@ class RSSInfoCleaner: # 中文标示 try: ass.append( - re.search("[((\[【]?(附?([内外][挂嵌封][+&]?){1,2}(字幕|[简中日英]*音轨)?)[))\]】]?", str(file_name)).group(1)) + re.search( + "[((\[【]?(附?([内外][挂嵌封][+&]?){1,2}(字幕|[简中日英]*音轨)?)[))\]】]?", + str(file_name), + ).group(1) + ) except Exception as e: - logging.info(e) + logger.info(e) # 英文标示 try: ass.append( - re.search("[ ((\[【+](([ +]?(ass|pgs|srt)){1,3})[))\]】]?", str(file_name)).group(1).strip(" ")) + re.search( + "[ ((\[【+](([ +]?(ass|pgs|srt)){1,3})[))\]】]?", str(file_name) + ) + .group(1) + .strip(" ") + ) except Exception as e: - logging.info(e) + logger.info(e) if ass: return ass else: @@ -442,9 +664,15 @@ class RSSInfoCleaner: # 混合验证 def all_verity(self, raw_name): - self.zh_list = re_verity(self.zh_list, raw_name) if self.zh_list is not None else None - self.en_list = re_verity(self.en_list, raw_name) if self.en_list is not None else None - self.jp_list = re_verity(self.jp_list, raw_name) if self.jp_list is not None else None + self.zh_list = ( + re_verity(self.zh_list, raw_name) if self.zh_list is not None else None + ) + self.en_list = ( + re_verity(self.en_list, raw_name) if self.en_list is not None else None + ) + self.jp_list = ( + re_verity(self.jp_list, raw_name) if self.jp_list is not None else None + ) # 汇总信息 def get_clean_name(self): @@ -459,7 +687,7 @@ class RSSInfoCleaner: "ass": self.Tag.ass, "type": self.Tag.type, "code": self.Tag.code, - "source": self.Tag.source + "source": self.Tag.source, } # 字母全部小写 clean_name = self.Name.raw.lower() @@ -469,21 +697,42 @@ class RSSInfoCleaner: if v is not None: if type(v) is list: for i in v: - clean_name = clean_name.replace(i, "") if i is not None else clean_name + clean_name = ( + clean_name.replace(i, "") if i is not None else clean_name + ) else: clean_name = clean_name.replace(v, "") # 除杂 - x_list = ["pc&psp", "pc&psv", "movie", "bangumi.online", "donghua", "[_]", - "仅限港澳台地区", "话全", "第话", "第集", "全集", "字幕", "话", "集", "粤", "+", "@"] + x_list = [ + "pc&psp", + "pc&psv", + "movie", + "bangumi.online", + "donghua", + "[_]", + "仅限港澳台地区", + "话全", + "第话", + "第集", + "全集", + "字幕", + "话", + "集", + "粤", + "+", + "@", + ] for i in x_list: clean_name = clean_name.replace(i, "") # 去除多余空格 - clean_name = re.sub(' +', ' ', clean_name).strip(" ").strip("-").strip(" ") + clean_name = re.sub(" +", " ", clean_name).strip(" ").strip("-").strip(" ") # 去除空括号 # !!! 不能删 clean_name = clean_name.replace("][", "/") - xx = re.search("[\u4e00-\u9fa5\u3040-\u31ff ]([(\[。_])[\u4e00-\u9fa5\a-z]", clean_name) + xx = re.search( + "[\u4e00-\u9fa5\u3040-\u31ff ]([(\[。_])[\u4e00-\u9fa5\a-z]", clean_name + ) if xx is not None: clean_name = clean_name.replace(xx.group(1), "/") clean_name = re.sub("([(\[] *| *[)\]])", "", clean_name) @@ -497,7 +746,9 @@ class RSSInfoCleaner: self.Name.zh, self.Name.en, self.Name.jp = None, None, None # 国漫筛选 if "国漫" in self.Name.raw: - zh = re.search("-?([\u4e00-\u9fa5]{2,10})_?", self.Name.raw.replace("[国漫]", "")) + zh = re.search( + "-?([\u4e00-\u9fa5]{2,10})_?", self.Name.raw.replace("[国漫]", "") + ) if zh is not None: self.Name.zh = clean_list([zh.group()]) return @@ -508,12 +759,17 @@ class RSSInfoCleaner: if en is not None: self.Name.en = clean_list([en.group()]) return - elif re.search( + elif ( + re.search( "(^[\u4e00-\u9fa5\u3040-\u31ff\d:\-·??、.。,!]{1,20}[a-z\d]{,3} ?!?)([a-z\d:\-.。,,!! ]* ?)", - self.Name.clean) is not None: + self.Name.clean, + ) + is not None + ): res = re.search( "(^[\u4e00-\u9fa5\u3040-\u31ff\d:\-·??、.。,!]{1,20}[a-z\d]{,3} ?!?)[._&]?([a-z\d:\-.。,,!! ]* ?)", - self.Name.clean) + self.Name.clean, + ) zh = res.group(1) en = res.group(2) zh = re.search(zh, self.Name.raw.lower()) @@ -524,12 +780,17 @@ class RSSInfoCleaner: self.Name.en = clean_list([en.group()]) return # 英中 - elif re.search( + elif ( + re.search( "(^([a-z\d:\-_.。,,!! ]* ?) ?)[._&]?([\u4e00-\u9fa5\u3040-\u31ffa-z\d:\-_·??、.。,!! ]{1,20})", - self.Name.clean) is not None: + self.Name.clean, + ) + is not None + ): res = re.search( "(^([a-z\d:\-_.。,,!! ]* ?) ?)[._&]?([\u4e00-\u9fa5\u3040-\u31ffa-z\d:\-_·??、.。,!! ]{1,20})", - self.Name.clean) + self.Name.clean, + ) zh = res.group(3) en = res.group(1) @@ -547,7 +808,9 @@ class RSSInfoCleaner: return if debug > 0: print("初筛:\r\n%s\r\n%s\r\n%s" % (self.zh_list, self.en_list, self.jp_list)) - if (has_zh(self.Name.clean) or has_jp(self.Name.clean)) and has_en(self.Name.clean): + if (has_zh(self.Name.clean) or has_jp(self.Name.clean)) and has_en( + self.Name.clean + ): self.Name.clean = add_separator(self.Name.clean) self.easy_split(self.Name.clean, self.zh_list, self.en_list, self.jp_list) @@ -586,7 +849,7 @@ class RSSInfoCleaner: self.en_list.remove(i) self.zh_list.append(res.group()) except Exception as e: - logging.info(e) + logger.info(e) if debug > 0: print("拼合:\r\n%s\r\n%s\r\n%s" % (self.zh_list, self.en_list, self.jp_list)) # 再次验证,这里只能验raw名 diff --git a/AutoBangumi/app/RssFilter/fliter_base.py b/AutoBangumi/app/RssFilter/fliter_base.py index 6131a5fd..a3bd7d41 100644 --- a/AutoBangumi/app/RssFilter/fliter_base.py +++ b/AutoBangumi/app/RssFilter/fliter_base.py @@ -3,6 +3,7 @@ import logging import csv +logger = logging.getLogger(__name__) def read_data(file_name, start, rows): if file_name == "mikan": @@ -27,7 +28,7 @@ def add_separator(clean_name): clean_name).group(1) clean_name = clean_name.replace(res, res.strip(" ") + "/") except Exception as e: - logging.info(e) + logger.exception(e) else: try: res = re.search( @@ -35,9 +36,9 @@ def add_separator(clean_name): clean_name).group(1) clean_name = clean_name.replace(res, res.strip(" ") + "/") except Exception as e: - logging.info(e) + logger.exception(e) except Exception as e: - logging.info(e) + logger.exception(e) clean_name = re.sub("(/ */)", "/", clean_name) return clean_name @@ -48,10 +49,10 @@ def splicing(frag_list, name_list, raw_name): for i in range(0, len(name_list) - 1): if name_list[i] in name_list[i + 1] and name_list[i] != name_list[i + 1]: name_list.remove(name_list[i]) - elif raw_list[i + 1] in name_list[i] and name_list[i] != name_list[i + 1]: + elif name_list[i + 1] in name_list[i] and name_list[i] != name_list[i + 1]: name_list.remove(name_list[i + 1]) except Exception as e: - logging.info(e) + logger.info(e) min_list = sorted(name_list, key=lambda i: len(i), reverse=False) for i in range(0, len(min_list) - 1): # 处理中英文混合名 @@ -68,10 +69,10 @@ def splicing(frag_list, name_list, raw_name): name_list.remove(piece_name) name_list.append(r_name.group()) except Exception as e: - logging.warning("bug--%s" % e) - logging.warning("piece_name:%s,fragment:%s" % (piece_name, fragment)) + logger.warning("bug--%s" % e) + logger.warning("piece_name:%s,fragment:%s" % (piece_name, fragment)) except Exception as e: - print(e) + logger.exception(e) # 清理列表 @@ -89,7 +90,7 @@ def clean_list(raw_list): elif raw_list[i + 1] in raw_list[i] and raw_list[i] != raw_list[i + 1]: raw_list.remove(raw_list[i + 1]) except Exception as e: - logging.info(e) + logger.info(e) if raw_list is not None and len(raw_list) > 1: try: for i in range(0, len(raw_list)): @@ -97,7 +98,7 @@ def clean_list(raw_list): if up_list[i] in up_list[-1] and up_list[i] != up_list[-1]: raw_list.remove(up_list[i]) except Exception as e: - logging.info(e) + logger.info(e) if raw_list: return set(raw_list) else: @@ -119,7 +120,7 @@ def extract_title(raw_name): title["zh"] = res.group(1).strip(" ") title["en"] = res.group(3).strip(" ") except Exception as e: - logging.info(e) + logger.info(e) # 本程序依赖此bug运行,这行不能删 if title["zh"] is None: # 中英 @@ -130,7 +131,7 @@ def extract_title(raw_name): title["zh"] = res.group(1).strip(" ") title["en"] = res.group(3).strip(" ") except Exception as e: - logging.info(e) + logger.info(e) # 英中 try: res = re.search( @@ -139,7 +140,7 @@ def extract_title(raw_name): title["en"] = res.group(1).strip(" ") title["zh"] = res.group(3).strip(" ") except Exception as e: - logging.info(e) + logger.info(e) else: if has_zh(clean_name): # 中文 @@ -147,14 +148,14 @@ def extract_title(raw_name): res = re.search("(([\u4e00-\u9fa5:]{2,15}[ /]?){1,5}) *", clean_name) title["zh"] = res.group(1).strip(" ") except Exception as e: - logging.info(e) + logger.info(e) elif has_en(clean_name): # 英文 try: res = re.search("(([a-z:]{2,15}[ /]?){1,15}) *", clean_name) title["en"] = res.group(1).strip(" ") except Exception as e: - logging.info(e) + logger.info(e) for k, v in title.items(): if v is not None and "/" in v: zh_list = v.split("/") diff --git a/AutoBangumi/app/auto_set_rule.py b/AutoBangumi/app/auto_set_rule.py index 9be50f2c..c0108fcd 100644 --- a/AutoBangumi/app/auto_set_rule.py +++ b/AutoBangumi/app/auto_set_rule.py @@ -1,13 +1,16 @@ import re +import logging from env import EnvInfo import qbittorrentapi import json import os +logger = logging.getLogger(__name__) + class SetRule: def __init__(self): - with open(EnvInfo.info_path) as f: + with open(EnvInfo.info_path, encoding="utf-8") as f: self.info = json.load(f) self.bangumi_info = self.info["bangumi_info"] self.rss_link = EnvInfo.rss_link @@ -15,11 +18,12 @@ class SetRule: self.user_name = EnvInfo.user_name self.password = EnvInfo.password self.download_path = EnvInfo.download_path - self.qb = qbittorrentapi.Client(host=self.host_ip, username=self.user_name, password=self.password) + self.qb = qbittorrentapi.Client( + host=self.host_ip, username=self.user_name, password=self.password) try: self.qb.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.exception(e) def set_rule(self, bangumi_name, group, season): rule = { @@ -36,7 +40,7 @@ class SetRule: 'addPaused': False, 'assignedCategory': 'Bangumi', 'savePath': str(os.path.join(EnvInfo.download_path, re.sub(EnvInfo.rule_name_re, " ", bangumi_name).strip(), season)) - } + } if EnvInfo.enable_group_tag: rule_name = f"[{group}] {bangumi_name}" else: @@ -47,27 +51,27 @@ class SetRule: try: self.qb.rss_remove_item(item_path="Mikan_RSS") except qbittorrentapi.exceptions.Conflict409Error: - print(f"[{EnvInfo.time_show_obj}] No feed exists, starting adding feed.") + logger.debug("No feed exists, starting adding feed.") try: self.qb.rss_add_feed(url=self.rss_link, item_path="Mikan_RSS") - print(f"[{EnvInfo.time_show_obj}] Successes adding RSS Feed.") + logger.debug("Successes adding RSS Feed.") except ConnectionError: - print(f"[{EnvInfo.time_show_obj}] Error with adding RSS Feed.") + logger.debug("Error with adding RSS Feed.") except qbittorrentapi.exceptions.Conflict409Error: - print(f"[{EnvInfo.time_show_obj}] RSS Already exists.") + logger.debug("RSS Already exists.") def run(self): - print(f"[{EnvInfo.time_show_obj}] Start adding rules.") + logger.debug("Start adding rules.") for info in self.bangumi_info: if not info["added"]: self.set_rule(info["title"], info["group"], info["season"]) info["added"] = True with open(EnvInfo.info_path, 'w', encoding='utf8') as f: - json.dump(self.info, f, indent=4, separators=(',', ': '), ensure_ascii=False) - print(f"[{EnvInfo.time_show_obj}] Finished.") + json.dump(self.info, f, indent=4, separators=( + ',', ': '), ensure_ascii=False) + logger.debug("Finished.") if __name__ == "__main__": put = SetRule() put.run() - diff --git a/AutoBangumi/app/collect_bangumi_info.py b/AutoBangumi/app/collect_bangumi_info.py index 8794a36e..e37acb2b 100644 --- a/AutoBangumi/app/collect_bangumi_info.py +++ b/AutoBangumi/app/collect_bangumi_info.py @@ -1,6 +1,7 @@ # -*- coding: UTF-8 -*- import os import sys +import logging import requests from bs4 import BeautifulSoup import json @@ -8,34 +9,40 @@ import re from env import EnvInfo, BColors from RSSFilter import RSSInfoCleaner as Filter +logger = logging.getLogger(__name__) + + class MatchRule: split_rule = r"\[|\]|\【|\】|\★|\(|\)|\(|\)" last_rule = r"(.*)( \-)" sub_title = r"[^\x00-\xff]{1,}| \d{1,2}^.*|\·" match_rule = r"(S\d{1,2}(.*))" season_match = r"(.*)(Season \d{1,2}|S\d{1,2}|第.*季|第.*期)" + season_number_match = r"(\d+)" class CollectRSS: def __init__(self): self.bangumi_list = [] - with open(EnvInfo.rule_path) as r: + with open(EnvInfo.rule_path, encoding="utf-8") as r: self.rules = json.load(r) try: self.rules = requests.get(EnvInfo.rule_url).json() - with open(EnvInfo.rule_path, 'w') as f: - json.dump(self.rules, f, indent=4, separators=(',', ': '), ensure_ascii=False) + with open(EnvInfo.rule_path, "w", encoding="utf-8") as f: + json.dump( + self.rules, f, indent=4, separators=(",", ": "), ensure_ascii=False + ) except: - with open(EnvInfo.rule_path) as r: + with open(EnvInfo.rule_path, encoding="utf-8") as r: self.rules = json.load(r) try: - rss = requests.get(EnvInfo.rss_link, 'utf-8') + rss = requests.get(EnvInfo.rss_link, "utf-8") except: - print(f"[{EnvInfo.time_show_obj}] ERROR with DNS/Connection.") + logger.debug("ERROR with DNS/Connection.") quit() - soup = BeautifulSoup(rss.text, 'xml') - self.items = soup.find_all('item') - with open(EnvInfo.info_path) as i: + soup = BeautifulSoup(rss.text, "xml") + self.items = soup.find_all("item") + with open(EnvInfo.info_path, encoding="utf-8") as i: self.info = json.load(i) def get_info_list(self): @@ -43,40 +50,43 @@ class CollectRSS: name = item.title.string # debug 用 if EnvInfo.get_rule_debug: - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Raw {name}") + logger.debug(f"Raw {name}") exit_flag = False for rule in self.rules: for group in rule["group_name"]: if re.search(group, name): exit_flag = True n = re.split(MatchRule.split_rule, name) - while '' in n: - n.remove('') - while ' ' in n: - n.remove(' ') + while "" in n: + n.remove("") + while " " in n: + n.remove(" ") try: - bangumi_title = n[rule['name_position']].strip() + bangumi_title = n[rule["name_position"]].strip() except IndexError: continue - sub_title = re.sub(MatchRule.sub_title, "", bangumi_title) + sub_title = re.sub( + MatchRule.sub_title, "", bangumi_title) b = re.split(r"\/|\_", sub_title) - while '' in b: - b.remove('') - pre_name = max(b, key=len, default='').strip() + while "" in b: + b.remove("") + pre_name = max(b, key=len, default="").strip() if len(pre_name.encode()) > 3: bangumi_title = pre_name for i in range(2): - match_obj = re.match(MatchRule.last_rule, bangumi_title, re.I) + match_obj = re.match( + MatchRule.last_rule, bangumi_title, re.I + ) if match_obj is not None: bangumi_title = match_obj.group(1).strip() - match_obj = re.match(MatchRule.match_rule, bangumi_title, re.I) + match_obj = re.match( + MatchRule.match_rule, bangumi_title, re.I) if match_obj is not None: bangumi_title = match_obj.group(2).strip() if bangumi_title not in self.bangumi_list: - self.bangumi_list.append({ - "title": bangumi_title, - "group": group - }) + self.bangumi_list.append( + {"title": bangumi_title, "group": group} + ) # debug # print(bangumi_title) # print(group) @@ -84,7 +94,7 @@ class CollectRSS: if exit_flag: break if not exit_flag: - print(f"[{EnvInfo.time_show_obj}] ERROR Not match with {name}") + logger.debug("ERROR Not match with {name}") def put_info_json(self): had_data = [] @@ -92,29 +102,43 @@ class CollectRSS: for data in self.info["bangumi_info"]: had_data.append(data["title"]) else: - self.info = { - "rss_link": EnvInfo.rss_link, - "bangumi_info": [] - } + self.info = {"rss_link": EnvInfo.rss_link, "bangumi_info": []} for item in self.bangumi_list: - match_title_season = re.match(MatchRule.season_match, item["title"], re.I) + title = item["title"] + match_title_season = re.match(MatchRule.season_match, title, re.I) if match_title_season is not None: json_title = match_title_season.group(1).strip() json_season = match_title_season.group(2) + match_season_number = re.findall( + MatchRule.season_number_match, json_season + ) + if len(match_season_number) != 0: + json_season_number = int(match_season_number[0]) + else: + logger.warning( + f"title:{title} season:{json_season} can't match season in number" + ) + json_season_number = 1 else: - json_season = 'S01' - json_title = item["title"] + json_season = "S01" + json_season_number = 1 + json_title = title if json_title not in had_data: - self.info["bangumi_info"].append({ - "title": json_title, - "season": json_season, - "group": item["group"], - "added": False - }) + self.info["bangumi_info"].append( + { + "title": json_title, + "season": json_season, + "season_number": json_season_number, + "group": item["group"], + "added": False, + } + ) had_data.append(json_title) - print(f"[{EnvInfo.time_show_obj}] add {json_title} {json_season}") - with open(EnvInfo.info_path, 'w', encoding='utf8') as f: - json.dump(self.info, f, indent=4, separators=(',', ': '), ensure_ascii=False) + logger.debug("add {json_title} {json_season}") + with open(EnvInfo.info_path, "w", encoding="utf8") as f: + json.dump( + self.info, f, indent=4, separators=(",", ": "), ensure_ascii=False + ) def run(self): self.get_info_list() diff --git a/AutoBangumi/app/docker_main.py b/AutoBangumi/app/docker_main.py index 86f66329..cb43bf26 100644 --- a/AutoBangumi/app/docker_main.py +++ b/AutoBangumi/app/docker_main.py @@ -1,22 +1,36 @@ import os import time +import json +import logging + from collect_bangumi_info import CollectRSS from auto_set_rule import SetRule from rename_qb import qBittorrentRename -import json from env import EnvInfo +def setup_logger(): + DATE_FORMAT = "%Y-%m-%d %X" + LOGGING_FORMAT = "%(asctime)s %(levelname)s: %(message)s" + logging.basicConfig( + level=logging.DEBUG, + datefmt=DATE_FORMAT, + format=LOGGING_FORMAT, + encoding="utf-8", + ) + + def create_data_file(): if not os.path.exists(EnvInfo.info_path): - bangumi_info = {"rss_link": "", - "bangumi_info": [] - } + bangumi_info = {"rss_link": "", "bangumi_info": []} with open(EnvInfo.info_path, "w") as i: - json.dump(bangumi_info, i, indent=4, separators=(',', ': '), ensure_ascii=False) + json.dump( + bangumi_info, i, indent=4, separators=(",", ": "), ensure_ascii=False + ) if __name__ == "__main__": + setup_logger() create_data_file() SetRule().rss_feed() while True: diff --git a/AutoBangumi/app/env.py b/AutoBangumi/app/env.py index 416c6ac4..7ddab44b 100644 --- a/AutoBangumi/app/env.py +++ b/AutoBangumi/app/env.py @@ -36,9 +36,6 @@ class EnvInfo: # Static ENV rule_url = "https://raw.githubusercontent.com/EstrellaXD/Bangumi_Auto_Collector/main/AutoBangumi/config/rule.json" - def time_show_obj(self): - return time.strftime('%Y-%m-%d %X') - rule_name_re = r"\:|\/|\." diff --git a/AutoBangumi/app/get_full_season.py b/AutoBangumi/app/get_full_season.py index 18f0b56e..7cc58576 100644 --- a/AutoBangumi/app/get_full_season.py +++ b/AutoBangumi/app/get_full_season.py @@ -3,10 +3,13 @@ import requests from qbittorrentapi import Client from env import EnvInfo, Other from bs4 import BeautifulSoup +import logging + +logger = logging.getLogger(__name__) class FullSeasonGet: - def __init__(self,group, bangumi_name, season): + def __init__(self, group, bangumi_name, season): self.torrents = None self.bangumi_name = bangumi_name self.group = group @@ -14,23 +17,29 @@ class FullSeasonGet: def get_season_rss(self): if self.season == "S01": - season = '' + season = "" else: season = self.season - season = requests.get(f"https://mikanani.me/RSS/Search?searchstr={self.group}+{self.bangumi_name}+{season}") - soup = BeautifulSoup(season.content, 'xml') - self.torrents = soup.find_all('enclosure') + season = requests.get( + f"https://mikanani.me/RSS/Search?searchstr={self.group}+{self.bangumi_name}+{season}") + soup = BeautifulSoup(season.content, "xml") + self.torrents = soup.find_all("enclosure") def add_torrents(self): - qb = Client(host=EnvInfo.host_ip, username=EnvInfo.user_name, password=EnvInfo.password) + qb = Client( + host=EnvInfo.host_ip, username=EnvInfo.user_name, password=EnvInfo.password + ) try: qb.auth_log_in() except: - print('Error') + logger.error("Error") for torrent in self.torrents: qb.torrents_add( urls=torrent["url"], - save_path=str(os.path.join(EnvInfo.download_path, self.bangumi_name, self.season)), + save_path=str( + os.path.join(EnvInfo.download_path, + self.bangumi_name, self.season) + ), category="Bangumi", ) @@ -41,7 +50,7 @@ class FullSeasonGet: if __name__ == "__main__": - a = FullSeasonGet('Lilith-Raws', 'Shijou Saikyou no Daimaou', 'S01') + a = FullSeasonGet("Lilith-Raws", "Shijou Saikyou no Daimaou", "S01") a.run() for torrent in a.torrents: - print(torrent['url']) + logger.debug(torrent["url"]) diff --git a/AutoBangumi/app/rename_qb.py b/AutoBangumi/app/rename_qb.py index 7ce1cfe8..474c2da1 100644 --- a/AutoBangumi/app/rename_qb.py +++ b/AutoBangumi/app/rename_qb.py @@ -1,95 +1,84 @@ import re -import sys import qbittorrentapi -import time +import logging + from env import EnvInfo +logger = logging.getLogger(__name__) + class qBittorrentRename: def __init__(self): - self.qbt_client = qbittorrentapi.Client(host=EnvInfo.host_ip, - username=EnvInfo.user_name, - password=EnvInfo.password) + self.qbt_client = qbittorrentapi.Client( + host=EnvInfo.host_ip, username=EnvInfo.user_name, password=EnvInfo.password + ) try: self.qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) - self.recent_info = self.qbt_client.torrents_info(status_filter='completed',category="Bangumi") - self.hash = None - self.name = None - self.new_name = None - self.path_name = None + logger.exception(e) + self.recent_info = self.qbt_client.torrents_info( + status_filter="completed", category="Bangumi" + ) self.count = 0 self.rename_count = 0 self.torrent_count = len(self.recent_info) - self.rules = [r'(.*)\[(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)', - r'(.*)\[E(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)', - r'(.*)\[第(\d*\.*\d*)话(?:END)?\](.*)', - r'(.*)\[第(\d*\.*\d*)話(?:END)?\](.*)', - r'(.*)第(\d*\.*\d*)话(?:END)?(.*)', - r'(.*)第(\d*\.*\d*)話(?:END)?(.*)', - r'(.*)- (\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)? (.*)'] + rules = [ + r"(.*)\[(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)", + r"(.*)\[E(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)", + r"(.*)\[第(\d*\.*\d*)话(?:END)?\](.*)", + r"(.*)\[第(\d*\.*\d*)話(?:END)?\](.*)", + r"(.*)第(\d*\.*\d*)话(?:END)?(.*)", + r"(.*)第(\d*\.*\d*)話(?:END)?(.*)", + r"(.*)- (\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)? (.*)", + ] + self.rules = [re.compile(rule) for rule in rules] - def rename_normal(self, idx): - self.name = self.recent_info[idx].name - self.hash = self.recent_info[idx].hash - self.path_name = self.recent_info[idx].content_path.split("/")[-1] - file_name = self.name + def rename_normal(self, name): for rule in self.rules: - matchObj = re.match(rule, file_name, re.I) + matchObj = rule.match(name, re.I) if matchObj is not None: - self.new_name = f'{matchObj.group(1).strip()} E{matchObj.group(2)}{matchObj.group(3)}' + new_name = f"{matchObj.group(1).strip()} E{matchObj.group(2)}{matchObj.group(3)}" + return new_name - def rename_pn(self, idx): - self.name = self.recent_info[idx].name - self.hash = self.recent_info[idx].hash - self.path_name = self.recent_info[idx].content_path.split("/")[-1] - n = re.split(r'\[|\]', self.name) - file_name = self.name.replace(f'[{n[1]}]', '') + def rename_pn(self, name): + n = re.split(r"\[|\]", name) + file_name = name.replace(f"[{n[1]}]", "") for rule in self.rules: - matchObj = re.match(rule, file_name, re.I) + matchObj = rule.match(file_name, re.I) if matchObj is not None: - self.new_name = re.sub(r'\[|\]', '', f'{matchObj.group(1).strip()} E{matchObj.group(2)}{n[-1]}') + new_name = re.sub( + r"\[|\]", + "", + f"{matchObj.group(1).strip()} E{matchObj.group(2)}{n[-1]}", + ) + return new_name - def rename(self): - if self.path_name != self.new_name: - self.qbt_client.torrents_rename_file(torrent_hash=self.hash, old_path=self.path_name, new_path=self.new_name) - print(f"[{time.strftime('%Y-%m-%d %X')}] {self.path_name} >> {self.new_name}") + def rename_torrent_file(self, hash, path_name, new_name): + if path_name != new_name: + self.qbt_client.torrents_rename_file( + torrent_hash=hash, old_path=path_name, new_path=new_name + ) + logger.debug(f"{path_name} >> {new_name}") self.count += 1 - else: - return - - def clear_info(self): - self.name = None - self.hash = None - self.new_name = None - self.path_name = None def print_result(self): - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 已完成对{self.torrent_count}个文件的检查" + '\n') - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 已对其中{self.count}个文件进行重命名" + '\n') - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 完成" + '\n') - sys.stdout.flush() + logger.debug(f"已完成对{self.torrent_count}个文件的检查") + logger.debug(f"已对其中{self.count}个文件进行重命名") + logger.debug(f"完成") def run(self): - if EnvInfo.method not in ['pn', 'normal']: - print('error method') - elif EnvInfo.method == 'normal': - for i in range(0, self.torrent_count + 1): + method_dict = {"pn": self.rename_pn, "normal": self.rename_normal} + if EnvInfo.method not in method_dict: + logger.error(f"error method") + else: + for i in range(0, self.torrent_count): try: - self.rename_normal(i) - self.rename() - self.clear_info() + info = self.recent_info[i] + name = info.name + hash = info.hash + path_name = info.content_path.split("/")[-1] + new_name = method_dict[EnvInfo.method](name) + self.rename_torrent_file(hash, path_name, new_name) except: - self.print_result() - elif EnvInfo.method == 'pn': - for i in range(0, self.torrent_count + 1): - try: - self.rename_pn(i) - self.rename() - self.clear_info() - except: - self.print_result() - - - + logger.warning(f"{name} rename fail") + self.print_result() diff --git a/Windows/win_main.py b/Windows/win_main.py index f1db4ac3..d3eab462 100644 --- a/Windows/win_main.py +++ b/Windows/win_main.py @@ -3,6 +3,7 @@ import re import sys import time import json +import logging import qbittorrentapi import requests @@ -12,6 +13,7 @@ import requests.packages.urllib3.util.ssl_ requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = 'ALL' +logger = logging.getLogger(__name__) class EnvInfo: if getattr(sys, 'frozen', False): @@ -31,7 +33,6 @@ class EnvInfo: method = info["method"] # rss_link = "https://mikanani.me/RSS/MyBangumi?token=Td8ceWZZv3s2OZm5ji9RoMer8vk5VS3xzC1Hmg8A26E%3d" rule_url = "https://raw.githubusercontent.com/EstrellaXD/Bangumi_Auto_Collector/main/AutoBangumi/config/rule.json" - time_show_obj = time.strftime('%Y-%m-%d %X') bangumi_info = info["bangumi_info"] @@ -47,7 +48,7 @@ class SetRule: try: self.qb.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.exception(e) def set_rule(self, bangumi_name, season): rule = { @@ -71,19 +72,17 @@ class SetRule: try: self.qb.rss_remove_item(item_path="Mikan_RSS") self.qb.rss_add_feed(url=self.rss_link, item_path="Mikan_RSS") - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Successes adding RSS Feed." + "\n") + logger.debug("Successes adding RSS Feed." + "\n") except ConnectionError: - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Error with adding RSS Feed." + "\n") + logger.debug("Error with adding RSS Feed." + "\n") except qbittorrentapi.exceptions.Conflict409Error: - sys.stdout.write(f"[{EnvInfo.time_show_obj}] RSS Already exists." + "\n") + logger.debug("RSS Already exists." + "\n") def run(self): - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Start adding rules." + "\n") - sys.stdout.flush() + logger.debug("Start adding rules." + "\n") for info in self.bangumi_info: self.set_rule(info["title"], info["season"]) - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Finished." + "\n") - sys.stdout.flush() + logger.debug("Finished." + "\n") class MatchRule: @@ -100,7 +99,7 @@ class CollectRSS: try: self.rules = requests.get(EnvInfo.rule_url).json() except ConnectionError: - sys.stdout.write(f"[{EnvInfo.time_show_obj}] Get rules Erroe=r") + logger.debug(" Get rules Erroe=r") rss = requests.get(EnvInfo.rss_link, 'utf-8') soup = BeautifulSoup(rss.text, 'xml') self.items = soup.find_all('item') @@ -147,7 +146,7 @@ class CollectRSS: if exit_flag: break if not exit_flag: - print(f"[{EnvInfo.time_show_obj}] ERROR Not match with {name}") + logger.debug("ERROR Not match with {name}") def put_info_json(self): had_data = [] @@ -166,8 +165,7 @@ class CollectRSS: "title": json_title, "season": json_season }) - sys.stdout.write(f"[{EnvInfo.time_show_obj}] add {json_title} {json_season}" + "\n") - sys.stdout.flush() + logger.debug("add {json_title} {json_season}" + "\n") EnvInfo.info["bangumi_info"] = self.info with open(EnvInfo.path, 'w', encoding='utf8') as f: data = json.dumps(EnvInfo.info, indent=4, separators=(',', ': '), ensure_ascii=False) @@ -187,7 +185,7 @@ class qBittorrentRename: try: self.qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.debug(e) self.recent_info = self.qbt_client.torrents_info(status_filter='completed', category="Bangumi") self.hash = None self.name = None @@ -229,7 +227,7 @@ class qBittorrentRename: if self.path_name != self.new_name: self.qbt_client.torrents_rename_file(torrent_hash=self.hash, old_path=self.path_name, new_path=self.new_name) - sys.stdout.write(f"[{time.strftime('%Y-%m-%d %X')}] {self.path_name} >> {self.new_name}") + logger.debug(f"{self.path_name} >> {self.new_name}") self.count += 1 else: return @@ -241,14 +239,13 @@ class qBittorrentRename: self.path_name = None def print_result(self): - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 已完成对{self.torrent_count}个文件的检查" + '\n') - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 已对其中{self.count}个文件进行重命名" + '\n') - sys.stdout.write(f"[{EnvInfo.time_show_obj}] 完成" + '\n') - sys.stdout.flush() + logger.debug("已完成对{self.torrent_count}个文件的检查") + logger.debug("已对其中{self.count}个文件进行重命名") + logger.debug("完成") def run(self): if EnvInfo.method not in ['pn', 'normal']: - print('error method') + logger.error('error method') elif EnvInfo.method == 'normal': for i in range(0, self.torrent_count + 1): try: diff --git a/old version/Docker/rename_qb.py b/old version/Docker/rename_qb.py index f10a1404..6ada288b 100644 --- a/old version/Docker/rename_qb.py +++ b/old version/Docker/rename_qb.py @@ -4,6 +4,9 @@ import sys import qbittorrentapi from os import environ import time +import logging + +logger = logging.getLogger(__name__) host_ip = environ['HOST'] user_name = environ['USER'] @@ -22,8 +25,6 @@ episode_rules = [r'(.*)\[(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)', r'(.*)- (\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)? (.*)'] # Suffixs of files we are going to rename suffixs = ['mp4', 'mkv', 'avi', 'mov', 'flv', 'rmvb', 'ass', 'idx'] -sys.stdout = io.TextIOWrapper(buffer=sys.stdout.buffer, encoding='utf8') - class QbittorrentRename: def __init__(self, rename_method): @@ -31,7 +32,7 @@ class QbittorrentRename: try: self.qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.exception(e) self.recent_info = self.qbt_client.torrents_info(status_filter='completed') self.hash = None self.name = None @@ -66,8 +67,7 @@ class QbittorrentRename: def rename(self): if self.path_name != self.new_name: self.qbt_client.torrents_rename_file(torrent_hash=self.hash, old_path=self.path_name, new_path=self.new_name) - sys.stdout.write(f"[{time.strftime('%X')}] {self.path_name} >> {self.new_name}" + '\n') - sys.stdout.flush() + logger.debug("{self.path_name} >> {self.new_name}") self.count += 1 else: return @@ -79,14 +79,13 @@ class QbittorrentRename: self.path_name = None def print_result(self): - sys.stdout.write(f"[{time.strftime('%X')}] 已完成对{self.torrent_count}个文件的检查" + '\n') - sys.stdout.write(f"[{time.strftime('%X')}] 已对其中{self.count}个文件进行重命名" + '\n') - sys.stdout.write(f"[{time.strftime('%X')}] 完成" + '\n') - sys.stdout.flush() + logger.debug("已完成对{self.torrent_count}个文件的检查") + logger.debug("已对其中{self.count}个文件进行重命名") + logger.debug("完成") def rename_app(self): if self.method not in ['pn', 'normal']: - print('error method') + logger.error('error method') elif self.method == 'normal': for i in range(0, self.torrent_count + 1): try: @@ -106,8 +105,7 @@ class QbittorrentRename: def rename_main(): - sys.stdout.write(f"[{time.strftime('%X')}] Program start." + '\n') - sys.stdout.flush() + logger.debug("Program start.") while True: rename = QbittorrentRename(method) rename.rename_app() diff --git a/old version/rename_qb.py b/old version/rename_qb.py index 717e8b56..275fb98d 100644 --- a/old version/rename_qb.py +++ b/old version/rename_qb.py @@ -3,10 +3,13 @@ import io import sys import os.path as op import time +import logging import qbittorrentapi import json +logger = logging.getLogger(__name__) + with open("config.json") as f: server_info = json.load(f) host_ip = "http://"+server_info['host_ip'] @@ -25,7 +28,6 @@ episode_rules = [r'(.*)\[(\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?\](.*)', r'(.*)- (\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)? (.*)'] # Suffixs of files we are going to rename suffixs = ['mp4', 'mkv', 'avi', 'mov', 'flv', 'rmvb', 'ass', 'idx'] -sys.stdout = io.TextIOWrapper(buffer=sys.stdout.buffer, encoding='utf8') class QbittorrentRename: @@ -34,7 +36,7 @@ class QbittorrentRename: try: self.qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.exception(e) self.recent_info = self.qbt_client.torrents_info(status_filter='completed') self.hash = None self.name = None @@ -68,7 +70,7 @@ class QbittorrentRename: def rename(self): if self.path_name != self.new_name: self.qbt_client.torrents_rename_file(torrent_hash=self.hash, old_path=self.path_name, new_path=self.new_name) - print(f"[{time.strftime('%X')}] {self.path_name} >> {self.new_name}") + logger.debug("{self.path_name} >> {self.new_name}") self.count += 1 else: return @@ -79,13 +81,13 @@ class QbittorrentRename: self.new_name = None def print_result(self): - print(f"[{time.strftime('%X')}] 已完成对{self.torrent_count}个文件的检查") - print(f"[{time.strftime('%X')}] 已对其中{self.count}个文件进行重命名") - print(f"[{time.strftime('%X')}] 完成") + logger.debug(f"已完成对{self.torrent_count}个文件的检查") + logger.debug(f"已对其中{self.count}个文件进行重命名") + logger.debug(f"完成") def rename_app(self): if self.method not in ['pn', 'normal']: - print('error method') + logger.error('error method') elif self.method == 'normal': for i in range(0, self.torrent_count + 1): try: diff --git a/old version/rule_set.py b/old version/rule_set.py index 72cc0ef1..bdc761aa 100644 --- a/old version/rule_set.py +++ b/old version/rule_set.py @@ -3,6 +3,9 @@ import qbittorrentapi import json import argparse import os +import logging + +logger = logging.getLogger(__name__) f = open("config.json") server_info = json.load(f) @@ -22,7 +25,7 @@ def rule_set(): try: qbt_client.auth_log_in() except qbittorrentapi.LoginFailed as e: - print(e) + logger.exception(e) args = parser.parse_args() bangumi_name = args.name rule = {'enable': True,