Turn comicapi.archivers.* into plugins
This commit is contained in:
parent
2f7e3921ef
commit
712986ee69
@ -1,19 +1,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from comicapi.archivers.unknown import UnknownArchiver
|
||||
|
||||
__all__ = ["UnknownArchiver"]
|
||||
from comicapi.archivers.archiver import Archiver
|
||||
from comicapi.archivers.folder import FolderArchiver
|
||||
from comicapi.archivers.rar import RarArchiver, rar_support
|
||||
from comicapi.archivers.sevenzip import SevenZipArchiver, z7_support
|
||||
from comicapi.archivers.rar import RarArchiver
|
||||
from comicapi.archivers.sevenzip import SevenZipArchiver
|
||||
from comicapi.archivers.zip import ZipArchiver
|
||||
|
||||
__all__ = [
|
||||
"UnknownArchiver",
|
||||
"FolderArchiver",
|
||||
"RarArchiver",
|
||||
"rar_support",
|
||||
"ZipArchiver",
|
||||
"SevenZipArchiver",
|
||||
"z7_support",
|
||||
]
|
||||
|
||||
class UnknownArchiver(Archiver):
|
||||
def name(self) -> str:
|
||||
return "Unknown"
|
||||
|
||||
|
||||
__all__ = ["Archiver", "UnknownArchiver", "FolderArchiver", "RarArchiver", "ZipArchiver", "SevenZipArchiver"]
|
||||
|
62
comicapi/archivers/archiver.py
Normal file
62
comicapi/archivers/archiver.py
Normal file
@ -0,0 +1,62 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Archiver(Protocol):
|
||||
|
||||
"""Archiver Protocol"""
|
||||
|
||||
path: pathlib.Path
|
||||
enabled: bool = True
|
||||
|
||||
def __init__(self):
|
||||
self.path = pathlib.Path()
|
||||
|
||||
def get_comment(self) -> str:
|
||||
return ""
|
||||
|
||||
def set_comment(self, comment: str) -> bool:
|
||||
return False
|
||||
|
||||
def supports_comment(self) -> bool:
|
||||
return False
|
||||
|
||||
def read_file(self, archive_file: str) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
def remove_file(self, archive_file: str) -> bool:
|
||||
return False
|
||||
|
||||
def write_file(self, archive_file: str, data: bytes) -> bool:
|
||||
return False
|
||||
|
||||
def get_filename_list(self) -> list[str]:
|
||||
return []
|
||||
|
||||
def rebuild(self, exclude_list: list[str]) -> bool:
|
||||
return False
|
||||
|
||||
def copy_from_archive(self, other_archive: Archiver) -> bool:
|
||||
return False
|
||||
|
||||
def is_writable(self) -> bool:
|
||||
return False
|
||||
|
||||
def extension(self) -> str:
|
||||
return ""
|
||||
|
||||
def name(self) -> str:
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls, path: pathlib.Path) -> bool:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def open(cls, path: pathlib.Path) -> Archiver:
|
||||
archiver = cls()
|
||||
archiver.path = path
|
||||
return archiver
|
@ -4,17 +4,17 @@ import logging
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
from comicapi.archivers import UnknownArchiver
|
||||
from comicapi.archivers import Archiver
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FolderArchiver(UnknownArchiver):
|
||||
class FolderArchiver(Archiver):
|
||||
|
||||
"""Folder implementation"""
|
||||
|
||||
def __init__(self, path: pathlib.Path | str) -> None:
|
||||
super().__init__(path)
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.comment_file_name = "ComicTaggerFolderComment.txt"
|
||||
|
||||
def get_comment(self) -> str:
|
||||
@ -70,7 +70,7 @@ class FolderArchiver(UnknownArchiver):
|
||||
logger.error("Error listing files in folder archive [%s]: %s", e, self.path)
|
||||
return []
|
||||
|
||||
def copy_from_archive(self, other_archive: UnknownArchiver) -> bool:
|
||||
def copy_from_archive(self, other_archive: Archiver) -> bool:
|
||||
"""Replace the current zip with one copied from another archive"""
|
||||
try:
|
||||
for filename in other_archive.get_filename_list():
|
||||
@ -88,3 +88,10 @@ class FolderArchiver(UnknownArchiver):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def name(self) -> str:
|
||||
return "Folder"
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls, path: pathlib.Path | str) -> bool:
|
||||
return os.path.isdir(path)
|
||||
|
@ -9,7 +9,7 @@ import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from comicapi.archivers import UnknownArchiver
|
||||
from comicapi.archivers import Archiver
|
||||
|
||||
try:
|
||||
from unrar.cffi import rarfile
|
||||
@ -25,11 +25,13 @@ if not rar_support:
|
||||
logger.error("unrar-cffi unavailable")
|
||||
|
||||
|
||||
class RarArchiver(UnknownArchiver):
|
||||
class RarArchiver(Archiver):
|
||||
"""RAR implementation"""
|
||||
|
||||
def __init__(self, path: pathlib.Path | str, rar_exe_path: str = "rar") -> None:
|
||||
super().__init__(path)
|
||||
enabled = rar_support
|
||||
|
||||
def __init__(self, rar_exe_path: str = "rar") -> None:
|
||||
super().__init__()
|
||||
self.rar_exe_path = shutil.which(rar_exe_path) or ""
|
||||
|
||||
# windows only, keeps the cmd.exe from popping up
|
||||
@ -199,7 +201,7 @@ class RarArchiver(UnknownArchiver):
|
||||
return namelist
|
||||
return []
|
||||
|
||||
def copy_from_archive(self, other_archive: UnknownArchiver) -> bool:
|
||||
def copy_from_archive(self, other_archive: Archiver) -> bool:
|
||||
"""Replace the current archive with one copied from another archive"""
|
||||
try:
|
||||
with tempfile.TemporaryDirectory() as tmp_dir:
|
||||
@ -234,6 +236,21 @@ class RarArchiver(UnknownArchiver):
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_writable(self) -> bool:
|
||||
return bool(self.rar_exe_path and os.path.exists(self.rar_exe_path))
|
||||
|
||||
def extension(self) -> str:
|
||||
return ".cbr"
|
||||
|
||||
def name(self) -> str:
|
||||
return "RAR"
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls, path: pathlib.Path | str) -> bool:
|
||||
if rar_support:
|
||||
return rarfile.is_rarfile(str(path))
|
||||
return False
|
||||
|
||||
def get_rar_obj(self) -> rarfile.RarFile | None:
|
||||
if rar_support:
|
||||
try:
|
||||
|
@ -6,7 +6,7 @@ import pathlib
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from comicapi.archivers import UnknownArchiver
|
||||
from comicapi.archivers import Archiver
|
||||
|
||||
try:
|
||||
import py7zr
|
||||
@ -18,12 +18,13 @@ except ImportError:
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SevenZipArchiver(UnknownArchiver):
|
||||
|
||||
class SevenZipArchiver(Archiver):
|
||||
"""7Z implementation"""
|
||||
|
||||
def __init__(self, path: pathlib.Path | str) -> None:
|
||||
super().__init__(path)
|
||||
enabled = z7_support
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
# @todo: Implement Comment?
|
||||
def get_comment(self) -> str:
|
||||
@ -100,7 +101,7 @@ class SevenZipArchiver(UnknownArchiver):
|
||||
return False
|
||||
return True
|
||||
|
||||
def copy_from_archive(self, other_archive: UnknownArchiver) -> bool:
|
||||
def copy_from_archive(self, other_archive: Archiver) -> bool:
|
||||
"""Replace the current zip with one copied from another archive"""
|
||||
try:
|
||||
with py7zr.SevenZipFile(self.path, "w") as zout:
|
||||
@ -115,3 +116,16 @@ class SevenZipArchiver(UnknownArchiver):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_writable(self) -> bool:
|
||||
return True
|
||||
|
||||
def extension(self) -> str:
|
||||
return ".cb7"
|
||||
|
||||
def name(self) -> str:
|
||||
return "Seven Zip"
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls, path: pathlib.Path | str) -> bool:
|
||||
return py7zr.is_7zfile(path)
|
||||
|
@ -1,35 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
|
||||
|
||||
class UnknownArchiver:
|
||||
|
||||
"""Unknown implementation"""
|
||||
|
||||
def __init__(self, path: pathlib.Path | str) -> None:
|
||||
self.path = pathlib.Path(path)
|
||||
|
||||
def get_comment(self) -> str:
|
||||
return ""
|
||||
|
||||
def set_comment(self, comment: str) -> bool:
|
||||
return False
|
||||
|
||||
def read_file(self, archive_file: str) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
||||
def remove_file(self, archive_file: str) -> bool:
|
||||
return False
|
||||
|
||||
def write_file(self, archive_file: str, data: bytes) -> bool:
|
||||
return False
|
||||
|
||||
def get_filename_list(self) -> list[str]:
|
||||
return []
|
||||
|
||||
def rebuild(self, exclude_list: list[str]) -> bool:
|
||||
return False
|
||||
|
||||
def copy_from_archive(self, other_archive: UnknownArchiver) -> bool:
|
||||
return False
|
@ -9,17 +9,17 @@ import tempfile
|
||||
import zipfile
|
||||
from typing import cast
|
||||
|
||||
from comicapi.archivers import UnknownArchiver
|
||||
from comicapi.archivers import Archiver
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ZipArchiver(UnknownArchiver):
|
||||
class ZipArchiver(Archiver):
|
||||
|
||||
"""ZIP implementation"""
|
||||
|
||||
def __init__(self, path: pathlib.Path | str) -> None:
|
||||
super().__init__(path)
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def get_comment(self) -> str:
|
||||
with zipfile.ZipFile(self.path, "r") as zf:
|
||||
@ -99,7 +99,7 @@ class ZipArchiver(UnknownArchiver):
|
||||
return False
|
||||
return True
|
||||
|
||||
def copy_from_archive(self, other_archive: UnknownArchiver) -> bool:
|
||||
def copy_from_archive(self, other_archive: Archiver) -> bool:
|
||||
"""Replace the current zip with one copied from another archive"""
|
||||
try:
|
||||
with zipfile.ZipFile(self.path, mode="w", allowZip64=True) as zout:
|
||||
@ -119,6 +119,19 @@ class ZipArchiver(UnknownArchiver):
|
||||
else:
|
||||
return True
|
||||
|
||||
def is_writable(self) -> bool:
|
||||
return True
|
||||
|
||||
def extension(self) -> str:
|
||||
return ".cbz"
|
||||
|
||||
def name(self) -> str:
|
||||
return "ZIP"
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls, path: pathlib.Path | str) -> bool:
|
||||
return zipfile.is_zipfile(path)
|
||||
|
||||
def write_zip_comment(self, filename: pathlib.Path | str, comment: str) -> bool:
|
||||
"""
|
||||
This is a custom function for writing a comment to a zip file,
|
||||
|
@ -19,31 +19,23 @@ import logging
|
||||
import os
|
||||
import pathlib
|
||||
import shutil
|
||||
import zipfile
|
||||
import sys
|
||||
from typing import cast
|
||||
|
||||
import natsort
|
||||
import wordninja
|
||||
|
||||
from comicapi import filenamelexer, filenameparser, utils
|
||||
from comicapi.archivers import FolderArchiver, RarArchiver, SevenZipArchiver, UnknownArchiver, ZipArchiver
|
||||
from comicapi.archivers import Archiver, UnknownArchiver, ZipArchiver
|
||||
from comicapi.comet import CoMet
|
||||
from comicapi.comicbookinfo import ComicBookInfo
|
||||
from comicapi.comicinfoxml import ComicInfoXml
|
||||
from comicapi.genericmetadata import GenericMetadata, PageType
|
||||
|
||||
try:
|
||||
import py7zr
|
||||
|
||||
z7_support = True
|
||||
except ImportError:
|
||||
z7_support = False
|
||||
try:
|
||||
from unrar.cffi import rarfile
|
||||
|
||||
rar_support = True
|
||||
except ImportError:
|
||||
rar_support = False
|
||||
if sys.version_info < (3, 10):
|
||||
from importlib_metadata import entry_points
|
||||
else:
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
@ -52,12 +44,26 @@ try:
|
||||
except ImportError:
|
||||
pil_available = False
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
if not pil_available:
|
||||
logger.error("PIL unavalable")
|
||||
|
||||
archivers: list[type[Archiver]] = []
|
||||
|
||||
|
||||
def load_archive_plugins() -> None:
|
||||
for arch in entry_points(group="comicapi_archivers"):
|
||||
try:
|
||||
archiver: type[Archiver] = arch.load()
|
||||
if archiver.enabled:
|
||||
if not arch.module.startswith("comicapi"):
|
||||
archivers.insert(0, archiver)
|
||||
else:
|
||||
archivers.append(archiver)
|
||||
except Exception:
|
||||
logger.warning("Failed to load talker: %s", arch.name)
|
||||
|
||||
|
||||
class MetaDataStyle:
|
||||
CBI = 0
|
||||
@ -70,9 +76,6 @@ class MetaDataStyle:
|
||||
class ComicArchive:
|
||||
logo_data = b""
|
||||
|
||||
class ArchiveType:
|
||||
SevenZip, Zip, Rar, Folder, Pdf, Unknown = list(range(6))
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path: pathlib.Path | str,
|
||||
@ -96,36 +99,12 @@ class ComicArchive:
|
||||
self.reset_cache()
|
||||
self.default_image_path = default_image_path
|
||||
|
||||
# Use file extension to decide which archive test we do first
|
||||
ext = self.path.suffix
|
||||
self.archiver: Archiver = UnknownArchiver.open(self.path)
|
||||
|
||||
self.archive_type = self.ArchiveType.Unknown
|
||||
self.archiver = UnknownArchiver(self.path)
|
||||
|
||||
if ext in [".cbr", ".rar"]:
|
||||
if self.rar_test():
|
||||
self.archive_type = self.ArchiveType.Rar
|
||||
self.archiver = RarArchiver(self.path, rar_exe_path=self.rar_exe_path)
|
||||
|
||||
elif self.zip_test():
|
||||
self.archive_type = self.ArchiveType.Zip
|
||||
self.archiver = ZipArchiver(self.path)
|
||||
else:
|
||||
if self.sevenzip_test():
|
||||
self.archive_type = self.ArchiveType.SevenZip
|
||||
self.archiver = SevenZipArchiver(self.path)
|
||||
|
||||
elif self.zip_test():
|
||||
self.archive_type = self.ArchiveType.Zip
|
||||
self.archiver = ZipArchiver(self.path)
|
||||
|
||||
elif self.rar_test():
|
||||
self.archive_type = self.ArchiveType.Rar
|
||||
self.archiver = RarArchiver(self.path, rar_exe_path=self.rar_exe_path)
|
||||
|
||||
elif self.folder_test():
|
||||
self.archive_type = self.ArchiveType.Folder
|
||||
self.archiver = FolderArchiver(self.path)
|
||||
for archiver in archivers:
|
||||
if archiver.is_valid(self.path):
|
||||
self.archiver = archiver.open(self.path)
|
||||
break
|
||||
|
||||
if not ComicArchive.logo_data and self.default_image_path:
|
||||
with open(self.default_image_path, mode="rb") as fd:
|
||||
@ -157,63 +136,33 @@ class ComicArchive:
|
||||
self.path = new_path
|
||||
self.archiver.path = pathlib.Path(path)
|
||||
|
||||
def sevenzip_test(self) -> bool:
|
||||
return z7_support and py7zr.is_7zfile(self.path)
|
||||
|
||||
def zip_test(self) -> bool:
|
||||
return zipfile.is_zipfile(self.path)
|
||||
|
||||
def rar_test(self) -> bool:
|
||||
return rar_support and rarfile.is_rarfile(str(self.path))
|
||||
|
||||
def folder_test(self) -> bool:
|
||||
return self.path.is_dir()
|
||||
|
||||
def is_sevenzip(self) -> bool:
|
||||
return self.archive_type == self.ArchiveType.SevenZip
|
||||
|
||||
def is_zip(self) -> bool:
|
||||
return self.archive_type == self.ArchiveType.Zip
|
||||
|
||||
def is_rar(self) -> bool:
|
||||
return self.archive_type == self.ArchiveType.Rar
|
||||
|
||||
def is_pdf(self) -> bool:
|
||||
return self.archive_type == self.ArchiveType.Pdf
|
||||
|
||||
def is_folder(self) -> bool:
|
||||
return self.archive_type == self.ArchiveType.Folder
|
||||
|
||||
def is_writable(self, check_rar_status: bool = True) -> bool:
|
||||
if self.archive_type == self.ArchiveType.Unknown:
|
||||
def is_writable(self, check_archive_status: bool = True) -> bool:
|
||||
if isinstance(self.archiver, UnknownArchiver):
|
||||
return False
|
||||
|
||||
if check_rar_status and self.is_rar() and not self.rar_exe_path:
|
||||
if check_archive_status and not self.archiver.is_writable():
|
||||
return False
|
||||
|
||||
if not os.access(self.path, os.W_OK):
|
||||
return False
|
||||
|
||||
if (self.archive_type != self.ArchiveType.Folder) and (not os.access(self.path.parent, os.W_OK)):
|
||||
if not (os.access(self.path, os.W_OK) or os.access(self.path.parent, os.W_OK)):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def is_writable_for_style(self, data_style: int) -> bool:
|
||||
return not (data_style == MetaDataStyle.CBI and not self.archiver.supports_comment)
|
||||
|
||||
if (self.is_rar() or self.is_sevenzip()) and data_style == MetaDataStyle.CBI:
|
||||
return False
|
||||
|
||||
return self.is_writable()
|
||||
def is_zip(self) -> bool:
|
||||
return self.archiver.name() == "ZIP"
|
||||
|
||||
def seems_to_be_a_comic_archive(self) -> bool:
|
||||
if (self.is_zip() or self.is_rar() or self.is_sevenzip() or self.is_folder()) and (
|
||||
self.get_number_of_pages() > 0
|
||||
):
|
||||
if not (isinstance(self.archiver, UnknownArchiver)) and self.get_number_of_pages() > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def extension(self) -> str:
|
||||
return self.archiver.extension()
|
||||
|
||||
def read_metadata(self, style: int) -> GenericMetadata:
|
||||
|
||||
if style == MetaDataStyle.CIX:
|
||||
@ -334,7 +283,6 @@ class ComicArchive:
|
||||
|
||||
# seems like some archive creators are on Windows, and don't know about case-sensitivity!
|
||||
if sort_list:
|
||||
|
||||
files = cast(list[str], natsort.os_sorted(files))
|
||||
|
||||
# make a sub-list of image files
|
||||
@ -653,10 +601,10 @@ class ComicArchive:
|
||||
|
||||
return metadata
|
||||
|
||||
def export_as_zip(self, zip_filename: pathlib.Path | str) -> bool:
|
||||
if self.archive_type == self.ArchiveType.Zip:
|
||||
def export_as_zip(self, zip_filename: pathlib.Path) -> bool:
|
||||
if self.archiver.name() == "ZIP":
|
||||
# nothing to do, we're already a zip
|
||||
return True
|
||||
|
||||
zip_archiver = ZipArchiver(zip_filename)
|
||||
zip_archiver = ZipArchiver.open(zip_filename)
|
||||
return zip_archiver.copy_from_archive(self.archiver)
|
||||
|
@ -217,14 +217,7 @@ class CLI:
|
||||
if self.batch_mode:
|
||||
brief = f"{ca.path}: "
|
||||
|
||||
if ca.is_sevenzip():
|
||||
brief += "7Z archive "
|
||||
elif ca.is_zip():
|
||||
brief += "ZIP archive "
|
||||
elif ca.is_rar():
|
||||
brief += "RAR archive "
|
||||
elif ca.is_folder():
|
||||
brief += "Folder archive "
|
||||
brief += ca.archiver.name() + " archive "
|
||||
|
||||
brief += f"({page_count: >3} pages)"
|
||||
brief += " tags:[ "
|
||||
@ -460,12 +453,7 @@ class CLI:
|
||||
|
||||
new_ext = "" # default
|
||||
if self.options.filename_rename_set_extension_based_on_archive:
|
||||
if ca.is_sevenzip():
|
||||
new_ext = ".cb7"
|
||||
elif ca.is_zip():
|
||||
new_ext = ".cbz"
|
||||
elif ca.is_rar():
|
||||
new_ext = ".cbr"
|
||||
new_ext = ca.extension()
|
||||
|
||||
renamer = FileRenamer(
|
||||
md,
|
||||
|
@ -18,6 +18,7 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import platform
|
||||
|
||||
import settngs
|
||||
@ -325,20 +326,23 @@ def validate_commandline_options(options: settngs.Config[settngs.Values], parser
|
||||
else:
|
||||
options[0].runtime_file_list = options[0].runtime_files
|
||||
|
||||
# take a crack at finding rar exe, if not set already
|
||||
if options[0].general_rar_exe_path.strip() in ("", "rar"):
|
||||
rar_path = pathlib.Path(options[0].general_rar_exe_path)
|
||||
if rar_path.is_absolute() and rar_path.exists():
|
||||
if rar_path.is_dir():
|
||||
utils.add_to_path(str(rar_path))
|
||||
else:
|
||||
utils.add_to_path(str(rar_path.parent))
|
||||
|
||||
# take a crack at finding rar exe if it's not in the path
|
||||
if not utils.which("rar"):
|
||||
if platform.system() == "Windows":
|
||||
# look in some likely places for Windows machines
|
||||
if os.path.exists(r"C:\Program Files\WinRAR\Rar.exe"):
|
||||
options[0].general_rar_exe_path = r"C:\Program Files\WinRAR\Rar.exe"
|
||||
utils.add_to_path(r"C:\Program Files\WinRAR")
|
||||
elif os.path.exists(r"C:\Program Files (x86)\WinRAR\Rar.exe"):
|
||||
options[0].general_rar_exe_path = r"C:\Program Files (x86)\WinRAR\Rar.exe"
|
||||
utils.add_to_path(r"C:\Program Files (x86)\WinRAR")
|
||||
else:
|
||||
if os.path.exists("/opt/homebrew/bin"):
|
||||
utils.add_to_path("/opt/homebrew/bin")
|
||||
# see if it's in the path of unix user
|
||||
rarpath = utils.which("rar")
|
||||
if rarpath is not None:
|
||||
options[0].general_rar_exe_path = "rar"
|
||||
|
||||
return options
|
||||
|
@ -193,7 +193,7 @@ class FileSelectionList(QtWidgets.QWidget):
|
||||
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
first_added = None
|
||||
rar_added = False
|
||||
rar_added_ro = False
|
||||
self.twList.setSortingEnabled(False)
|
||||
for idx, f in enumerate(filelist):
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
@ -206,8 +206,7 @@ class FileSelectionList(QtWidgets.QWidget):
|
||||
row = self.add_path_item(f)
|
||||
if row is not None:
|
||||
ca = self.get_archive_by_row(row)
|
||||
if ca and ca.is_rar():
|
||||
rar_added = True
|
||||
rar_added_ro = bool(ca and ca.archiver.name() == "RAR" and not ca.archiver.is_writable())
|
||||
if first_added is None:
|
||||
first_added = row
|
||||
|
||||
@ -224,7 +223,7 @@ class FileSelectionList(QtWidgets.QWidget):
|
||||
else:
|
||||
QtWidgets.QMessageBox.information(self, "File/Folder Open", "No readable comic archives were found.")
|
||||
|
||||
if rar_added and not utils.which(self.options.general_rar_exe_path or "rar"):
|
||||
if rar_added_ro:
|
||||
self.rar_ro_message()
|
||||
|
||||
self.twList.setSortingEnabled(True)
|
||||
@ -339,14 +338,7 @@ class FileSelectionList(QtWidgets.QWidget):
|
||||
filename_item.setText(item_text)
|
||||
filename_item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text)
|
||||
|
||||
if fi.ca.is_sevenzip():
|
||||
item_text = "7Z"
|
||||
elif fi.ca.is_zip():
|
||||
item_text = "ZIP"
|
||||
elif fi.ca.is_rar():
|
||||
item_text = "RAR"
|
||||
else:
|
||||
item_text = ""
|
||||
item_text = fi.ca.archiver.name()
|
||||
type_item.setText(item_text)
|
||||
type_item.setData(QtCore.Qt.ItemDataRole.ToolTipRole, item_text)
|
||||
|
||||
|
@ -160,6 +160,9 @@ class App:
|
||||
f"Failed to load settings, check the log located in '{self.options[0].runtime_config.user_log_dir}' for more details",
|
||||
True,
|
||||
)
|
||||
|
||||
comicapi.comicarchive.load_archive_plugins()
|
||||
|
||||
if self.options[0].runtime_no_gui:
|
||||
if error and error[1]:
|
||||
print(f"A fatal error occurred please check the log for more information: {error[0]}") # noqa: T201
|
||||
|
@ -73,13 +73,8 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
self.renamer.replacements = self.options[0].rename_replacements
|
||||
|
||||
new_ext = ca.path.suffix # default
|
||||
if self.options[0].rename_set_extension_based_on_archive:
|
||||
if ca.is_sevenzip():
|
||||
new_ext = ".cb7"
|
||||
elif ca.is_zip():
|
||||
new_ext = ".cbz"
|
||||
elif ca.is_rar():
|
||||
new_ext = ".cbr"
|
||||
if self.options[0].filename_rename_set_extension_based_on_archive:
|
||||
new_ext = ca.extension()
|
||||
|
||||
if md is None:
|
||||
md = ca.read_metadata(self.data_style)
|
||||
@ -206,7 +201,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
logger.info("%s: Filename is already good!", comic[1])
|
||||
continue
|
||||
|
||||
if not comic[0].is_writable(check_rar_status=False):
|
||||
if not comic[0].is_writable(check_archive_status=False):
|
||||
continue
|
||||
|
||||
comic[0].rename(utils.unique_file(full_path))
|
||||
|
@ -696,16 +696,7 @@ Have fun!
|
||||
|
||||
self.lblFilename.setText(filename)
|
||||
|
||||
if ca.is_sevenzip():
|
||||
self.lblArchiveType.setText("7Z archive")
|
||||
elif ca.is_zip():
|
||||
self.lblArchiveType.setText("ZIP archive")
|
||||
elif ca.is_rar():
|
||||
self.lblArchiveType.setText("RAR archive")
|
||||
elif ca.is_folder():
|
||||
self.lblArchiveType.setText("Folder archive")
|
||||
else:
|
||||
self.lblArchiveType.setText("")
|
||||
self.lblArchiveType.setText(ca.archiver.name() + " archive")
|
||||
|
||||
page_count = f" ({ca.get_number_of_pages()} pages)"
|
||||
self.lblPageCount.setText(page_count)
|
||||
|
14
setup.py
14
setup.py
@ -59,12 +59,18 @@ setup(
|
||||
exclude=["tests", "testing"],
|
||||
),
|
||||
package_data={"comictaggerlib": ["ui/*", "graphics/*"], "comicapi": ["data/*"]},
|
||||
entry_points=dict(
|
||||
console_scripts=["comictagger=comictaggerlib.main:main"],
|
||||
pyinstaller40=[
|
||||
entry_points={
|
||||
"console_scripts": ["comictagger=comictaggerlib.main:main"],
|
||||
"pyinstaller40": [
|
||||
"hook-dirs = comictaggerlib.__pyinstaller:get_hook_dirs",
|
||||
],
|
||||
),
|
||||
"comicapi.archivers": [
|
||||
"zip = comicapi.archivers.zip:ZipArchiver",
|
||||
"sevenzip = comicapi.archivers.sevenzip:SevenZipArchiver",
|
||||
"rar = comicapi.archivers.rar:RarArchiver",
|
||||
"folder = comicapi.archivers.folder:FolderArchiver",
|
||||
],
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
|
@ -4,15 +4,17 @@ import platform
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
from importlib_metadata import entry_points
|
||||
|
||||
import comicapi.comicarchive
|
||||
import comicapi.genericmetadata
|
||||
from testing.filenames import datadir
|
||||
|
||||
|
||||
@pytest.mark.xfail(not comicapi.comicarchive.rar_support, reason="rar support")
|
||||
def test_getPageNameList():
|
||||
@pytest.mark.xfail(not comicapi.archivers.rar.rar_support, reason="rar support")
|
||||
def test_getPageNameList(load_archive_plugins):
|
||||
c = comicapi.comicarchive.ComicArchive(datadir / "fake_cbr.cbr")
|
||||
assert c.seems_to_be_a_comic_archive()
|
||||
pageNameList = c.get_page_name_list()
|
||||
|
||||
assert pageNameList == [
|
||||
@ -56,24 +58,26 @@ def test_save_cbi(tmp_comic):
|
||||
md = tmp_comic.read_cbi()
|
||||
|
||||
|
||||
@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support")
|
||||
@pytest.mark.xfail(not (comicapi.archivers.rar.rar_support and shutil.which("rar")), reason="rar support")
|
||||
def test_save_cix_rar(tmp_path):
|
||||
cbr_path = datadir / "fake_cbr.cbr"
|
||||
shutil.copy(cbr_path, tmp_path)
|
||||
|
||||
tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name)
|
||||
assert tmp_comic.seems_to_be_a_comic_archive()
|
||||
assert tmp_comic.write_cix(comicapi.genericmetadata.md_test)
|
||||
|
||||
md = tmp_comic.read_cix()
|
||||
assert md.replace(pages=[]) == comicapi.genericmetadata.md_test.replace(pages=[])
|
||||
|
||||
|
||||
@pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support")
|
||||
@pytest.mark.xfail(not (comicapi.archivers.rar.rar_support and shutil.which("rar")), reason="rar support")
|
||||
def test_save_cbi_rar(tmp_path):
|
||||
cbr_path = datadir / "fake_cbr.cbr"
|
||||
shutil.copy(cbr_path, tmp_path)
|
||||
|
||||
tmp_comic = comicapi.comicarchive.ComicArchive(tmp_path / cbr_path.name)
|
||||
assert tmp_comic.seems_to_be_a_comic_archive()
|
||||
assert tmp_comic.write_cbi(comicapi.genericmetadata.md_test)
|
||||
|
||||
md = tmp_comic.read_cbi()
|
||||
@ -118,16 +122,8 @@ def test_invalid_zip(tmp_comic):
|
||||
|
||||
|
||||
archivers = [
|
||||
comicapi.comicarchive.ZipArchiver,
|
||||
comicapi.comicarchive.FolderArchiver,
|
||||
pytest.param(
|
||||
comicapi.comicarchive.SevenZipArchiver,
|
||||
marks=pytest.mark.xfail(not (comicapi.comicarchive.z7_support), reason="7z support"),
|
||||
),
|
||||
pytest.param(
|
||||
comicapi.comicarchive.RarArchiver,
|
||||
marks=pytest.mark.xfail(not (comicapi.comicarchive.rar_support and shutil.which("rar")), reason="rar support"),
|
||||
),
|
||||
pytest.param(x.load(), marks=pytest.mark.xfail(not (x.load().enabled), reason="archiver not enabled"))
|
||||
for x in entry_points(group="comicapi_archivers")
|
||||
]
|
||||
|
||||
|
||||
@ -135,7 +131,7 @@ archivers = [
|
||||
def test_copy_from_archive(archiver, tmp_path, cbz):
|
||||
comic_path = tmp_path / cbz.path.with_suffix("").name
|
||||
|
||||
archive = archiver(comic_path)
|
||||
archive = archiver.open(comic_path)
|
||||
|
||||
assert archive.copy_from_archive(cbz.archiver)
|
||||
|
||||
|
@ -23,12 +23,17 @@ from testing.comicdata import all_seed_imprints, seed_imprints
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cbz():
|
||||
def cbz(load_archive_plugins):
|
||||
yield comicapi.comicarchive.ComicArchive(filenames.cbz_path)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_comic(tmp_path):
|
||||
def load_archive_plugins():
|
||||
comicapi.comicarchive.load_archive_plugins()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_comic(tmp_path, load_archive_plugins):
|
||||
shutil.copy(filenames.cbz_path, tmp_path)
|
||||
yield comicapi.comicarchive.ComicArchive(tmp_path / filenames.cbz_path.name)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user