From 6cccf22d54e71dfc7bf208eb16228597eb20c5ee Mon Sep 17 00:00:00 2001 From: Timmy Welch Date: Mon, 18 Apr 2022 18:59:17 -0700 Subject: [PATCH] Allow switching between old and new rename templates Show a message dialog explaining that there is a new template format Add a dynamic label to show the effect of a rename Add tests for FileRenamer Remove the filename parameter from the determine_name function --- comicapi/genericmetadata.py | 9 ++ comictaggerlib/cli.py | 18 +++- comictaggerlib/filerenamer.py | 148 ++++++++++++++++++++++++++-- comictaggerlib/renamewindow.py | 10 +- comictaggerlib/settings.py | 14 ++- comictaggerlib/settingswindow.py | 139 +++++++++++++++++++++++--- comictaggerlib/taggerwindow.py | 9 ++ comictaggerlib/ui/settingswindow.ui | 98 +++++++----------- tests/filenames.py | 12 +++ tests/test_rename.py | 21 ++++ 10 files changed, 384 insertions(+), 94 deletions(-) create mode 100644 tests/test_rename.py diff --git a/comicapi/genericmetadata.py b/comicapi/genericmetadata.py index ed8baf0..280dc84 100644 --- a/comicapi/genericmetadata.py +++ b/comicapi/genericmetadata.py @@ -259,6 +259,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: diff --git a/comictaggerlib/cli.py b/comictaggerlib/cli.py index 47d1ecb..25f90f8 100644 --- a/comictaggerlib/cli.py +++ b/comictaggerlib/cli.py @@ -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,20 +452,23 @@ 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) + 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 try: - new_name = renamer.determine_name(filename, ext=new_ext) + 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", + "https://docs.python.org/3/library/string.html#format-string-syntax".format(e), file=sys.stderr, ) return diff --git a/comictaggerlib/filerenamer.py b/comictaggerlib/filerenamer.py index 3a79e44..742a321 100644 --- a/comictaggerlib/filerenamer.py +++ b/comictaggerlib/filerenamer.py @@ -14,9 +14,11 @@ # 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 from pathvalidate import sanitize_filepath @@ -27,6 +29,122 @@ from comicapi.issuestring import IssueString logger = logging.getLogger(__name__) +class FileRenamer: + def __init__(self, metadata): + self.template = "%series% v%volume% #%issue% (of %issuecount%) (%year%)" + self.smart_cleanup = True + self.issue_zero_padding = 3 + self.metadata = metadata + + 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 replace_token(self, text, value, token): + # helper func + def is_token(word): + return word[0] == "%" and word[-1:] == "%" + + if value is not None: + return text.replace(token, str(value)) + + if self.smart_cleanup: + # smart cleanup means we want to remove anything appended to token if it's empty (e.g "#%issue%" or "v%volume%") + # (TODO: This could fail if there is more than one token appended together, I guess) + text_list = text.split() + + # special case for issuecount, remove preceding non-token word, + # as in "...(of %issuecount%)..." + if token == "%issuecount%": + for idx, word in enumerate(text_list): + if token in word and not is_token(text_list[idx - 1]): + text_list[idx - 1] = "" + + text_list = [x for x in text_list if token not in x] + return " ".join(text_list) + + return text.replace(token, "") + + def determine_name(self, ext): + + md = self.metadata + new_name = self.template + + new_name = self.replace_token(new_name, md.series, "%series%") + new_name = self.replace_token(new_name, md.volume, "%volume%") + + if md.issue is not None: + issue_str = IssueString(md.issue).as_string(pad=self.issue_zero_padding) + else: + issue_str = None + new_name = self.replace_token(new_name, issue_str, "%issue%") + + new_name = self.replace_token(new_name, md.issue_count, "%issuecount%") + new_name = self.replace_token(new_name, md.year, "%year%") + new_name = self.replace_token(new_name, md.publisher, "%publisher%") + new_name = self.replace_token(new_name, md.title, "%title%") + new_name = self.replace_token(new_name, md.month, "%month%") + month_name = None + if md.month is not None: + if (isinstance(md.month, str) and md.month.isdigit()) or isinstance(md.month, int): + if int(md.month) in range(1, 13): + dt = datetime.datetime(1970, int(md.month), 1, 0, 0) + month_name = dt.strftime("%B") + new_name = self.replace_token(new_name, month_name, "%month_name%") + + new_name = self.replace_token(new_name, md.genre, "%genre%") + new_name = self.replace_token(new_name, md.language, "%language_code%") + new_name = self.replace_token(new_name, md.critical_rating, "%criticalrating%") + new_name = self.replace_token(new_name, md.alternate_series, "%alternateseries%") + new_name = self.replace_token(new_name, md.alternate_number, "%alternatenumber%") + new_name = self.replace_token(new_name, md.alternate_count, "%alternatecount%") + new_name = self.replace_token(new_name, md.imprint, "%imprint%") + new_name = self.replace_token(new_name, md.format, "%format%") + new_name = self.replace_token(new_name, md.maturity_rating, "%maturityrating%") + new_name = self.replace_token(new_name, md.story_arc, "%storyarc%") + new_name = self.replace_token(new_name, md.series_group, "%seriesgroup%") + new_name = self.replace_token(new_name, md.scan_info, "%scaninfo%") + + if self.smart_cleanup: + # remove empty braces,brackets, parentheses + new_name = re.sub(r"\(\s*[-:]*\s*\)", "", new_name) + new_name = re.sub(r"\[\s*[-:]*\s*]", "", new_name) + new_name = re.sub(r"{\s*[-:]*\s*}", "", new_name) + + # remove duplicate spaces + new_name = " ".join(new_name.split()) + + # remove remove duplicate -, _, + new_name = re.sub(r"[-_]{2,}\s+", "-- ", new_name) + new_name = re.sub(r"(\s--)+", " --", new_name) + new_name = re.sub(r"(\s-)+", " -", new_name) + + # remove dash or double dash at end of line + new_name = re.sub(r"[-]{1,2}\s*$", "", new_name) + + # remove duplicate spaces (again!) + new_name = " ".join(new_name.split()) + + new_name += ext + + # some tweaks to keep various filesystems happy + new_name = new_name.replace("/", "-") + new_name = new_name.replace(" :", " -") + new_name = new_name.replace(": ", " - ") + new_name = new_name.replace(":", "-") + new_name = new_name.replace("?", "") + + return new_name + + class MetadataFormatter(string.Formatter): def __init__(self, smart_cleanup=False): super().__init__() @@ -53,6 +171,7 @@ class MetadataFormatter(string.Formatter): 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 @@ -96,9 +215,9 @@ class MetadataFormatter(string.Formatter): return "".join(result), auto_arg_index -class FileRenamer: +class FileRenamer2: def __init__(self, metadata): - self.template = "{publisher}/{series}/{series} v{volume} #{issue} (of {issueCount}) ({year})" + self.template = "{publisher}/{series}/{series} v{volume} #{issue} (of {issue_count}) ({year})" self.smart_cleanup = True self.issue_zero_padding = 3 self.metadata = metadata @@ -116,7 +235,7 @@ class FileRenamer: def set_template(self, template: str): self.template = template - def determine_name(self, filename, ext=None): + def determine_name(self, ext): class Default(dict): def __missing__(self, key): return "{" + key + "}" @@ -132,19 +251,28 @@ class FileRenamer: new_name = "" fmt = MetadataFormatter(self.smart_cleanup) + 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: new_name = os.path.join( - new_name, fmt.vformat(Component, args=[], kwargs=Default(vars(md))).replace("/", "-") + new_name, fmt.vformat(Component, args=[], kwargs=Default(md_dict)).replace("/", "-") ) - if ext is None or ext == "": - ext = os.path.splitext(filename)[1] - new_name += ext - # some tweaks to keep various filesystems happy - new_name = new_name.replace(": ", " - ") - new_name = new_name.replace(":", "-") + # # some tweaks to keep various filesystems happy + # new_name = new_name.replace(": ", " - ") + # new_name = new_name.replace(":", "-") # remove padding md.issue = IssueString(md.issue).as_string() diff --git a/comictaggerlib/renamewindow.py b/comictaggerlib/renamewindow.py index d452d92..a7102f5 100644 --- a/comictaggerlib/renamewindow.py +++ b/comictaggerlib/renamewindow.py @@ -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) + else: + self.renamer = FileRenamer(None) + self.config_renamer() self.do_preview() @@ -85,7 +89,7 @@ class RenameWindow(QtWidgets.QDialog): self.renamer.move = self.settings.rename_move_dir try: - new_name = self.renamer.determine_name(ca.path, ext=new_ext) + new_name = self.renamer.determine_name(new_ext) except Exception as e: QtWidgets.QMessageBox.critical( self, diff --git a/comictaggerlib/settings.py b/comictaggerlib/settings.py index 307d439..12f159e 100644 --- a/comictaggerlib/settings.py +++ b/comictaggerlib/settings.py @@ -83,6 +83,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 @@ -110,12 +111,13 @@ class ComicTaggerSettings: self.apply_cbl_transform_on_bulk_operation = False # Rename settings - self.rename_template = "{publisher}/{series}/{series} #{issue} - {title} ({year})" + self.rename_template = "%series% #%issue% (%year%)" 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 # Auto-tag stickies self.save_on_low_confidence = False @@ -158,6 +160,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 @@ -185,12 +188,13 @@ class ComicTaggerSettings: self.apply_cbl_transform_on_bulk_operation = False # Rename settings - self.rename_template = "{publisher}/{series}/{series} #{issue} - {title} ({year})" + self.rename_template = "%series% #%issue% (%year%)" 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 # Auto-tag stickies self.save_on_low_confidence = False @@ -293,6 +297,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") @@ -352,6 +358,8 @@ class ComicTaggerSettings: 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("autotag", "save_on_low_confidence"): self.save_on_low_confidence = self.config.getboolean("autotag", "save_on_low_confidence") @@ -408,6 +416,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") @@ -451,6 +460,7 @@ class ComicTaggerSettings: 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) if not self.config.has_section("autotag"): self.config.add_section("autotag") diff --git a/comictaggerlib/settingswindow.py b/comictaggerlib/settingswindow.py index cba663b..9115f12 100644 --- a/comictaggerlib/settingswindow.py +++ b/comictaggerlib/settingswindow.py @@ -17,6 +17,7 @@ import logging import os import platform +import re from PyQt5 import QtCore, QtGui, QtWidgets, uic @@ -24,7 +25,7 @@ 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 +from comictaggerlib.filerenamer import FileRenamer, FileRenamer2 from comictaggerlib.imagefetcher import ImageFetcher from comictaggerlib.settings import ComicTaggerSettings @@ -56,6 +57,82 @@ macRarHelp = """

Once homebrew is installed, run: brew install caskroom/cask/rar """ +old_template_tooltip = """ +

The template for the new filename. Accepts the following variables:

%series%
%issue%
%volume%
%issuecount%
%year%
%month%
%month_name%
%publisher%
%title%
+%genre%
+%language_code%
+%criticalrating%
+%alternateseries%
+%alternatenumber%
+%alternatecount%
+%imprint%
+%format%
+%maturityrating%
+%storyarc%
+%seriesgroup%
+%scaninfo% +

Examples:

%series% %issue% (%year%)
%series% #%issue% - %title%

+""" + +new_template_tooltip = """ +
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
+
+""" + class SettingsWindow(QtWidgets.QDialog): def __init__(self, parent, settings): @@ -107,23 +184,55 @@ 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.leRenameTemplate.text()) + + 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) - def config_renamer(self): + 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(self.leRenameTemplate.text()) - self.renamer = FileRenamer(md_test) - self.renamer.set_template(str(self.leRenameTemplate.text())) - self.renamer.set_issue_zero_padding(self.settings.rename_issue_number_padding) - self.renamer.set_smart_cleanup(self.settings.rename_use_smart_string_cleanup) + def rename_test(self, template): + fr = FileRenamer(md_test) + if self.cbxNewRenamer.isChecked(): + fr = FileRenamer2(md_test) + fr.set_template(template) + fr.set_issue_zero_padding(int(self.leIssueNumPadding.text())) + fr.set_smart_cleanup(self.cbxSmartCleanup.isChecked()) + fr.move = self.cbxMoveFiles.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)) @@ -181,12 +290,8 @@ class SettingsWindow(QtWidgets.QDialog): self.leDirectory.setText(self.settings.rename_dir) def accept(self): - - self.config_renamer() - - try: - new_name = self.renamer.determine_name("test.cbz") - except Exception as e: + self.rename_test(self.leRenameTemplate.text()) + if self.rename_error is not None: QtWidgets.QMessageBox.critical( self, "Invalid format string!", @@ -195,7 +300,7 @@ class SettingsWindow(QtWidgets.QDialog): "Please 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), + "https://docs.python.org/3/library/string.html#format-string-syntax".format(self.rename_error), ) return @@ -246,6 +351,8 @@ class SettingsWindow(QtWidgets.QDialog): 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.save() QtWidgets.QDialog.accept(self) @@ -298,6 +405,8 @@ class SettingsWindow(QtWidgets.QDialog): 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() diff --git a/comictaggerlib/taggerwindow.py b/comictaggerlib/taggerwindow.py index 323e6f1..6cffdea 100644 --- a/comictaggerlib/taggerwindow.py +++ b/comictaggerlib/taggerwindow.py @@ -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.

" + "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", + ) if self.settings.check_for_new_version: # self.checkLatestVersionOnline() diff --git a/comictaggerlib/ui/settingswindow.ui b/comictaggerlib/ui/settingswindow.ui index 7ca3dbf..d0a8fc7 100644 --- a/comictaggerlib/ui/settingswindow.ui +++ b/comictaggerlib/ui/settingswindow.ui @@ -28,7 +28,7 @@ - 1 + 0 @@ -556,62 +556,20 @@ - <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({&apos;role&apos;: &apos;str&apos;, &apos;person&apos;: &apos;str&apos;, &apos;primary&apos;: boolean})) -{tags} (list of str) -{pages} (list of dict({&apos;Image&apos;: &apos;str(int)&apos;, &apos;Type&apos;: &apos;str&apos;})) - -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> + <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> @@ -665,7 +623,7 @@ Spider-Geddon #1 - New Players; Check In - + If checked moves files to specified folder @@ -675,16 +633,36 @@ Spider-Geddon #1 - New Players; Check In - + Destination Directory: - + + + + + Enable the new renamer + + + + + + + + + + Qt::PlainText + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + diff --git a/tests/filenames.py b/tests/filenames.py index 40ed63b..46c0b25 100644 --- a/tests/filenames.py +++ b/tests/filenames.py @@ -419,3 +419,15 @@ fnames = [ marks=pytest.mark.xfail, ), ] + +rnames = [ + ( + "{series} #{issue} - {title} ({year})", + "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game (2007).cbz", + ), + pytest.param( + "{series} #{issue} - {title} - {WriteR}, {EDITOR} ({year})", + "Cory Doctorow's Futuristic Tales of the Here and Now #001 - Anda's Game - Dara Naraghi, Ted Adams (2007).cbz", + marks=pytest.mark.xfail, + ), +] diff --git a/tests/test_rename.py b/tests/test_rename.py new file mode 100644 index 0000000..bcd57db --- /dev/null +++ b/tests/test_rename.py @@ -0,0 +1,21 @@ +import re + +import pytest +from filenames import rnames + +from comicapi.genericmetadata import md_test +from comictaggerlib.filerenamer import FileRenamer, FileRenamer2 + + +@pytest.mark.parametrize("template,result", rnames) +def test_rename_old(template, result): + fr = FileRenamer(md_test) + fr.set_template(re.sub(r"{(\w+)}", r"%\1%", template)) + assert fr.determine_name(".cbz") == result + + +@pytest.mark.parametrize("template,result", rnames) +def test_rename_new(template, result): + fr = FileRenamer2(md_test) + fr.set_template(template) + assert fr.determine_name(".cbz") == result