Merge pull request #734 from lededev/link-mode-1

新增硬链接支持及命令行开关
This commit is contained in:
Yoshiko2
2022-03-26 20:42:13 +08:00
committed by GitHub
4 changed files with 36 additions and 29 deletions

View File

@@ -49,6 +49,8 @@ def argparse_function(ver: str) -> typing.Tuple[str, str, str, str, bool]:
help="Main mode. 1:Scraping 2:Organizing 3:Scraping in analysis folder") help="Main mode. 1:Scraping 2:Organizing 3:Scraping in analysis folder")
parser.add_argument("-n", "--number", default='', nargs='?', help="Custom file number of single movie file.") parser.add_argument("-n", "--number", default='', nargs='?', help="Custom file number of single movie file.")
# 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.")
parser.add_argument("-L", "--link-mode", default='', nargs='?',
help="Create movie file link. 0:moving movie file, do not create link 1:soft link 2:try hard link first")
default_logdir = str(Path.home() / '.mlogs') default_logdir = str(Path.home() / '.mlogs')
parser.add_argument("-o", "--log-dir", dest='logdir', default=default_logdir, nargs='?', 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. help=f"""Duplicate stdout and stderr to logfiles in logging folder, default on.
@@ -83,6 +85,7 @@ is performed. It may help you correct wrong numbers before real job.""")
return True if isinstance(value, bool) and value else None return True if isinstance(value, bool) and value else None
config.G_conf_override["common:main_mode"] = get_natural_number_or_none(args.main_mode) config.G_conf_override["common:main_mode"] = get_natural_number_or_none(args.main_mode)
config.G_conf_override["common:link_mode"] = get_natural_number_or_none(args.link_mode)
config.G_conf_override["common:source_folder"] = get_str_or_none(args.path) config.G_conf_override["common:source_folder"] = get_str_or_none(args.path)
config.G_conf_override["common:auto_exit"] = get_bool_or_none(args.auto_exit) config.G_conf_override["common:auto_exit"] = get_bool_or_none(args.auto_exit)
config.G_conf_override["common:nfo_skip_days"] = get_natural_number_or_none(args.days) config.G_conf_override["common:nfo_skip_days"] = get_natural_number_or_none(args.days)
@@ -288,7 +291,7 @@ def movie_lists(source_folder, regexstr: str) -> typing.List[str]:
main_mode = conf.main_mode() main_mode = conf.main_mode()
debug = conf.debug() debug = conf.debug()
nfo_skip_days = conf.nfo_skip_days() nfo_skip_days = conf.nfo_skip_days()
soft_link = conf.soft_link() link_mode = conf.link_mode()
file_type = conf.media_type().lower().split(",") file_type = conf.media_type().lower().split(",")
trailerRE = re.compile(r'-trailer\.', re.IGNORECASE) trailerRE = re.compile(r'-trailer\.', re.IGNORECASE)
cliRE = None cliRE = None
@@ -299,7 +302,7 @@ def movie_lists(source_folder, regexstr: str) -> typing.List[str]:
pass pass
failed_list_txt_path = Path(conf.failed_folder()).resolve() / 'failed_list.txt' failed_list_txt_path = Path(conf.failed_folder()).resolve() / 'failed_list.txt'
failed_set = set() failed_set = set()
if (main_mode == 3 or soft_link) and not conf.ignore_failed_list(): if (main_mode == 3 or link_mode) and not conf.ignore_failed_list():
try: try:
flist = failed_list_txt_path.read_text(encoding='utf-8').splitlines() flist = failed_list_txt_path.read_text(encoding='utf-8').splitlines()
failed_set = set(flist) failed_set = set(flist)
@@ -351,7 +354,7 @@ def movie_lists(source_folder, regexstr: str) -> typing.List[str]:
if skip_nfo_days_cnt: if skip_nfo_days_cnt:
print( print(
f"[!]Skip {skip_nfo_days_cnt} movies in source folder '{source}' who's .nfo modified within {nfo_skip_days} days.") f"[!]Skip {skip_nfo_days_cnt} movies in source folder '{source}' who's .nfo modified within {nfo_skip_days} days.")
if nfo_skip_days <= 0 or not soft_link or main_mode == 3: if nfo_skip_days <= 0 or not link_mode or main_mode == 3:
return total return total
# 软连接方式,已经成功削刮的也需要从成功目录中检查.nfo更新天数跳过N天内更新过的 # 软连接方式,已经成功削刮的也需要从成功目录中检查.nfo更新天数跳过N天内更新过的
skip_numbers = set() skip_numbers = set()
@@ -458,7 +461,7 @@ def create_data_and_move_with_custom_number(file_path: str, custom_number, oCC):
print("[-] [{}] ERROR:".format(file_path)) print("[-] [{}] ERROR:".format(file_path))
print('[-]', err) print('[-]', err)
if conf.soft_link(): if conf.link_mode():
print("[-]Link {} to failed folder".format(file_path)) print("[-]Link {} to failed folder".format(file_path))
os.symlink(file_path, os.path.join(conf.failed_folder(), file_name)) os.symlink(file_path, os.path.join(conf.failed_folder(), file_name))
else: else:
@@ -511,7 +514,7 @@ def main():
print(f"[+]Load Config file '{conf.ini_path}'.") print(f"[+]Load Config file '{conf.ini_path}'.")
if conf.debug(): if conf.debug():
print('[+]Enable debug') print('[+]Enable debug')
if conf.soft_link(): if conf.link_mode():
print('[!]Enable soft link') print('[!]Enable soft link')
if len(sys.argv) > 1: if len(sys.argv) > 1:
print('[!]CmdLine:', " ".join(sys.argv[1:])) print('[!]CmdLine:', " ".join(sys.argv[1:]))

View File

@@ -5,7 +5,7 @@ main_mode=1
source_folder=./ source_folder=./
failed_output_folder=failed failed_output_folder=failed
success_output_folder=JAV_output success_output_folder=JAV_output
soft_link=0 link_mode=0
; 0: 不刮削硬链接文件 1: 刮削硬链接文件 ; 0: 不刮削硬链接文件 1: 刮削硬链接文件
scan_hardlink=0 scan_hardlink=0
failed_move=1 failed_move=1

View File

@@ -10,6 +10,7 @@ G_conf_override = {
0: None, 0: None,
# register override config items # register override config items
"common:main_mode": None, "common:main_mode": None,
"common:link_mode": None,
"common:source_folder": None, "common:source_folder": None,
"common:auto_exit": None, "common:auto_exit": None,
"common:nfo_skip_days": None, "common:nfo_skip_days": None,
@@ -128,8 +129,8 @@ class Config:
def actor_gender(self) -> str: def actor_gender(self) -> str:
return self.conf.get("common", "actor_gender") return self.conf.get("common", "actor_gender")
def soft_link(self) -> bool: def link_mode(self) -> bool:
return self.conf.getboolean("common", "soft_link") return self.getint_override("common", "link_mode")
def scan_hardlink(self) -> bool: def scan_hardlink(self) -> bool:
return self.conf.getboolean("common", "scan_hardlink", fallback=False)#未找到配置选项,默认不刮削 return self.conf.getboolean("common", "scan_hardlink", fallback=False)#未找到配置选项,默认不刮削
@@ -361,7 +362,7 @@ class Config:
conf.set(sec1, "source_folder", "./") conf.set(sec1, "source_folder", "./")
conf.set(sec1, "failed_output_folder", "failed") conf.set(sec1, "failed_output_folder", "failed")
conf.set(sec1, "success_output_folder", "JAV_output") conf.set(sec1, "success_output_folder", "JAV_output")
conf.set(sec1, "soft_link", "0") conf.set(sec1, "link_mode", "0")
conf.set(sec1, "scan_hardlink", "0") conf.set(sec1, "scan_hardlink", "0")
conf.set(sec1, "failed_move", "1") conf.set(sec1, "failed_move", "1")
conf.set(sec1, "auto_exit", "0") conf.set(sec1, "auto_exit", "0")

43
core.py
View File

@@ -27,15 +27,15 @@ def escape_path(path, escape_literals: str): # Remove escape literals
def moveFailedFolder(filepath): def moveFailedFolder(filepath):
conf = config.getInstance() conf = config.getInstance()
failed_folder = conf.failed_folder() failed_folder = conf.failed_folder()
soft_link = conf.soft_link() link_mode = conf.link_mode()
# 模式3或软连接改为维护一个失败列表启动扫描时加载用于排除该路径以免反复处理 # 模式3或软连接改为维护一个失败列表启动扫描时加载用于排除该路径以免反复处理
# 原先的创建软连接到失败目录,并不直观,不方便找到失败文件位置,不如直接记录该文件路径 # 原先的创建软连接到失败目录,并不直观,不方便找到失败文件位置,不如直接记录该文件路径
if conf.main_mode() == 3 or soft_link: if conf.main_mode() == 3 or link_mode:
ftxt = os.path.abspath(os.path.join(failed_folder, 'failed_list.txt')) ftxt = os.path.abspath(os.path.join(failed_folder, 'failed_list.txt'))
print("[-]Add to Failed List file, see '%s'" % ftxt) print("[-]Add to Failed List file, see '%s'" % ftxt)
with open(ftxt, 'a', encoding='utf-8') as flt: with open(ftxt, 'a', encoding='utf-8') as flt:
flt.write(f'{filepath}\n') flt.write(f'{filepath}\n')
elif conf.failed_move() and not soft_link: elif conf.failed_move() and not link_mode:
failed_name = os.path.join(failed_folder, os.path.basename(filepath)) failed_name = os.path.join(failed_folder, os.path.basename(filepath))
mtxt = os.path.abspath(os.path.join(failed_folder, 'where_was_i_before_being_moved.txt')) mtxt = os.path.abspath(os.path.join(failed_folder, 'where_was_i_before_being_moved.txt'))
print("'[-]Move to Failed output folder, see '%s'" % mtxt) print("'[-]Move to Failed output folder, see '%s'" % mtxt)
@@ -472,11 +472,19 @@ def paste_file_to_folder(filepath, path, number, leak_word, c_word, hack_word):
# 同名覆盖致使全部文件损失且不可追回的最坏情况 # 同名覆盖致使全部文件损失且不可追回的最坏情况
if os.path.exists(targetpath): if os.path.exists(targetpath):
raise FileExistsError('File Exists on destination path, we will never overwriting.') raise FileExistsError('File Exists on destination path, we will never overwriting.')
soft_link = config.getInstance().soft_link() link_mode = config.getInstance().link_mode()
# 如果soft_link=1 使用软链接 # 如果link_mode 1: 建立软链接 2: 硬链接优先、无法建立硬链接再尝试软链接
if soft_link == 0: # 移除原先soft_link=2的功能代码因默认记录日志已经可追溯文件来源
create_softlink = False
if link_mode not in (1, 2):
shutil.move(filepath, targetpath) shutil.move(filepath, targetpath)
elif soft_link == 1: elif link_mode == 2:
# 跨卷或跨盘符无法建立硬链接导致异常,回落到建立软链接
try:
os.link(filepath, targetpath, follow_symlinks=False)
except:
create_softlink = True
if link_mode == 1 or create_softlink:
# 先尝试采用相对路径,以便网络访问时能正确打开视频,失败则可能是因为跨盘符等原因无法支持 # 先尝试采用相对路径,以便网络访问时能正确打开视频,失败则可能是因为跨盘符等原因无法支持
# 相对路径径,改用绝对路径方式尝试建立软链接 # 相对路径径,改用绝对路径方式尝试建立软链接
try: try:
@@ -484,15 +492,6 @@ def paste_file_to_folder(filepath, path, number, leak_word, c_word, hack_word):
os.symlink(filerelpath, targetpath) os.symlink(filerelpath, targetpath)
except: except:
os.symlink(filepath_obj.resolve(), targetpath) os.symlink(filepath_obj.resolve(), targetpath)
elif soft_link == 2:
shutil.move(filepath, targetpath)
# 移走文件后,在原来位置增加一个可追溯的软链接,指向文件新位置
# 以便追查文件从原先位置被移动到哪里了,避免因为得到错误番号后改名移动导致的文件失踪
# 便于手工找回文件。由于目前软链接已经不会被刮削,文件名后缀无需再修改。
targetabspath = os.path.abspath(targetpath)
if targetabspath != os.path.abspath(filepath):
targetrelpath = os.path.relpath(targetabspath, file_parent_origin_path)
os.symlink(targetrelpath, filepath)
sub_res = config.getInstance().sub_rule() sub_res = config.getInstance().sub_rule()
for subname in sub_res: for subname in sub_res:
@@ -504,8 +503,12 @@ def paste_file_to_folder(filepath, path, number, leak_word, c_word, hack_word):
sub_filepath = sub_filepath.replace(subname, ".cht" + subname) sub_filepath = sub_filepath.replace(subname, ".cht" + subname)
subname = ".cht" + subname subname = ".cht" + subname
if os.path.isfile(sub_filepath): if os.path.isfile(sub_filepath):
shutil.move(sub_filepath, os.path.join(path, f"{number}{leak_word}{c_word}{hack_word}{subname}")) if link_mode not in (1, 2):
print('[+]Sub moved!') shutil.move(sub_filepath, os.path.join(path, f"{number}{leak_word}{c_word}{hack_word}{subname}"))
print('[+]Sub moved!')
else:
shutil.copyfile(sub_filepath, os.path.join(path, f"{number}{leak_word}{c_word}{hack_word}{subname}"))
print('[+]Sub Copied!')
return True return True
except FileExistsError as fee: except FileExistsError as fee:
@@ -530,7 +533,7 @@ def paste_file_to_folder_mode2(filepath, path, multi_part, number, part, leak_wo
if os.path.exists(targetpath): if os.path.exists(targetpath):
raise FileExistsError('File Exists on destination path, we will never overwriting.') raise FileExistsError('File Exists on destination path, we will never overwriting.')
try: try:
if config.getInstance().soft_link(): if config.getInstance().link_mode():
os.symlink(filepath, targetpath) os.symlink(filepath, targetpath)
else: else:
shutil.move(filepath, targetpath) shutil.move(filepath, targetpath)
@@ -686,7 +689,7 @@ def core_main(file_path, number_th, oCC):
except: except:
pass pass
# 裁剪图 # 裁剪图
cutImage(imagecut, path , fanart_path, poster_path) cutImage(imagecut, path , fanart_path, poster_path)