Compare commits

..

38 Commits

Author SHA1 Message Date
afcbde7fc6 update todo
git-svn-id: http://comictagger.googlecode.com/svn/trunk@651 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:45:15 +00:00
151fac5bf1 updated release notes
git-svn-id: http://comictagger.googlecode.com/svn/trunk@650 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:45:06 +00:00
57c1efdab9 makefile TAGGER_BASE can be set in the environment
git-svn-id: http://comictagger.googlecode.com/svn/trunk@649 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:43:22 +00:00
6b272cef87 When searching for a title, convert the string to a list of words separated by "ANDS", and then back to a string
git-svn-id: http://comictagger.googlecode.com/svn/trunk@648 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:40:58 +00:00
1cdc732739 Added a message when not able to open selected folder or file
git-svn-id: http://comictagger.googlecode.com/svn/trunk@647 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:39:47 +00:00
d1b00d162d Allow any size archive to be considered a comic
git-svn-id: http://comictagger.googlecode.com/svn/trunk@646 6c5673fe-1810-88d6-992b-cd32ca31540c
2014-01-31 04:37:13 +00:00
3dd3980bc1 update todo file
git-svn-id: http://comictagger.googlecode.com/svn/trunk@645 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 18:01:01 +00:00
cbf475eb26 removed filtering out of period (".")
git-svn-id: http://comictagger.googlecode.com/svn/trunk@644 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 18:00:04 +00:00
ac8b575659 bumped version number
git-svn-id: http://comictagger.googlecode.com/svn/trunk@643 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-08-18 17:56:38 +00:00
ac8ef286a4 Perform the rar test first, since some rars can be falsly identified as zips, somehow...
git-svn-id: http://comictagger.googlecode.com/svn/trunk@641 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-07-23 17:06:35 +00:00
f567dc37be Handle case of None value credit tags in XML
git-svn-id: http://comictagger.googlecode.com/svn/trunk@640 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-07-08 23:32:24 +00:00
15c5fc5258 release notes update
git-svn-id: http://comictagger.googlecode.com/svn/trunk@637 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-09 01:31:26 +00:00
cc985b52a5 Do the limited series check/elimination after cover matching
git-svn-id: http://comictagger.googlecode.com/svn/trunk@636 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-08 02:39:06 +00:00
910b0386be Remove tooltip if not expandable
git-svn-id: http://comictagger.googlecode.com/svn/trunk@635 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-08 02:38:06 +00:00
0fece23405 Allow rename w/smart cleanup to have "--"
git-svn-id: http://comictagger.googlecode.com/svn/trunk@634 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 22:30:32 +00:00
eee320e0c7 bumped version number
git-svn-id: http://comictagger.googlecode.com/svn/trunk@632 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 21:07:00 +00:00
accabf8e21 Added keyboard shortcut for form clear
git-svn-id: http://comictagger.googlecode.com/svn/trunk@631 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 21:06:48 +00:00
acc253d35c todo update
git-svn-id: http://comictagger.googlecode.com/svn/trunk@630 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 18:36:30 +00:00
ede0154efe issueCount now gets passed to issueidentifier.
a possible technique for eliminating potential volumes is coded, but commented out for now

git-svn-id: http://comictagger.googlecode.com/svn/trunk@629 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 18:24:57 +00:00
5b805b1428 auto-tag progress window now uses coverimagewidget
git-svn-id: http://comictagger.googlecode.com/svn/trunk@628 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 18:22:14 +00:00
2e6b2a89db Added a raw image data mode for the coverimagewidget
git-svn-id: http://comictagger.googlecode.com/svn/trunk@627 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-06 18:21:34 +00:00
c028bb4ddc Make sure to catch all non-numeric characters after a # for the issue number
git-svn-id: http://comictagger.googlecode.com/svn/trunk@626 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-04 01:48:42 +00:00
b70beb5684 more file name parser enhancements
git-svn-id: http://comictagger.googlecode.com/svn/trunk@625 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-04 01:22:39 +00:00
128af4521b better filename parsing
git-svn-id: http://comictagger.googlecode.com/svn/trunk@623 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-02 16:31:50 +00:00
43cf7a80c8 remove print
git-svn-id: http://comictagger.googlecode.com/svn/trunk@622 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-01 22:33:05 +00:00
3223ed190c Make sure form is updated when removing top item from list
git-svn-id: http://comictagger.googlecode.com/svn/trunk@621 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-01 22:32:20 +00:00
9e2817c037 deal with CV bug (wrong result set count) when not specifying page=1
git-svn-id: http://comictagger.googlecode.com/svn/trunk@620 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-01 22:31:25 +00:00
6e7bd10fb9 deal with pagination bug on comicvine side reporting wrong result set size when not specifiying page=1
git-svn-id: http://comictagger.googlecode.com/svn/trunk@619 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-05-01 22:30:30 +00:00
c099205779 Reworked the issue string parsing
git-svn-id: http://comictagger.googlecode.com/svn/trunk@618 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-30 18:05:10 +00:00
47d8da0e80 removed extra line
git-svn-id: http://comictagger.googlecode.com/svn/trunk@615 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-22 02:37:13 +00:00
0f7e88e58c bump to 1.1.8-beta
git-svn-id: http://comictagger.googlecode.com/svn/trunk@614 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-22 00:49:20 +00:00
65902a15b1 add-on script for renaming files based on transform list
git-svn-id: http://comictagger.googlecode.com/svn/trunk@613 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-21 06:55:32 +00:00
a68b2babeb some reworking so scripts get passed all options after scriptname
git-svn-id: http://comictagger.googlecode.com/svn/trunk@612 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-21 06:53:44 +00:00
4098802e43 sleep 1 sec before retrying after http 500 error
git-svn-id: http://comictagger.googlecode.com/svn/trunk@611 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-21 06:51:43 +00:00
9c14258e9f verify need to check version in GUI
git-svn-id: http://comictagger.googlecode.com/svn/trunk@610 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-17 18:10:08 +00:00
33bdbe8be8 verify need to check version in CLI
git-svn-id: http://comictagger.googlecode.com/svn/trunk@609 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-17 18:09:43 +00:00
a76864c109 be a little smarted in colon replacement in renaming
git-svn-id: http://comictagger.googlecode.com/svn/trunk@608 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-17 18:09:10 +00:00
cb68d07751 Added special handling of HTTP 500 error that Comic Vine seems to give occasionally.
git-svn-id: http://comictagger.googlecode.com/svn/trunk@607 6c5673fe-1810-88d6-992b-cd32ca31540c
2013-04-17 18:08:39 +00:00
23 changed files with 631 additions and 315 deletions

View File

@ -1,4 +1,4 @@
TAGGER_BASE := $(HOME)/Dropbox/tagger/comictagger
TAGGER_BASE ?:= $(HOME)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
VERSION_STR := $(shell grep version $(TAGGER_SRC)/ctversion.py| cut -d= -f2 | sed 's/\"//g')
PASSWORD := $(shell cat $(TAGGER_BASE)/project_password.txt)

View File

@ -22,6 +22,7 @@ import sys
from PyQt4 import QtCore, QtGui, uic
import os
from settings import ComicTaggerSettings
from coverimagewidget import CoverImageWidget
import utils
class AutoTagProgressWindow(QtGui.QDialog):
@ -31,8 +32,17 @@ class AutoTagProgressWindow(QtGui.QDialog):
super(AutoTagProgressWindow, self).__init__(parent)
uic.loadUi(ComicTaggerSettings.getUIFile('autotagprogresswindow.ui' ), self)
self.lblTest.setPixmap(QtGui.QPixmap(ComicTaggerSettings.getGraphic('nocover.png')))
self.lblArchive.setPixmap(QtGui.QPixmap(ComicTaggerSettings.getGraphic('nocover.png')))
self.archiveCoverWidget = CoverImageWidget( self.archiveCoverContainer, CoverImageWidget.DataMode, False )
gridlayout = QtGui.QGridLayout( self.archiveCoverContainer )
gridlayout.addWidget( self.archiveCoverWidget )
gridlayout.setContentsMargins(0,0,0,0)
self.testCoverWidget = CoverImageWidget( self.testCoverContainer, CoverImageWidget.DataMode, False )
gridlayout = QtGui.QGridLayout( self.testCoverContainer )
gridlayout.addWidget( self.testCoverWidget )
gridlayout.setContentsMargins(0,0,0,0)
self.isdone = False
self.setWindowFlags(self.windowFlags() |
@ -42,23 +52,16 @@ class AutoTagProgressWindow(QtGui.QDialog):
utils.reduceWidgetFontSize( self.textEdit )
def setArchiveImage( self, img_data):
self.setCoverImage( img_data, self.lblArchive )
self.setCoverImage( img_data, self.archiveCoverWidget)
def setTestImage( self, img_data):
self.setCoverImage( img_data, self.lblTest )
self.setCoverImage( img_data, self.testCoverWidget)
def setCoverImage( self, img_data , label):
if img_data is not None:
img = QtGui.QImage()
img.loadFromData( img_data )
label.setPixmap(QtGui.QPixmap(img))
label.setScaledContents(True)
else:
label.setPixmap(QtGui.QPixmap(ComicTaggerSettings.getGraphic('nocover.png')))
label.setScaledContents(True)
def setCoverImage( self, img_data , widget):
widget.setImageData( img_data )
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
def reject(self):
QtGui.QDialog.reject(self)
self.isdone = True

View File

@ -517,14 +517,14 @@ class ComicArchive:
self.resetCache()
self.settings = settings
if self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver( self.path )
elif self.rarTest():
if self.rarTest():
self.archive_type = self.ArchiveType.Rar
self.archiver = RarArchiver( self.path, settings )
elif self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver( self.path )
elif os.path.isdir( self.path ):
self.archive_type = self.ArchiveType.Folder
self.archiver = FolderArchiver( self.path )
@ -609,7 +609,7 @@ class ComicArchive:
if (
( self.isZip() or self.isRar() ) #or self.isFolder() )
and
( self.getNumberOfPages() > 2)
( self.getNumberOfPages() > 0)
):
return True

View File

@ -260,12 +260,14 @@ class ComicInfoXml:
n.tag == 'Letterer' or
n.tag == 'Editor'
):
for name in n.text.split(','):
metadata.addCredit( name.strip(), n.tag )
if n.text is not None:
for name in n.text.split(','):
metadata.addCredit( name.strip(), n.tag )
if n.tag == 'CoverArtist':
for name in n.text.split(','):
metadata.addCredit( name.strip(), "Cover" )
if n.text is not None:
for name in n.text.split(','):
metadata.addCredit( name.strip(), "Cover" )
# parse page data now
pages_node = root.find( "Pages" )

View File

@ -24,6 +24,7 @@ from pprint import pprint
import urllib2, urllib
import math
import re
import time
import datetime
import ctversion
import sys
@ -106,12 +107,28 @@ class ComicVineTalker(QObject):
return cv_response[ 'status_code' ] != 100
def getUrlContent( self, url ):
try:
resp = urllib2.urlopen( url )
return resp.read()
except Exception as e:
self.writeLog( str(e) )
raise ComicVineTalkerException("Network Error!")
# connect to server:
# if there is a 500 error, try a few more times before giving up
# any other error, just bail
#print "ATB---", url
for tries in range(3):
try:
resp = urllib2.urlopen( url )
return resp.read()
except urllib2.HTTPError as e:
if e.getcode() == 500:
self.writeLog( "Try #{0}: ".format(tries+1) )
time.sleep(1)
self.writeLog( str(e) + "\n" )
if e.getcode() != 500:
break
except Exception as e:
self.writeLog( str(e) + "\n" )
raise ComicVineTalkerException("Network Error!")
raise ComicVineTalkerException("Error on Comic Vine server")
def searchForSeries( self, series_name , callback=None, refresh_cache=False ):
@ -128,11 +145,23 @@ class ComicVineTalker(QObject):
return cached_search_results
original_series_name = series_name
series_name = urllib.quote_plus(series_name.encode("utf-8"))
#series_name = urllib.quote_plus(unicode(series_name))
search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + series_name + "&field_list=name,id,start_year,publisher,image,description,count_of_issues"
content = self.getUrlContent(search_url)
# We need to make the series name into an "AND"ed query list
query_word_list = series_name.split()
and_list = ['AND'] * (len(query_word_list)-1)
and_list.append('')
# zipper up the two lists
query_list = zip(query_word_list, and_list)
# flatten the list
query_list = [ item for sublist in query_list for item in sublist]
# convert back to a string
query_string = " ".join( query_list ).strip()
#print "Query string = ", query_string
query_string = urllib.quote_plus(query_string.encode("utf-8"))
search_url = self.api_base_url + "/search/?api_key=" + self.api_key + "&format=json&resources=volume&query=" + query_string + "&field_list=name,id,start_year,publisher,image,description,count_of_issues"
content = self.getUrlContent(search_url + "&page=1")
cv_response = json.loads(content)
@ -574,7 +603,7 @@ class ComicVineTalker(QObject):
self.urlFetchComplete.emit( details['image_url'],details['thumb_image_url'], self.issue_id )
return
issue_url = "http://www.comicvine.com/api/issue/" + CVTypeID.Issue + "-" + str(issue_id) + "/?api_key=" + self.api_key + "&format=json&field_list=image,cover_date,site_detail_url"
issue_url = self.api_base_url + "/issue/" + CVTypeID.Issue + "-" + str(issue_id) + "/?api_key=" + self.api_key + "&format=json&field_list=image,cover_date,site_detail_url"
self.nam = QNetworkAccessManager()
self.nam.finished.connect( self.asyncFetchIssueCoverURLComplete )
self.nam.get(QNetworkRequest(QUrl(issue_url)))

View File

@ -1,5 +1,7 @@
"""
A PyQt4 widget display cover images from either local archive, or from ComicVine
(TODO: This should be re-factored using subclasses!)
"""
"""
@ -59,8 +61,9 @@ class CoverImageWidget(QWidget):
ArchiveMode = 0
AltCoverMode = 1
URLMode = 1
DataMode = 3
def __init__(self, parent, mode ):
def __init__(self, parent, mode, expand_on_click = True ):
super(CoverImageWidget, self).__init__(parent)
uic.loadUi(ComicTaggerSettings.getUIFile('coverimagewidget.ui' ), self)
@ -78,7 +81,10 @@ class CoverImageWidget(QWidget):
self.btnLeft.clicked.connect( self.decrementImage )
self.btnRight.clicked.connect( self.incrementImage )
self.resetWidget()
clickable(self.lblImage).connect(self.showPopup)
if expand_on_click:
clickable(self.lblImage).connect(self.showPopup)
else:
self.lblImage.setToolTip( "" )
self.updateContent()
@ -93,11 +99,12 @@ class CoverImageWidget(QWidget):
self.page_loader = None
self.imageIndex = -1
self.imageCount = 1
self.imageData = None
def clear( self ):
self.resetWidget()
self.updateContent()
def incrementImage( self ):
self.imageIndex += 1
if self.imageIndex == self.imageCount:
@ -138,7 +145,19 @@ class CoverImageWidget(QWidget):
self.comicVine = ComicVineTalker()
self.comicVine.urlFetchComplete.connect( self.primaryUrlFetchComplete )
self.comicVine.asyncFetchIssueCoverURLs( int(self.issue_id) )
def setImageData( self, image_data ):
if self.mode == CoverImageWidget.DataMode:
self.resetWidget()
if image_data is None:
self.imageIndex = -1
else:
self.imageIndex = 0
self.imageData = image_data
self.updateContent()
def primaryUrlFetchComplete( self, primary_url, thumb_url, issue_id ):
self.url_list.append(str(primary_url))
self.imageIndex = 0
@ -179,11 +198,13 @@ class CoverImageWidget(QWidget):
self.loadDefault()
elif self.mode in [ CoverImageWidget.AltCoverMode, CoverImageWidget.URLMode ]:
self.loadURL()
elif self.mode == CoverImageWidget.DataMode:
self.coverRemoteFetchComplete( self.imageData, 0 )
else:
self.loadPage()
def updateControls( self ):
if not self.showControls:
if not self.showControls or self.mode == CoverImageWidget.DataMode:
self.btnLeft.hide()
self.btnRight.hide()
self.label.hide()
@ -288,3 +309,4 @@ class CoverImageWidget(QWidget):
def showPopup( self ):
self.popup = ImagePopup(self, self.current_pixmap)

View File

@ -1,3 +1,3 @@
# This file should contan only these comments, and the line below.
# Used by packaging makefiles and app
version="1.1.7-beta"
version="1.1.10-beta"

View File

@ -30,31 +30,25 @@ import os
from urllib import unquote
class FileNameParser:
def repl(self, m):
return ' ' * len(m.group())
def fixSpaces( self, string, remove_dashes=True ):
if remove_dashes:
placeholders = ['[-_]',' +']
else:
placeholders = ['[_]',' +']
for ph in placeholders:
string = re.sub(ph, ' ', string )
return string.strip()
# check for silly .1 or .5 style issue strings
# allow up to 5 chars total
def isPointIssue( self, word ):
ret = False
try:
float(word)
if (len(word) < 5 and not word.isdigit()):
ret = True
except ValueError:
pass
return ret
string = re.sub(ph, self.repl, string )
return string #.strip()
def getIssueCount( self,filename ):
def getIssueCount( self,filename, issue_end ):
count = ""
filename = filename[issue_end:]
# replace any name seperators with spaces
tmpstr = self.fixSpaces(filename)
found = False
@ -74,113 +68,144 @@ class FileNameParser:
count = count.lstrip("0")
return count
def getIssueNumber( self, filename ):
# Returns a tuple of issue number string, and start and end indexs in the filename
# (The indexes will be used to split the string up for further parsing)
found = False
issue = ''
start = 0
end = 0
# first, look for multiple "--", this means it's formatted differently from most:
if "--" in filename:
# the pattern seems to be that anything to left of the first "--" is the series name followed by issue
filename = filename.split("--")[0]
elif "___" in filename:
filename = re.sub("--.*", self.repl, filename)
elif "__" in filename:
# the pattern seems to be that anything to left of the first "__" is the series name followed by issue
filename = filename.split("__")[0]
filename = re.sub("__.*", self.repl, filename)
filename = filename.replace("+", " ")
# remove parenthetical phrases
filename = re.sub( "\(.*\)", "", filename)
filename = re.sub( "\[.*\]", "", filename)
# guess based on position
# replace parenthetical phrases with spaces
filename = re.sub( "\(.*?\)", self.repl, filename)
filename = re.sub( "\[.*?\]", self.repl, filename)
# replace any name seperators with spaces
tmpstr = self.fixSpaces(filename)
word_list = tmpstr.split(' ')
filename = self.fixSpaces(filename)
# remove any "of NN" phrase with spaces (problem: this could break on some titles)
filename = re.sub( "of [\d]+", self.repl, filename)
#print u"[{0}]".format(filename)
#before we search, remove any kind of likely "of X" phrase
for i in range(0, len(word_list)-2):
if ( word_list[i].isdigit() and
word_list[i+1] == "of" and
word_list[i+2].isdigit() ):
word_list[i+1] ="XXX"
word_list[i+2] ="XXX"
# first look for the last "#" followed by a digit in the filename. this is almost certainly the issue number
#issnum = re.search('#\d+', filename)
matchlist = re.findall("#[-+]?(([0-9]*\.[0-9]+|[0-9]+)(\w*))", filename)
if len(matchlist) > 0:
#get the last item
issue = matchlist[ len(matchlist) - 1][0]
found = True
# assume the last number in the filename that is under 4 digits is the issue number
if not found:
for word in reversed(word_list):
if len(word) > 0 and word[0] == "#":
word = word[1:]
if (
(word.isdigit() and len(word) < 4) or
(self.isPointIssue(word))
):
issue = word
found = True
#print 'Assuming issue number is ' + str(issue) + ' based on the position.'
break
if not found:
# try a regex
issnum = re.search('(?<=[_#\s-])(\d+[a-zA-Z]+|\d+\.\d|\d+)', filename)
if issnum:
issue = issnum.group()
# we should now have a cleaned up filename version with all the words in
# the same positions as original filename
# make a list of each word and its position
word_list = list()
for m in re.finditer("\S+", filename):
word_list.append( (m.group(0), m.start(), m.end()) )
# remove the first word, since it can't be the issue number
if len(word_list) > 1:
word_list = word_list[1:]
else:
#only one word?? just bail.
return issue, start, end
# Now try to search for the likely issue number word in the list
# first look for a word with "#" followed by digits with optional sufix
# this is almost certainly the issue number
for w in reversed(word_list):
if re.match("#[-]?(([0-9]*\.[0-9]+|[0-9]+)(\w*))", w[0]):
found = True
#print 'Got the issue using regex. Issue is ' + issue
break
# same as above but w/o a '#', and only look at the last word in the list
if not found:
w = word_list[-1]
if re.match("[-]?(([0-9]*\.[0-9]+|[0-9]+)(\w*))", w[0]):
found = True
# now try to look for a # followed by any characters
if not found:
for w in reversed(word_list):
if re.match("#\S+", w[0]):
found = True
break
if found:
issue = w[0]
start = w[1]
end = w[2]
if issue[0] == '#':
issue = issue[1:]
return issue, start, end
return issue.strip()
def getSeriesName(self, filename, issue ):
# use the issue number string to split the filename string
# assume first element of list is the series name, plus cruft
#!!! this could fail in the case of small numerics in the series name!!!
# TODO: we really should pass in the *INDEX* of the issue, that makes
# finding it easier
def getSeriesName(self, filename, issue_start ):
# use the issue number string index to split the filename string
if issue_start != 0:
filename = filename[:issue_start]
# in case there is no issue number, remove some obvious stuff
if "--" in filename:
# the pattern seems to be that anything to left of the first "--" is the series name followed by issue
filename = re.sub("--.*", self.repl, filename)
elif "__" in filename:
# the pattern seems to be that anything to left of the first "__" is the series name followed by issue
filename = re.sub("__.*", self.repl, filename)
filename = filename.replace("+", " ")
tmpstr = self.fixSpaces(filename, remove_dashes=False)
#remove pound signs. this might mess up the series name if there is a# in it.
tmpstr = tmpstr.replace("#", " ")
if issue != "":
# assume that issue substr has at least one space before it
issue_str = " " + str(issue)
series = tmpstr.split(issue_str)[0]
else:
# no issue to work off of
#!!! TODO we should look for the year, and split from that
# and if that doesn't exist, remove parenthetical phrases
series = tmpstr
series = re.sub( "\(.*\)", "", tmpstr)
series = tmpstr
volume = ""
#save the last word
last_word = series.split()[-1]
series = series.rstrip("#")
# remove any parenthetical phrases
series = re.sub( "\(.*?\)", "", series)
# search for volume number
match = re.search('(.+)([vV]|[Vv][oO][Ll]\.?\s?)(\d+)\s*$', series)
if match:
series = match.group(1)
volume = match.group(3)
return series.strip(), volume.strip()
# if a volume wasn't found, see if the last word is a year in parentheses
# since that's a common way to designate the volume
if volume == "":
#match either (YEAR), (YEAR-), or (YEAR-YEAR2)
match = re.search("(\()(\d{4})(-(\d{4}|)|)(\))", last_word)
if match:
volume = match.group(2)
def getYear( self,filename):
series = series.strip()
# if we don't have an issue number (issue_start==0), look
# for hints i.e. "TPB", "one-shot", "OS", "OGN", etc that might
# be removed to help search online
if issue_start == 0:
one_shot_words = [ "tpb", "os", "one-shot", "ogn", "gn" ]
last_word = series.split()[-1]
if last_word.lower() in one_shot_words:
series = series.rsplit(' ', 1)[0]
return series, volume.strip()
def getYear( self,filename, issue_end):
filename = filename[issue_end:]
year = ""
# look for four digit number with "(" ")" or "--" around it
@ -191,17 +216,17 @@ class FileNameParser:
year = re.sub("[^0-9]", "", year)
return year
def getRemainder( self, filename, year, count ):
#make a guess at where the the non-interesting stuff begins
def getRemainder( self, filename, year, count, issue_end ):
#make a guess at where the the non-interesting stuff begins
remainder = ""
if "--" in filename:
remainder = filename.split("--",1)[1]
elif "__" in filename:
remainder = filename.split("__",1)[1]
elif "(" in filename:
remainder = "(" + filename.split("(",1)[1]
elif issue_end != 0:
remainder = filename[issue_end:]
remainder = self.fixSpaces(remainder, remove_dashes=False)
if year != "":
@ -231,11 +256,11 @@ class FileNameParser:
filename = filename.replace("_28", "(")
filename = filename.replace("_29", ")")
self.issue = self.getIssueNumber(filename)
self.series, self.volume = self.getSeriesName(filename, self.issue)
self.year = self.getYear(filename)
self.issue_count = self.getIssueCount(filename)
self.remainder = self.getRemainder( filename, self.year, self.issue_count )
self.issue, issue_start, issue_end = self.getIssueNumber(filename)
self.series, self.volume = self.getSeriesName(filename, issue_start)
self.year = self.getYear(filename, issue_end)
self.issue_count = self.getIssueCount(filename, issue_end)
self.remainder = self.getRemainder( filename, self.year, self.issue_count, issue_end )
if self.issue != "":
# strip off leading zeros

View File

@ -118,12 +118,20 @@ class FileRenamer:
new_name = re.sub("\[\s*[-:]*\s*\]", "", new_name )
new_name = re.sub("\{\s*[-:]*\s*\}", "", new_name )
# remove remove duplicate -, _,
new_name = re.sub("[-_]+\s+", "- ", new_name )
new_name = re.sub("(\s-)+", " -", new_name )
# remove duplicate spaces
new_name = u" ".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 = u" ".join(new_name.split())
if ext is None:
ext = os.path.splitext( filename )[1]
@ -132,7 +140,9 @@ class FileRenamer:
# 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(":", "-")
new_name = new_name.replace("?", "")
return new_name

View File

@ -162,6 +162,9 @@ class FileSelectionList(QWidget):
self.twList.currentItemChanged.connect( self.currentItemChangedCB )
if self.twList.rowCount() > 0:
# since on a removal, we select row 0, make sure callback occurs if we're already there
if self.twList.currentRow() == 0:
self.currentItemChangedCB( self.twList.currentItem(), None)
self.twList.selectRow(0)
else:
self.listCleared.emit()
@ -195,7 +198,13 @@ class FileSelectionList(QWidget):
progdialog.close()
if firstAdded is not None:
self.twList.selectRow(firstAdded)
else:
if len(pathlist) == 1 and os.path.isfile(pathlist[0]):
QMessageBox.information(self, self.tr("File Open"), self.tr("Selected file doesn't seem to be a comic archive."))
else:
QMessageBox.information(self, self.tr("File/Folder Open"), self.tr("No comic archives were found."))
self.twList.setSortingEnabled(True)
# Adjust column size

View File

@ -157,6 +157,7 @@ class IssueIdentifier:
search_keys['issue_number'] = None
search_keys['month'] = None
search_keys['year'] = None
search_keys['issue_count'] = None
if ca is None:
return
@ -166,6 +167,7 @@ class IssueIdentifier:
search_keys['issue_number'] = self.additional_metadata.issue
search_keys['year'] = self.additional_metadata.year
search_keys['month'] = self.additional_metadata.month
search_keys['issue_count'] = self.additional_metadata.issueCount
return search_keys
# see if the archive has any useful meta data for searching with
@ -211,6 +213,13 @@ class IssueIdentifier:
search_keys['month'] = internal_metadata.month
else:
search_keys['month'] = md_from_filename.month
if self.additional_metadata.issueCount is not None:
search_keys['issue_count'] = self.additional_metadata.issueCount
elif internal_metadata.issueCount is not None:
search_keys['issue_count'] = internal_metadata.issueCount
else:
search_keys['issue_count'] = md_from_filename.issueCount
return search_keys
@ -360,6 +369,8 @@ class IssueIdentifier:
self.log_msg( "Going to search for:" )
self.log_msg( "\tSeries: " + keys['series'] )
self.log_msg( "\tIssue : " + keys['issue_number'] )
if keys['issue_count'] is not None:
self.log_msg( "\tCount : " + str(keys['issue_count']) )
if keys['year'] is not None:
self.log_msg( "\tYear : " + str(keys['year']) )
if keys['month'] is not None:
@ -409,11 +420,6 @@ class IssueIdentifier:
if length_approved and publisher_approved and date_approved:
series_second_round_list.append(item)
# if we don't think it's an issue number 1, remove any series' that are one-shots
#if keys['issue_number'] not in [ '1', '0', '0.1' ]:
# #self.log_msg( "Removing one-shots" )
# series_second_round_list[:] = [x for x in series_second_round_list if not x['count_of_issues'] == 1]
self.log_msg( "Searching in " + str(len(series_second_round_list)) +" series" )
@ -423,7 +429,6 @@ class IssueIdentifier:
# now sort the list by name length
series_second_round_list.sort(key=lambda x: len(x['name']), reverse=False)
#--------new way---------------
#build a list of volume IDs
volume_id_list = list()
for series in series_second_round_list:
@ -446,47 +451,6 @@ class IssueIdentifier:
shortlist.append( (series, issue) )
break
#--------new way---------------
"""
#--vvvv---old way---------------
# Now we've got a list of series that we can dig into look for matching issue number
counter = 0
shortlist = []
for series in series_second_round_list:
if self.callback is not None:
self.callback( counter, len(series_second_round_list)*3)
counter += 1
self.log_msg( u"Fetching info for ID: {0} {1} ({2}) ...".format(
series['id'],
series['name'],
series['start_year']), newline=True )
try:
issue_list = comicVine.fetchIssuesByVolume( series['id'] )
except ComicVineTalkerException:
self.log_msg( "Network issue while searching for series details. Aborting...")
return []
for issue in issue_list:
num_s = IssueString(issue['issue_number']).asString()
# look for a matching issue number
if num_s.lower() == keys['issue_number'].lower():
# now, if we have an issue year key given, reject this one if not a match
month, year = comicVine.fetchIssueDate( issue['id'] )
if keys['year'] is not None:
if unicode(keys['year']) != unicode(year):
break
# found a matching issue number! add it to short list
shortlist.append( (series, issue) )
#--^^^^---old way---------------
"""
if keys['year'] is None:
self.log_msg( u"Found {0} series that have an issue #{1}".format(len(shortlist), keys['issue_number']) )
else:
@ -528,6 +492,7 @@ class IssueIdentifier:
match['series'] = u"{0} ({1})".format(series['name'], series['start_year'])
match['distance'] = score_item['score']
match['issue_number'] = keys['issue_number']
match['cv_issue_count'] = series['count_of_issues']
match['url_image_hash'] = score_item['hash']
match['issue_title'] = issue['name']
match['issue_id'] = issue['id']
@ -640,6 +605,19 @@ class IssueIdentifier:
if item['distance'] > best_score + self.min_score_distance:
self.match_list.remove(item)
# One more test for the case choosing limited series first issue vs a trade with the same cover:
# if we have a given issue count > 1 and the volume from CV has count==1, remove it from match list
if len(self.match_list) >= 2 and keys['issue_count'] is not None and keys['issue_count'] != 1:
new_list = list()
for match in self.match_list:
if match['cv_issue_count'] != 1:
new_list.append(match)
else:
self.log_msg("Removing volume {0} [{1}] from consideration (only 1 issue)".format(match['series'], match['volume_id']))
if len(new_list) > 0:
self.match_list = new_list
if len(self.match_list) == 1:
self.log_msg( u"--------------------------------------------------")
print_match(self.match_list[0])
@ -652,6 +630,7 @@ class IssueIdentifier:
self.log_msg( u"--------------------------------------------------")
self.search_result = self.ResultNoMatches
else:
# we've got multiple good matches:
self.log_msg( "More than one likley candiate." )
self.search_result = self.ResultMultipleGoodMatches
self.log_msg( u"--------------------------------------------------")

View File

@ -8,6 +8,7 @@ e.g.:
"0"
"-1"
"5AU"
"100-2"
"""
@ -34,31 +35,53 @@ import re
class IssueString:
def __init__(self, text):
if text is None:
self.num = None
self.suffix = ""
return
self.text = unicode(text)
#strip out non float-y stuff
tmp_num_str = re.sub('[^0-9.-]',"", self.text )
# break up the issue number string into 2 parts: the numeric and suffix string.
# ( assumes that the numeric portion is always first )
self.num = None
self.suffix = ""
if tmp_num_str == "":
self.num = None
self.suffix = self.text
if text is None:
return
text = unicode(text)
#skip the minus sign if it's first
if text[0] == '-':
start = 1
else:
if tmp_num_str.count(".") > 1:
#make sure it's a valid float or int.
parts = tmp_num_str.split('.')
self.num = float( parts[0] + '.' + parts[1] )
else:
self.num = float( tmp_num_str )
self.suffix = ""
parts = self.text.split(tmp_num_str)
if len( parts ) > 1 :
self.suffix = parts[1]
start = 0
# walk through the string, look for split point (the first non-numeric)
decimal_count = 0
for idx in range( start, len(text) ):
if text[idx] not in "0123456789.":
break
# special case: also split on second "."
if text[idx] == ".":
decimal_count += 1
if decimal_count > 1:
break
else:
idx = len(text)
# move trailing numeric decimal to suffix
# (only if there is other junk after )
if text[idx-1] == "." and len(text) != idx:
idx = idx -1
# if there is no numeric after the minus, make the minus part of the suffix
if idx == 1 and start == 1:
idx = 0
part1 = text[0:idx]
part2 = text[idx:len(text)]
if part1 != "":
self.num = float( part1 )
self.suffix = part2
#print "num: {0} suf: {1}".format(self.num, self.suffix)
def asString( self, pad = 0 ):
#return the float, left side zero-padded, with suffix attached

View File

@ -25,6 +25,11 @@ import os
import traceback
import ctversion
import utils
try:
import argparse
except:
pass
from genericmetadata import GenericMetadata
from comicarchive import MetaDataStyle
from versionchecker import VersionChecker
@ -175,6 +180,41 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
#print md
return md
def launch_script(self, scriptfile):
# we were given a script. special case for the args:
# 1. ignore everthing before the -S,
# 2. pass all the ones that follow (including script name) to the script
script_args = list()
for idx, arg in enumerate(sys.argv):
if arg in [ '-S', '--script']:
#found script!
script_args = sys.argv[idx+1:]
break
sys.argv = script_args
if not os.path.exists(scriptfile):
print "Can't find {0}".format( scriptfile )
else:
# I *think* this makes sense:
# assume the base name of the file is the module name
# add the folder of the given file to the python path
# import module
dirname = os.path.dirname(scriptfile)
module_name = os.path.splitext(os.path.basename(scriptfile))[0]
sys.path = [dirname] + sys.path
try:
script = __import__(module_name)
# Determine if the entry point exists before trying to run it
if "main" in dir(script):
script.main()
else:
print "Can't find entry point \"main()\" in module \"{0}\"".format( module_name )
except Exception as e:
print "Script raised an unhandled exception: ", e
print traceback.format_exc()
sys.exit(0)
def parseCmdLineArgs(self):
if platform.system() == "Darwin" and hasattr(sys, "frozen") and sys.frozen == 1:
@ -183,6 +223,15 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
else:
input_args = sys.argv[1:]
# first check if we're launching a script:
for n in range(len(input_args)):
if ( input_args[n] in [ "-S", "--script" ] and
n+1 < len(input_args)):
# insert a "--" which will cause getopt to ignore the remaining args
# so they will be passed to the script
input_args.insert(n+2, "--")
break
# parse command line options
try:
opts, args = getopt.getopt( input_args,
@ -258,7 +307,7 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
print "ComicTagger {0}: Copyright (c) 2012-2013 Anthony Beville".format(ctversion.version)
print "Distributed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)"
new_version = VersionChecker().getLatestVersion("", False)
if new_version is not None:
if new_version is not None and new_version != ctversion.version:
print "----------------------------------------"
print "New version available online: {0}".format(new_version)
print "----------------------------------------"
@ -289,40 +338,8 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
self.display_msg_and_quit( "Must choose only one action of print, delete, save, copy, rename, export, or run script", 1 )
if self.script is not None:
# we were given a script. special case for the args:
# 1. ignore everthing before the -S,
# 2. pass all the ones that follow (including script name) to the script
script_args = list()
for idx, arg in enumerate(sys.argv):
if arg in [ '-S', '--script']:
#found script!
script_args = sys.argv[idx+1:]
break
sys.argv = script_args
if not os.path.exists(self.script):
print "Can't find {0}".format( self.script )
else:
# I *think* this makes sense:
# assume the base name of the file is the module name
# add the folder of the given file to the python path
# import module
dirname = os.path.dirname(self.script)
module_name = os.path.splitext(os.path.basename(self.script))[0]
sys.path = [dirname] + sys.path
try:
script = __import__(module_name)
# Determine if the entry point exists before trying to run it
if "main" in dir(script):
script.main()
else:
print "Can't find entry point \"main()\" in module \"{0}\"".format( module_name )
except Exception as e:
print "Script raised an unhandled exception: ", e
print traceback.format_exc()
sys.exit(0)
self.launch_script( self.script )
if len(args) > 0:
if platform.system() == "Windows":
# no globbing on windows shell, so do it for them

View File

@ -220,7 +220,8 @@ class TaggerWindow( QtGui.QMainWindow):
self.settings.send_usage_stats = True
self.settings.ask_about_usage_stats = False
self.checkLatestVersionOnline()
if self.settings.check_for_new_version:
self.checkLatestVersionOnline()
def sigint_handler(self, *args):
# defer the actual close in the app loop thread
@ -330,7 +331,7 @@ class TaggerWindow( QtGui.QMainWindow):
self.actionApplyCBLTransform.setStatusTip( 'Modify tags specifically for CBL format' )
self.actionApplyCBLTransform.triggered.connect( self.applyCBLTransform )
#self.actionClearEntryForm.setShortcut( 'Ctrl+C' )
self.actionClearEntryForm.setShortcut( 'Ctrl+Shift+C' )
self.actionClearEntryForm.setStatusTip( 'Clear all the data on the screen' )
self.actionClearEntryForm.triggered.connect( self.clearForm )
@ -926,14 +927,17 @@ class TaggerWindow( QtGui.QMainWindow):
else:
QtGui.QMessageBox.information(self, self.tr("Online Search"), self.tr("Need to enter a series name to search."))
return
year = str(self.lePubYear.text()).strip()
if year == "":
year = None
issue_count = str(self.leIssueCount.text()).strip()
if issue_count == "":
issue_count = None
cover_index_list = self.metadata.getCoverPageIndexList()
selector = VolumeSelectionWindow( self, series_name, issue_number, year, cover_index_list, self.comic_archive, self.settings, autoselect )
selector = VolumeSelectionWindow( self, series_name, issue_number, year, issue_count, cover_index_list, self.comic_archive, self.settings, autoselect )
title = "Search: '" + series_name + "' - "
selector.setWindowTitle( title + "Select Series")

View File

@ -21,6 +21,44 @@
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="QWidget" name="archiveCoverContainer">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
</widget>
</item>
<item row="0" column="4">
<widget class="QWidget" name="testCoverContainer">
<property name="minimumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
</widget>
</item>
<item row="0" column="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QProgressBar" name="progressBar">
@ -66,50 +104,6 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lblArchive">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="lblTest">
<property name="minimumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>110</width>
<height>165</height>
</size>
</property>
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>

View File

@ -126,9 +126,13 @@ def removearticles( text ):
# now get rid of some other junk
newText = newText.replace(":", "")
newText = newText.replace(".", "")
newText = newText.replace(",", "")
newText = newText.replace("-", " ")
# since the CV api changed, searches for series names with periods
# now explicity require the period to be in the search key,
# so the line below is removed (for now)
#newText = newText.replace(".", "")
return newText

View File

@ -87,7 +87,7 @@ class IdentifyThread( QtCore.QThread):
class VolumeSelectionWindow(QtGui.QDialog):
def __init__(self, parent, series_name, issue_number, year, cover_index_list, comic_archive, settings, autoselect=False):
def __init__(self, parent, series_name, issue_number, year, issue_count, cover_index_list, comic_archive, settings, autoselect=False):
super(VolumeSelectionWindow, self).__init__(parent)
uic.loadUi(ComicTaggerSettings.getUIFile('volumeselectionwindow.ui' ), self)
@ -108,6 +108,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
self.series_name = series_name
self.issue_number = issue_number
self.year = year
self.issue_count = issue_count
self.volume_id = 0
self.comic_archive = comic_archive
self.immediate_autoselect = autoselect
@ -161,6 +162,7 @@ class VolumeSelectionWindow(QtGui.QDialog):
md.series = self.series_name
md.issue = self.issue_number
md.year = self.year
md.issueCount = self.issue_count
self.ii.setAdditionalMetadata( md )
self.ii.onlyUseAdditionalMetaData = True

View File

@ -1,6 +1,6 @@
#PYINSTALLER_CMD := VERSIONER_PYTHON_PREFER_32_BIT=yes arch -i386 python $(HOME)/pyinstaller-2.0/pyinstaller.py
PYINSTALLER_CMD := python $(HOME)/pyinstaller-2.0/pyinstaller.py
TAGGER_BASE := $(HOME)/Dropbox/tagger/comictagger
TAGGER_BASE ?= $(HOME)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
APP_NAME := ComicTagger

View File

@ -1,3 +1,25 @@
---------------------------------
1.1.10-beta - 30-Jan-2014
---------------------------------
* Updated series query to match changes on Comic Vine side
* Added a message when not able to open a file or folder
* Fixed an issue where series names with periods would fail on search
* Other misc bug fixes
---------------------------------
1.1.9-beta - 8-May-2013
---------------------------------
* Filename parser and identification enhancements
* Misc bug fixes
---------------------------------
1.1.8-beta - 21-Apr-2013
---------------------------------
* Handle occasional error 500 from Comic Vine by retrying a few times
* Nicer handling of colon (":") in file rename
* Fixed command-line option parsing issue for add-on scripts
* Misc bug fixes
---------------------------------
1.1.7-beta - 12-Apr-2013
---------------------------------

151
scripts/name_fixer.py Executable file
View File

@ -0,0 +1,151 @@
#!/usr/bin/python
"""
fix the comic file names using a list of transforms
"""
"""
Copyright 2013 Anthony Beville
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
import sys
import os
import re
import argparse
import json
from comictaggerlib.comicarchive import *
from comictaggerlib.settings import *
from comictaggerlib.filerenamer import *
import comictaggerlib.utils
def parse_args():
input_args = sys.argv[1:]
parser = argparse.ArgumentParser(description='a script to rename comic files')
parser.add_argument('-t', '--transforms', metavar='xformfile', help="the file with transforms")
parser.add_argument('-n', '--noconfirm', action='store_true', help="don't confirm before rename")
parser.add_argument('paths', metavar='PATH', type=str, nargs='+', help='path to look for comic files')
parsed_args = parser.parse_args(input_args)
return parsed_args
def caclulate_rename(ca, md, settings):
new_ext = None # default
if settings.rename_extension_based_on_archive:
if ca.isZip():
new_ext = ".cbz"
elif ca.isRar():
new_ext = ".cbr"
renamer = FileRenamer( md )
renamer.setTemplate( "%series% V%volume% #%issue% (of %issuecount%) (%year%) %scaninfo%" )
renamer.setIssueZeroPadding( 0 )
renamer.setSmartCleanup( settings.rename_use_smart_string_cleanup )
return renamer.determineName( ca.path, ext=new_ext )
def perform_rename(filelist):
for old_name,new_name in filelist:
folder = os.path.dirname( os.path.abspath( old_name ) )
new_abs_path = utils.unique_file( os.path.join( folder, new_name ) )
os.rename( old_name, new_abs_path )
print u"renamed '{0}' -> '{1}'".format(os.path.basename(old_name), new_name)
def main():
default_xform_list = [
[ "^2000AD$", "2000 AD" ],
[ "^G\.{0,1}I\.{0,1}Joe$", "G.I. Joe" ],
]
utils.fix_output_encoding()
settings = ComicTaggerSettings()
style = MetaDataStyle.CIX
parsed_args = parse_args()
#parsed_args.noconfirm
if parsed_args.transforms is not None:
print "Reading in transforms from:", parsed_args.transforms
json_data=open(parsed_args.transforms).read()
data = json.loads(json_data)
xform_list = data['xforms']
else:
xform_list = default_xform_list
#pprint( xform_list, indent=4)
filelist = utils.get_recursive_filelist( parsed_args.paths )
#first find all comics
print "reading in all comics..."
comic_list = []
max_name_len = 2
fmt_str = ""
for filename in filelist:
ca = ComicArchive(filename, settings )
# do we care if it already has metadata?
if ca.seemsToBeAComicArchive() and not ca.hasMetadata( style ):
comic_list.append(ca)
max_name_len = max ( max_name_len, len(filename))
fmt_str = u"{{0:{0}}}".format(max_name_len)
print >> sys.stderr, fmt_str.format( filename ) + "\r",
sys.stderr.flush()
print >> sys.stderr, fmt_str.format( "" )
print "Found {0} comics.".format( len(comic_list))
modify_list = list()
# walk through the comic list fix the filenames
for ca in comic_list:
# 1. parse the filename into a MD object
md = ca.metadataFromFilename()
# 2. walk thru list of transforms
if md.series is not None and md.series != "":
for pattern, replacement in xform_list:
# apply each transform
new_series = re.sub( pattern, replacement, md.series )
if new_series != md.series:
md.series = new_series
new_name = caclulate_rename(ca, md, settings)
#found a match. add to proposed list, and bail on this file
modify_list.append( (ca.path, new_name ) )
break
print "{0} filenames to modify".format(len(modify_list))
if len(modify_list) > 0:
if parsed_args.noconfirm:
print "Not confirming before rename"
else:
for old_name, new_name in modify_list:
print u"'{0}' -> '{1}'".format(os.path.basename(old_name), new_name)
i = raw_input("Do you want to proceed with rename? [y/N] ")
if i.lower() not in ('y', 'yes'):
print "exiting without rename."
sys.exit(0)
perform_rename(modify_list)
if __name__ == '__main__':
main()

8
scripts/xforms Normal file
View File

@ -0,0 +1,8 @@
{
"_comment": "This file contains JSON data. Any backslashes should be escaped with another backslash",
"xforms":
[
[ "^2000AD$", "2000 AD" ],
[ "^G\\.{0,1}I\\.{0,1}Joe$", "G.I. Joe" ]
]
}

View File

@ -4,13 +4,25 @@ Features
Rename dialog:
check-box for rows?
manual edit the preview?
Maybe replace configparser -- seems to be causing all sorts of problems
Feature Requests:
Move CBR to other folder after conversion to ZIP
NO - AUto-rename on auto-tag
NO- Re-zip (to remove compression)
Pre-process series name before identification
(using a list of regex transforms)
(GC #28) Save auto-tag options
(GC #24) Multiple options for -t i.e. "-t cr,cbl"
(GC #18 ) Option for handling colon in rename
(GC #31 ) Specify CV Series ID for auto-tag
Re-org - move to new folder based on template
Denied Requests (for now):
Auto-rename on auto-tag
Re-zip (to remove compression)
Selective fields on CLI print (use -m option. Maybe internally remove all but specified fields in MD object before print )
Docs:
Auto-Tagging Tips:
@ -20,18 +32,18 @@ Docs:
Bugs
-----------------------------------------------------
Non-numeric issues?? Filename parsing... can we only rely on '#'??
Zip flakes out when filename differs from index (or whatever) i.e "\" vs "/". Python issue
-----------------------------------------------------
Big Future Features
-----------------------------------------------------
Support for ACBF metatdata in CBZ
GCD scraper or DB reader
Batch Edit
(GC #29) Batch Edit
Form Mode: Single vs Batch
-----------------------------------------------------

View File

@ -2,7 +2,7 @@
# rm, cp, grep, cut, cat
HOMEPATH ?= $(HOME)
TAGGER_BASE:= $(HOMEPATH)/Dropbox/tagger/comictagger
TAGGER_BASE?= $(HOMEPATH)/Dropbox/tagger/comictagger
TAGGER_SRC := $(TAGGER_BASE)/comictaggerlib
DIST_DIR := $(TAGGER_BASE)\windows\dist
NSIS_CMD := "C:\Program Files (x86)\NSIS\makensis.exe"