Merge branch 'Renaming' into temp
This commit is contained in:
commit
37e6e81894
@ -24,39 +24,33 @@ from . import utils
|
||||
|
||||
|
||||
class ComicBookInfo:
|
||||
|
||||
def metadataFromString(self, string):
|
||||
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return None
|
||||
cbi_container = json.loads(str(string, 'utf-8'))
|
||||
|
||||
metadata = GenericMetadata()
|
||||
|
||||
cbi = cbi_container['ComicBookInfo/1.0']
|
||||
cbi = Default(cbi_container['ComicBookInfo/1.0'])
|
||||
|
||||
# helper func
|
||||
# If item is not in CBI, return None
|
||||
def xlate(cbi_entry):
|
||||
if cbi_entry in cbi:
|
||||
return cbi[cbi_entry]
|
||||
else:
|
||||
return None
|
||||
metadata.series = utils.xlate(cbi['series'])
|
||||
metadata.title = utils.xlate(cbi['title'])
|
||||
metadata.issue = utils.xlate(cbi['issue'])
|
||||
metadata.publisher = utils.xlate(cbi['publisher'])
|
||||
metadata.month = utils.xlate(cbi['publicationMonth'], True)
|
||||
metadata.year = utils.xlate(cbi['publicationYear'], True)
|
||||
metadata.issueCount = utils.xlate(cbi['numberOfIssues'], True)
|
||||
metadata.comments = utils.xlate(cbi['comments'])
|
||||
metadata.genre = utils.xlate(cbi['genre'])
|
||||
metadata.volume = utils.xlate(cbi['volume'], True)
|
||||
metadata.volumeCount = utils.xlate(cbi['numberOfVolumes'], True)
|
||||
metadata.language = utils.xlate(cbi['language'])
|
||||
metadata.country = utils.xlate(cbi['country'])
|
||||
metadata.criticalRating = utils.xlate(cbi['rating'])
|
||||
|
||||
metadata.series = xlate('series')
|
||||
metadata.title = xlate('title')
|
||||
metadata.issue = xlate('issue')
|
||||
metadata.publisher = xlate('publisher')
|
||||
metadata.month = xlate('publicationMonth')
|
||||
metadata.year = xlate('publicationYear')
|
||||
metadata.issueCount = xlate('numberOfIssues')
|
||||
metadata.comments = xlate('comments')
|
||||
metadata.credits = xlate('credits')
|
||||
metadata.genre = xlate('genre')
|
||||
metadata.volume = xlate('volume')
|
||||
metadata.volumeCount = xlate('numberOfVolumes')
|
||||
metadata.language = xlate('language')
|
||||
metadata.country = xlate('country')
|
||||
metadata.criticalRating = xlate('rating')
|
||||
metadata.tags = xlate('tags')
|
||||
metadata.credits = cbi['credits']
|
||||
metadata.tags = cbi['tags']
|
||||
|
||||
# make sure credits and tags are at least empty lists and not None
|
||||
if metadata.credits is None:
|
||||
@ -103,33 +97,23 @@ class ComicBookInfo:
|
||||
|
||||
# helper func
|
||||
def assign(cbi_entry, md_entry):
|
||||
if md_entry is not None:
|
||||
if md_entry is not None or isinstance(md_entry, str) and md_entry != "":
|
||||
cbi[cbi_entry] = md_entry
|
||||
|
||||
# helper func
|
||||
def toInt(s):
|
||||
i = None
|
||||
if type(s) in [str, str, int]:
|
||||
try:
|
||||
i = int(s)
|
||||
except ValueError:
|
||||
pass
|
||||
return i
|
||||
|
||||
assign('series', metadata.series)
|
||||
assign('title', metadata.title)
|
||||
assign('issue', metadata.issue)
|
||||
assign('publisher', metadata.publisher)
|
||||
assign('publicationMonth', toInt(metadata.month))
|
||||
assign('publicationYear', toInt(metadata.year))
|
||||
assign('numberOfIssues', toInt(metadata.issueCount))
|
||||
assign('comments', metadata.comments)
|
||||
assign('genre', metadata.genre)
|
||||
assign('volume', toInt(metadata.volume))
|
||||
assign('numberOfVolumes', toInt(metadata.volumeCount))
|
||||
assign('language', utils.getLanguageFromISO(metadata.language))
|
||||
assign('country', metadata.country)
|
||||
assign('rating', metadata.criticalRating)
|
||||
assign('series', utils.xlate(metadata.series))
|
||||
assign('title', utils.xlate(metadata.title))
|
||||
assign('issue', utils.xlate(metadata.issue))
|
||||
assign('publisher', utils.xlate(metadata.publisher))
|
||||
assign('publicationMonth', utils.xlate(metadata.month, True))
|
||||
assign('publicationYear', utils.xlate(metadata.year, True))
|
||||
assign('numberOfIssues', utils.xlate(metadata.issueCount, True))
|
||||
assign('comments', utils.xlate(metadata.comments))
|
||||
assign('genre', utils.xlate(metadata.genre))
|
||||
assign('volume', utils.xlate(metadata.volume, True))
|
||||
assign('numberOfVolumes', utils.xlate(metadata.volumeCount, True))
|
||||
assign('language', utils.xlate(utils.getLanguageFromISO(metadata.language)))
|
||||
assign('country', utils.xlate(metadata.country))
|
||||
assign('rating', utils.xlate(metadata.criticalRating))
|
||||
assign('credits', metadata.credits)
|
||||
assign('tags', metadata.tags)
|
||||
|
||||
|
@ -20,6 +20,7 @@ import xml.etree.ElementTree as ET
|
||||
#import zipfile
|
||||
|
||||
from .genericmetadata import GenericMetadata
|
||||
from .issuestring import IssueString
|
||||
from . import utils
|
||||
|
||||
|
||||
@ -206,48 +207,44 @@ class ComicInfoXml:
|
||||
raise 1
|
||||
return None
|
||||
|
||||
metadata = GenericMetadata()
|
||||
md = metadata
|
||||
|
||||
# Helper function
|
||||
def xlate(tag):
|
||||
node = root.find(tag)
|
||||
if node is not None:
|
||||
return node.text
|
||||
else:
|
||||
def get(name):
|
||||
tag = root.find(name)
|
||||
if tag is None:
|
||||
return None
|
||||
return tag.text
|
||||
|
||||
md.series = xlate('Series')
|
||||
md.title = xlate('Title')
|
||||
md.issue = xlate('Number')
|
||||
md.issueCount = xlate('Count')
|
||||
md.volume = xlate('Volume')
|
||||
md.alternateSeries = xlate('AlternateSeries')
|
||||
md.alternateNumber = xlate('AlternateNumber')
|
||||
md.alternateCount = xlate('AlternateCount')
|
||||
md.comments = xlate('Summary')
|
||||
md.notes = xlate('Notes')
|
||||
md.year = xlate('Year')
|
||||
md.month = xlate('Month')
|
||||
md.day = xlate('Day')
|
||||
md.publisher = xlate('Publisher')
|
||||
md.imprint = xlate('Imprint')
|
||||
md.genre = xlate('Genre')
|
||||
md.webLink = xlate('Web')
|
||||
md.language = xlate('LanguageISO')
|
||||
md.format = xlate('Format')
|
||||
md.manga = xlate('Manga')
|
||||
md.characters = xlate('Characters')
|
||||
md.teams = xlate('Teams')
|
||||
md.locations = xlate('Locations')
|
||||
md.pageCount = xlate('PageCount')
|
||||
md.scanInfo = xlate('ScanInformation')
|
||||
md.storyArc = xlate('StoryArc')
|
||||
md.seriesGroup = xlate('SeriesGroup')
|
||||
md.maturityRating = xlate('AgeRating')
|
||||
md = GenericMetadata()
|
||||
|
||||
tmp = xlate('BlackAndWhite')
|
||||
md.blackAndWhite = False
|
||||
md.series = utils.xlate(get('Series'))
|
||||
md.title = utils.xlate(get('Title'))
|
||||
md.issue = IssueString(utils.xlate(get('Number'))).asString()
|
||||
md.issueCount = utils.xlate(get('Count'), True)
|
||||
md.volume = utils.xlate(get('Volume'), True)
|
||||
md.alternateSeries = utils.xlate(get('AlternateSeries'))
|
||||
md.alternateNumber = IssueString(utils.xlate(get('AlternateNumber'))).asString()
|
||||
md.alternateCount = utils.xlate(get('AlternateCount'), True)
|
||||
md.comments = utils.xlate(get('Summary'))
|
||||
md.notes = utils.xlate(get('Notes'))
|
||||
md.year = utils.xlate(get('Year'), True)
|
||||
md.month = utils.xlate(get('Month'), True)
|
||||
md.day = utils.xlate(get('Day'), True)
|
||||
md.publisher = utils.xlate(get('Publisher'))
|
||||
md.imprint = utils.xlate(get('Imprint'))
|
||||
md.genre = utils.xlate(get('Genre'))
|
||||
md.webLink = utils.xlate(get('Web'))
|
||||
md.language = utils.xlate(get('LanguageISO'))
|
||||
md.format = utils.xlate(get('Format'))
|
||||
md.manga = utils.xlate(get('Manga'))
|
||||
md.characters = utils.xlate(get('Characters'))
|
||||
md.teams = utils.xlate(get('Teams'))
|
||||
md.locations = utils.xlate(get('Locations'))
|
||||
md.pageCount = utils.xlate(get('PageCount'), True)
|
||||
md.scanInfo = utils.xlate(get('ScanInformation'))
|
||||
md.storyArc = utils.xlate(get('StoryArc'))
|
||||
md.seriesGroup = utils.xlate(get('SeriesGroup'))
|
||||
md.maturityRating = utils.xlate(get('AgeRating'))
|
||||
|
||||
tmp = utils.xlate(get('BlackAndWhite'))
|
||||
if tmp is not None and tmp.lower() in ["yes", "true", "1"]:
|
||||
md.blackAndWhite = True
|
||||
# Now extract the credit info
|
||||
@ -261,23 +258,23 @@ class ComicInfoXml:
|
||||
):
|
||||
if n.text is not None:
|
||||
for name in n.text.split(','):
|
||||
metadata.addCredit(name.strip(), n.tag)
|
||||
md.addCredit(name.strip(), n.tag)
|
||||
|
||||
if n.tag == 'CoverArtist':
|
||||
if n.text is not None:
|
||||
for name in n.text.split(','):
|
||||
metadata.addCredit(name.strip(), "Cover")
|
||||
md.addCredit(name.strip(), "Cover")
|
||||
|
||||
# parse page data now
|
||||
pages_node = root.find("Pages")
|
||||
if pages_node is not None:
|
||||
for page in pages_node:
|
||||
metadata.pages.append(page.attrib)
|
||||
md.pages.append(page.attrib)
|
||||
# print page.attrib
|
||||
|
||||
metadata.isEmpty = False
|
||||
md.isEmpty = False
|
||||
|
||||
return metadata
|
||||
return md
|
||||
|
||||
def writeToExternalFile(self, filename, metadata):
|
||||
|
||||
|
@ -121,6 +121,23 @@ def which(program):
|
||||
return None
|
||||
|
||||
|
||||
def xlate(data, isInt=False):
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return None
|
||||
if data is None or data == "":
|
||||
return None
|
||||
if isInt:
|
||||
i = str(data).translate(Default(zip((ord(c) for c in "1234567890"),"1234567890")))
|
||||
if i == "0":
|
||||
return "0"
|
||||
if i is "":
|
||||
return None
|
||||
return int(i)
|
||||
else:
|
||||
return str(data)
|
||||
|
||||
|
||||
def removearticles(text):
|
||||
text = text.lower()
|
||||
articles = ['and', 'a', '&', 'issue', 'the']
|
||||
|
@ -516,19 +516,24 @@ def process_file_cli(filename, opts, settings, match_results):
|
||||
renamer.setTemplate(settings.rename_template)
|
||||
renamer.setIssueZeroPadding(settings.rename_issue_number_padding)
|
||||
renamer.setSmartCleanup(settings.rename_use_smart_string_cleanup)
|
||||
renamer.move = settings.rename_move_dir
|
||||
|
||||
new_name = renamer.determineName(filename, ext=new_ext)
|
||||
|
||||
if new_name == os.path.basename(filename):
|
||||
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
|
||||
|
||||
folder = os.path.dirname(os.path.abspath(filename))
|
||||
new_abs_path = utils.unique_file(os.path.join(folder, new_name))
|
||||
|
||||
suffix = ""
|
||||
if not opts.dryrun:
|
||||
# rename the file
|
||||
os.makedirs(os.path.dirname(new_abs_path), 0o777, True)
|
||||
os.rename(filename, new_abs_path)
|
||||
else:
|
||||
suffix = " (dry-run, no change)"
|
||||
|
@ -124,11 +124,11 @@ class ComicVineTalker(QObject):
|
||||
year = None
|
||||
if date_str is not None:
|
||||
parts = date_str.split('-')
|
||||
year = parts[0]
|
||||
year = utils.xlate(parts[0], True)
|
||||
if len(parts) > 1:
|
||||
month = parts[1]
|
||||
month = utils.xlate(parts[1], True)
|
||||
if len(parts) > 2:
|
||||
day = parts[2]
|
||||
day = utils.xlate(parts[2], True)
|
||||
return day, month, year
|
||||
|
||||
def testKey(self, key):
|
||||
@ -497,15 +497,13 @@ class ComicVineTalker(QObject):
|
||||
# Now, map the Comic Vine data to generic metadata
|
||||
metadata = GenericMetadata()
|
||||
|
||||
metadata.series = issue_results['volume']['name']
|
||||
metadata.series = utils.xlate(issue_results['volume']['name'])
|
||||
metadata.issue = IssueString(issue_results['issue_number']).asString()
|
||||
metadata.title = utils.xlate(issue_results['name'])
|
||||
|
||||
num_s = IssueString(issue_results['issue_number']).asString()
|
||||
metadata.issue = num_s
|
||||
metadata.title = issue_results['name']
|
||||
|
||||
metadata.publisher = volume_results['publisher']['name']
|
||||
metadata.day, metadata.month, metadata.year = self.parseDateStr(
|
||||
issue_results['cover_date'])
|
||||
if volume_results['publisher'] is not None:
|
||||
metadata.publisher = utils.xlate(volume_results['publisher']['name'])
|
||||
metadata.day, metadata.month, metadata.year = self.parseDateStr(issue_results['cover_date'])
|
||||
|
||||
#metadata.issueCount = volume_results['count_of_issues']
|
||||
metadata.comments = self.cleanup_html(
|
||||
|
@ -17,19 +17,96 @@
|
||||
import os
|
||||
import re
|
||||
import datetime
|
||||
import sys
|
||||
import string
|
||||
|
||||
from pathvalidate import sanitize_filepath
|
||||
|
||||
from . import utils
|
||||
from .issuestring import IssueString
|
||||
|
||||
|
||||
class MetadataFormatter(string.Formatter):
|
||||
def __init__(self, smart_cleanup=False):
|
||||
super().__init__()
|
||||
self.smart_cleanup = smart_cleanup
|
||||
|
||||
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:
|
||||
result.append(literal_text.lstrip("-_)}]#"))
|
||||
else:
|
||||
result.append(literal_text)
|
||||
lstrip = False
|
||||
# if there's a field, output it
|
||||
if field_name is not None:
|
||||
# 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
|
||||
fmtObj = self.format_field(obj, format_spec)
|
||||
if fmtObj == "" and len(result) > 0 and self.smart_cleanup:
|
||||
lstrip = True
|
||||
result.pop()
|
||||
result.append(fmtObj)
|
||||
|
||||
return ''.join(result), auto_arg_index
|
||||
|
||||
|
||||
class FileRenamer:
|
||||
|
||||
def __init__(self, metadata):
|
||||
self.setMetadata(metadata)
|
||||
self.setTemplate(
|
||||
"%series% v%volume% #%issue% (of %issuecount%) (%year%)")
|
||||
"{publisher}/{series}/{series} v{volume} #{issue} (of {issueCount}) ({year})")
|
||||
self.smart_cleanup = True
|
||||
self.issue_zero_padding = 3
|
||||
self.move = False
|
||||
|
||||
def setMetadata(self, metadata):
|
||||
self.metdata = metadata
|
||||
@ -43,114 +120,37 @@ class FileRenamer:
|
||||
def setTemplate(self, template):
|
||||
self.template = template
|
||||
|
||||
def replaceToken(self, text, value, token):
|
||||
# helper func
|
||||
def isToken(word):
|
||||
return (word[0] == "%" and word[-1:] == "%")
|
||||
|
||||
if value is not None:
|
||||
return text.replace(token, str(value))
|
||||
else:
|
||||
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 isToken(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)
|
||||
else:
|
||||
return text.replace(token, "")
|
||||
|
||||
def determineName(self, filename, ext=None):
|
||||
|
||||
class Default(dict):
|
||||
def __missing__(self, key):
|
||||
return "{" + key + "}"
|
||||
md = self.metdata
|
||||
new_name = self.template
|
||||
preferred_encoding = utils.get_actual_preferred_encoding()
|
||||
|
||||
# print(u"{0}".format(md))
|
||||
|
||||
new_name = self.replaceToken(new_name, md.series, '%series%')
|
||||
new_name = self.replaceToken(new_name, md.volume, '%volume%')
|
||||
# padding for issue
|
||||
md.issue = IssueString(md.issue).asString(pad=self.issue_zero_padding)
|
||||
|
||||
if md.issue is not None:
|
||||
issue_str = "{0}".format(
|
||||
IssueString(md.issue).asString(pad=self.issue_zero_padding))
|
||||
else:
|
||||
issue_str = None
|
||||
new_name = self.replaceToken(new_name, issue_str, '%issue%')
|
||||
template = self.template
|
||||
|
||||
new_name = self.replaceToken(new_name, md.issueCount, '%issuecount%')
|
||||
new_name = self.replaceToken(new_name, md.year, '%year%')
|
||||
new_name = self.replaceToken(new_name, md.publisher, '%publisher%')
|
||||
new_name = self.replaceToken(new_name, md.title, '%title%')
|
||||
new_name = self.replaceToken(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".encode(preferred_encoding)).decode(preferred_encoding)
|
||||
month_name = dt.strftime("%B")
|
||||
new_name = self.replaceToken(new_name, month_name, '%month_name%')
|
||||
pathComponents = template.split(os.sep)
|
||||
new_name = ""
|
||||
|
||||
new_name = self.replaceToken(new_name, md.genre, '%genre%')
|
||||
new_name = self.replaceToken(new_name, md.language, '%language_code%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.criticalRating, '%criticalrating%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateSeries, '%alternateseries%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateNumber, '%alternatenumber%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.alternateCount, '%alternatecount%')
|
||||
new_name = self.replaceToken(new_name, md.imprint, '%imprint%')
|
||||
new_name = self.replaceToken(new_name, md.format, '%format%')
|
||||
new_name = self.replaceToken(
|
||||
new_name, md.maturityRating, '%maturityrating%')
|
||||
new_name = self.replaceToken(new_name, md.storyArc, '%storyarc%')
|
||||
new_name = self.replaceToken(new_name, md.seriesGroup, '%seriesgroup%')
|
||||
new_name = self.replaceToken(new_name, md.scanInfo, '%scaninfo%')
|
||||
fmt = MetadataFormatter(self.smart_cleanup)
|
||||
for Component in pathComponents:
|
||||
new_name = os.path.join(new_name, fmt.vformat(Component, args=[], kwargs=Default(vars(md))).replace("/", "-"))
|
||||
|
||||
if self.smart_cleanup:
|
||||
|
||||
# remove empty braces,brackets, parentheses
|
||||
new_name = re.sub("\(\s*[-:]*\s*\)", "", new_name)
|
||||
new_name = re.sub("\[\s*[-:]*\s*\]", "", new_name)
|
||||
new_name = re.sub("\{\s*[-:]*\s*\}", "", new_name)
|
||||
|
||||
# remove duplicate spaces
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
# remove remove duplicate -, _,
|
||||
new_name = re.sub("[-_]{2,}\s+", "-- ", new_name)
|
||||
new_name = re.sub("(\s--)+", " --", new_name)
|
||||
new_name = re.sub("(\s-)+", " -", new_name)
|
||||
|
||||
# remove dash or double dash at end of line
|
||||
new_name = re.sub("[-]{1,2}\s*$", "", new_name)
|
||||
|
||||
# remove duplicate spaces (again!)
|
||||
new_name = " ".join(new_name.split())
|
||||
|
||||
if ext is None:
|
||||
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(" :", " -")
|
||||
new_name = new_name.replace(": ", " - ")
|
||||
new_name = new_name.replace(":", "-")
|
||||
new_name = new_name.replace("?", "")
|
||||
|
||||
return new_name
|
||||
# remove padding
|
||||
md.issue = IssueString(md.issue).asString()
|
||||
if self.move:
|
||||
return sanitize_filepath(new_name.strip())
|
||||
else:
|
||||
return os.path.basename(sanitize_filepath(new_name.strip()))
|
||||
|
@ -76,6 +76,7 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
if md.isEmpty:
|
||||
md = ca.metadataFromFilename(self.settings.parse_scan_info)
|
||||
self.renamer.setMetadata(md)
|
||||
self.renamer.move = self.settings.rename_move_dir
|
||||
new_name = self.renamer.determineName(ca.path, ext=new_ext)
|
||||
|
||||
row = self.twList.rowCount()
|
||||
@ -149,17 +150,20 @@ class RenameWindow(QtWidgets.QDialog):
|
||||
centerWindowOnParent(progdialog)
|
||||
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!")
|
||||
continue
|
||||
|
||||
if not item['archive'].isWritable(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)
|
||||
|
@ -44,7 +44,7 @@ class ComicTaggerSettings:
|
||||
@staticmethod
|
||||
def haveOwnUnrarLib():
|
||||
return os.path.exists(ComicTaggerSettings.defaultLibunrarPath())
|
||||
|
||||
|
||||
@staticmethod
|
||||
def baseDir():
|
||||
if getattr(sys, 'frozen', None):
|
||||
@ -119,10 +119,12 @@ class ComicTaggerSettings:
|
||||
self.apply_cbl_transform_on_bulk_operation = False
|
||||
|
||||
# Rename settings
|
||||
self.rename_template = "%series% #%issue% (%year%)"
|
||||
self.rename_template = "{publisher}/{series}/{series} #{issue} - {title} ({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
|
||||
|
||||
# Auto-tag stickies
|
||||
self.save_on_low_confidence = False
|
||||
@ -167,15 +169,15 @@ class ComicTaggerSettings:
|
||||
if self.rar_exe_path != "":
|
||||
self.save()
|
||||
if self.rar_exe_path != "":
|
||||
# make sure rar program is now in the path for the rar class
|
||||
# make sure rar program is now in the path for the rar class
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
|
||||
if self.haveOwnUnrarLib():
|
||||
# We have a 'personal' copy of the unrar lib in the basedir, so
|
||||
# don't search and change the setting
|
||||
# NOTE: a manual edit of the settings file overrides this below
|
||||
os.environ["UNRAR_LIB_PATH"] = self.defaultLibunrarPath()
|
||||
|
||||
|
||||
elif self.unrar_lib_path == "":
|
||||
# Priority is for unrar lib search is:
|
||||
# 1. explicit setting in settings file
|
||||
@ -187,11 +189,11 @@ class ComicTaggerSettings:
|
||||
# look in some platform specific places:
|
||||
if platform.system() == "Windows":
|
||||
# Default location for the RARLab DLL installer
|
||||
if (platform.architecture()[0] == '64bit' and
|
||||
if (platform.architecture()[0] == '64bit' and
|
||||
os.path.exists("C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files (x86)\\UnrarDLL\\x64\\UnRAR64.dll"
|
||||
elif (platform.architecture()[0] == '32bit' and
|
||||
elif (platform.architecture()[0] == '32bit' and
|
||||
os.path.exists("C:\\Program Files\\UnrarDLL\\UnRAR.dll")
|
||||
):
|
||||
self.unrar_lib_path = "C:\\Program Files\\UnrarDLL\\UnRAR.dll"
|
||||
@ -203,14 +205,14 @@ class ComicTaggerSettings:
|
||||
if os.path.exists("/usr/local/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/local/lib/libunrar.so"
|
||||
elif os.path.exists("/usr/lib/libunrar.so"):
|
||||
self.unrar_lib_path = "/usr/lib/libunrar.so"
|
||||
|
||||
self.unrar_lib_path = "/usr/lib/libunrar.so"
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
self.save()
|
||||
|
||||
|
||||
if self.unrar_lib_path != "":
|
||||
# This needs to occur before the unrar module is loaded for the first time
|
||||
os.environ["UNRAR_LIB_PATH"] = self.unrar_lib_path
|
||||
os.environ["UNRAR_LIB_PATH"] = self.unrar_lib_path
|
||||
|
||||
def reset(self):
|
||||
os.unlink(self.settings_file)
|
||||
@ -359,6 +361,10 @@ class ComicTaggerSettings:
|
||||
'rename', 'rename_extension_based_on_archive'):
|
||||
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('autotag', 'save_on_low_confidence'):
|
||||
self.save_on_low_confidence = self.config.getboolean(
|
||||
@ -525,6 +531,8 @@ class ComicTaggerSettings:
|
||||
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)
|
||||
|
||||
if not self.config.has_section('autotag'):
|
||||
self.config.add_section('autotag')
|
||||
|
@ -44,13 +44,13 @@ linuxRarHelp = """
|
||||
<a href="https://www.rarlab.com/download.htm">here</a></span>,
|
||||
and install in your path. </p></body></html>
|
||||
"""
|
||||
|
||||
|
||||
macRarHelp = """
|
||||
<html><head/><body><p>To write to CBR/RAR archives,
|
||||
you will need the rar tool. The easiest way to get this is
|
||||
to install <span style=" text-decoration: underline; color:#0000ff;">
|
||||
<a href="https://brew.sh/">homebrew</a></span>.
|
||||
</p>Once homebrew is installed, run: <b>brew install caskroom/cask/rar</b></body></html>
|
||||
</p>Once homebrew is installed, run: <b>brew install caskroom/cask/rar</b></body></html>
|
||||
"""
|
||||
|
||||
windowsUnrarHelp = """
|
||||
@ -70,7 +70,7 @@ linuxUnrarHelp = """
|
||||
<a href="https://www.rarlab.com/rar_add.htm">here</a></span>
|
||||
for the UnRAR source (which is easy to compile on Linux). </p></body></html>
|
||||
"""
|
||||
|
||||
|
||||
macUnrarHelp = """
|
||||
<html><head/><body><p>To read CBR/RAR archives,
|
||||
you will need the unrar library. The easiest way to get this is
|
||||
@ -92,13 +92,13 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
|
||||
self.settings = settings
|
||||
self.name = "Settings"
|
||||
|
||||
|
||||
self.priorUnrarLibPath = self.settings.unrar_lib_path
|
||||
|
||||
if self.settings.haveOwnUnrarLib():
|
||||
# We have our own unrarlib, so no need for this GUI
|
||||
self.grpBoxUnrar.hide()
|
||||
|
||||
|
||||
if platform.system() == "Windows":
|
||||
self.lblRarHelp.setText(windowsRarHelp)
|
||||
self.lblUnrarHelp.setText(windowsUnrarHelp)
|
||||
@ -111,7 +111,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
# Mac file dialog hides "/usr" and others, so allow user to type
|
||||
self.leUnrarLibPath.setReadOnly(False)
|
||||
self.leRarExePath.setReadOnly(False)
|
||||
|
||||
|
||||
self.lblRarHelp.setText(macRarHelp)
|
||||
self.lblUnrarHelp.setText(macUnrarHelp)
|
||||
self.name = "Preferences"
|
||||
@ -152,6 +152,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.btnClearCache.clicked.connect(self.clearCache)
|
||||
self.btnResetSettings.clicked.connect(self.resetSettings)
|
||||
self.btnTestKey.clicked.connect(self.testAPIKey)
|
||||
self.btnTemplateHelp.clicked.connect(self.showTemplateHelp)
|
||||
|
||||
def settingsToForm(self):
|
||||
|
||||
@ -205,12 +206,15 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
self.cbxSmartCleanup.setCheckState(QtCore.Qt.Checked)
|
||||
if self.settings.rename_extension_based_on_archive:
|
||||
self.cbxChangeExtension.setCheckState(QtCore.Qt.Checked)
|
||||
if self.settings.rename_move_dir:
|
||||
self.cbxMoveFiles.setCheckState(QtCore.Qt.Checked)
|
||||
self.leDirectory.setText(self.settings.rename_dir)
|
||||
|
||||
def accept(self):
|
||||
|
||||
# Copy values from form to settings and save
|
||||
self.settings.rar_exe_path = str(self.leRarExePath.text())
|
||||
|
||||
|
||||
# Don't accept the form info if we have our own unrar lib
|
||||
if not self.settings.haveOwnUnrarLib():
|
||||
self.settings.unrar_lib_path = str(self.leUnrarLibPath.text())
|
||||
@ -222,7 +226,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
if self.settings.unrar_lib_path:
|
||||
os.environ["UNRAR_LIB_PATH"] = self.settings.unrar_lib_path
|
||||
# This doesn't do anything... we need to restart!
|
||||
|
||||
|
||||
if not str(self.leNameLengthDeltaThresh.text()).isdigit():
|
||||
self.leNameLengthDeltaThresh.setText("0")
|
||||
|
||||
@ -258,6 +262,8 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
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.save()
|
||||
QtWidgets.QDialog.accept(self)
|
||||
@ -266,7 +272,7 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
QtWidgets.QMessageBox.information(
|
||||
self, "UnRar Library Change",
|
||||
"ComicTagger will need to be restarted for changes to take effect.")
|
||||
|
||||
|
||||
def selectRar(self):
|
||||
self.selectFile(self.leRarExePath, "RAR")
|
||||
|
||||
@ -317,10 +323,23 @@ class SettingsWindow(QtWidgets.QDialog):
|
||||
dialog.setWindowTitle("Find " + name + " program")
|
||||
else:
|
||||
dialog.setWindowTitle("Find " + name + " library")
|
||||
|
||||
|
||||
if (dialog.exec_()):
|
||||
fileList = dialog.selectedFiles()
|
||||
control.setText(str(fileList[0]))
|
||||
|
||||
def showRenameTab(self):
|
||||
self.tabWidget.setCurrentIndex(5)
|
||||
|
||||
def showTemplateHelp(self):
|
||||
TemplateHelpWin = TemplateHelpWindow(self)
|
||||
TemplateHelpWin.exec_()
|
||||
|
||||
class TemplateHelpWindow(QtWidgets.QDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
super(TemplateHelpWindow, self).__init__(parent)
|
||||
|
||||
uic.loadUi(ComicTaggerSettings.getUIFile('TemplateHelp.ui'), self)
|
||||
|
||||
|
||||
|
@ -25,6 +25,7 @@ import json
|
||||
import webbrowser
|
||||
import re
|
||||
import pickle
|
||||
import datetime
|
||||
#import signal
|
||||
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets, uic
|
||||
@ -51,6 +52,7 @@ from .cbltransformer import CBLTransformer
|
||||
from .renamewindow import RenameWindow
|
||||
from .exportwindow import ExportWindow, ExportConflictOpts
|
||||
from .issueidentifier import IssueIdentifier
|
||||
from .issuestring import IssueString
|
||||
from .autotagstartwindow import AutoTagStartWindow
|
||||
from .autotagprogresswindow import AutoTagProgressWindow
|
||||
from .autotagmatchwindow import AutoTagMatchWindow
|
||||
@ -86,7 +88,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def __init__(self, file_list, settings, parent=None, opts=None):
|
||||
super(TaggerWindow, self).__init__(parent)
|
||||
|
||||
|
||||
uic.loadUi(ComicTaggerSettings.getUIFile('taggerwindow.ui'), self)
|
||||
self.settings = settings
|
||||
|
||||
@ -183,7 +185,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.resetApp()
|
||||
|
||||
# set up some basic field validators
|
||||
validator = QtGui.QIntValidator(1900, 2099, self)
|
||||
validator = QtGui.QIntValidator(1900, datetime.date.today().year + 15, self)
|
||||
self.lePubYear.setValidator(validator)
|
||||
|
||||
validator = QtGui.QIntValidator(1, 12, self)
|
||||
@ -295,7 +297,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
self.setWindowIcon(
|
||||
QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png')))
|
||||
|
||||
|
||||
if self.comic_archive is None:
|
||||
self.setWindowTitle(self.appName)
|
||||
else:
|
||||
@ -611,7 +613,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
)
|
||||
|
||||
local = QUrl(str(absCFURL[0])).toLocalFile()
|
||||
|
||||
|
||||
return local
|
||||
|
||||
def dropEvent(self, event):
|
||||
@ -790,14 +792,12 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
for child in widget.children():
|
||||
self.clearChildren(child)
|
||||
|
||||
# Copy all of the metadata object into to the form.
|
||||
# Merging of metadata should be done via the overlay function
|
||||
def metadataToForm(self):
|
||||
# copy the the metadata object into to the form
|
||||
|
||||
# helper func
|
||||
def assignText(field, value):
|
||||
if value is not None:
|
||||
field.setText(str(value))
|
||||
|
||||
md = self.metadata
|
||||
|
||||
assignText(self.leSeries, md.series)
|
||||
@ -839,23 +839,33 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
self.cbMaturityRating.setEditText(md.maturityRating)
|
||||
else:
|
||||
self.cbMaturityRating.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbMaturityRating.setCurrentIndex(0)
|
||||
|
||||
if md.language is not None:
|
||||
i = self.cbLanguage.findData(md.language)
|
||||
self.cbLanguage.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbLanguage.setCurrentIndex(0)
|
||||
|
||||
if md.country is not None:
|
||||
i = self.cbCountry.findText(md.country)
|
||||
self.cbCountry.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbCountry.setCurrentIndex(0)
|
||||
|
||||
if md.manga is not None:
|
||||
i = self.cbManga.findData(md.manga)
|
||||
self.cbManga.setCurrentIndex(i)
|
||||
else:
|
||||
self.cbManga.setCurrentIndex(0)
|
||||
|
||||
if md.blackAndWhite is not None and md.blackAndWhite:
|
||||
if md.blackAndWhite != None and md.blackAndWhite:
|
||||
self.cbBW.setChecked(True)
|
||||
else:
|
||||
self.cbBW.setChecked(False)
|
||||
|
||||
assignText(self.teTags, utils.listToString(md.tags))
|
||||
self.teTags.setText(utils.listToString(md.tags))
|
||||
|
||||
# !!! Should we clear the credits table or just avoid duplicates?
|
||||
while self.twCredits.rowCount() > 0:
|
||||
@ -914,58 +924,47 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
return False
|
||||
|
||||
def formToMetadata(self):
|
||||
|
||||
# helper func
|
||||
def xlate(data, type_str):
|
||||
s = "{0}".format(data).strip()
|
||||
if s == "":
|
||||
return None
|
||||
elif type_str == "str":
|
||||
return s
|
||||
else:
|
||||
return int(s)
|
||||
|
||||
# copy the data from the form into the metadata
|
||||
md = self.metadata
|
||||
md.series = xlate(self.leSeries.text(), "str")
|
||||
md.issue = xlate(self.leIssueNum.text(), "str")
|
||||
md.issueCount = xlate(self.leIssueCount.text(), "int")
|
||||
md.volume = xlate(self.leVolumeNum.text(), "int")
|
||||
md.volumeCount = xlate(self.leVolumeCount.text(), "int")
|
||||
md.title = xlate(self.leTitle.text(), "str")
|
||||
md.publisher = xlate(self.lePublisher.text(), "str")
|
||||
md.month = xlate(self.lePubMonth.text(), "int")
|
||||
md.year = xlate(self.lePubYear.text(), "int")
|
||||
md.day = xlate(self.lePubDay.text(), "int")
|
||||
md.genre = xlate(self.leGenre.text(), "str")
|
||||
md.imprint = xlate(self.leImprint.text(), "str")
|
||||
md.comments = xlate(self.teComments.toPlainText(), "str")
|
||||
md.notes = xlate(self.teNotes.toPlainText(), "str")
|
||||
md.criticalRating = xlate(self.leCriticalRating.text(), "int")
|
||||
md.maturityRating = xlate(self.cbMaturityRating.currentText(), "str")
|
||||
md = GenericMetadata()
|
||||
md.isEmpty = False
|
||||
md.alternateNumber = IssueString(self.leAltIssueNum.text()).asString()
|
||||
md.issue = IssueString(self.leIssueNum.text()).asString()
|
||||
md.issueCount = utils.xlate(self.leIssueCount.text(), True)
|
||||
md.volume = utils.xlate(self.leVolumeNum.text(), True)
|
||||
md.volumeCount = utils.xlate(self.leVolumeCount.text(), True)
|
||||
md.month = utils.xlate(self.lePubMonth.text(), True)
|
||||
md.year = utils.xlate(self.lePubYear.text(), True)
|
||||
md.day = utils.xlate(self.lePubDay.text(), True)
|
||||
md.criticalRating = utils.xlate(self.leCriticalRating.text(), True)
|
||||
md.alternateCount = utils.xlate(self.leAltIssueCount.text(), True)
|
||||
|
||||
md.storyArc = xlate(self.leStoryArc.text(), "str")
|
||||
md.scanInfo = xlate(self.leScanInfo.text(), "str")
|
||||
md.seriesGroup = xlate(self.leSeriesGroup.text(), "str")
|
||||
md.alternateSeries = xlate(self.leAltSeries.text(), "str")
|
||||
md.alternateNumber = xlate(self.leAltIssueNum.text(), "int")
|
||||
md.alternateCount = xlate(self.leAltIssueCount.text(), "int")
|
||||
md.webLink = xlate(self.leWebLink.text(), "str")
|
||||
md.characters = xlate(self.teCharacters.toPlainText(), "str")
|
||||
md.teams = xlate(self.teTeams.toPlainText(), "str")
|
||||
md.locations = xlate(self.teLocations.toPlainText(), "str")
|
||||
md.series = self.leSeries.text()
|
||||
md.title = self.leTitle.text()
|
||||
md.publisher = self.lePublisher.text()
|
||||
md.genre = self.leGenre.text()
|
||||
md.imprint = self.leImprint.text()
|
||||
md.comments = self.teComments.toPlainText()
|
||||
md.notes = self.teNotes.toPlainText()
|
||||
md.maturityRating = self.cbMaturityRating.currentText()
|
||||
|
||||
md.format = xlate(self.cbFormat.currentText(), "str")
|
||||
md.country = xlate(self.cbCountry.currentText(), "str")
|
||||
md.storyArc = self.leStoryArc.text()
|
||||
md.scanInfo = self.leScanInfo.text()
|
||||
md.seriesGroup = self.leSeriesGroup.text()
|
||||
md.alternateSeries = self.leAltSeries.text()
|
||||
md.webLink = self.leWebLink.text()
|
||||
md.characters = self.teCharacters.toPlainText()
|
||||
md.teams = self.teTeams.toPlainText()
|
||||
md.locations = self.teLocations.toPlainText()
|
||||
|
||||
langiso = self.cbLanguage.itemData(self.cbLanguage.currentIndex())
|
||||
md.language = xlate(langiso, "str")
|
||||
md.format = self.cbFormat.currentText()
|
||||
md.country = self.cbCountry.currentText()
|
||||
|
||||
manga_code = self.cbManga.itemData(self.cbManga.currentIndex())
|
||||
md.manga = xlate(manga_code, "str")
|
||||
md.language = utils.xlate(self.cbLanguage.itemData(self.cbLanguage.currentIndex()))
|
||||
|
||||
md.manga = utils.xlate(self.cbManga.itemData(self.cbManga.currentIndex()))
|
||||
|
||||
# Make a list from the coma delimited tags string
|
||||
tmp = xlate(self.teTags.toPlainText(), "str")
|
||||
tmp = self.teTags.toPlainText()
|
||||
if tmp is not None:
|
||||
def striplist(l):
|
||||
return([x.strip() for x in l])
|
||||
@ -989,6 +988,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
row += 1
|
||||
|
||||
md.pages = self.pageListEditor.getPageList()
|
||||
self.metadata = md
|
||||
|
||||
def useFilename(self):
|
||||
if self.comic_archive is not None:
|
||||
@ -1198,11 +1198,11 @@ class TaggerWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def updateCreditColors(self):
|
||||
#!!!ATB qt5 porting TODO
|
||||
#return
|
||||
#return
|
||||
inactive_color = QtGui.QColor(255, 170, 150)
|
||||
active_palette = self.leSeries.palette()
|
||||
active_color = active_palette.color(QtGui.QPalette.Base)
|
||||
|
||||
|
||||
inactive_brush = QtGui.QBrush(inactive_color)
|
||||
active_brush = QtGui.QBrush(active_color)
|
||||
|
||||
|
105
comictaggerlib/ui/TemplateHelp.ui
Normal file
105
comictaggerlib/ui/TemplateHelp.ui
Normal file
@ -0,0 +1,105 @@
|
||||
<?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="QTextBrowser" name="textBrowser">
|
||||
<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)
|
||||
{alternateinteger}	(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}		(integer)
|
||||
{isVersionOf}	(boolean)
|
||||
{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">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -503,7 +503,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>
|
||||
@ -512,31 +512,79 @@
|
||||
<item row="1" column="1">
|
||||
<widget class="QLineEdit" name="leRenameTemplate">
|
||||
<property name="toolTip">
|
||||
<string><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></string>
|
||||
<string><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} (number)
|
||||
{year} (number)
|
||||
{day} (number)
|
||||
{date} (Date)
|
||||
{issueCount} (number)
|
||||
{volume} (number)
|
||||
{genre} (string)
|
||||
{language} (string)
|
||||
{comments} (string)
|
||||
{volumeCount} (number)
|
||||
{criticalRating} (string)
|
||||
{country} (string)
|
||||
{alternateSeries} (string)
|
||||
{alternateNumber} (string)
|
||||
{alternateCount} (number)
|
||||
{imprint} (string)
|
||||
{notes} (string)
|
||||
{webLink} (string)
|
||||
{format} (string)
|
||||
{manga} (string)
|
||||
{blackAndWhite} (boolean)
|
||||
{pageCount} (number)
|
||||
{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} (number)
|
||||
{isVersionOf} (boolean)
|
||||
{rights} (string)
|
||||
{identifier} (string)
|
||||
{lastMark} (string)
|
||||
{coverImage} (string)
|
||||
|
||||
Examples:
|
||||
|
||||
{series} {issue} ({year})
|
||||
|
||||
{series} #{issue} - {title}
|
||||
</pre></string>
|
||||
</property>
|
||||
</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="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">
|
||||
@ -555,7 +603,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>
|
||||
@ -565,13 +613,33 @@
|
||||
</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="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="7" column="0">
|
||||
<widget class="QLabel" name="lblDirectory">
|
||||
<property name="text">
|
||||
<string>Destination Directory:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1">
|
||||
<widget class="QLineEdit" name="leDirectory"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -5,4 +5,5 @@ natsort==3.5.2
|
||||
PyPDF2==1.24
|
||||
pillow>=4.3.0
|
||||
PyQt5>=5.10.1
|
||||
git+https://github.com/pyinstaller/pyinstaller@develop
|
||||
git+https://github.com/pyinstaller/pyinstaller@develop
|
||||
pathvalidate
|
||||
|
Loading…
Reference in New Issue
Block a user