This commit is contained in:
Sean
2022-06-04 01:11:32 +08:00
parent 0137b71e5b
commit e3c76754d8
51 changed files with 131 additions and 359 deletions

23
.dockerignore Normal file
View File

@@ -0,0 +1,23 @@
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.log
.git
.mypy_cache
.pytest_cache
.hypothesis
auto_bangumi/const_dev.py
config/bangumi.json

3
.gitignore vendored
View File

@@ -160,4 +160,5 @@ cython_debug/
#.idea/
# Custom
/AutoBangumi/config/bangumi.json
/auto_bangumi/const_dev.py
/config/bangumi.json

4
.vscode/launch.json vendored
View File

@@ -8,11 +8,11 @@
"name": "Python: app",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/AutoBangumi/app/app.py",
"program": "${workspaceFolder}/auto_bangumi/app.py",
"args": [
"-d"
],
"cwd": "${workspaceFolder}/AutoBangumi/app",
"cwd": "${workspaceFolder}/auto_bangumi",
"console": "integratedTerminal",
"justMyCode": true
},

11
.vscode/settings.json vendored
View File

@@ -1,3 +1,12 @@
{
"python.formatting.provider": "black"
"python.formatting.provider": "black",
"python.testing.unittestArgs": [
"-v",
"-s",
"./tests",
"-p",
"test_*.py"
],
"python.testing.pytestEnabled": false,
"python.testing.unittestEnabled": true,
}

View File

@@ -1,22 +0,0 @@
# syntax=docker/dockerfile:1
FROM python:3.10-slim-buster
WORKDIR /auto-bangumi
COPY requirements.txt .
RUN pip install -r requirements.txt
ENV TZ=Asia/Shanghai
ENV TIME=1800
ENV HOST=localhost:8080
ENV USER=admin
ENV PASSWORD=adminadmin
ENV METHOD=pn
ENV GROUP_TAG=False
ENV NOT_CONTAIN=720
ENV RULE_DEBUG=False
ENV EP_COMPLETE=True
COPY ./app /app
COPY ./config /config
CMD [ "python3", "/app/app.py"]

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
# syntax=docker/dockerfile:1
FROM python:3.10-slim-buster
WORKDIR /auto_bangumi
ADD requirements.txt .
RUN pip install -r requirements.txt
ADD ./auto_bangumi /auto_bangumi
ADD ./config /config
CMD [ "python3", "app.py"]

View File

@@ -10,8 +10,8 @@
<img title="platform arch" src="https://img.shields.io/badge/arch-%20AMD64%20%2F%20ARM64-lightgrey" alt="">
</p>
# 项目说明
<p align="center">
<img title="mikan project" src="https://mikanani.me/images/mikan-pic.png" alt="" width="10%">
<img title="qbittorrent" src="https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/New_qBittorrent_Logo.svg/600px-New_qBittorrent_Logo.svg.png" width="10%">
@@ -29,7 +29,6 @@
- 修改文件名但是不破坏作种
- 傻瓜式文件整理
## 相关文档和群组
- [AutoBangumi V2 简易说明](https://www.craft.do/s/4viN6M3tBqigLp)
@@ -38,11 +37,13 @@
- Bug 反馈群:[Telegram](https://t.me/+yNisOnDGaX5jMTM9)
# 部署说明
1. 安装 qBittorrent:
2. 用 Docker 部署 `AutoBangumi` :
最简部署方法
```dash
docker run -d \
--name=AutoBangumi \
@@ -53,6 +54,7 @@ docker run -d \
--restart unless-stopped \
estrellaxd/auto_bangumi:latest
```
**进阶部署:**
Docker-Compose
@@ -101,6 +103,7 @@ docker run -d \
--restart unless-stopped \
estrellaxd/auto_bangumi:latest
```
### 参数说明
| 环境变量 | 作用 | 参数 |
@@ -119,8 +122,8 @@ docker run -d \
- `TIME` : 程序运行的间隔时间,默认为 `1800` 也就是 30 分钟,如果更新时间要求比较高可以适当降低该值。
- `HOST`, `USER`, `PASSWORD`: qBittorrent 的地址,用户名,密码。
- `METHOD`: 重命名规则
- `pn`: Pure Name 模式,去掉所有字幕组以及番剧额外信息,只保留名称、季度和集数。
- `normal`: 正常模式,仅重命名会影响搜刮的非正常字符。
- `pn`: Pure Name 模式,去掉所有字幕组以及番剧额外信息,只保留名称、季度和集数。
- `normal`: 正常模式,仅重命名会影响搜刮的非正常字符。
- `GROUP_TAG`: 开启后自动在自动下载规则中创建组名,方便管理。
- `DOWNLOAD_PATH`: qBittorrent 的下载地址。
- `RSS`: Mikan Project 的个人 RSS 订阅链接
@@ -161,13 +164,16 @@ docker run -d \
## Roadmap
***开发中的功能:***
- RSS 解析器AutoBangumi 可以自行解析分析种子无需依赖下载器。
- Transmission & Aria2 的支持。
- 遗漏番剧下载:中间开始追番可以补全之前的剧集。
***计划开发的功能:***
- Web UI
- 更为智能细致的分类预设。
# 声明
本项目的自动改名规则根据 [miracleyoo/anime_renamer](https://github.com/miracleyoo/anime_renamer) 项目

View File

Can't render this file because it is too large.

View File

Can't render this file because it is too large.

4
auto_bangumi/__main__.py Normal file
View File

@@ -0,0 +1,4 @@
import app
if __name__ == "__main__":
app.run()

View File

@@ -33,8 +33,10 @@ def save_data_file(bangumi_data):
def run():
args = parse()
if args.debug:
from const_dev import DEV_SETTINGS
try:
from const_dev import DEV_SETTINGS
except ModuleNotFoundError:
logger.debug("Please copy `const_dev.sample.py` to `const_dev.py` to use custom settings")
settings.init(DEV_SETTINGS)
else:
settings.init()

View File

@@ -1,8 +1,8 @@
import logging
import re
from bangumi_parser.episode import Episode
from utils import json_config
from conf import settings
from bangumi_parser.episode import Episode
logger = logging.getLogger(__name__)
@@ -81,7 +81,10 @@ class ParserLV2:
def process(self, raw_name):
raw_name = raw_name.replace("", "[").replace("", "]")
match_obj = re.match(r"(.*|\[.*])( -? \d{1,3} |\[\d{1,3}]|\[\d{1,3}.?[vV]\d{1}]|[第第]\d{1,3}[话話集集]|\[\d{1,3}.?END])(.*)", raw_name)
match_obj = re.match(
r"(.*|\[.*])( -? \d{1,3} |\[\d{1,3}]|\[\d{1,3}.?[vV]\d{1}]|[第第]\d{1,3}[话話集集]|\[\d{1,3}.?END])(.*)",
raw_name,
)
name_season = self.pre_process(match_obj.group(1))
name, season_number, season_raw = self.season_process(name_season)
name = self.name_process(name).strip()
@@ -124,8 +127,13 @@ if __name__ == "__main__":
print(season)
print(episode)
except:
if re.search(r"\d{1,3}[-~]\d{1,3}|OVA|BD|電影|剧场版|老番|冷番|OAD|合集|劇場版|柯南|海賊王|蜡笔小新|整理|樱桃小丸子", name) is None:
if (
re.search(
r"\d{1,3}[-~]\d{1,3}|OVA|BD|電影|剧场版|老番|冷番|OAD|合集|劇場版|柯南|海賊王|蜡笔小新|整理|樱桃小丸子",
name,
)
is None
):
print(f"{BCOLORS._(BCOLORS.HEADER, name)}")
err_count += 1
print(BCOLORS._(BCOLORS.WARNING, err_count))

View File

@@ -1,10 +1,7 @@
# -*- encoding: utf-8 -*-
from math import fabs
DEFAULT_SETTINGS = {
"host_ip": "192.168.31.10:10101",
"host_ip": "localhost:8080",
"sleep_time": 1800,
"user_name": "admin",
"password": "adminadmin",
@@ -22,17 +19,20 @@ DEFAULT_SETTINGS = {
}
ENV_TO_ATTR = {
"HOST": "host_ip",
"TIME": ("sleep_time", lambda e: float(e)),
"USER": "user_name",
"PASSWORD": "password",
"RSS": "rss_link",
"DOWNLOAD_PATH": "download_path",
"METHOD": "method",
"GROUP_TAG": ("enable_group_tag", lambda e: e.lower() in ("true", "1", "t")),
"NOT_CONTAIN": "not_contain",
"RULE_DEBUG": ("get_rule_debug", lambda e: e.lower() in ("true", "1", "t")),
"EP_COMPLETE": ("enable_eps_complete", lambda e: e.lower() in ("true", "1", "t")),
"AB_DOWNLOADER_HOST": "host_ip",
"AB_INTERVAL_TIME": ("sleep_time", lambda e: float(e)),
"AB_DOWNLOADER_USERNAME": "user_name",
"AB_DOWNLOADER_PASSWORD": "password",
"AB_RSS": "rss_link",
"AB_DOWNLOAD_PATH": "download_path",
"AB_METHOD": "method",
"AB_GROUP_TAG": ("enable_group_tag", lambda e: e.lower() in ("true", "1", "t")),
"AB_NOT_CONTAIN": "not_contain",
"AB_RULE_DEBUG": ("get_rule_debug", lambda e: e.lower() in ("true", "1", "t")),
"AB_EP_COMPLETE": (
"enable_eps_complete",
lambda e: e.lower() in ("true", "1", "t"),
),
}
FULL_SEASON_SUPPORT_GROUP = ["Lilith-Raws"]
@@ -40,8 +40,9 @@ FULL_SEASON_SUPPORT_GROUP = ["Lilith-Raws"]
class BCOLORS:
@staticmethod
def _(color: str, string: str) -> str:
return f"{color}{string}{BCOLORS.ENDC}"
def _(color: str, *args: str) -> str:
strings = [str(s) for s in args]
return f"{color}{', '.join(strings)}{BCOLORS.ENDC}"
HEADER = "\033[95m"
OKBLUE = "\033[94m"

View File

@@ -1,8 +1,8 @@
DEV_SETTINGS = {
"host_ip": "192.168.31.10:10101",
"host_ip": "localhost:8080",
"user_name": "admin",
"password": "adminadmin",
"rss_link": "https://mikanani.me/RSS/MyBangumi?token=Td8ceWZZv3s2OZm5ji9RoMer8vk5VS3xzC1Hmg8A26E%3d",
"rss_link": "https://mikanani.me/RSS/classic",
"sleep_time": 10,
"info_path": "../config/bangumi.json",
"rule_path": "../config/rule.json",

View File

@@ -70,7 +70,7 @@ class DownloadClient:
def eps_collect(self, bangumi_info):
logger.debug("Start collect past eps.")
for info in bangumi_info:
if not info["download_past"]:
if "download_past" not in info:
FullSeasonGet(info["group"], info["title"], info["season"]).run()

View File

@@ -1,8 +1,8 @@
import re
import logging
from core.download_client import DownloadClient
from conf import settings
from core.download_client import DownloadClient
logger = logging.getLogger(__name__)

View File

@@ -1,12 +0,0 @@
# syntax=docker/dockerfile:1
FROM python:3.10-slim-buster
WORKDIR /app
COPY requirements.txt requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
CMD [ "python3", "rename_qb.py"]

View File

@@ -1,116 +0,0 @@
import re
import io
import sys
import qbittorrentapi
from os import environ
import time
import logging
logger = logging.getLogger(__name__)
host_ip = environ['HOST']
user_name = environ['USER']
password = environ['PASSWORD']
method = environ['METHOD']
delay_time = environ['TIME']
# Episode Regular Expression Matching Rules
episode_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)? (.*)']
# Suffixs of files we are going to rename
suffixs = ['mp4', 'mkv', 'avi', 'mov', 'flv', 'rmvb', 'ass', 'idx']
class QbittorrentRename:
def __init__(self, rename_method):
self.qbt_client = qbittorrentapi.Client(host=host_ip, username=user_name, password=password)
try:
self.qbt_client.auth_log_in()
except qbittorrentapi.LoginFailed as e:
logger.exception(e)
self.recent_info = self.qbt_client.torrents_info(status_filter='completed')
self.hash = None
self.name = None
self.new_name = None
self.path_name = None
self.count = 0
self.rename_count = 0
self.torrent_count = len(self.recent_info)
self.method = rename_method
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
for rule in episode_rules:
matchObj = re.match(rule, file_name, re.I)
if matchObj is not None:
self.new_name = f'{matchObj.group(1).strip()} E{matchObj.group(2)}{matchObj.group(3)}'
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]}]', '')
for rule in episode_rules:
matchObj = re.match(rule, 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]}')
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)
logger.debug("{self.path_name} >> {self.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):
logger.debug("已完成对{self.torrent_count}个文件的检查")
logger.debug("已对其中{self.count}个文件进行重命名")
logger.debug("完成")
def rename_app(self):
if self.method not in ['pn', 'normal']:
logger.error('error method')
elif self.method == 'normal':
for i in range(0, self.torrent_count + 1):
try:
self.rename_normal(i)
self.rename()
self.clear_info()
except:
self.print_result()
elif self.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()
def rename_main():
logger.debug("Program start.")
while True:
rename = QbittorrentRename(method)
rename.rename_app()
time.sleep(float(delay_time))
if __name__ == "__main__":
rename_main()

View File

@@ -1 +0,0 @@
qbittorrent_api==2022.4.30

View File

@@ -1,7 +0,0 @@
{
"host_ip": "192.168.31.10:8181",
"username": "admin",
"password": "adminadmin",
"savepath": "/downloads/Bangumi",
"method": "pn"
}

View File

@@ -1,111 +0,0 @@
import re
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 = "192.168.1.18:6363"
user_name = "admin"
password = "adminadmin"
log_name = op.join(op.dirname(op.realpath(__file__)), 'log.txt')
method = "pn"
# Episode Regular Expression Matching Rules
episode_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)? (.*)']
# Suffixs of files we are going to rename
suffixs = ['mp4', 'mkv', 'avi', 'mov', 'flv', 'rmvb', 'ass', 'idx']
class QbittorrentRename:
def __init__(self, rename_method):
self.qbt_client = qbittorrentapi.Client(host=host_ip, username=user_name, password=password)
try:
self.qbt_client.auth_log_in()
except qbittorrentapi.LoginFailed as e:
logger.exception(e)
self.recent_info = self.qbt_client.torrents_info(status_filter='completed')
self.hash = None
self.name = None
self.new_name = None
self.path_name = None
self.count = 0
self.rename_count = 0
self.torrent_count = len(self.recent_info)
self.method = rename_method
def rename_normal(self, idx):
self.name = self.recent_info[idx].name
self.hash = self.recent_info[idx].hash
file_name = self.name
for rule in episode_rules:
matchObj = re.match(rule, file_name, re.I)
if matchObj is not None:
self.new_name = f'{matchObj.group(1)} E{matchObj.group(2)} {matchObj.group(3)}'
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]}]', '')
for rule in episode_rules:
matchObj = re.match(rule, 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]}')
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)
logger.debug("{self.path_name} >> {self.new_name}")
self.count += 1
else:
return
def clear_info(self):
self.name = None
self.hash = None
self.new_name = None
def print_result(self):
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']:
logger.error('error method')
elif self.method == 'normal':
for i in range(0, self.torrent_count + 1):
try:
self.rename_normal(i)
self.rename()
self.clear_info()
except:
self.print_result()
elif self.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()
if __name__ == "__main__":
rename = QbittorrentRename(method)
rename.rename_app()

View File

@@ -1,5 +0,0 @@
qbittorrent_api==2022.4.30
requests~=2.27.1
bs4~=0.0.1
beautifulsoup4~=4.11.1

View File

@@ -1,49 +0,0 @@
# -*- coding: UTF-8 -*-
import qbittorrentapi
import json
import argparse
import os
import logging
logger = logging.getLogger(__name__)
f = open("config.json")
server_info = json.load(f)
host_ip = "http://" + server_info['host_ip']
user_name = server_info['username']
password = server_info['password']
save_path = server_info['savepath']
parser = argparse.ArgumentParser(description='Set RSS download rule.')
parser.add_argument('--name', default='',
help='Bangumi Name')
def rule_set():
qbt_client = qbittorrentapi.Client(host=host_ip, username=user_name, password=password)
try:
qbt_client.auth_log_in()
except qbittorrentapi.LoginFailed as e:
logger.exception(e)
args = parser.parse_args()
bangumi_name = args.name
rule = {'enable': True,
'mustContain': bangumi_name,
'mustNotContain': '720',
'useRegx': True,
'episodeFilter': '',
'smartFilter': False,
'previouslyMatchedEpisodes': [],
'affectedFeeds': [],
'ignoreDays': 0,
'lastMatch': '',
'addPaused': False,
'assignedCategory': 'Bangumi',
'savePath': os.path.join(save_path, bangumi_name)
}
qbt_client.rss_set_rule(rule_name=bangumi_name, rule_def=rule)
if __name__ == "__main__":
rule_set()

28
setup.py Normal file
View File

@@ -0,0 +1,28 @@
# coding:utf-8
from setuptools import setup, find_packages
setup(
name="auto_bangumi", # 包名字
version="2.4.0b4", # 包版本
description="一个全自动追番整理下载工具",
long_description="""
本项目是基于 Mikan Project、qBittorrent 的全自动追番整理下载工具。
只需要在 Mikan Project 上订阅番剧,就可以全自动追番。
并且整理完成的名称和目录可以直接被 Plex、Jellyfin 等媒体库软件识别,
无需二次刮削。""", # 简单描述
author="Estrella Pan", # 作者
author_email="estrellaxd05@gmail.com", # 作者邮箱
url="https://github.com/EstrellaXD/Auto_Bangumi", # 包的主页
packages=find_packages(where=".", exclude=("tests",), include=('*',)),
package_data={"auto_bangumi.RssFilter":["*.json"]},
package_dir={"auto_bangumi":"auto_bangumi"},
data_files=[("config", ["config/rule.json"])],
install_requires= [
"qbittorrent-api",
"bs4",
"requests",
"lxml",
"zhconv",
]
)