mirror of
https://github.com/EstrellaXD/Auto_Bangumi.git
synced 2026-04-14 02:20:53 +08:00
2.5.0
This commit is contained in:
@@ -13,4 +13,6 @@ ADD ./auto_bangumi /auto_bangumi
|
||||
ADD ./config /config
|
||||
ADD ./templates /templates
|
||||
|
||||
CMD ["python3", "app.py"]
|
||||
RUN chmod a+x run.sh
|
||||
|
||||
CMD ["./run.sh"]
|
||||
|
||||
@@ -7,7 +7,7 @@ import logging
|
||||
|
||||
from core import RSSAnalyser
|
||||
from core import DownloadClient
|
||||
from conf import settings
|
||||
from conf import settings, parse
|
||||
from utils import json_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -15,13 +15,13 @@ logger = logging.getLogger(__name__)
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
templates = Jinja2Templates(directory="templates")
|
||||
# templates = Jinja2Templates(directory="templates")
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
def index(request: Request):
|
||||
context = {"request": request}
|
||||
return templates.TemplateResponse("index.html", context)
|
||||
# @app.get("/", response_class=HTMLResponse)
|
||||
# def index(request: Request):
|
||||
# context = {"request": request}
|
||||
# return templates.TemplateResponse("index.html", context)
|
||||
|
||||
|
||||
class Config(BaseModel):
|
||||
@@ -65,13 +65,16 @@ class RSS(BaseModel):
|
||||
|
||||
@app.post("/api/v1/subscriptions")
|
||||
async def receive(link: RSS):
|
||||
data = RSSAnalyser().rss_to_data(link.link)
|
||||
from conf.const_dev import DEV_SETTINGS
|
||||
settings.init(DEV_SETTINGS)
|
||||
client = DownloadClient()
|
||||
client.add_collection_feed(link.link, item_path=data["title"])
|
||||
client.set_rule(data, link.link)
|
||||
return "Successes"
|
||||
try:
|
||||
data = RSSAnalyser().rss_to_data(link.link)
|
||||
client.add_collection_feed(link.link, item_path=data["official_title"])
|
||||
client.set_rule(data, link.link)
|
||||
return data
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
return "Error"
|
||||
|
||||
|
||||
|
||||
class Search(BaseModel):
|
||||
@@ -85,5 +88,19 @@ async def search(input: Search):
|
||||
return "Nothing Happened"
|
||||
|
||||
|
||||
def run():
|
||||
args = parse()
|
||||
if args.debug:
|
||||
try:
|
||||
from conf.const_dev import DEV_SETTINGS
|
||||
settings.init(DEV_SETTINGS)
|
||||
except ModuleNotFoundError:
|
||||
logger.debug("Please copy `const_dev.py` to `const_dev.py` to use custom settings")
|
||||
else:
|
||||
settings.init()
|
||||
uvicorn.run(app, host="0.0.0.0", port=settings.webui_port)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run(app, host="0.0.0.0", port=settings.webui_port)
|
||||
run()
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import os
|
||||
import time
|
||||
import logging
|
||||
from multiprocessing import Process
|
||||
|
||||
from conf import settings, parse
|
||||
from conf.log import setup_logger
|
||||
from utils import json_config
|
||||
|
||||
from core import RSSAnalyser, DownloadClient, Renamer
|
||||
from core import RSSAnalyser, DownloadClient, Renamer, FullSeasonGet
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,12 +20,15 @@ def load_data_file():
|
||||
"data_version": settings.data_version,
|
||||
"bangumi_info": []
|
||||
}
|
||||
logger.info("Building data information...")
|
||||
else:
|
||||
bangumi_data = json_config.load(info_path)
|
||||
if bangumi_data["data_version"] != settings.data_version or bangumi_data["rss_link"] != settings.rss_link:
|
||||
bangumi_data["bangumi_info"] = []
|
||||
bangumi_data["data_version"] = settings.data_version
|
||||
bangumi_data["rss_link"] = settings.rss_link
|
||||
bangumi_data = {
|
||||
"rss_link": settings.rss_link,
|
||||
"data_version": settings.data_version,
|
||||
"bangumi_info": []
|
||||
}
|
||||
logger.info("Rebuilding data information...")
|
||||
return bangumi_data
|
||||
|
||||
@@ -50,15 +53,22 @@ def show_info():
|
||||
|
||||
|
||||
def main_process(bangumi_data, download_client: DownloadClient):
|
||||
rss_analyser = RSSAnalyser()
|
||||
rename = Renamer(download_client)
|
||||
rss_analyser = RSSAnalyser()
|
||||
first_run = True
|
||||
while True:
|
||||
rss_analyser.run(bangumi_data["bangumi_info"], download_client)
|
||||
save_data_file(bangumi_data)
|
||||
times = 0
|
||||
if settings.enable_rss_collector:
|
||||
rss_analyser.run(bangumi_data["bangumi_info"], download_client)
|
||||
if settings.eps_complete and first_run:
|
||||
FullSeasonGet().eps_complete(bangumi_data["bangumi_info"], download_client)
|
||||
first_run = False
|
||||
logger.info("Running....")
|
||||
save_data_file(bangumi_data)
|
||||
while times < settings.times:
|
||||
rename.refresh()
|
||||
rename.run()
|
||||
if settings.enable_rename:
|
||||
rename.refresh()
|
||||
rename.run()
|
||||
times += 1
|
||||
time.sleep(settings.sleep_time/settings.times)
|
||||
|
||||
@@ -77,7 +87,7 @@ def run():
|
||||
# 初始化
|
||||
setup_logger()
|
||||
show_info()
|
||||
time.sleep(3)
|
||||
time.sleep(1)
|
||||
download_client = DownloadClient()
|
||||
download_client.init_downloader()
|
||||
if settings.rss_link is None:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from .conf import settings, Settings
|
||||
from .const import BCOLORS
|
||||
from .const_dev import DEV_SETTINGS
|
||||
# from .const_dev import DEV_SETTINGS
|
||||
from .parse import parse
|
||||
from .log import setup_logger
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
"version": "2.5.0-beta2",
|
||||
"version": "2.5.0-beta10",
|
||||
"data_version": 4.0,
|
||||
"host_ip": "localhost:8080",
|
||||
"sleep_time": 1800,
|
||||
"times": 10,
|
||||
"sleep_time": 7200,
|
||||
"times": 20,
|
||||
"user_name": "admin",
|
||||
"password": "adminadmin",
|
||||
"download_path": "/downloads/Bangumi",
|
||||
@@ -21,6 +21,11 @@ DEFAULT_SETTINGS = {
|
||||
"eps_complete": False,
|
||||
"webui_port": 7892,
|
||||
"title_language": "zh",
|
||||
"tmdb_api": "",
|
||||
"enable_tmdb": False,
|
||||
"socks": None,
|
||||
"enable_rss_collector": True,
|
||||
"enable_rename": True
|
||||
}
|
||||
|
||||
ENV_TO_ATTR = {
|
||||
@@ -36,13 +41,17 @@ ENV_TO_ATTR = {
|
||||
"AB_NOT_CONTAIN": "not_contain",
|
||||
"AB_DEBUG_MODE": ("debug_mode", lambda e: e.lower() in ("true", "1", "t")),
|
||||
"AB_EP_COMPLETE": (
|
||||
"enable_eps_complete",
|
||||
"eps_complete",
|
||||
lambda e: e.lower() in ("true", "1", "t")
|
||||
),
|
||||
"AB_REMOVE_BAD_BT": ("remove_bad_torrent", lambda e: e.lower() in ("true", "1", "t")),
|
||||
"AB_WEBUI_PORT": ("webui_port", lambda e: int(e)),
|
||||
"AB_HTTP_PROXY": "http_proxy",
|
||||
"AB_LANGUAGE": "title_language",
|
||||
"AB_ENABLE_TMDB": ("enable_tmdb", lambda e: e.lower() in ("true", "1", "t")),
|
||||
"AB_SOCKS": "socks",
|
||||
"AB_RENAME": ("enable_rename", lambda e: e.lower() in ("true", "1", "t")),
|
||||
"AB_RSS_COLLECTOR": ("enable_rss_collector", lambda e: e.lower() in ("true", "1", "t")),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,13 +14,17 @@ class FullSeasonGet:
|
||||
self._get_rss = RequestContent()
|
||||
|
||||
def init_eps_complete_search_str(self, data: dict):
|
||||
search_str = re.sub(r"[\W_]", "+",
|
||||
f"{data['group']} {data['title_raw']} {data['season_raw']} {data['subtitle']} {data['source']} {data['dpi']}")
|
||||
search_str_pre = ""
|
||||
for i in [data['group'], data['title_raw'], data['season_raw'], data['subtitle'], data['source'], data['dpi']]:
|
||||
if i is not None:
|
||||
search_str_pre += f" {i}"
|
||||
search_str = re.sub(r"[\W_ ]", "+",
|
||||
search_str_pre.strip())
|
||||
return search_str
|
||||
|
||||
def get_season_torrents(self, data: dict):
|
||||
keyword = self.init_eps_complete_search_str(data)
|
||||
torrents = self._get_rss.get_torrents(f"https://mikanani.me/RSS/Search?str={keyword}")
|
||||
torrents = self._get_rss.get_torrents(f"https://mikanani.me/RSS/Search?searchstr={keyword}")
|
||||
return torrents
|
||||
|
||||
def collect_season_torrents(self, data: dict):
|
||||
@@ -28,7 +32,7 @@ class FullSeasonGet:
|
||||
downloads = []
|
||||
for torrent in torrents:
|
||||
download_info = {
|
||||
"url": torrent["url"],
|
||||
"url": torrent,
|
||||
"save_path": os.path.join(
|
||||
settings.download_path,
|
||||
data["official_title"],
|
||||
@@ -39,12 +43,28 @@ class FullSeasonGet:
|
||||
|
||||
def eps_complete(self, bangumi_info, download_client: DownloadClient):
|
||||
for data in bangumi_info:
|
||||
if data["eps_complete"]:
|
||||
if data["eps_collect"]:
|
||||
logger.info(f"Start collecting past episodes of {data['official_title']} Season {data['season']}...")
|
||||
downloads = self.collect_season_torrents(data)
|
||||
for download in downloads:
|
||||
download_client.add_torrent(download)
|
||||
logger.info("Completed!")
|
||||
data["eps_complete"] = False
|
||||
data["eps_collect"] = False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = FullSeasonGet()
|
||||
data = {
|
||||
"official_title": "指名!",
|
||||
"title_raw": "CUE!",
|
||||
"season": 1,
|
||||
"season_raw": "",
|
||||
"group": "喵萌Production",
|
||||
"dpi": "1080p",
|
||||
"source": None,
|
||||
"subtitle": "简日双语",
|
||||
"added": True,
|
||||
"eps_collect": True
|
||||
}
|
||||
torrents = a.collect_season_torrents(data)
|
||||
print(torrents)
|
||||
@@ -34,11 +34,14 @@ class Renamer:
|
||||
if PurePath(info.content_path).name != info.content_path \
|
||||
else PureWindowsPath(info.content_path).parts
|
||||
path_name = path_parts[-1]
|
||||
season = int(re.search(r"\d", path_parts[-2]).group())
|
||||
try:
|
||||
new_name = self._renamer.download_parser(name, season, settings.method)
|
||||
logger.debug(f"Origin name: {path_name}")
|
||||
logger.debug(f"New name: {new_name}")
|
||||
season = int(re.search(r"\d", path_parts[-2]).group())
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
season = 1
|
||||
folder_name = path_parts[-3]
|
||||
try:
|
||||
new_name = self._renamer.download_parser(name, folder_name, season, settings.method)
|
||||
if path_name != new_name:
|
||||
self.client.rename_torrent_file(torrent_hash, path_name, new_name)
|
||||
self.rename_count += 1
|
||||
@@ -46,6 +49,7 @@ class Renamer:
|
||||
continue
|
||||
except:
|
||||
logger.warning(f"{path_name} rename failed")
|
||||
logger.debug(f"origin: {name}")
|
||||
if settings.remove_bad_torrent:
|
||||
self.client.delete_torrent(torrent_hash)
|
||||
self.print_result()
|
||||
|
||||
@@ -24,6 +24,7 @@ class RSSAnalyser:
|
||||
extra_add = True
|
||||
for d in bangumi_info:
|
||||
if re.search(d["title_raw"], raw_title) is not None:
|
||||
logger.debug(f"Had added {d['title_raw']} before")
|
||||
extra_add = False
|
||||
break
|
||||
if extra_add:
|
||||
@@ -38,8 +39,14 @@ class RSSAnalyser:
|
||||
return data
|
||||
|
||||
def run(self, bangumi_info: list, download_client: DownloadClient):
|
||||
self.rss_to_datas(bangumi_info)
|
||||
download_client.add_rules(bangumi_info, rss_link=settings.rss_link)
|
||||
logger.info("Start collecting RSS info.")
|
||||
try:
|
||||
self.rss_to_datas(bangumi_info)
|
||||
download_client.add_rules(bangumi_info, rss_link=settings.rss_link)
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.info("Connection error.")
|
||||
logger.info("Finished")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import time
|
||||
|
||||
import requests
|
||||
import socket
|
||||
import socks
|
||||
import logging
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
@@ -14,13 +16,15 @@ class RequestURL:
|
||||
def __init__(self):
|
||||
self.session = requests.session()
|
||||
if settings.http_proxy is not None:
|
||||
self.proxy = {
|
||||
self.session.proxies = {
|
||||
"https": settings.http_proxy,
|
||||
"http": settings.http_proxy,
|
||||
"socks": settings.http_proxy
|
||||
}
|
||||
else:
|
||||
self.proxy = None
|
||||
elif settings.socks is not None:
|
||||
socks_info = settings.socks.split(",")
|
||||
socks.set_default_proxy(socks.SOCKS5, addr=socks_info[0], port=int(socks_info[1]), rdns=True,
|
||||
username=socks_info[2], password=socks_info[3])
|
||||
socket.socket = socks.socksocket
|
||||
self.header = {
|
||||
"user-agent": "Mozilla/5.0",
|
||||
"Accept": "application/xml"
|
||||
@@ -30,11 +34,11 @@ class RequestURL:
|
||||
times = 0
|
||||
while times < 5:
|
||||
try:
|
||||
req = self.session.get(url=url, headers=self.header, proxies=self.proxy)
|
||||
req = self.session.get(url=url, headers=self.header)
|
||||
return req
|
||||
except Exception as e:
|
||||
logger.debug(f"URL: {url}")
|
||||
logger.error("ERROR with DNS/Connection.")
|
||||
logger.warning("ERROR with DNS/Connection.")
|
||||
time.sleep(settings.connect_retry_interval)
|
||||
times += 1
|
||||
|
||||
@@ -48,3 +52,11 @@ class RequestURL:
|
||||
self.session.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
a = RequestURL()
|
||||
socks.set_default_proxy(socks.SOCKS5, "192.168.30.2", 19990, True, username="abc", password="abc")
|
||||
socket.socket = socks.socksocket
|
||||
b = a.get_url('https://www.themoviedb.org').text
|
||||
print(b)
|
||||
|
||||
|
||||
|
||||
@@ -15,14 +15,15 @@ class DownloadParser:
|
||||
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)? (.*)",
|
||||
r"(.*)- (\d{1,3}|\d{1,3}\.\d{1,2})(?:v\d{1,2})?(?:END)?(.*)",
|
||||
]
|
||||
|
||||
def rename_normal(self, name, season):
|
||||
for rule in self.rules:
|
||||
matchObj = re.match(rule, name, re.I)
|
||||
if matchObj is not None:
|
||||
new_name = f"{matchObj.group(1).strip()} S{season}E{matchObj.group(2)}{matchObj.group(3)}"
|
||||
match_obj = re.match(rule, name, re.I)
|
||||
if match_obj is not None:
|
||||
title = re.sub(r"([Ss]|Season )\d{1,3}", "", match_obj.group(1)).strip()
|
||||
new_name = f"{title} S{season}E{match_obj.group(2)}{match_obj.group(3)}"
|
||||
return new_name
|
||||
|
||||
def rename_pn(self, name, season):
|
||||
@@ -31,26 +32,44 @@ class DownloadParser:
|
||||
if season < 10:
|
||||
season = f"0{season}"
|
||||
for rule in self.rules:
|
||||
matchObj = re.match(rule, file_name, re.I)
|
||||
if matchObj is not None:
|
||||
match_obj = re.match(rule, file_name, re.I)
|
||||
if match_obj is not None:
|
||||
title = re.sub(r"([Ss]|Season )\d{1,3}", "", match_obj.group(1)).strip()
|
||||
new_name = re.sub(
|
||||
r"[\[\]]",
|
||||
"",
|
||||
f"{matchObj.group(1).strip()} S{season}E{matchObj.group(2)}{path.splitext(name)[-1]}",
|
||||
f"{title} S{season}E{match_obj.group(2)}{path.splitext(name)[-1]}",
|
||||
)
|
||||
return new_name
|
||||
|
||||
def download_rename(self, name, season, method):
|
||||
def rename_advance(self, name, folder_name, season):
|
||||
n = re.split(r"[\[\]()【】()]", name)
|
||||
file_name = name.replace(f"[{n[1]}]", "")
|
||||
if season < 10:
|
||||
season = f"0{season}"
|
||||
for rule in self.rules:
|
||||
match_obj = re.match(rule, file_name, re.I)
|
||||
if match_obj is not None:
|
||||
new_name = re.sub(
|
||||
r"[\[\]]",
|
||||
"",
|
||||
f"{folder_name} S{season}E{match_obj.group(2)}{path.splitext(name)[-1]}",
|
||||
)
|
||||
return new_name
|
||||
|
||||
def download_rename(self, name, folder_name, season, method):
|
||||
if method.lower() == "pn":
|
||||
return self.rename_pn(name, season)
|
||||
elif method.lower() == "normal":
|
||||
return self.rename_normal(name, season)
|
||||
elif method.lower() == "none":
|
||||
return name
|
||||
elif method.lower() == "advance":
|
||||
return self.rename_advance(name, folder_name, season)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
name = "[NC-Raws]间谍过家家 - 09(B-Global 3840x2160 HEVC AAC MKV).mkv"
|
||||
name = "[NC-Raws]Summer Time Rendering S02 - 09(B-Global 3840x2160 HEVC AAC MKV).mkv"
|
||||
rename = DownloadParser()
|
||||
new_name = rename.rename_pn(name, "pn")
|
||||
new_name = rename.rename_pn(name, 1)
|
||||
print(new_name)
|
||||
@@ -16,9 +16,9 @@ class TMDBInfo:
|
||||
class TMDBMatcher:
|
||||
def __init__(self):
|
||||
self.search_url = lambda e: \
|
||||
f"https://api.themoviedb.org/3/search/tv?api_key={settings.tdmb_api}&page=1&query={e}&include_adult=false"
|
||||
f"https://api.themoviedb.org/3/search/tv?api_key={settings.tmdb_api}&page=1&query={e}&include_adult=false"
|
||||
self.info_url = lambda e: \
|
||||
f"https://api.themoviedb.org/3/tv/{e}?api_key={settings.tdmb_api}&language=zh-CN"
|
||||
f"https://api.themoviedb.org/3/tv/{e}?api_key={settings.tmdb_api}&language=zh-CN"
|
||||
self._request = RequestContent()
|
||||
|
||||
def is_animation(self, id):
|
||||
|
||||
@@ -14,20 +14,24 @@ class TitleParser:
|
||||
def raw_parser(self, raw):
|
||||
return self._raw_parser.analyse(raw)
|
||||
|
||||
def download_parser(self, download_raw, season, method=settings.method):
|
||||
return self._download_parser.download_rename(download_raw, season, method)
|
||||
def download_parser(self, download_raw, folder_name, season, method=settings.method):
|
||||
return self._download_parser.download_rename(download_raw, folder_name, season, method)
|
||||
|
||||
def return_dict(self, raw):
|
||||
tmdb = TMDBMatcher()
|
||||
try:
|
||||
episode = self.raw_parser(raw)
|
||||
try:
|
||||
tmdb_info = tmdb.tmdb_search(episode.title)
|
||||
official_title = tmdb_info.title_zh if settings.title_language == "zh" else tmdb_info.title_jp
|
||||
season = tmdb_info.last_season
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.info("Not Match in TMDB")
|
||||
if settings.enable_tmdb:
|
||||
try:
|
||||
tmdb_info = tmdb.tmdb_search(episode.title)
|
||||
official_title = tmdb_info.title_zh if settings.title_language == "zh" else tmdb_info.title_jp
|
||||
season = tmdb_info.last_season
|
||||
except Exception as e:
|
||||
logger.debug(e)
|
||||
logger.info("Not Match in TMDB")
|
||||
official_title = episode.title
|
||||
season = episode.season_info.number
|
||||
else:
|
||||
official_title = episode.title
|
||||
season = episode.season_info.number
|
||||
data = {
|
||||
|
||||
4
auto_bangumi/run.sh
Executable file
4
auto_bangumi/run.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
exec python3 app.py &
|
||||
exec python3 api.py
|
||||
@@ -1,6 +1,7 @@
|
||||
qbittorrent-api
|
||||
bs4
|
||||
requests~=2.27.1
|
||||
requests==2.28.0
|
||||
pysocks
|
||||
lxml
|
||||
|
||||
setuptools~=62.1.0
|
||||
|
||||
Reference in New Issue
Block a user