& 특수문자에 amp; 처리가 되어 있지 않은 경우 --fix-xml 옵션을 통해 수정할 수 있도록 추가
This commit is contained in:
55
main.py
55
main.py
@@ -40,6 +40,7 @@ def main(argc, argv):
|
|||||||
parser.add_argument('--metadata', dest='bMetadata', action='store_true', help='.metadata 파일로부터 ComicInfo.xml 생성 및 CBZ에 추가')
|
parser.add_argument('--metadata', dest='bMetadata', action='store_true', help='.metadata 파일로부터 ComicInfo.xml 생성 및 CBZ에 추가')
|
||||||
parser.add_argument('--verbose', dest='bVerbose', action='store_true', help='상세 로그 출력')
|
parser.add_argument('--verbose', dest='bVerbose', action='store_true', help='상세 로그 출력')
|
||||||
parser.add_argument('--fix-arc-path', dest='bFixArcPath', action='store_true', help='CBZ 아카이브의 내부 폴더 구조를 제거합니다.')
|
parser.add_argument('--fix-arc-path', dest='bFixArcPath', action='store_true', help='CBZ 아카이브의 내부 폴더 구조를 제거합니다.')
|
||||||
|
parser.add_argument('--fix-xml', dest='bFixXml', action='store_true', help='ComicInfo.xml의 특수문자(&) 이스케이프 및 문법 오류를 검사하고 수정합니다.')
|
||||||
parser.add_argument('-v', '--version', action='version', version='ComicInfoXMLConv 1.0')
|
parser.add_argument('-v', '--version', action='version', version='ComicInfoXMLConv 1.0')
|
||||||
|
|
||||||
args = parser.parse_args(argv[1:]) # argv[0]은 스크립트 이름 제외
|
args = parser.parse_args(argv[1:]) # argv[0]은 스크립트 이름 제외
|
||||||
@@ -114,6 +115,25 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
|||||||
util.DbgOut(f"comicinfo.xml 발견됨: {archive_path} (경로: {comicinfo_path_in_zip})", True)
|
util.DbgOut(f"comicinfo.xml 발견됨: {archive_path} (경로: {comicinfo_path_in_zip})", True)
|
||||||
comicinfo_xml_bytes = util.GetZippedFileByte(archive_path, comicinfo_path_in_zip)
|
comicinfo_xml_bytes = util.GetZippedFileByte(archive_path, comicinfo_path_in_zip)
|
||||||
if comicinfo_xml_bytes:
|
if comicinfo_xml_bytes:
|
||||||
|
fixed_xml = None
|
||||||
|
if args.bFixXml:
|
||||||
|
fixed_xml, msgs = ValidateAndFixComicInfoXML(comicinfo_xml_bytes)
|
||||||
|
for msg in msgs:
|
||||||
|
util.DbgOut(msg, True)
|
||||||
|
if fixed_xml is not None:
|
||||||
|
comicinfo_xml_bytes = fixed_xml
|
||||||
|
|
||||||
|
has_mods = any([arc_title, arc_writer, arc_series, arc_tags,
|
||||||
|
arc_number, arc_volume, args.info_count])
|
||||||
|
|
||||||
|
if not has_mods:
|
||||||
|
if fixed_xml is not None:
|
||||||
|
util.AddFileToZip(archive_path, comicinfo_path_in_zip, fixed_xml)
|
||||||
|
util.DbgOut(f"ComicInfo.xml 특수문자 수정 완료: {archive_path}", True)
|
||||||
|
if args.bPrint:
|
||||||
|
print(comicinfo_xml_bytes.decode('utf-8'))
|
||||||
|
return
|
||||||
|
|
||||||
modified = ModComicInfoXML(comicinfo_xml_bytes, arc_title, arc_writer, arc_series, arc_tags,
|
modified = ModComicInfoXML(comicinfo_xml_bytes, arc_title, arc_writer, arc_series, arc_tags,
|
||||||
info_number=arc_number, info_volume=arc_volume, info_count=args.info_count)
|
info_number=arc_number, info_volume=arc_volume, info_count=args.info_count)
|
||||||
|
|
||||||
@@ -123,6 +143,9 @@ def process_archive_file(archive_path: str, args: argparse.Namespace):
|
|||||||
elif modified:
|
elif modified:
|
||||||
util.AddFileToZip(archive_path, comicinfo_path_in_zip, modified)
|
util.AddFileToZip(archive_path, comicinfo_path_in_zip, modified)
|
||||||
util.DbgOut(f"ComicInfo.xml 업데이트됨: {archive_path} (경로: {comicinfo_path_in_zip})", True)
|
util.DbgOut(f"ComicInfo.xml 업데이트됨: {archive_path} (경로: {comicinfo_path_in_zip})", True)
|
||||||
|
elif fixed_xml is not None:
|
||||||
|
util.AddFileToZip(archive_path, comicinfo_path_in_zip, fixed_xml)
|
||||||
|
util.DbgOut(f"ComicInfo.xml 특수문자 수정 완료: {archive_path}", True)
|
||||||
else:
|
else:
|
||||||
util.DbgOut(f"comicinfo.xml 없음: {archive_path}", True)
|
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 args.info_count:
|
if arc_volume or arc_number or arc_title or arc_writer or arc_series or arc_tags or args.info_count:
|
||||||
@@ -255,6 +278,38 @@ def ResolveRegexTransform(text: str | None, current_value: str | None) -> str |
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def ValidateAndFixComicInfoXML(xml_bytes: bytes) -> tuple[bytes | None, list[str]]:
|
||||||
|
try:
|
||||||
|
ET.fromstring(xml_bytes)
|
||||||
|
return (None, ["XML is well-formed."])
|
||||||
|
except ET.ParseError as e:
|
||||||
|
msgs = [f"Parse error: {e}"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
text = xml_bytes.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
msgs.append("Cannot decode XML (expected UTF-8).")
|
||||||
|
return (None, msgs)
|
||||||
|
|
||||||
|
text, count = re.subn(
|
||||||
|
r'&(?!(?:amp|lt|gt|quot|apos);|#\d+;|#x[0-9a-fA-F]+;)',
|
||||||
|
'&',
|
||||||
|
text
|
||||||
|
)
|
||||||
|
if count > 0:
|
||||||
|
msgs.append(f"Fixed {count} unescaped & character(s).")
|
||||||
|
fixed_bytes = text.encode('utf-8')
|
||||||
|
try:
|
||||||
|
ET.fromstring(fixed_bytes)
|
||||||
|
msgs.append("XML is now well-formed.")
|
||||||
|
except ET.ParseError as e2:
|
||||||
|
msgs.append(f"Remaining issues after fix: {e2}")
|
||||||
|
return (fixed_bytes, msgs)
|
||||||
|
|
||||||
|
msgs.append("No fixable issues found.")
|
||||||
|
return (None, msgs)
|
||||||
|
|
||||||
|
|
||||||
def ParsePathForComicInfo(strFullPath: str) -> dict:
|
def ParsePathForComicInfo(strFullPath: str) -> dict:
|
||||||
"""
|
"""
|
||||||
파일 경로에서 작가, 제목, 번호를 추출합니다.
|
파일 경로에서 작가, 제목, 번호를 추출합니다.
|
||||||
|
|||||||
Reference in New Issue
Block a user