Merge branch 'Renaming' into develop
This commit is contained in:
commit
caa94c4e28
@ -255,6 +255,15 @@ class GenericMetadata:
|
||||
if not found:
|
||||
self.credits.append(credit)
|
||||
|
||||
def get_primary_credit(self, role):
|
||||
primary = ""
|
||||
for credit in self.credits:
|
||||
if (primary == "" and credit["role"].lower() == role.lower()) or (
|
||||
credit["role"].lower() == role.lower() and credit["primary"]
|
||||
):
|
||||
primary = credit["person"]
|
||||
return primary
|
||||
|
||||
def __str__(self):
|
||||
vals = []
|
||||
if self.is_empty:
|
||||
@ -328,3 +337,93 @@ class GenericMetadata:
|
||||
outstr += fmt_str.format(i[0] + ":", i[1])
|
||||
|
||||
return outstr
|
||||
|
||||
|
||||
md_test = GenericMetadata()
|
||||
|
||||
md_test.is_empty = False
|
||||
md_test.tag_origin = None
|
||||
md_test.series = "Cory Doctorow's Futuristic Tales of the Here and Now"
|
||||
md_test.issue = "1"
|
||||
md_test.title = "Anda's Game"
|
||||
md_test.publisher = "IDW Publishing"
|
||||
md_test.month = 10
|
||||
md_test.year = 2007
|
||||
md_test.day = 1
|
||||
md_test.issue_count = 6
|
||||
md_test.volume = 1
|
||||
md_test.genre = "Sci-Fi"
|
||||
md_test.language = "en"
|
||||
md_test.comments = (
|
||||
"For 12-year-old Anda, getting paid real money to kill the characters of players who were cheating in her favorite online "
|
||||
"computer game was a win-win situation. Until she found out who was paying her, and what those characters meant to the "
|
||||
"livelihood of children around the world."
|
||||
)
|
||||
md_test.volume_count = None
|
||||
md_test.critical_rating = None
|
||||
md_test.country = None
|
||||
md_test.alternate_series = "Tales"
|
||||
md_test.alternate_number = "2"
|
||||
md_test.alternate_count = 7
|
||||
md_test.imprint = "craphound.com"
|
||||
md_test.notes = "Tagged with ComicTagger 1.3.2a5 using info from Comic Vine on 2022-04-16 15:52:26. [Issue ID 140529]"
|
||||
md_test.web_link = "https://comicvine.gamespot.com/cory-doctorows-futuristic-tales-of-the-here-and-no/4000-140529/"
|
||||
md_test.format = "Series"
|
||||
md_test.manga = "No"
|
||||
md_test.black_and_white = None
|
||||
md_test.page_count = 24
|
||||
md_test.maturity_rating = "Everyone 10+"
|
||||
md_test.story_arc = "Here and Now"
|
||||
md_test.series_group = "Futuristic Tales"
|
||||
md_test.scan_info = "(CC BY-NC-SA 3.0)"
|
||||
md_test.characters = "Anda"
|
||||
md_test.teams = "Fahrenheit"
|
||||
md_test.locations = "lonely cottage "
|
||||
md_test.credits = [
|
||||
{"person": "Dara Naraghi", "role": "Writer"},
|
||||
{"person": "Esteve Polls", "role": "Penciller"},
|
||||
{"person": "Esteve Polls", "role": "Inker"},
|
||||
{"person": "Neil Uyetake", "role": "Letterer"},
|
||||
{"person": "Sam Kieth", "role": "Cover"},
|
||||
{"person": "Ted Adams", "role": "Editor"},
|
||||
]
|
||||
md_test.tags = []
|
||||
md_test.pages = [
|
||||
{"Image": "0", "ImageHeight": "1280", "ImageSize": "195977", "ImageWidth": "800", "Type": "FrontCover"},
|
||||
{"Image": "1", "ImageHeight": "2039", "ImageSize": "611993", "ImageWidth": "1327"},
|
||||
{"Image": "2", "ImageHeight": "2039", "ImageSize": "783726", "ImageWidth": "1327"},
|
||||
{"Image": "3", "ImageHeight": "2039", "ImageSize": "679584", "ImageWidth": "1327"},
|
||||
{"Image": "4", "ImageHeight": "2039", "ImageSize": "788179", "ImageWidth": "1327"},
|
||||
{"Image": "5", "ImageHeight": "2039", "ImageSize": "864433", "ImageWidth": "1327"},
|
||||
{"Image": "6", "ImageHeight": "2039", "ImageSize": "765606", "ImageWidth": "1327"},
|
||||
{"Image": "7", "ImageHeight": "2039", "ImageSize": "876427", "ImageWidth": "1327"},
|
||||
{"Image": "8", "ImageHeight": "2039", "ImageSize": "852622", "ImageWidth": "1327"},
|
||||
{"Image": "9", "ImageHeight": "2039", "ImageSize": "800205", "ImageWidth": "1327"},
|
||||
{"Image": "10", "ImageHeight": "2039", "ImageSize": "746243", "ImageWidth": "1326"},
|
||||
{"Image": "11", "ImageHeight": "2039", "ImageSize": "718062", "ImageWidth": "1327"},
|
||||
{"Image": "12", "ImageHeight": "2039", "ImageSize": "532179", "ImageWidth": "1326"},
|
||||
{"Image": "13", "ImageHeight": "2039", "ImageSize": "686708", "ImageWidth": "1327"},
|
||||
{"Image": "14", "ImageHeight": "2039", "ImageSize": "641907", "ImageWidth": "1327"},
|
||||
{"Image": "15", "ImageHeight": "2039", "ImageSize": "805388", "ImageWidth": "1327"},
|
||||
{"Image": "16", "ImageHeight": "2039", "ImageSize": "668927", "ImageWidth": "1326"},
|
||||
{"Image": "17", "ImageHeight": "2039", "ImageSize": "710605", "ImageWidth": "1327"},
|
||||
{"Image": "18", "ImageHeight": "2039", "ImageSize": "761398", "ImageWidth": "1326"},
|
||||
{"Image": "19", "ImageHeight": "2039", "ImageSize": "743807", "ImageWidth": "1327"},
|
||||
{"Image": "20", "ImageHeight": "2039", "ImageSize": "552911", "ImageWidth": "1326"},
|
||||
{"Image": "21", "ImageHeight": "2039", "ImageSize": "556827", "ImageWidth": "1327"},
|
||||
{"Image": "22", "ImageHeight": "2039", "ImageSize": "675078", "ImageWidth": "1326"},
|
||||
{
|
||||
"Bookmark": "Interview",
|
||||
"Image": "23",
|
||||
"ImageHeight": "2032",
|
||||
"ImageSize": "800965",
|
||||
"ImageWidth": "1338",
|
||||
"Type": "Letters",
|
||||
},
|
||||
]
|
||||
md_test.price = None
|
||||
md_test.is_version_of = None
|
||||
md_test.rights = None
|
||||
md_test.identifier = None
|
||||
md_test.last_mark = None
|
||||
md_test.cover_image = None
|
||||
|
@ -27,7 +27,7 @@ from comicapi.comicarchive import ComicArchive, MetaDataStyle
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comictaggerlib.cbltransformer import CBLTransformer
|
||||
from comictaggerlib.comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from comictaggerlib.filerenamer import FileRenamer
|
||||
from comictaggerlib.filerenamer import FileRenamer, FileRenamer2
|
||||
from comictaggerlib.issueidentifier import IssueIdentifier
|
||||
from comictaggerlib.resulttypes import MultipleMatch, OnlineMatchResults
|
||||
from comictaggerlib.settings import ComicTaggerSettings
|
||||
@ -155,6 +155,13 @@ def cli_mode(opts, settings):
|
||||
logger.error("You must specify at least one filename. Use the -h option for more info")
|
||||
return
|
||||
|
||||
if not settings.hide_rename_message:
|
||||
print(
|
||||
"There is a new rename template format available. "
|
||||
"Please use the settings window to enable and test if you use this feature.\n\n"
|
||||
"The old rename template format will be removed in the next release, "
|
||||
"please reference the template help button in the settings or https://github.com/comictagger/comictagger/wiki/UserGuide#rename",
|
||||
)
|
||||
match_results = OnlineMatchResults()
|
||||
|
||||
for f in opts.file_list:
|
||||
@ -445,24 +452,42 @@ def process_file_cli(filename, opts, settings, match_results: OnlineMatchResults
|
||||
elif ca.is_rar():
|
||||
new_ext = ".cbr"
|
||||
|
||||
renamer = FileRenamer(md)
|
||||
if settings.rename_new_renamer:
|
||||
renamer = FileRenamer2(md, platform="universal" if settings.rename_strict else "auto")
|
||||
else:
|
||||
renamer = FileRenamer(md)
|
||||
renamer.set_template(settings.rename_template)
|
||||
renamer.set_issue_zero_padding(settings.rename_issue_number_padding)
|
||||
renamer.set_smart_cleanup(settings.rename_use_smart_string_cleanup)
|
||||
renamer.move = settings.rename_move_dir
|
||||
|
||||
new_name = renamer.determine_name(ca.path, ext=new_ext)
|
||||
|
||||
if new_name == os.path.basename(ca.path):
|
||||
logger.error(msg_hdr + "Filename is already good!")
|
||||
try:
|
||||
new_name = renamer.determine_name(ext=new_ext)
|
||||
except Exception as e:
|
||||
print(
|
||||
msg_hdr + "Invalid format string!\nYour rename template is invalid!\n\n"
|
||||
"{}\n\nPlease consult the template help in the settings "
|
||||
"and the documentation on the format at "
|
||||
"https://docs.python.org/3/library/string.html#format-string-syntax".format(e),
|
||||
file=sys.stderr,
|
||||
)
|
||||
return
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(ca.path))
|
||||
folder = os.path.dirname(os.path.abspath(filename))
|
||||
if settings.rename_move_dir and len(settings.rename_dir.strip()) > 3:
|
||||
folder = settings.rename_dir.strip()
|
||||
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, new_name))
|
||||
|
||||
if os.path.join(folder, new_name) == os.path.abspath(filename):
|
||||
print(msg_hdr + "Filename is already good!", file=sys.stderr)
|
||||
return
|
||||
|
||||
suffix = ""
|
||||
if not opts.dryrun:
|
||||
# rename the file
|
||||
os.rename(ca.path, new_abs_path)
|
||||
os.makedirs(os.path.dirname(new_abs_path), 0o777, True)
|
||||
os.rename(filename, new_abs_path)
|
||||
else:
|
||||
suffix = " (dry-run, no change)"
|
||||
|
||||
|
@ -14,10 +14,15 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import string
|
||||
import sys
|
||||
|
||||
from pathvalidate import sanitize_filename
|
||||
|
||||
from comicapi.genericmetadata import GenericMetadata
|
||||
from comicapi.issuestring import IssueString
|
||||
@ -69,7 +74,7 @@ class FileRenamer:
|
||||
|
||||
return text.replace(token, "")
|
||||
|
||||
def determine_name(self, filename, ext=None):
|
||||
def determine_name(self, ext):
|
||||
|
||||
md = self.metadata
|
||||
new_name = self.template
|
||||
@ -129,9 +134,6 @@ class FileRenamer:
|
||||
# remove duplicate spaces (again!)
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
if ext is None:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
|
||||
new_name += ext
|
||||
|
||||
# some tweaks to keep various filesystems happy
|
||||
@ -142,3 +144,155 @@ class FileRenamer:
|
||||
new_name = new_name.replace("?", "")
|
||||
|
||||
return new_name
|
||||
|
||||
|
||||
class MetadataFormatter(string.Formatter):
|
||||
def __init__(self, smart_cleanup=False, platform="auto"):
|
||||
super().__init__()
|
||||
self.smart_cleanup = smart_cleanup
|
||||
self.platform = platform
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
if value is None or value == "":
|
||||
return ""
|
||||
return super().format_field(value, format_spec)
|
||||
|
||||
def _vformat(self, format_string, args, kwargs, used_args, recursion_depth, auto_arg_index=0):
|
||||
if recursion_depth < 0:
|
||||
raise ValueError("Max string recursion exceeded")
|
||||
result = []
|
||||
lstrip = False
|
||||
for literal_text, field_name, format_spec, conversion in self.parse(format_string):
|
||||
|
||||
# output the literal text
|
||||
if literal_text:
|
||||
if lstrip:
|
||||
literal_text = literal_text.lstrip("-_)}]#")
|
||||
if self.smart_cleanup:
|
||||
lspace = literal_text[0].isspace()
|
||||
rspace = literal_text[-1].isspace()
|
||||
literal_text = " ".join(literal_text.split())
|
||||
if literal_text == "":
|
||||
literal_text = " "
|
||||
else:
|
||||
if lspace:
|
||||
literal_text = " " + literal_text
|
||||
if rspace:
|
||||
literal_text += " "
|
||||
result.append(literal_text)
|
||||
|
||||
lstrip = False
|
||||
# if there's a field, output it
|
||||
if field_name is not None:
|
||||
field_name = field_name.lower()
|
||||
# this is some markup, find the object and do the formatting
|
||||
|
||||
# handle arg indexing when empty field_names are given.
|
||||
if field_name == "":
|
||||
if auto_arg_index is False:
|
||||
raise ValueError("cannot switch from manual field specification to automatic field numbering")
|
||||
field_name = str(auto_arg_index)
|
||||
auto_arg_index += 1
|
||||
elif field_name.isdigit():
|
||||
if auto_arg_index:
|
||||
raise ValueError("cannot switch from manual field specification to automatic field numbering")
|
||||
# disable auto arg incrementing, if it gets used later on, then an exception will be raised
|
||||
auto_arg_index = False
|
||||
|
||||
# given the field_name, find the object it references
|
||||
# and the argument it came from
|
||||
obj, arg_used = self.get_field(field_name, args, kwargs)
|
||||
used_args.add(arg_used)
|
||||
|
||||
# do any conversion on the resulting object
|
||||
obj = self.convert_field(obj, conversion)
|
||||
|
||||
# expand the format spec, if needed
|
||||
format_spec, auto_arg_index = self._vformat(
|
||||
format_spec, args, kwargs, used_args, recursion_depth - 1, auto_arg_index=auto_arg_index
|
||||
)
|
||||
|
||||
# format the object and append to the result
|
||||
fmt_obj = self.format_field(obj, format_spec)
|
||||
if fmt_obj == "" and len(result) > 0 and self.smart_cleanup:
|
||||
lstrip = True
|
||||
result.pop()
|
||||
if self.smart_cleanup:
|
||||
fmt_obj = " ".join(fmt_obj.split())
|
||||
fmt_obj = sanitize_filename(fmt_obj, platform=self.platform)
|
||||
result.append(fmt_obj)
|
||||
|
||||
return "".join(result), auto_arg_index
|
||||
|
||||
|
||||
class FileRenamer2:
|
||||
def __init__(self, metadata, platform="auto"):
|
||||
self.template = "{publisher}/{series}/{series} v{volume} #{issue} (of {issue_count}) ({year})"
|
||||
self.smart_cleanup = True
|
||||
self.issue_zero_padding = 3
|
||||
self.metadata = metadata
|
||||
self.move = False
|
||||
self.platform = platform
|
||||
|
||||
def set_metadata(self, metadata: GenericMetadata):
|
||||
self.metadata = metadata
|
||||
|
||||
def set_issue_zero_padding(self, count):
|
||||
self.issue_zero_padding = count
|
||||
|
||||
def set_smart_cleanup(self, on):
|
||||
self.smart_cleanup = on
|
||||
|
||||
def set_template(self, template: str):
|
||||
self.template = template
|
||||
|
||||
def determine_name(self, ext):
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return "{" + key + "}"
|
||||
|
||||
md = self.metadata
|
||||
|
||||
# padding for issue
|
||||
md.issue = IssueString(md.issue).as_string(pad=self.issue_zero_padding)
|
||||
|
||||
template = self.template
|
||||
|
||||
path_components = template.split(os.sep)
|
||||
new_name = ""
|
||||
|
||||
fmt = MetadataFormatter(self.smart_cleanup, platform=self.platform)
|
||||
md_dict = vars(md)
|
||||
for role in ["writer", "penciller", "inker", "colorist", "letterer", "cover artist", "editor"]:
|
||||
md_dict[role] = md.get_primary_credit(role)
|
||||
|
||||
if (isinstance(md.month, int) or isinstance(md.month, str) and md.month.isdigit()) and 0 < int(md.month) < 13:
|
||||
md_dict["month_name"] = calendar.month_name[int(md.month)]
|
||||
md_dict["month_abbr"] = calendar.month_abbr[int(md.month)]
|
||||
else:
|
||||
print(md.month)
|
||||
md_dict["month_name"] = ""
|
||||
md_dict["month_abbr"] = ""
|
||||
|
||||
for Component in path_components:
|
||||
if (
|
||||
self.platform.lower() in ["universal", "windows"] or sys.platform.lower() in ["windows"]
|
||||
) and self.smart_cleanup:
|
||||
# colons get special treatment
|
||||
Component = Component.replace(": ", " - ")
|
||||
Component = Component.replace(":", "-")
|
||||
|
||||
new_basename = sanitize_filename(
|
||||
fmt.vformat(Component, args=[], kwargs=Default(md_dict)), platform=self.platform
|
||||
).strip()
|
||||
new_name = os.path.join(new_name, new_basename)
|
||||
|
||||
new_name += ext
|
||||
new_basename += ext
|
||||
|
||||
# remove padding
|
||||
md.issue = IssueString(md.issue).as_string()
|
||||
if self.move:
|
||||
return new_name.strip()
|
||||
else:
|
||||
return new_basename.strip()
|
||||
|
@ -23,7 +23,7 @@ from PyQt5 import QtCore, QtWidgets, uic
|
||||
import comicapi.comicarchive
|
||||
from comicapi import utils
|
||||
from comicapi.comicarchive import MetaDataStyle
|
||||
from comictaggerlib.filerenamer import FileRenamer
|
||||
from comictaggerlib.filerenamer import FileRenamer, FileRenamer2
|
||||
from comictaggerlib.settings import ComicTaggerSettings
|
||||
from comictaggerlib.settingswindow import SettingsWindow
|
||||
from comictaggerlib.ui.qtutils import center_window_on_parent
|
||||
@ -52,7 +52,11 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
self.rename_list = []
|
||||
|
||||
self.btnSettings.clicked.connect(self.modify_settings)
|
||||
self.renamer = FileRenamer(None)
|
||||
if self.settings.rename_new_renamer:
|
||||
self.renamer = FileRenamer2(None, platform="universal" if self.settings.rename_strict else "auto")
|
||||
else:
|
||||
self.renamer = FileRenamer(None)
|
||||
|
||||
self.config_renamer()
|
||||
self.do_preview()
|
||||
|
||||
@ -82,7 +86,22 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
if md.is_empty:
|
||||
md = ca.metadata_from_filename(self.settings.parse_scan_info)
|
||||
self.renamer.set_metadata(md)
|
||||
new_name = self.renamer.determine_name(ca.path, ext=new_ext)
|
||||
self.renamer.move = self.settings.rename_move_dir
|
||||
|
||||
try:
|
||||
new_name = self.renamer.determine_name(new_ext)
|
||||
except Exception as e:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
"Your rename template is invalid!"
|
||||
"<br/><br/>{}<br/><br/>"
|
||||
"Please consult the template help in the "
|
||||
"settings and the documentation on the format at "
|
||||
"<a href='https://docs.python.org/3/library/string.html#format-string-syntax'>"
|
||||
"https://docs.python.org/3/library/string.html#format-string-syntax</a>".format(e),
|
||||
)
|
||||
return
|
||||
|
||||
row = self.twList.rowCount()
|
||||
self.twList.insertRow(row)
|
||||
@ -150,7 +169,13 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
center_window_on_parent(prog_dialog)
|
||||
QtCore.QCoreApplication.processEvents()
|
||||
|
||||
if item["new_name"] == os.path.basename(item["archive"].path):
|
||||
folder = os.path.dirname(os.path.abspath(item["archive"].path))
|
||||
if self.settings.rename_move_dir and len(self.settings.rename_dir.strip()) > 3:
|
||||
folder = self.settings.rename_dir.strip()
|
||||
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, item["new_name"]))
|
||||
|
||||
if os.path.join(folder, item["new_name"]) == item["archive"].path:
|
||||
print(item["new_name"], "Filename is already good!")
|
||||
logger.info(item["new_name"], "Filename is already good!")
|
||||
continue
|
||||
@ -158,9 +183,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
if not item["archive"].is_writable(check_rar_status=False):
|
||||
continue
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(item["archive"].path))
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, item["new_name"]))
|
||||
|
||||
os.makedirs(os.path.dirname(new_abs_path), 0o777, True)
|
||||
os.rename(item["archive"].path, new_abs_path)
|
||||
|
||||
item["archive"].rename(new_abs_path)
|
||||
|
@ -86,6 +86,7 @@ class ComicTaggerSettings:
|
||||
self.show_disclaimer = True
|
||||
self.dont_notify_about_this_version = ""
|
||||
self.ask_about_usage_stats = True
|
||||
self.hide_rename_message = False
|
||||
|
||||
# filename parsing settings
|
||||
self.parse_scan_info = True
|
||||
@ -117,6 +118,10 @@ class ComicTaggerSettings:
|
||||
self.rename_issue_number_padding = 3
|
||||
self.rename_use_smart_string_cleanup = True
|
||||
self.rename_extension_based_on_archive = True
|
||||
self.rename_dir = ""
|
||||
self.rename_move_dir = False
|
||||
self.rename_new_renamer = False
|
||||
self.rename_strict = False
|
||||
|
||||
# Auto-tag stickies
|
||||
self.save_on_low_confidence = False
|
||||
@ -156,6 +161,7 @@ class ComicTaggerSettings:
|
||||
self.show_disclaimer = True
|
||||
self.dont_notify_about_this_version = ""
|
||||
self.ask_about_usage_stats = True
|
||||
self.hide_rename_message = False
|
||||
|
||||
# filename parsing settings
|
||||
self.parse_scan_info = True
|
||||
@ -187,6 +193,10 @@ class ComicTaggerSettings:
|
||||
self.rename_issue_number_padding = 3
|
||||
self.rename_use_smart_string_cleanup = True
|
||||
self.rename_extension_based_on_archive = True
|
||||
self.rename_dir = ""
|
||||
self.rename_move_dir = False
|
||||
self.rename_new_renamer = False
|
||||
self.rename_strict = False
|
||||
|
||||
# Auto-tag stickies
|
||||
self.save_on_low_confidence = False
|
||||
@ -292,6 +302,8 @@ class ComicTaggerSettings:
|
||||
self.dont_notify_about_this_version = self.config.get("dialogflags", "dont_notify_about_this_version")
|
||||
if self.config.has_option("dialogflags", "ask_about_usage_stats"):
|
||||
self.ask_about_usage_stats = self.config.getboolean("dialogflags", "ask_about_usage_stats")
|
||||
if self.config.has_option("dialogflags", "hide_rename_message"):
|
||||
self.hide_rename_message = self.config.getboolean("dialogflags", "hide_rename_message")
|
||||
|
||||
if self.config.has_option("comicvine", "use_series_start_as_volume"):
|
||||
self.use_series_start_as_volume = self.config.getboolean("comicvine", "use_series_start_as_volume")
|
||||
@ -347,6 +359,14 @@ class ComicTaggerSettings:
|
||||
self.rename_extension_based_on_archive = self.config.getboolean(
|
||||
"rename", "rename_extension_based_on_archive"
|
||||
)
|
||||
if self.config.has_option("rename", "rename_dir"):
|
||||
self.rename_dir = self.config.get("rename", "rename_dir")
|
||||
if self.config.has_option("rename", "rename_move_dir"):
|
||||
self.rename_move_dir = self.config.getboolean("rename", "rename_move_dir")
|
||||
if self.config.has_option("rename", "rename_new_renamer"):
|
||||
self.rename_new_renamer = self.config.getboolean("rename", "rename_new_renamer")
|
||||
if self.config.has_option("rename", "rename_strict"):
|
||||
self.rename_strict = self.config.getboolean("rename", "rename_strict")
|
||||
|
||||
if self.config.has_option("autotag", "save_on_low_confidence"):
|
||||
self.save_on_low_confidence = self.config.getboolean("autotag", "save_on_low_confidence")
|
||||
@ -403,6 +423,7 @@ class ComicTaggerSettings:
|
||||
self.config.set("dialogflags", "show_disclaimer", self.show_disclaimer)
|
||||
self.config.set("dialogflags", "dont_notify_about_this_version", self.dont_notify_about_this_version)
|
||||
self.config.set("dialogflags", "ask_about_usage_stats", self.ask_about_usage_stats)
|
||||
self.config.set("dialogflags", "hide_rename_message", self.hide_rename_message)
|
||||
|
||||
if not self.config.has_section("filenameparser"):
|
||||
self.config.add_section("filenameparser")
|
||||
@ -444,6 +465,10 @@ class ComicTaggerSettings:
|
||||
self.config.set("rename", "rename_issue_number_padding", self.rename_issue_number_padding)
|
||||
self.config.set("rename", "rename_use_smart_string_cleanup", self.rename_use_smart_string_cleanup)
|
||||
self.config.set("rename", "rename_extension_based_on_archive", self.rename_extension_based_on_archive)
|
||||
self.config.set("rename", "rename_dir", self.rename_dir)
|
||||
self.config.set("rename", "rename_move_dir", self.rename_move_dir)
|
||||
self.config.set("rename", "rename_new_renamer", self.rename_new_renamer)
|
||||
self.config.set("rename", "rename_strict", self.rename_strict)
|
||||
|
||||
if not self.config.has_section("autotag"):
|
||||
self.config.add_section("autotag")
|
||||
|
@ -17,12 +17,15 @@
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
|
||||
from comicapi import utils
|
||||
from comicapi.genericmetadata import md_test
|
||||
from comictaggerlib.comicvinecacher import ComicVineCacher
|
||||
from comictaggerlib.comicvinetalker import ComicVineTalker
|
||||
from comictaggerlib.filerenamer import FileRenamer, FileRenamer2
|
||||
from comictaggerlib.imagefetcher import ImageFetcher
|
||||
from comictaggerlib.settings import ComicTaggerSettings
|
||||
|
||||
@ -54,6 +57,82 @@ macRarHelp = """
|
||||
</p>Once homebrew is installed, run: <b>brew install caskroom/cask/rar</b></body></html>
|
||||
"""
|
||||
|
||||
old_template_tooltip = """
|
||||
<html><head/><body><p>The template for the new filename. Accepts the following variables:</p><p>%series%<br/>%issue%<br/>%volume%<br/>%issuecount%<br/>%year%<br/>%month%<br/>%month_name%<br/>%publisher%<br/>%title%<br/>
|
||||
%genre%<br/>
|
||||
%language_code%<br/>
|
||||
%criticalrating%<br/>
|
||||
%alternateseries%<br/>
|
||||
%alternatenumber%<br/>
|
||||
%alternatecount%<br/>
|
||||
%imprint%<br/>
|
||||
%format%<br/>
|
||||
%maturityrating%<br/>
|
||||
%storyarc%<br/>
|
||||
%seriesgroup%<br/>
|
||||
%scaninfo%
|
||||
</p><p>Examples:</p><p><span style=" font-style:italic;">%series% %issue% (%year%)</span><br/><span style=" font-style:italic;">%series% #%issue% - %title%</span></p></body></html>
|
||||
"""
|
||||
|
||||
new_template_tooltip = """
|
||||
<pre>The template for the new filename. Uses python format strings https://docs.python.org/3/library/string.html#format-string-syntax
|
||||
Accepts the following variables:
|
||||
{isEmpty} (boolean)
|
||||
{tagOrigin} (string)
|
||||
{series} (string)
|
||||
{issue} (string)
|
||||
{title} (string)
|
||||
{publisher} (string)
|
||||
{month} (integer)
|
||||
{year} (integer)
|
||||
{day} (integer)
|
||||
{issueCount} (integer)
|
||||
{volume} (integer)
|
||||
{genre} (string)
|
||||
{language} (string)
|
||||
{comments} (string)
|
||||
{volumeCount} (integer)
|
||||
{criticalRating} (string)
|
||||
{country} (string)
|
||||
{alternateSeries} (string)
|
||||
{alternateNumber} (string)
|
||||
{alternateCount} (integer)
|
||||
{imprint} (string)
|
||||
{notes} (string)
|
||||
{webLink} (string)
|
||||
{format} (string)
|
||||
{manga} (string)
|
||||
{blackAndWhite} (boolean)
|
||||
{pageCount} (integer)
|
||||
{maturityRating} (string)
|
||||
{storyArc} (string)
|
||||
{seriesGroup} (string)
|
||||
{scanInfo} (string)
|
||||
{characters} (string)
|
||||
{teams} (string)
|
||||
{locations} (string)
|
||||
{credits} (list of dict({'role': 'str', 'person': 'str', 'primary': boolean}))
|
||||
{tags} (list of str)
|
||||
{pages} (list of dict({'Image': 'str(int)', 'Type': 'str'}))
|
||||
|
||||
CoMet-only items:
|
||||
{price} (float)
|
||||
{isVersionOf} (string)
|
||||
{rights} (string)
|
||||
{identifier} (string)
|
||||
{lastMark} (string)
|
||||
{coverImage} (string)
|
||||
|
||||
Examples:
|
||||
|
||||
{series} {issue} ({year})
|
||||
Spider-Geddon 1 (2018)
|
||||
|
||||
{series} #{issue} - {title}
|
||||
Spider-Geddon #1 - New Players; Check In
|
||||
</pre>
|
||||
"""
|
||||
|
||||
|
||||
class SettingsWindow(QtWidgets.QDialog):
|
||||
def __init__(self, parent, settings):
|
||||
@ -105,15 +184,61 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
validator = QtGui.QIntValidator(0, 99, self)
|
||||
self.leNameLengthDeltaThresh.setValidator(validator)
|
||||
|
||||
self.settings_to_form()
|
||||
new_rename = self.settings.rename_new_renamer
|
||||
self.cbxNewRenamer.setChecked(new_rename)
|
||||
self.cbxMoveFiles.setEnabled(new_rename)
|
||||
self.leDirectory.setEnabled(new_rename)
|
||||
self.lblDirectory.setEnabled(new_rename)
|
||||
if self.settings.rename_new_renamer:
|
||||
self.leRenameTemplate.setToolTip(new_template_tooltip)
|
||||
|
||||
self.settings_to_form()
|
||||
self.rename_error = None
|
||||
self.rename_test()
|
||||
|
||||
self.cbxNewRenamer.clicked.connect(self.new_rename_toggle)
|
||||
self.btnBrowseRar.clicked.connect(self.select_rar)
|
||||
self.btnClearCache.clicked.connect(self.clear_cache)
|
||||
self.btnResetSettings.clicked.connect(self.reset_settings)
|
||||
self.btnTestKey.clicked.connect(self.test_api_key)
|
||||
self.btnTemplateHelp.clicked.connect(self.show_template_help)
|
||||
self.leRenameTemplate.textEdited.connect(self.rename__test)
|
||||
self.cbxMoveFiles.clicked.connect(self.rename_test)
|
||||
self.cbxRenameStrict.clicked.connect(self.rename_test)
|
||||
self.leDirectory.textEdited.connect(self.rename_test)
|
||||
|
||||
def new_rename_toggle(self):
|
||||
new_rename = self.cbxNewRenamer.isChecked()
|
||||
if new_rename:
|
||||
self.leRenameTemplate.setText(re.sub(r"%(\w+)%", r"{\1}", self.leRenameTemplate.text()))
|
||||
self.leRenameTemplate.setToolTip(new_template_tooltip)
|
||||
else:
|
||||
self.leRenameTemplate.setText(re.sub(r"{(\w+)}", r"%\1%", self.leRenameTemplate.text()))
|
||||
self.leRenameTemplate.setToolTip(old_template_tooltip)
|
||||
self.cbxMoveFiles.setEnabled(new_rename)
|
||||
self.leDirectory.setEnabled(new_rename)
|
||||
self.lblDirectory.setEnabled(new_rename)
|
||||
self.rename_test()
|
||||
|
||||
def rename_test(self):
|
||||
self.rename__test(self.leRenameTemplate.text())
|
||||
|
||||
def rename__test(self, template):
|
||||
fr = FileRenamer(md_test)
|
||||
if self.cbxNewRenamer.isChecked():
|
||||
fr = FileRenamer2(md_test, platform="universal" if self.cbxRenameStrict.isChecked() else "auto")
|
||||
fr.move = self.cbxMoveFiles.isChecked()
|
||||
fr.set_template(template)
|
||||
fr.set_issue_zero_padding(int(self.leIssueNumPadding.text()))
|
||||
fr.set_smart_cleanup(self.cbxSmartCleanup.isChecked())
|
||||
try:
|
||||
self.lblRenameTest.setText(fr.determine_name(".cbz"))
|
||||
self.rename_error = None
|
||||
except Exception as e:
|
||||
self.rename_error = e
|
||||
self.lblRenameTest.setText(str(e))
|
||||
|
||||
def settings_to_form(self):
|
||||
|
||||
# Copy values from settings to form
|
||||
self.leRarExePath.setText(self.settings.rar_exe_path)
|
||||
self.leNameLengthDeltaThresh.setText(str(self.settings.id_length_delta_thresh))
|
||||
@ -166,8 +291,26 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.cbxSmartCleanup.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
if self.settings.rename_extension_based_on_archive:
|
||||
self.cbxChangeExtension.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
if self.settings.rename_move_dir:
|
||||
self.cbxMoveFiles.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
self.leDirectory.setText(self.settings.rename_dir)
|
||||
if self.settings.rename_strict:
|
||||
self.cbxRenameStrict.setCheckState(QtCore.Qt.CheckState.Checked)
|
||||
|
||||
def accept(self):
|
||||
self.rename_test()
|
||||
if self.rename_error is not None:
|
||||
QtWidgets.QMessageBox.critical(
|
||||
self,
|
||||
"Invalid format string!",
|
||||
"Your rename template is invalid!"
|
||||
"<br/><br/>{}<br/><br/>"
|
||||
"Please consult the template help in the "
|
||||
"settings and the documentation on the format at "
|
||||
"<a href='https://docs.python.org/3/library/string.html#format-string-syntax'>"
|
||||
"https://docs.python.org/3/library/string.html#format-string-syntax</a>".format(self.rename_error),
|
||||
)
|
||||
return
|
||||
|
||||
# Copy values from form to settings and save
|
||||
self.settings.rar_exe_path = str(self.leRarExePath.text())
|
||||
@ -213,6 +356,11 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.settings.rename_issue_number_padding = int(self.leIssueNumPadding.text())
|
||||
self.settings.rename_use_smart_string_cleanup = self.cbxSmartCleanup.isChecked()
|
||||
self.settings.rename_extension_based_on_archive = self.cbxChangeExtension.isChecked()
|
||||
self.settings.rename_move_dir = self.cbxMoveFiles.isChecked()
|
||||
self.settings.rename_dir = self.leDirectory.text()
|
||||
|
||||
self.settings.rename_new_renamer = self.cbxNewRenamer.isChecked()
|
||||
self.settings.rename_strict = self.cbxRenameStrict.isChecked()
|
||||
|
||||
self.settings.save()
|
||||
QtWidgets.QDialog.accept(self)
|
||||
@ -262,3 +410,17 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
def show_rename_tab(self):
|
||||
self.tabWidget.setCurrentIndex(5)
|
||||
|
||||
def show_template_help(self):
|
||||
template_help_win = TemplateHelpWindow(self)
|
||||
template_help_win.setModal(False)
|
||||
if not self.cbxNewRenamer.isChecked():
|
||||
template_help_win.textEdit.setHtml(old_template_tooltip)
|
||||
template_help_win.show()
|
||||
|
||||
|
||||
class TemplateHelpWindow(QtWidgets.QDialog):
|
||||
def __init__(self, parent):
|
||||
super(TemplateHelpWindow, self).__init__(parent)
|
||||
|
||||
uic.loadUi(ComicTaggerSettings.get_ui_file("TemplateHelp.ui"), self)
|
||||
|
@ -246,6 +246,15 @@ Have fun!
|
||||
""",
|
||||
)
|
||||
self.settings.show_disclaimer = not checked
|
||||
if not self.settings.hide_rename_message:
|
||||
self.settings.hide_rename_message = OptionalMessageDialog.msg(
|
||||
self,
|
||||
"New rename template!",
|
||||
"There is a new rename template format available. "
|
||||
"Please use the settings window to enable and test if you use this feature.<br><br>"
|
||||
"The old rename template format will be removed in the next release, "
|
||||
"please reference the template help button in the settings or <a href='https://github.com/comictagger/comictagger/wiki/UserGuide#rename'>https://github.com/comictagger/comictagger/wiki/UserGuide#rename</a>",
|
||||
)
|
||||
|
||||
if self.settings.check_for_new_version:
|
||||
# self.checkLatestVersionOnline()
|
||||
|
110
comictaggerlib/ui/TemplateHelp.ui
Normal file
110
comictaggerlib/ui/TemplateHelp.ui
Normal file
@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>702</width>
|
||||
<height>452</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Template Help</string>
|
||||
</property>
|
||||
<property name="sizeGripEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="html">
|
||||
<string><html>
|
||||
<head/>
|
||||
<body>
|
||||
<h1 style="text-align: center">Template help</h1>
|
||||
<p>The template uses Python format strings, in the simplest use it replaces the field (e.g. {issue}) with the value for that particular comic (e.g. 1) for advanced formatting please reference the
|
||||
|
||||
<a href="https://docs.python.org/3/library/string.html#format-string-syntax">Python 3 documentation</a></p>
|
||||
<pre>Accepts the following variables:
|
||||
{isEmpty}		(boolean)
|
||||
{tagOrigin}		(string)
|
||||
{series}		(string)
|
||||
{issue}		(string)
|
||||
{title}		(string)
|
||||
{publisher}		(string)
|
||||
{month}		(integer)
|
||||
{year}		(integer)
|
||||
{day}		(integer)
|
||||
{issueCount}	(integer)
|
||||
{volume}		(integer)
|
||||
{genre}		(string)
|
||||
{language}		(string)
|
||||
{comments}		(string)
|
||||
{volumeCount}	(integer)
|
||||
{criticalRating}	(string)
|
||||
{country}		(string)
|
||||
{alternateSeries}	(string)
|
||||
{alternateNumber}	(string)
|
||||
{alternateCount}	(integer)
|
||||
{imprint}		(string)
|
||||
{notes}		(string)
|
||||
{webLink}		(string)
|
||||
{format}		(string)
|
||||
{manga}		(string)
|
||||
{blackAndWhite}	(boolean)
|
||||
{pageCount}		(integer)
|
||||
{maturityRating}	(string)
|
||||
{storyArc}		(string)
|
||||
{seriesGroup}	(string)
|
||||
{scanInfo}		(string)
|
||||
{characters}	(string)
|
||||
{teams}		(string)
|
||||
{locations}		(string)
|
||||
{credits}		(list of dict({'role': 'str', 'person': 'str', 'primary': boolean}))
|
||||
{tags}		(list of str)
|
||||
{pages}		(list of dict({'Image': 'str(int)', 'Type': 'str'}))
|
||||
|
||||
CoMet-only items:
|
||||
{price}		(float)
|
||||
{isVersionOf}	(string)
|
||||
{rights}		(string)
|
||||
{identifier}	(string)
|
||||
{lastMark}	(string)
|
||||
{coverImage}	(string)
|
||||
|
||||
Examples:
|
||||
|
||||
{series} {issue} ({year})
|
||||
Spider-Geddon 1 (2018)
|
||||
|
||||
{series} #{issue} - {title}
|
||||
Spider-Geddon #1 - New Players; Check In
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html></string>
|
||||
</property>
|
||||
<property name="openExternalLinks" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -28,7 +28,7 @@
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
@ -547,7 +547,7 @@
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="lblTemplate">
|
||||
<property name="text">
|
||||
<string>Template:</string>
|
||||
</property>
|
||||
@ -574,13 +574,33 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<widget class="QPushButton" name="btnTemplateHelp">
|
||||
<property name="text">
|
||||
<string>Template Help</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="lblRenameTest">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="lblPadding">
|
||||
<property name="text">
|
||||
<string>Issue # Zero Padding</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="3" column="1">
|
||||
<widget class="QLineEdit" name="leIssueNumPadding">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
@ -599,7 +619,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxSmartCleanup">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p><span style=" font-weight:600;">&quot;Smart Text Cleanup&quot; </span>will attempt to clean up the new filename if there are missing fields from the template. For example, removing empty braces, repeated spaces and dashes, and more. Experimental feature.</p></body></html></string>
|
||||
@ -609,13 +629,51 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="cbxChangeExtension">
|
||||
<property name="text">
|
||||
<string>Change Extension Based On Archive Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="cbxNewRenamer">
|
||||
<property name="text">
|
||||
<string>Enable the new renamer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="cbxMoveFiles">
|
||||
<property name="toolTip">
|
||||
<string>If checked moves files to specified folder</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Move files when renaming</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="lblDirectory">
|
||||
<property name="text">
|
||||
<string>Destination Directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QLineEdit" name="leDirectory"/>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="cbxRenameStrict">
|
||||
<property name="toolTip">
|
||||
<string>If checked will ensure reserved characters and filenames are removed for all Operating Systems.
|
||||
By default only removes restricted characters and filenames for the current Operating System.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Strict renaming</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -2,5 +2,6 @@ beautifulsoup4 >= 4.1
|
||||
natsort>=8.1.0
|
||||
pillow>=4.3.0
|
||||
requests==2.*
|
||||
pathvalidate
|
||||
pycountry
|
||||
py7zr
|
||||
|
@ -537,3 +537,59 @@ fnames = [
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
]
|
||||
|
||||
rnames = [
|
||||
(
|
||||
"{series} #{issue} - {title} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz",
|
||||
),
|
||||
(
|
||||
"{series}: {title} #{issue} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now - Anda's Game #001 (2007).cbz",
|
||||
),
|
||||
pytest.param(
|
||||
"{series}: {title} #{issue} ({year})",
|
||||
False,
|
||||
"Linux",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now: Anda's Game #001 (2007).cbz",
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
pytest.param(
|
||||
"{publisher}/ {series} #{issue} - {title} ({year})",
|
||||
True,
|
||||
"universal",
|
||||
"IDW Publishing/Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz",
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
pytest.param(
|
||||
"{publisher}/ {series} #{issue} - {title} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz",
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
(
|
||||
"{series} # {issue} - {title} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now # 001 - Anda's Game (2007).cbz",
|
||||
),
|
||||
pytest.param(
|
||||
"{series} # {issue} - {locations} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now # 001 - lonely cottage (2007).cbz",
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
pytest.param(
|
||||
"{series} #{issue} - {title} - {WriteR}, {EDITOR} ({year})",
|
||||
False,
|
||||
"universal",
|
||||
"Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game - Dara Naraghi, Ted Adams (2007).cbz",
|
||||
marks=pytest.mark.xfail,
|
||||
),
|
||||
]
|
||||
|
24
tests/test_rename.py
Normal file
24
tests/test_rename.py
Normal file
@ -0,0 +1,24 @@
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from filenames import rnames
|
||||
|
||||
from comicapi.genericmetadata import md_test
|
||||
from comictaggerlib.filerenamer import FileRenamer, FileRenamer2
|
||||
|
||||
|
||||
@pytest.mark.parametrize("template, move, platform, expected", rnames)
|
||||
def test_rename_old(template, platform, move, expected):
|
||||
_ = platform
|
||||
_ = move
|
||||
fr = FileRenamer(md_test)
|
||||
fr.set_template(re.sub(r"{(\w+)}", r"%\1%", template))
|
||||
assert fr.determine_name(".cbz") == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize("template, move, platform, expected", rnames)
|
||||
def test_rename_new(template, platform, move, expected):
|
||||
fr = FileRenamer2(md_test, platform=platform)
|
||||
fr.move = move
|
||||
fr.set_template(template)
|
||||
assert fr.determine_name(".cbz") == expected
|
Loading…
Reference in New Issue
Block a user