Compare commits
1
Commits
main
..
12f23f0d24
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12f23f0d24 |
+29
-12
@@ -7,7 +7,6 @@ import shutil
|
||||
import difflib
|
||||
import subprocess
|
||||
import hashlib
|
||||
import glob
|
||||
|
||||
try:
|
||||
import rarfile
|
||||
@@ -233,9 +232,10 @@ def ExtractZIP(zip_file: str, extract_to: str):
|
||||
|
||||
#
|
||||
def CreateZIP(output_zip: str, files: list[str]) -> bool:
|
||||
with zipfile.ZipFile(output_zip, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
|
||||
with zipfile.ZipFile(output_zip, 'w') as zf:
|
||||
for file in files:
|
||||
zf.write(file, os.path.basename(file))
|
||||
pathTemp = os.path.join('root', os.path.basename(file))
|
||||
zf.write(file, pathTemp)
|
||||
|
||||
bRet = False
|
||||
if os.path.exists(output_zip):
|
||||
@@ -245,19 +245,36 @@ def CreateZIP(output_zip: str, files: list[str]) -> bool:
|
||||
|
||||
# 파일 리스트에 들어있는 파일만 골라서 압축을 합니다. 상대경로를 제거하는게 기본값.
|
||||
def CreateZIPShell(zipName: str, files: list[str], bRmvRPath: bool = True) -> bool:
|
||||
command = ["zip"]
|
||||
command = "zip "
|
||||
|
||||
if True == bRmvRPath:
|
||||
command.append("-j")
|
||||
command.extend([zipName, *files])
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
return result.returncode == 0
|
||||
command += "-j "
|
||||
|
||||
command += f"\"{zipName}\" "
|
||||
|
||||
# 이중 리스트인 이유를 모르겠다.
|
||||
for file in files:
|
||||
command += f"\"{file}\" "
|
||||
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||
|
||||
bRet = False
|
||||
if 0 == result.returncode:
|
||||
bRet = True
|
||||
|
||||
return bRet
|
||||
|
||||
# 특정 확장자만 쉘을 이용해서 압축한다
|
||||
def CreateZIPShExt(zipName: str, TrgExt: str)-> bool:
|
||||
matching_files = glob.glob(f"*.{TrgExt}")
|
||||
command = ["zip", "-j", zipName, *matching_files]
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
return result.returncode == 0
|
||||
command = f"zip -j {zipName} *.{TrgExt}"
|
||||
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||
|
||||
bRet = False
|
||||
if 0 == result.returncode:
|
||||
bRet = True
|
||||
|
||||
return bRet
|
||||
|
||||
# 압축 파일 내의 모든 파일 및 디렉토리 목록 가져오기
|
||||
def GetZipContentList(path: str) -> list[str]:
|
||||
|
||||
@@ -26,17 +26,7 @@ DEFAULT_VALUES = {
|
||||
|
||||
#
|
||||
def main(argc, argv):
|
||||
parser = argparse.ArgumentParser(
|
||||
description="ComicInfo.XML 생성기",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog=(
|
||||
"사용 예시:\n"
|
||||
" 폴더명이 '[작가] 제목'인 경우:\n"
|
||||
" python3 main.py --in \"/path/[작가] 제목/book.cbz\" "
|
||||
"--title '<currentfolder>:s/^\\[([^\\]]+)\\]\\s*(.+)$/$2/' "
|
||||
"--author '<currentfolder>:s/^\\[([^\\]]+)\\]\\s*(.+)$/$1/'"
|
||||
),
|
||||
)
|
||||
parser = argparse.ArgumentParser(description="ComicInfo.XML 생성기")
|
||||
parser.add_argument('--root', dest='root_dir', required=False, help='입력 폴더 지정')
|
||||
parser.add_argument('--in', dest='arc_file', required=False, help='대상 파일 지정')
|
||||
parser.add_argument('--title', dest='info_title', required=False, help='제목')
|
||||
@@ -103,13 +93,10 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
||||
arc_number = ExpandPathVars(args.info_number, archive_path)
|
||||
arc_tags = args.info_tags.split(',') if args.info_tags else []
|
||||
|
||||
# info_count를 int로 변환 (argparse에서 str로 파싱됨)
|
||||
arc_count = int(args.info_count) if args.info_count is not None else None
|
||||
|
||||
# .metadata 옵션이 활성화된 경우
|
||||
if args.bMetadata:
|
||||
ProcessMetadataToCBZ(archive_path, args.bPrint,
|
||||
arc_volume, arc_number, arc_count,
|
||||
arc_volume, arc_number, args.info_count,
|
||||
arc_title, arc_writer, arc_series,
|
||||
arc_tags if arc_tags else None)
|
||||
return
|
||||
@@ -137,7 +124,7 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
||||
comicinfo_xml_bytes = fixed_xml
|
||||
|
||||
has_mods = any([arc_title, arc_writer, arc_series, arc_tags,
|
||||
arc_number, arc_volume, arc_count])
|
||||
arc_number, arc_volume, args.info_count])
|
||||
|
||||
if not has_mods:
|
||||
if fixed_xml is not None:
|
||||
@@ -148,7 +135,7 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
||||
return
|
||||
|
||||
modified = ModComicInfoXML(comicinfo_xml_bytes, arc_title, arc_writer, arc_series, arc_tags,
|
||||
info_number=arc_number, info_volume=arc_volume, info_count=arc_count)
|
||||
info_number=arc_number, info_volume=arc_volume, info_count=args.info_count)
|
||||
|
||||
if args.bPrint:
|
||||
output = modified.decode('utf-8') if modified else comicinfo_xml_bytes.decode('utf-8')
|
||||
@@ -161,7 +148,7 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
||||
util.DbgOut(f"ComicInfo.xml 특수문자 수정 완료: {archive_path}", True)
|
||||
else:
|
||||
util.DbgOut(f"comicinfo.xml 없음: {archive_path}", True)
|
||||
if arc_volume or arc_number or arc_title or arc_writer or arc_series or arc_tags or arc_count is not None:
|
||||
if arc_volume or arc_number or arc_title or arc_writer or arc_series or arc_tags or args.info_count:
|
||||
new_xml = CreateComicInfoXML(
|
||||
info_title=arc_title or "",
|
||||
info_writer=arc_writer or "",
|
||||
@@ -170,7 +157,7 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
||||
pages=[],
|
||||
volume=arc_volume or "",
|
||||
number=arc_number or "",
|
||||
count=arc_count if arc_count is not None else -1
|
||||
count=int(args.info_count) if args.info_count is not None else -1
|
||||
)
|
||||
if new_xml:
|
||||
if args.bPrint:
|
||||
@@ -186,9 +173,7 @@ def ExpandPathVars(text: str | None, filepath: str) -> str | None:
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
# 경로 정규화: ./ 또는 ../ 등 상대 경로 표현을 제거하여 정확한 폴더명 추출
|
||||
normpath = os.path.normpath(filepath)
|
||||
dirpath = os.path.dirname(normpath)
|
||||
dirpath = os.path.dirname(filepath)
|
||||
cur_folder = os.path.basename(dirpath) if dirpath else ""
|
||||
par_folder = os.path.basename(os.path.dirname(dirpath)) if dirpath and os.path.dirname(dirpath) else ""
|
||||
filename_no_ext = os.path.splitext(os.path.basename(filepath))[0]
|
||||
@@ -208,31 +193,23 @@ def ExpandPathVars(text: str | None, filepath: str) -> str | None:
|
||||
for key, value in var_map.items():
|
||||
result = result.replace(f"<{key}>", value)
|
||||
|
||||
# After variable expansion, check for :s/pattern/replacement/ suffix.
|
||||
transform_pos = result.find(':s')
|
||||
if transform_pos >= 0:
|
||||
source = result[:transform_pos]
|
||||
transform = result[transform_pos + 1:]
|
||||
m = re.match(r'^s(?P<delim>.)(?P<pattern>.*?)(?P=delim)(?P<replacement>.*?)(?P=delim)(?P<flags>[ig]*)$', transform, re.DOTALL)
|
||||
else:
|
||||
source = ''
|
||||
m = None
|
||||
|
||||
# After variable expansion, check for :s/pattern/replacement/ suffix
|
||||
m = re.match(r'^(.*):s(.)(.+?)\2(.*?)(?:\2([ig]*))?$', result, re.DOTALL)
|
||||
if m:
|
||||
pattern = m.group('pattern')
|
||||
replacement = m.group('replacement')
|
||||
flags_str = m.group('flags')
|
||||
replacement_safe = re.sub(r'\$(\d+)', lambda match: '\\' + match.group(1), replacement)
|
||||
source = m.group(1)
|
||||
delim = m.group(2)
|
||||
pattern = m.group(3)
|
||||
replacement = m.group(4)
|
||||
flags_str = m.group(5) or ''
|
||||
replacement_safe = re.sub(r'\$(\d+)', r'\\\1', replacement)
|
||||
flags = 0
|
||||
if 'i' in flags_str:
|
||||
flags |= re.IGNORECASE
|
||||
count = 0 if 'g' in flags_str else 1
|
||||
# source가 비어있으면 정규식 미적용 (ResolveRegexTransform에서 처리)
|
||||
if source:
|
||||
try:
|
||||
result = re.sub(pattern, replacement_safe, source, count=count, flags=flags)
|
||||
except re.error:
|
||||
pass
|
||||
try:
|
||||
result = re.sub(pattern, replacement_safe, source, count=count, flags=flags)
|
||||
except re.error:
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
@@ -327,7 +304,6 @@ def ValidateAndFixComicInfoXML(xml_bytes: bytes) -> tuple[bytes | None, list[str
|
||||
msgs.append("XML is now well-formed.")
|
||||
except ET.ParseError as e2:
|
||||
msgs.append(f"Remaining issues after fix: {e2}")
|
||||
return (None, msgs)
|
||||
return (fixed_bytes, msgs)
|
||||
|
||||
msgs.append("No fixable issues found.")
|
||||
@@ -519,7 +495,7 @@ def ConvertLanguageToISO(lang_name: str) -> str:
|
||||
|
||||
#
|
||||
def ProcessMetadataToCBZ(strArcPath: str, bPrint: bool = False,
|
||||
volume: str = "-1", number: str = "", count: int | None = None,
|
||||
volume: str = "-1", number: str = "", count: int = -1,
|
||||
info_title: str = None, info_writer: str = None,
|
||||
info_series: str = None, tags: list[str] = None) -> bool:
|
||||
"""
|
||||
@@ -621,7 +597,6 @@ def ProcessMetadataToCBZ(strArcPath: str, bPrint: bool = False,
|
||||
xml_content = output.getvalue().decode('utf-8')
|
||||
except Exception as e:
|
||||
util.DbgOut(f"XML 수정 중 오류: {e}", True)
|
||||
return False
|
||||
|
||||
# 출력 옵션이 켜져 있으면 내용 출력
|
||||
if True == bPrint:
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
rarfile>=4.0
|
||||
comicapi
|
||||
|
||||
Reference in New Issue
Block a user