Merge pull request #596 from lededev/m3newfeture
新功能:1.增量整理 2.保存运行及错误日志到目录
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
from os import replace
|
from os import replace
|
||||||
import requests
|
import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
import pathlib
|
from pathlib import Path
|
||||||
import random
|
import random
|
||||||
import os.path
|
import os.path
|
||||||
import uuid
|
import uuid
|
||||||
@@ -467,6 +467,9 @@ def translate(
|
|||||||
+ src
|
+ src
|
||||||
)
|
)
|
||||||
result = get_html(url=url, return_type="object")
|
result = get_html(url=url, return_type="object")
|
||||||
|
if not result.ok:
|
||||||
|
print('[-]Google-free translate web API calling failed.')
|
||||||
|
return ''
|
||||||
|
|
||||||
translate_list = [i["trans"] for i in result.json()["sentences"]]
|
translate_list = [i["trans"] for i in result.json()["sentences"]]
|
||||||
trans_result = trans_result.join(translate_list)
|
trans_result = trans_result.join(translate_list)
|
||||||
@@ -548,7 +551,7 @@ def load_cookies(filename):
|
|||||||
|
|
||||||
# 文件修改时间距此时的天数
|
# 文件修改时间距此时的天数
|
||||||
def file_modification_days(filename) -> int:
|
def file_modification_days(filename) -> int:
|
||||||
mfile = pathlib.Path(filename)
|
mfile = Path(filename)
|
||||||
if not mfile.exists():
|
if not mfile.exists():
|
||||||
return 9999
|
return 9999
|
||||||
mtime = int(mfile.stat().st_mtime)
|
mtime = int(mfile.stat().st_mtime)
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import typing
|
|||||||
import urllib3
|
import urllib3
|
||||||
|
|
||||||
import config
|
import config
|
||||||
|
from datetime import datetime
|
||||||
import time
|
import time
|
||||||
from ADC_function import get_html, is_link
|
from pathlib import Path
|
||||||
|
from ADC_function import file_modification_days, get_html, is_link
|
||||||
from number_parser import get_number
|
from number_parser import get_number
|
||||||
from core import core_main
|
from core import core_main, moveFailedFolder
|
||||||
|
|
||||||
|
|
||||||
def check_update(local_version):
|
def check_update(local_version):
|
||||||
@@ -33,35 +35,173 @@ def check_update(local_version):
|
|||||||
|
|
||||||
|
|
||||||
def argparse_function(ver: str) -> typing.Tuple[str, str, bool]:
|
def argparse_function(ver: str) -> typing.Tuple[str, str, bool]:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
|
||||||
parser.add_argument("file", default='', nargs='?', help="Single Movie file path.")
|
parser.add_argument("file", default='', nargs='?', help="Single Movie file path.")
|
||||||
parser.add_argument("-p","--path",default='',nargs='?',help="Analysis folder path.")
|
parser.add_argument("-p","--path",default='',nargs='?',help="Analysis folder path.")
|
||||||
# parser.add_argument("-c", "--config", default='config.ini', nargs='?', help="The config file Path.")
|
# parser.add_argument("-c", "--config", default='config.ini', nargs='?', help="The config file Path.")
|
||||||
|
default_logdir = os.path.join(Path.home(),'.avlogs')
|
||||||
|
parser.add_argument("-o","--log-dir",dest='logdir',default=default_logdir,nargs='?',
|
||||||
|
help=f"""Duplicate stdout and stderr to logfiles
|
||||||
|
in logging folder, default on.
|
||||||
|
default for current user: {default_logdir}
|
||||||
|
Use --log-dir= to turn off logging feature.""")
|
||||||
parser.add_argument("-n", "--number", default='', nargs='?', help="Custom file number")
|
parser.add_argument("-n", "--number", default='', nargs='?', help="Custom file number")
|
||||||
parser.add_argument("-a", "--auto-exit", dest='autoexit', action="store_true",
|
parser.add_argument("-a", "--auto-exit", dest='autoexit', action="store_true",
|
||||||
help="Auto exit after program complete")
|
help="Auto exit after program complete")
|
||||||
|
parser.add_argument("-q","--regex-query",dest='regexstr',default='',nargs='?',help="python re module regex filepath filtering.")
|
||||||
parser.add_argument("-v", "--version", action="version", version=ver)
|
parser.add_argument("-v", "--version", action="version", version=ver)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
return args.file, args.path, args.number, args.autoexit
|
return args.file, args.path, args.number, args.autoexit, args.logdir, args.regexstr
|
||||||
|
|
||||||
|
|
||||||
G_trailerRE = re.compile(r'-trailer\.', re.IGNORECASE)
|
class OutLogger(object):
|
||||||
|
def __init__(self, logfile) -> None:
|
||||||
|
self.term = sys.stdout
|
||||||
|
self.log = open(logfile,"w",encoding='utf-8')
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
def __enter__(self):
|
||||||
|
pass
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.close()
|
||||||
|
def write(self,msg):
|
||||||
|
self.term.write(msg)
|
||||||
|
self.log.write(msg)
|
||||||
|
def flush(self):
|
||||||
|
self.term.flush()
|
||||||
|
self.log.flush()
|
||||||
|
os.fsync(self.log.fileno())
|
||||||
|
def close(self):
|
||||||
|
if self.term != None:
|
||||||
|
sys.stdout = self.term
|
||||||
|
self.term = None
|
||||||
|
if self.log != None:
|
||||||
|
self.log.close()
|
||||||
|
self.log = None
|
||||||
|
|
||||||
def movie_lists(root, escape_folder):
|
|
||||||
if os.path.basename(root) in escape_folder:
|
class ErrLogger(OutLogger):
|
||||||
return []
|
def __init__(self, logfile) -> None:
|
||||||
|
self.term = sys.stderr
|
||||||
|
self.log = open(logfile,"w",encoding='utf-8')
|
||||||
|
def close(self):
|
||||||
|
if self.term != None:
|
||||||
|
sys.stderr = self.term
|
||||||
|
self.term = None
|
||||||
|
if self.log != None:
|
||||||
|
self.log.close()
|
||||||
|
self.log = None
|
||||||
|
|
||||||
|
|
||||||
|
def dupe_stdout_to_logfile(logdir: str):
|
||||||
|
if not isinstance(logdir, str) or len(logdir) == 0:
|
||||||
|
return
|
||||||
|
if not os.path.isdir(logdir):
|
||||||
|
os.makedirs(logdir)
|
||||||
|
if not os.path.isdir(logdir):
|
||||||
|
return
|
||||||
|
|
||||||
|
log_tmstr = datetime.now().strftime("%Y%m%dT%H%M%S")
|
||||||
|
logfile = os.path.join(logdir, f'avdc_{log_tmstr}.txt')
|
||||||
|
errlog = os.path.join(logdir, f'avdc_{log_tmstr}_err.txt')
|
||||||
|
|
||||||
|
sys.stdout = OutLogger(logfile)
|
||||||
|
sys.stderr = ErrLogger(errlog)
|
||||||
|
|
||||||
|
|
||||||
|
def close_logfile(logdir: str):
|
||||||
|
if not isinstance(logdir, str) or len(logdir) == 0 or not os.path.isdir(logdir):
|
||||||
|
return
|
||||||
|
sys.stdout.close()
|
||||||
|
sys.stderr.close()
|
||||||
|
# 清理空文件
|
||||||
|
for current_dir, subdirs, files in os.walk(logdir, topdown=False):
|
||||||
|
try:
|
||||||
|
for f in files:
|
||||||
|
full_name = os.path.join(current_dir, f)
|
||||||
|
if os.path.getsize(full_name) == 0:
|
||||||
|
os.remove(full_name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
_print = print # Hook print
|
||||||
|
_stdout = sys.stdout
|
||||||
|
def print(*args, **kw):
|
||||||
|
_print(*args, **kw)
|
||||||
|
if _stdout != sys.stdout:
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
|
||||||
|
# 重写视频文件扫描,消除递归,取消全局变量,新增失败文件列表跳过处理
|
||||||
|
def movie_lists(root, conf, regexstr):
|
||||||
|
escape_folder = re.split("[,,]", conf.escape_folder())
|
||||||
|
main_mode = conf.main_mode()
|
||||||
|
debug = conf.debug()
|
||||||
|
nfo_skip_days = conf.nfo_skip_days()
|
||||||
|
soft_link = conf.soft_link()
|
||||||
total = []
|
total = []
|
||||||
file_type = conf.media_type().upper().split(",")
|
file_type = conf.media_type().upper().split(",")
|
||||||
dirs = os.listdir(root)
|
trailerRE = re.compile(r'-trailer\.', re.IGNORECASE)
|
||||||
for entry in dirs:
|
cliRE = None
|
||||||
f = os.path.join(root, entry)
|
if len(regexstr):
|
||||||
if os.path.isdir(f):
|
try:
|
||||||
total += movie_lists(f, escape_folder)
|
cliRE = re.compile(regexstr, re.IGNORECASE)
|
||||||
elif os.path.splitext(f)[1].upper() in file_type:
|
except:
|
||||||
absf = os.path.abspath(f)
|
pass
|
||||||
if (conf.main_mode() == 3 or not is_link(absf)) and not G_trailerRE.search(f):
|
failed_set = set()
|
||||||
|
if main_mode == 3 or soft_link:
|
||||||
|
try:
|
||||||
|
with open(os.path.join(conf.failed_folder(), 'failed_list.txt'), 'r', encoding='utf-8') as flt:
|
||||||
|
flist = flt.read().splitlines()
|
||||||
|
failed_set = set(flist)
|
||||||
|
flt.close()
|
||||||
|
if len(flist) != len(failed_set):
|
||||||
|
with open(os.path.join(conf.failed_folder(), 'failed_list.txt'), 'w', encoding='utf-8') as flt:
|
||||||
|
flt.writelines([line + '\n' for line in failed_set])
|
||||||
|
flt.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
for current_dir, subdirs, files in os.walk(root, topdown=False):
|
||||||
|
if current_dir in escape_folder:
|
||||||
|
continue
|
||||||
|
for f in files:
|
||||||
|
full_name = os.path.join(current_dir, f)
|
||||||
|
if not os.path.splitext(full_name)[1].upper() in file_type:
|
||||||
|
continue
|
||||||
|
absf = os.path.abspath(full_name)
|
||||||
|
if absf in failed_set:
|
||||||
|
if debug:
|
||||||
|
print('[!]Skip failed file:', absf)
|
||||||
|
continue
|
||||||
|
if cliRE and not cliRE.search(absf):
|
||||||
|
continue
|
||||||
|
if main_mode == 3 and nfo_skip_days > 0:
|
||||||
|
nfo = Path(absf).with_suffix('.nfo')
|
||||||
|
if file_modification_days(nfo) <= nfo_skip_days:
|
||||||
|
continue
|
||||||
|
if (main_mode == 3 or not is_link(absf)) and not trailerRE.search(f):
|
||||||
total.append(absf)
|
total.append(absf)
|
||||||
|
if nfo_skip_days <= 0 or not soft_link or main_mode == 3:
|
||||||
|
return total
|
||||||
|
# 软连接方式,已经成功削刮的也需要从成功目录中检查.nfo更新天数,跳过N天内更新过的
|
||||||
|
skip_numbers = set()
|
||||||
|
success_folder = conf.success_folder()
|
||||||
|
for current_dir, subdirs, files in os.walk(success_folder, topdown=False):
|
||||||
|
for f in files:
|
||||||
|
if not os.path.splitext(f)[1].upper() in file_type:
|
||||||
|
continue
|
||||||
|
nfo_file = os.path.join(current_dir, str(Path(f).with_suffix('.nfo')))
|
||||||
|
if file_modification_days(nfo_file) > nfo_skip_days:
|
||||||
|
continue
|
||||||
|
number = get_number(False, os.path.basename(f))
|
||||||
|
if number:
|
||||||
|
skip_numbers.add(number.upper())
|
||||||
|
for f in total:
|
||||||
|
n_number = get_number(False, os.path.basename(f))
|
||||||
|
if n_number and n_number.upper() in skip_numbers:
|
||||||
|
total.pop(total.index(f))
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
|
||||||
@@ -97,7 +237,7 @@ def create_data_and_move(file_path: str, c: config.Config, debug):
|
|||||||
file_path = os.path.abspath(file_path)
|
file_path = os.path.abspath(file_path)
|
||||||
|
|
||||||
if debug == True:
|
if debug == True:
|
||||||
print("[!]Making Data for [{}], the number is [{}]".format(file_path, n_number))
|
print(f"[!]Making Data for [{file_path}], the number is [{n_number}]")
|
||||||
if n_number:
|
if n_number:
|
||||||
core_main(file_path, n_number, c)
|
core_main(file_path, n_number, c)
|
||||||
else:
|
else:
|
||||||
@@ -105,31 +245,20 @@ def create_data_and_move(file_path: str, c: config.Config, debug):
|
|||||||
print("[*]======================================================")
|
print("[*]======================================================")
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
print("[!]Making Data for [{}], the number is [{}]".format(file_path, n_number))
|
print(f"[!]Making Data for [{file_path}], the number is [{n_number}]")
|
||||||
if n_number:
|
if n_number:
|
||||||
core_main(file_path, n_number, c)
|
core_main(file_path, n_number, c)
|
||||||
else:
|
else:
|
||||||
raise ValueError("number empty")
|
raise ValueError("number empty")
|
||||||
print("[*]======================================================")
|
print("[*]======================================================")
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print("[-] [{}] ERROR:".format(file_path))
|
print(f"[-] [{file_path}] ERROR:")
|
||||||
print('[-]', err)
|
print('[-]', err)
|
||||||
|
|
||||||
# 3.7.2 New: Move or not move to failed folder.
|
try:
|
||||||
if c.failed_move() == False:
|
moveFailedFolder(file_path, conf)
|
||||||
if c.soft_link():
|
except Exception as err:
|
||||||
print("[-]Link {} to failed folder".format(file_path))
|
print('[!]', err)
|
||||||
os.symlink(file_path, os.path.join(conf.failed_folder(), file_name))
|
|
||||||
elif c.failed_move() == True:
|
|
||||||
if c.soft_link():
|
|
||||||
print("[-]Link {} to failed folder".format(file_path))
|
|
||||||
os.symlink(file_path, os.path.join(conf.failed_folder(), file_name))
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
print("[-]Move [{}] to failed folder".format(file_path))
|
|
||||||
shutil.move(file_path, os.path.join(conf.failed_folder(), file_name))
|
|
||||||
except Exception as err:
|
|
||||||
print('[!]', err)
|
|
||||||
|
|
||||||
|
|
||||||
def create_data_and_move_with_custom_number(file_path: str, c: config.Config, custom_number):
|
def create_data_and_move_with_custom_number(file_path: str, c: config.Config, custom_number):
|
||||||
@@ -157,7 +286,9 @@ if __name__ == '__main__':
|
|||||||
version = '4.7.2'
|
version = '4.7.2'
|
||||||
urllib3.disable_warnings() #Ignore http proxy warning
|
urllib3.disable_warnings() #Ignore http proxy warning
|
||||||
# Parse command line args
|
# Parse command line args
|
||||||
single_file_path, folder_path, custom_number, auto_exit = argparse_function(version)
|
single_file_path, folder_path, custom_number, auto_exit, logdir, regexstr = argparse_function(version)
|
||||||
|
|
||||||
|
dupe_stdout_to_logfile(logdir)
|
||||||
|
|
||||||
print('[*]================== AV Data Capture ===================')
|
print('[*]================== AV Data Capture ===================')
|
||||||
print('[*]' + version.center(54))
|
print('[*]' + version.center(54))
|
||||||
@@ -175,6 +306,7 @@ if __name__ == '__main__':
|
|||||||
print('[+]Enable debug')
|
print('[+]Enable debug')
|
||||||
if conf.soft_link():
|
if conf.soft_link():
|
||||||
print('[!]Enable soft link')
|
print('[!]Enable soft link')
|
||||||
|
print('[!]CmdLine:'," ".join(sys.argv[1:]))
|
||||||
|
|
||||||
create_failed_folder(conf.failed_folder())
|
create_failed_folder(conf.failed_folder())
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
@@ -189,17 +321,28 @@ if __name__ == '__main__':
|
|||||||
if folder_path == '':
|
if folder_path == '':
|
||||||
folder_path = os.path.abspath(".")
|
folder_path = os.path.abspath(".")
|
||||||
|
|
||||||
movie_list = movie_lists(folder_path, re.split("[,,]", conf.escape_folder()))
|
movie_list = movie_lists(folder_path, conf, regexstr)
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
count_all = str(len(movie_list))
|
count_all = str(len(movie_list))
|
||||||
print('[+]Find', count_all, 'movies')
|
print('[+]Find', count_all, 'movies')
|
||||||
|
main_mode = conf.main_mode()
|
||||||
|
stop_count = conf.stop_counter()
|
||||||
|
if stop_count<1:
|
||||||
|
stop_count = 999999
|
||||||
|
else:
|
||||||
|
count_all = str(min(len(movie_list), stop_count))
|
||||||
|
if main_mode == 3:
|
||||||
|
print(
|
||||||
|
f'[!]运行模式:**维护模式**,本程序将在处理{count_all}个视频文件后停止,如需后台执行自动退出请结合 -a 参数。')
|
||||||
for movie_path in movie_list: # 遍历电影列表 交给core处理
|
for movie_path in movie_list: # 遍历电影列表 交给core处理
|
||||||
count = count + 1
|
count = count + 1
|
||||||
percentage = str(count / int(count_all) * 100)[:4] + '%'
|
percentage = str(count / int(count_all) * 100)[:4] + '%'
|
||||||
print('[!] - ' + percentage + ' [' + str(count) + '/' + count_all + '] -')
|
print('[!] - ' + percentage + ' [' + str(count) + '/' + count_all + '] -')
|
||||||
create_data_and_move(movie_path, conf, conf.debug())
|
create_data_and_move(movie_path, conf, conf.debug())
|
||||||
|
if count >= stop_count:
|
||||||
|
print("[!]Stop counter triggered!")
|
||||||
|
break
|
||||||
|
|
||||||
if conf.del_empty_folder():
|
if conf.del_empty_folder():
|
||||||
rm_empty_folder(conf.success_folder())
|
rm_empty_folder(conf.success_folder())
|
||||||
@@ -214,4 +357,7 @@ if __name__ == '__main__':
|
|||||||
print("[+]All finished!!!")
|
print("[+]All finished!!!")
|
||||||
if not (conf.auto_exit() or auto_exit):
|
if not (conf.auto_exit() or auto_exit):
|
||||||
input("Press enter key exit, you can check the error message before you exit...")
|
input("Press enter key exit, you can check the error message before you exit...")
|
||||||
|
|
||||||
|
close_logfile(logdir)
|
||||||
|
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ def get_data_from_json(file_number, conf: config.Config): # 从JSON返回元数
|
|||||||
|
|
||||||
# Return if data not found in all sources
|
# Return if data not found in all sources
|
||||||
if not json_data:
|
if not json_data:
|
||||||
print('[-]Movie Data not found!')
|
print('[-]Movie Number not found!')
|
||||||
return
|
return None
|
||||||
|
|
||||||
# ================================================网站规则添加结束================================================
|
# ================================================网站规则添加结束================================================
|
||||||
|
|
||||||
@@ -165,8 +165,8 @@ def get_data_from_json(file_number, conf: config.Config): # 从JSON返回元数
|
|||||||
actor = str(actor_list).strip("[ ]").replace("'", '').replace(" ", '')
|
actor = str(actor_list).strip("[ ]").replace("'", '').replace(" ", '')
|
||||||
|
|
||||||
if title == '' or number == '':
|
if title == '' or number == '':
|
||||||
print('[-]Movie Data not found!')
|
print('[-]Movie Number or Title not found!')
|
||||||
return
|
return None
|
||||||
|
|
||||||
# if imagecut == '3':
|
# if imagecut == '3':
|
||||||
# DownloadFileWithFilename()
|
# DownloadFileWithFilename()
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ multi_threading=1
|
|||||||
;actor_gender value: female(♀) or male(♂) or both(♀ ♂) or all(♂ ♀ ⚧)
|
;actor_gender value: female(♀) or male(♂) or both(♀ ♂) or all(♂ ♀ ⚧)
|
||||||
actor_gender=female
|
actor_gender=female
|
||||||
del_empty_folder=1
|
del_empty_folder=1
|
||||||
|
; 跳过最近(默认:30)天新修改过的.NFO,可避免整理模式(main_mode=3)和软连接(soft_link=0)时
|
||||||
|
; 反复刮削靠前的视频文件,0为处理所有视频文件
|
||||||
|
nfo_skip_days=30
|
||||||
|
; 处理完多少个视频文件后停止,0为处理所有视频文件
|
||||||
|
stop_counter=0
|
||||||
|
; 以上两个参数配合使用可以以多次少量的方式刮削或整理数千个文件而不触发翻译或元数据站封禁
|
||||||
|
|
||||||
[proxy]
|
[proxy]
|
||||||
;proxytype: http or socks5 or socks5h switch: 0 1
|
;proxytype: http or socks5 or socks5h switch: 0 1
|
||||||
@@ -74,3 +80,4 @@ water=2
|
|||||||
[extrafanart]
|
[extrafanart]
|
||||||
switch=0
|
switch=0
|
||||||
extrafanart_folder=extrafanart
|
extrafanart_folder=extrafanart
|
||||||
|
|
||||||
|
|||||||
34
config.py
34
config.py
@@ -2,15 +2,29 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import configparser
|
import configparser
|
||||||
import codecs
|
import codecs
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
def __init__(self, path: str = "config.ini"):
|
def __init__(self, path: str = "config.ini"):
|
||||||
if os.path.exists(path):
|
path_search_order = [
|
||||||
|
path,
|
||||||
|
"./config.ini",
|
||||||
|
os.path.join(Path.home(), "avdc.ini"),
|
||||||
|
os.path.join(Path.home(), ".avdc.ini"),
|
||||||
|
os.path.join(Path.home(), ".avdc/config.ini"),
|
||||||
|
os.path.join(Path.home(), ".config/avdc/config.ini")
|
||||||
|
]
|
||||||
|
ini_path = None
|
||||||
|
for p in path_search_order:
|
||||||
|
if os.path.exists(p):
|
||||||
|
ini_path = p
|
||||||
|
break
|
||||||
|
if ini_path:
|
||||||
self.conf = configparser.ConfigParser()
|
self.conf = configparser.ConfigParser()
|
||||||
try:
|
try:
|
||||||
self.conf.read(path, encoding="utf-8-sig")
|
self.conf.read(ini_path, encoding="utf-8-sig")
|
||||||
except:
|
except:
|
||||||
self.conf.read(path, encoding="utf-8")
|
self.conf.read(ini_path, encoding="utf-8")
|
||||||
else:
|
else:
|
||||||
print("[-]Config file not found!")
|
print("[-]Config file not found!")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
@@ -54,6 +68,16 @@ class Config:
|
|||||||
return self.conf.getboolean("common", "multi_threading")
|
return self.conf.getboolean("common", "multi_threading")
|
||||||
def del_empty_folder(self) -> bool:
|
def del_empty_folder(self) -> bool:
|
||||||
return self.conf.getboolean("common", "del_empty_folder")
|
return self.conf.getboolean("common", "del_empty_folder")
|
||||||
|
def nfo_skip_days(self) -> int:
|
||||||
|
try:
|
||||||
|
return self.conf.getint("common", "nfo_skip_days")
|
||||||
|
except:
|
||||||
|
return 30
|
||||||
|
def stop_counter(self) -> int:
|
||||||
|
try:
|
||||||
|
return self.conf.getint("common", "stop_counter")
|
||||||
|
except:
|
||||||
|
return 0
|
||||||
def is_transalte(self) -> bool:
|
def is_transalte(self) -> bool:
|
||||||
return self.conf.getboolean("transalte", "switch")
|
return self.conf.getboolean("transalte", "switch")
|
||||||
def is_trailer(self) -> bool:
|
def is_trailer(self) -> bool:
|
||||||
@@ -171,6 +195,8 @@ class Config:
|
|||||||
# actor_gender value: female or male or both or all(含人妖)
|
# actor_gender value: female or male or both or all(含人妖)
|
||||||
conf.set(sec1, "actor_gender", "female")
|
conf.set(sec1, "actor_gender", "female")
|
||||||
conf.set(sec1, "del_empty_folder", "1")
|
conf.set(sec1, "del_empty_folder", "1")
|
||||||
|
conf.set(sec1, "nfo_skip_days", 30)
|
||||||
|
conf.set(sec1, "stop_counter", 0)
|
||||||
|
|
||||||
sec2 = "proxy"
|
sec2 = "proxy"
|
||||||
conf.add_section(sec2)
|
conf.add_section(sec2)
|
||||||
@@ -239,8 +265,6 @@ class Config:
|
|||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class IniProxy():
|
class IniProxy():
|
||||||
""" Proxy Config from .ini
|
""" Proxy Config from .ini
|
||||||
"""
|
"""
|
||||||
|
|||||||
130
core.py
130
core.py
@@ -9,6 +9,8 @@ import sys
|
|||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from ADC_function import *
|
from ADC_function import *
|
||||||
from WebCrawler import get_data_from_json
|
from WebCrawler import get_data_from_json
|
||||||
@@ -21,18 +23,26 @@ def escape_path(path, escape_literals: str): # Remove escape literals
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
def moveFailedFolder(filepath):
|
def moveFailedFolder(filepath, conf):
|
||||||
conf = config.Config()
|
failed_folder = conf.failed_folder()
|
||||||
if conf.failed_move():
|
soft_link = conf.soft_link()
|
||||||
failed_folder = conf.failed_folder()
|
# 模式3或软连接,改为维护一个失败列表,启动扫描时加载用于排除该路径,以免反复处理
|
||||||
file_name = os.path.basename(filepath)
|
# 原先的创建软连接到失败目录,并不直观,不方便找到失败文件位置,不如直接记录该文件路径
|
||||||
if conf.soft_link():
|
if conf.main_mode() == 3 or soft_link:
|
||||||
print('[-]Create symlink to Failed output folder')
|
ftxt = os.path.join(failed_folder, 'failed_list.txt')
|
||||||
os.symlink(filepath, os.path.join(failed_folder, file_name))
|
print("[-]Add to Failed List file, see '%s'" % ftxt)
|
||||||
else:
|
with open(ftxt, 'a', encoding='utf-8') as flt:
|
||||||
print('[-]Move to Failed output folder')
|
flt.write(f'{filepath}\n')
|
||||||
shutil.move(filepath, os.path.join(failed_folder, file_name))
|
flt.close()
|
||||||
return
|
elif conf.failed_move() and not soft_link:
|
||||||
|
failed_name = os.path.join(failed_folder, os.path.basename(filepath))
|
||||||
|
mtxt = os.path.join(failed_folder, 'where_was_i_before_being_moved.txt')
|
||||||
|
print("'[-]Move to Failed output folder, see '%s'" % mtxt)
|
||||||
|
with open(mtxt, 'a', encoding='utf-8') as wwibbmt:
|
||||||
|
tmstr = datetime.now().strftime("%Y-%m-%d %H:%M")
|
||||||
|
wwibbmt.write(f'{tmstr} FROM[{filepath}]TO[{failed_name}]\n')
|
||||||
|
wwibbmt.close()
|
||||||
|
shutil.move(filepath, failed_name)
|
||||||
|
|
||||||
|
|
||||||
def get_info(json_data): # 返回json里的数据
|
def get_info(json_data): # 返回json里的数据
|
||||||
@@ -54,8 +64,9 @@ def get_info(json_data): # 返回json里的数据
|
|||||||
|
|
||||||
|
|
||||||
def small_cover_check(path, number, cover_small, leak_word, c_word, conf: config.Config, filepath):
|
def small_cover_check(path, number, cover_small, leak_word, c_word, conf: config.Config, filepath):
|
||||||
download_file_with_filename(cover_small, number + leak_word+ c_word + '-poster.jpg', path, conf, filepath)
|
filename = f"{number}{leak_word}{c_word}-poster.jpg"
|
||||||
print('[+]Image Downloaded! ' + path + '/' + number + leak_word + c_word + '-poster.jpg')
|
download_file_with_filename(cover_small, filename, path, conf, filepath)
|
||||||
|
print('[+]Image Downloaded! ' + os.path.join(path, filename))
|
||||||
|
|
||||||
|
|
||||||
def create_folder(json_data, conf: config.Config): # 创建文件夹
|
def create_folder(json_data, conf: config.Config): # 创建文件夹
|
||||||
@@ -110,9 +121,9 @@ def download_file_with_filename(url, filename, path, conf: config.Config, filepa
|
|||||||
'User-Agent': G_USER_AGENT}
|
'User-Agent': G_USER_AGENT}
|
||||||
r = requests.get(url, headers=headers, timeout=configProxy.timeout, proxies=proxies)
|
r = requests.get(url, headers=headers, timeout=configProxy.timeout, proxies=proxies)
|
||||||
if r == '':
|
if r == '':
|
||||||
print('[-]Movie Data not found!')
|
print('[-]Movie Download Data not found!')
|
||||||
return
|
return
|
||||||
with open(os.path.join(str(path), filename), "wb") as code:
|
with open(os.path.join(path, filename), "wb") as code:
|
||||||
code.write(r.content)
|
code.write(r.content)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -122,9 +133,9 @@ def download_file_with_filename(url, filename, path, conf: config.Config, filepa
|
|||||||
'User-Agent': G_USER_AGENT}
|
'User-Agent': G_USER_AGENT}
|
||||||
r = requests.get(url, timeout=configProxy.timeout, headers=headers)
|
r = requests.get(url, timeout=configProxy.timeout, headers=headers)
|
||||||
if r == '':
|
if r == '':
|
||||||
print('[-]Movie Data not found!')
|
print('[-]Movie Download Data not found!')
|
||||||
return
|
return
|
||||||
with open(os.path.join(str(path), filename), "wb") as code:
|
with open(os.path.join(path, filename), "wb") as code:
|
||||||
code.write(r.content)
|
code.write(r.content)
|
||||||
return
|
return
|
||||||
except requests.exceptions.RequestException:
|
except requests.exceptions.RequestException:
|
||||||
@@ -140,7 +151,7 @@ def download_file_with_filename(url, filename, path, conf: config.Config, filepa
|
|||||||
i += 1
|
i += 1
|
||||||
print('[-]Image Download : Connect retry ' + str(i) + '/' + str(configProxy.retry))
|
print('[-]Image Download : Connect retry ' + str(i) + '/' + str(configProxy.retry))
|
||||||
print('[-]Connect Failed! Please check your Proxy or Network!')
|
print('[-]Connect Failed! Please check your Proxy or Network!')
|
||||||
moveFailedFolder(filepath)
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
|
|
||||||
def trailer_download(trailer, leak_word, c_word, number, path, filepath, conf: config.Config):
|
def trailer_download(trailer, leak_word, c_word, number, path, filepath, conf: config.Config):
|
||||||
@@ -161,53 +172,61 @@ def trailer_download(trailer, leak_word, c_word, number, path, filepath, conf: c
|
|||||||
# 剧照下载成功,否则移动到failed
|
# 剧照下载成功,否则移动到failed
|
||||||
def extrafanart_download(data, path, conf: config.Config, filepath):
|
def extrafanart_download(data, path, conf: config.Config, filepath):
|
||||||
j = 1
|
j = 1
|
||||||
path = path + '/' + conf.get_extrafanart()
|
path = os.path.join(path, conf.get_extrafanart())
|
||||||
for url in data:
|
for url in data:
|
||||||
if download_file_with_filename(url, 'extrafanart-' + str(j)+'.jpg', path, conf, filepath) == 'failed':
|
jpg_filename = f'extrafanart-{j}.jpg'
|
||||||
moveFailedFolder(filepath)
|
jpg_fullpath = os.path.join(path, jpg_filename)
|
||||||
|
if download_file_with_filename(url, jpg_filename, path, conf, filepath) == 'failed':
|
||||||
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
configProxy = conf.proxy()
|
configProxy = conf.proxy()
|
||||||
for i in range(configProxy.retry):
|
for i in range(configProxy.retry):
|
||||||
if os.path.getsize(path + '/extrafanart-' + str(j) + '.jpg') == 0:
|
if os.path.getsize(jpg_fullpath) == 0:
|
||||||
print('[!]Image Download Failed! Trying again. [{}/3]', i + 1)
|
print('[!]Image Download Failed! Trying again. [{}/3]', i + 1)
|
||||||
download_file_with_filename(url, 'extrafanart-' + str(j)+'.jpg', path, conf, filepath)
|
download_file_with_filename(url, jpg_filename, path, conf, filepath)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if os.path.getsize(path + '/extrafanart-' + str(j) + '.jpg') == 0:
|
if os.path.getsize(jpg_fullpath) == 0:
|
||||||
return
|
return
|
||||||
print('[+]Image Downloaded!', path + '/extrafanart-' + str(j) + '.jpg')
|
print('[+]Image Downloaded!', jpg_fullpath)
|
||||||
j += 1
|
j += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 封面是否下载成功,否则移动到failed
|
# 封面是否下载成功,否则移动到failed
|
||||||
def image_download(cover, number, leak_word, c_word, path, conf: config.Config, filepath):
|
def image_download(cover, number, leak_word, c_word, path, conf: config.Config, filepath):
|
||||||
if download_file_with_filename(cover, number + leak_word + c_word + '-fanart.jpg', path, conf, filepath) == 'failed':
|
filename = f"{number}{leak_word}{c_word}-fanart.jpg"
|
||||||
moveFailedFolder(filepath)
|
full_filepath = os.path.join(path, filename)
|
||||||
|
if download_file_with_filename(cover, filename, path, conf, filepath) == 'failed':
|
||||||
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
|
|
||||||
configProxy = conf.proxy()
|
configProxy = conf.proxy()
|
||||||
for i in range(configProxy.retry):
|
for i in range(configProxy.retry):
|
||||||
if os.path.getsize(path + '/' + number + leak_word + c_word + '-fanart.jpg') == 0:
|
if os.path.getsize(full_filepath) == 0:
|
||||||
print('[!]Image Download Failed! Trying again. [{}/3]', i + 1)
|
print('[!]Image Download Failed! Trying again. [{}/3]', i + 1)
|
||||||
download_file_with_filename(cover, number + leak_word + c_word + '-fanart.jpg', path, conf, filepath)
|
download_file_with_filename(cover, filename, path, conf, filepath)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
if os.path.getsize(path + '/' + number + leak_word + c_word + '-fanart.jpg') == 0:
|
if os.path.getsize(full_filepath) == 0:
|
||||||
return
|
return
|
||||||
print('[+]Image Downloaded!', path + '/' + number + leak_word + c_word + '-fanart.jpg')
|
print('[+]Image Downloaded!', full_filepath)
|
||||||
shutil.copyfile(path + '/' + number + leak_word + c_word + '-fanart.jpg',path + '/' + number + leak_word + c_word + '-thumb.jpg')
|
shutil.copyfile(full_filepath, os.path.join(path, f"{number}{leak_word}{c_word}-thumb.jpg"))
|
||||||
|
|
||||||
|
|
||||||
def print_files(path, leak_word, c_word, naming_rule, part, cn_sub, json_data, filepath, failed_folder, tag, actor_list, liuchu, uncensored):
|
def print_files(path, leak_word, c_word, naming_rule, part, cn_sub, json_data, filepath, tag, actor_list, liuchu, uncensored, conf):
|
||||||
title, studio, year, outline, runtime, director, actor_photo, release, number, cover, trailer, website, series, label = get_info(json_data)
|
title, studio, year, outline, runtime, director, actor_photo, release, number, cover, trailer, website, series, label = get_info(json_data)
|
||||||
|
failed_folder = conf.failed_folder()
|
||||||
|
if conf.main_mode() == 3: # 模式3下,由于视频文件不做任何改变,.nfo文件必须和视频文件名称除后缀外完全一致,KODI等软件方可支持
|
||||||
|
nfo_path = str(Path(filepath).with_suffix('.nfo'))
|
||||||
|
else:
|
||||||
|
nfo_path = os.path.join(path,f"{number}{part}{leak_word}{c_word}.nfo")
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
os.makedirs(path)
|
os.makedirs(path)
|
||||||
with open(path + "/" + number + part + leak_word + c_word + ".nfo", "wt", encoding='UTF-8') as code:
|
with open(nfo_path, "wt", encoding='UTF-8') as code:
|
||||||
print('<?xml version="1.0" encoding="UTF-8" ?>', file=code)
|
print('<?xml version="1.0" encoding="UTF-8" ?>', file=code)
|
||||||
print("<movie>", file=code)
|
print("<movie>", file=code)
|
||||||
print(" <title>" + naming_rule + "</title>", file=code)
|
print(" <title>" + naming_rule + "</title>", file=code)
|
||||||
@@ -262,35 +281,35 @@ def print_files(path, leak_word, c_word, naming_rule, part, cn_sub, json_data, f
|
|||||||
print(" <trailer>" + trailer + "</trailer>", file=code)
|
print(" <trailer>" + trailer + "</trailer>", file=code)
|
||||||
print(" <website>" + website + "</website>", file=code)
|
print(" <website>" + website + "</website>", file=code)
|
||||||
print("</movie>", file=code)
|
print("</movie>", file=code)
|
||||||
print("[+]Wrote! " + path + "/" + number + part + leak_word + c_word + ".nfo")
|
print("[+]Wrote! " + nfo_path)
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
print("[-]Write Failed!")
|
print("[-]Write Failed!")
|
||||||
print(e)
|
print(e)
|
||||||
moveFailedFolder(filepath)
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
except Exception as e1:
|
except Exception as e1:
|
||||||
print(e1)
|
print(e1)
|
||||||
print("[-]Write Failed!")
|
print("[-]Write Failed!")
|
||||||
moveFailedFolder(filepath)
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def cutImage(imagecut, path, number, leak_word, c_word):
|
def cutImage(imagecut, path, number, leak_word, c_word):
|
||||||
|
fullpath_noext = os.path.join(path, f"{number}{leak_word}{c_word}")
|
||||||
if imagecut == 1: # 剪裁大封面
|
if imagecut == 1: # 剪裁大封面
|
||||||
try:
|
try:
|
||||||
img = Image.open(path + '/' + number + leak_word + c_word + '-fanart.jpg')
|
img = Image.open(fullpath_noext + '-fanart.jpg')
|
||||||
imgSize = img.size
|
imgSize = img.size
|
||||||
w = img.width
|
w = img.width
|
||||||
h = img.height
|
h = img.height
|
||||||
img2 = img.crop((w / 1.9, 0, w, h))
|
img2 = img.crop((w / 1.9, 0, w, h))
|
||||||
img2.save(path + '/' + number + leak_word + c_word + '-poster.jpg')
|
img2.save(fullpath_noext + '-poster.jpg')
|
||||||
print('[+]Image Cutted! ' + path + '/' + number + leak_word + c_word + '-poster.jpg')
|
print('[+]Image Cutted! ' + fullpath_noext + '-poster.jpg')
|
||||||
except:
|
except:
|
||||||
print('[-]Cover cut failed!')
|
print('[-]Cover cut failed!')
|
||||||
elif imagecut == 0: # 复制封面
|
elif imagecut == 0: # 复制封面
|
||||||
shutil.copyfile(path + '/' + number + leak_word + c_word + '-fanart.jpg',
|
shutil.copyfile(fullpath_noext + '-fanart.jpg', fullpath_noext + '-poster.jpg')
|
||||||
path + '/' + number + leak_word + c_word + '-poster.jpg')
|
print('[+]Image Copyed! ' + fullpath_noext + '-poster.jpg')
|
||||||
print('[+]Image Copyed! ' + path + '/' + number + leak_word + c_word + '-poster.jpg')
|
|
||||||
|
|
||||||
# 此函数从gui版copy过来用用
|
# 此函数从gui版copy过来用用
|
||||||
# 参数说明
|
# 参数说明
|
||||||
@@ -407,7 +426,7 @@ def paste_file_to_folder(filepath, path, number, leak_word, c_word, conf: config
|
|||||||
print('[-]Error! Please run as administrator!')
|
print('[-]Error! Please run as administrator!')
|
||||||
return
|
return
|
||||||
except OSError as oserr:
|
except OSError as oserr:
|
||||||
print('[-]OS Error errno ' + oserr.errno)
|
print(f'[-]OS Error errno {oserr.errno}')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -437,10 +456,10 @@ def paste_file_to_folder_mode2(filepath, path, multi_part, number, part, leak_wo
|
|||||||
print('[-]Error! Please run as administrator!')
|
print('[-]Error! Please run as administrator!')
|
||||||
return
|
return
|
||||||
except OSError as oserr:
|
except OSError as oserr:
|
||||||
print('[-]OS Error errno ' + oserr.errno)
|
print(f'[-]OS Error errno {oserr.errno}')
|
||||||
return
|
return
|
||||||
|
|
||||||
def get_part(filepath):
|
def get_part(filepath, conf):
|
||||||
try:
|
try:
|
||||||
if re.search('-CD\d+', filepath):
|
if re.search('-CD\d+', filepath):
|
||||||
return re.findall('-CD\d+', filepath)[0]
|
return re.findall('-CD\d+', filepath)[0]
|
||||||
@@ -448,7 +467,7 @@ def get_part(filepath):
|
|||||||
return re.findall('-cd\d+', filepath)[0]
|
return re.findall('-cd\d+', filepath)[0]
|
||||||
except:
|
except:
|
||||||
print("[-]failed!Please rename the filename again!")
|
print("[-]failed!Please rename the filename again!")
|
||||||
moveFailedFolder(filepath)
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -486,7 +505,7 @@ def core_main(file_path, number_th, conf: config.Config):
|
|||||||
|
|
||||||
# Return if blank dict returned (data not found)
|
# Return if blank dict returned (data not found)
|
||||||
if not json_data:
|
if not json_data:
|
||||||
moveFailedFolder(filepath)
|
moveFailedFolder(filepath, conf)
|
||||||
return
|
return
|
||||||
|
|
||||||
if json_data["number"] != number:
|
if json_data["number"] != number:
|
||||||
@@ -501,7 +520,7 @@ def core_main(file_path, number_th, conf: config.Config):
|
|||||||
# =======================================================================判断-C,-CD后缀
|
# =======================================================================判断-C,-CD后缀
|
||||||
if '-CD' in filepath or '-cd' in filepath:
|
if '-CD' in filepath or '-cd' in filepath:
|
||||||
multi_part = 1
|
multi_part = 1
|
||||||
part = get_part(filepath)
|
part = get_part(filepath, conf)
|
||||||
if '-c.' in filepath or '-C.' in filepath or '中文' in filepath or '字幕' in filepath:
|
if '-c.' in filepath or '-C.' in filepath or '中文' in filepath or '字幕' in filepath:
|
||||||
cn_sub = '1'
|
cn_sub = '1'
|
||||||
c_word = '-C' # 中文字幕影片后缀
|
c_word = '-C' # 中文字幕影片后缀
|
||||||
@@ -562,7 +581,7 @@ def core_main(file_path, number_th, conf: config.Config):
|
|||||||
cutImage(imagecut, path, number, leak_word, c_word)
|
cutImage(imagecut, path, number, leak_word, c_word)
|
||||||
|
|
||||||
# 打印文件
|
# 打印文件
|
||||||
print_files(path, leak_word, c_word, json_data.get('naming_rule'), part, cn_sub, json_data, filepath, conf.failed_folder(), tag, json_data.get('actor_list'), liuchu, uncensored)
|
print_files(path, leak_word, c_word, json_data.get('naming_rule'), part, cn_sub, json_data, filepath, tag, json_data.get('actor_list'), liuchu, uncensored, conf)
|
||||||
|
|
||||||
# 移动文件
|
# 移动文件
|
||||||
paste_file_to_folder(filepath, path, number, leak_word, c_word, conf)
|
paste_file_to_folder(filepath, path, number, leak_word, c_word, conf)
|
||||||
@@ -583,8 +602,7 @@ def core_main(file_path, number_th, conf: config.Config):
|
|||||||
add_mark(poster_path, thumb_path, cn_sub, leak, uncensored, conf)
|
add_mark(poster_path, thumb_path, cn_sub, leak, uncensored, conf)
|
||||||
|
|
||||||
elif conf.main_mode() == 3:
|
elif conf.main_mode() == 3:
|
||||||
path = file_path.rsplit('/', 1)[0]
|
path = str(Path(file_path).parent)
|
||||||
path = path.rsplit('\\', 1)[0]
|
|
||||||
if multi_part == 1:
|
if multi_part == 1:
|
||||||
number += part # 这时number会被附加上CD1后缀
|
number += part # 这时number会被附加上CD1后缀
|
||||||
|
|
||||||
@@ -608,8 +626,8 @@ def core_main(file_path, number_th, conf: config.Config):
|
|||||||
cutImage(imagecut, path, number, leak_word, c_word)
|
cutImage(imagecut, path, number, leak_word, c_word)
|
||||||
|
|
||||||
# 打印文件
|
# 打印文件
|
||||||
print_files(path, leak_word, c_word, json_data.get('naming_rule'), part, cn_sub, json_data, filepath, conf.failed_folder(),
|
print_files(path, leak_word, c_word, json_data.get('naming_rule'), part, cn_sub, json_data, filepath,
|
||||||
tag, json_data.get('actor_list'), liuchu, uncensored)
|
tag, json_data.get('actor_list'), liuchu, uncensored, conf)
|
||||||
|
|
||||||
poster_path = os.path.join(path, f"{number}{leak_word}{c_word}-poster.jpg")
|
poster_path = os.path.join(path, f"{number}{leak_word}{c_word}-poster.jpg")
|
||||||
thumb_path = os.path.join(path, f"{number}{leak_word}{c_word}-thumb.jpg")
|
thumb_path = os.path.join(path, f"{number}{leak_word}{c_word}-thumb.jpg")
|
||||||
|
|||||||
Reference in New Issue
Block a user