Compare commits
49 Commits
1.1.1-beta
...
1.1.2a-bet
Author | SHA1 | Date | |
---|---|---|---|
188024c2db | |||
324b56a623 | |||
782d424392 | |||
cf63bfda9d | |||
903d4c647c | |||
407b83fe90 | |||
27edc80d2b | |||
01f48f8b91 | |||
527e690170 | |||
d100572aa4 | |||
42640c4ad5 | |||
a61972e503 | |||
464e147223 | |||
8759784561 | |||
ee5b4a689e | |||
71ccf1eea8 | |||
a9ee7c463b | |||
6f683a71c7 | |||
24b192b22c | |||
b6b1a4737f | |||
00202cc865 | |||
235524b06d | |||
8a7f822970 | |||
ff3f048bb4 | |||
abda202f32 | |||
2d4ac84de0 | |||
86732e7827 | |||
693b5b1978 | |||
e3d3ecfd31 | |||
ce6b81ab73 | |||
501365b5a3 | |||
c6741d4392 | |||
42feae53dd | |||
c65695b8dc | |||
4da71e262b | |||
c519fd33d5 | |||
07ef0211b9 | |||
c45b56a5b6 | |||
6f27fc7669 | |||
4530ac017c | |||
400fe6efa3 | |||
ac7a12d18d | |||
c2ff11fab7 | |||
34019ff338 | |||
176bc43888 | |||
2e290c4c74 | |||
74a374d46b | |||
58f5f10c78 | |||
7d8ed954a9 |
@ -1,2 +1,4 @@
|
||||
include readme.txt
|
||||
include README.txt
|
||||
include release_notes.txt
|
||||
include requirements.txt
|
||||
recursive-include scripts *.py *.txt
|
71
Makefile
71
Makefile
@ -7,51 +7,54 @@ all: clean
|
||||
|
||||
clean:
|
||||
rm -rf *~ *.pyc *.pyo
|
||||
cd comictagger; rm -f *~ *.pyc *.pyo
|
||||
sudo rm -rf dist MANIFEST
|
||||
rm -rf scripts/*.pyc
|
||||
cd comictaggerlib; rm -f *~ *.pyc *.pyo
|
||||
rm -rf dist MANIFEST
|
||||
rm -rf *.deb
|
||||
rm -rf logdict*.log
|
||||
make -C mac clean
|
||||
make -C windows clean
|
||||
|
||||
zip:
|
||||
cd release; \
|
||||
rm -rf *zip comictagger-src-$(VERSION_STR) ; \
|
||||
svn export https://comictagger.googlecode.com/svn/trunk/ comictagger-src-$(VERSION_STR); \
|
||||
zip -r comictagger-src-$(VERSION_STR).zip comictagger-src-$(VERSION_STR); \
|
||||
rm -rf comictagger-src-$(VERSION_STR)
|
||||
|
||||
@echo When satisfied with release, do this:
|
||||
@echo make svn_tag
|
||||
rm -rf build
|
||||
|
||||
pydist:
|
||||
python setup.py sdist --formats=gztar,zip
|
||||
mkdir -p release
|
||||
rm -f release/*.zip
|
||||
python setup.py sdist --formats=zip #,gztar
|
||||
mv dist/comictagger-$(VERSION_STR).zip release
|
||||
@echo When satisfied with release, do this:
|
||||
@echo make svn_tag
|
||||
|
||||
remove_test_install:
|
||||
sudo rm -rf /usr/local/bin/comictagger.py
|
||||
sudo rm -rf /usr/local/lib/python2.7/dist-packages/comictagger*
|
||||
|
||||
deb:
|
||||
fpm -s python -t deb \
|
||||
-n 'comictagger' \
|
||||
--category 'utilities' \
|
||||
--maintainer 'comictagger@gmail.com' \
|
||||
--after-install debian_scripts/after_install.sh \
|
||||
--before-remove debian_scripts/before_remove.sh \
|
||||
-d 'python >= 2.6' \
|
||||
-d 'python < 2.8' \
|
||||
-d 'python-imaging >= 1.1.7' \
|
||||
-d 'python-bs4 >= 4.1' \
|
||||
setup.py
|
||||
|
||||
# For now, don't require PyQt, since command-line is available without it
|
||||
#-d 'python-qt4 >= 4.8'
|
||||
|
||||
svn_tag:
|
||||
svn copy https://comictagger.googlecode.com/svn/trunk \
|
||||
https://comictagger.googlecode.com/svn/tags/$(VERSION_STR) -m "Release $(VERSION_STR)"
|
||||
|
||||
#deb:
|
||||
# fpm -s python -t deb \
|
||||
# -n 'comictagger' \
|
||||
# --category 'utilities' \
|
||||
# --maintainer 'comictagger@gmail.com' \
|
||||
# --after-install debian_scripts/after_install.sh \
|
||||
# --before-remove debian_scripts/before_remove.sh \
|
||||
# -d 'python >= 2.6' \
|
||||
# -d 'python < 2.8' \
|
||||
# -d 'python-imaging' \
|
||||
# -d 'python-bs4' \
|
||||
# --deb-suggests 'rar' \
|
||||
# --deb-suggests 'unrar-free' \
|
||||
# --python-install-bin /usr/share/comictagger \
|
||||
# --python-install-lib /usr/share/comictagger \
|
||||
# setup.py
|
||||
#
|
||||
# # For now, don't require PyQt, since command-line is available without it
|
||||
# #-d 'python-qt4 >= 4.8'
|
||||
|
||||
upload:
|
||||
$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Source" -l Featured,Type-Source -u beville -w $(PASSWORD) "release/comictagger-src-$(VERSION_STR).zip"
|
||||
$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Source" -l Featured,Type-Source -u beville -w $(PASSWORD) "release/comictagger-$(VERSION_STR).zip"
|
||||
$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Mac OS X" -l Featured,Type-Archive -u beville -w $(PASSWORD) "release/ComicTagger-$(VERSION_STR).dmg"
|
||||
$(UPLOAD_TOOL) -p comictagger -s "ComicTagger $(VERSION_STR) Windows" -l Featured,Type-Installer -u beville -w $(PASSWORD) "release/ComicTagger v$(VERSION_STR).exe"
|
||||
python setup.py register
|
||||
|
||||
svn_tag:
|
||||
svn copy https://comictagger.googlecode.com/svn/trunk \
|
||||
https://comictagger.googlecode.com/svn/tags/$(VERSION_STR) -m "Release $(VERSION_STR)"
|
||||
|
||||
|
@ -7,16 +7,16 @@ Features:
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack, with more planned).
|
||||
* Reads and writes RAR, Zip, and folder archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations. For example, to scrape and tag a folder, just one line
|
||||
ComicTagger -s -o -f -t cr -v -i --nooverwrite *.cb?
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations. For example, to recusrively scrape and tag all archives in a folder
|
||||
comictagger.py -R -s -o -f -t cr -v -i --nooverwrite /path/to/comics/
|
||||
|
||||
For details, screenshots, release notes, and more, visit http://code.google.com/p/comictagger/
|
||||
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* python imaging (PIL) >= 1.1.7
|
||||
* python imaging (PIL) >= 1.1.6
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
Optional requirement (for GUI):
|
||||
@ -27,4 +27,4 @@ Install and run:
|
||||
|
||||
* ComicTagger can be run directly from this directory, using the launcher script "comictagger.py"
|
||||
|
||||
* To install on your system use: "python setup.py install". Make note in the output where comictagger.py goes!
|
||||
* To install on your system use: "python setup.py install". Take note in the output where comictagger.py goes!
|
@ -1,4 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
from comictaggerlib.main import ctmain
|
||||
|
||||
ctmain()
|
||||
if __name__ == '__main__':
|
||||
ctmain()
|
@ -26,7 +26,7 @@ from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
|
||||
from imagefetcher import ImageFetcher
|
||||
from settings import ComicTaggerSettings
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from comicvinetalker import ComicVineTalker
|
||||
import utils
|
||||
|
@ -33,8 +33,8 @@ import locale
|
||||
filename_encoding = sys.getfilesystemencoding()
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from options import Options, MetaDataStyle
|
||||
from comicarchive import ComicArchive
|
||||
from options import Options
|
||||
from comicarchive import ComicArchive, MetaDataStyle
|
||||
from issueidentifier import IssueIdentifier
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
@ -115,9 +115,7 @@ def display_match_set_for_choice( label, match_set, opts, settings ):
|
||||
i = int(i) - 1
|
||||
# save the data!
|
||||
# we know at this point, that the file is all good to go
|
||||
ca = ComicArchive( match_set.filename )
|
||||
if settings.rar_exe_path != "":
|
||||
ca.setExternalRarProgram( settings.rar_exe_path )
|
||||
ca = ComicArchive( match_set.filename, settings )
|
||||
md = create_local_metadata( opts, ca, ca.hasMetadata(opts.data_style) )
|
||||
cv_md = actual_issue_data_fetch(match_set.matches[int(i)], settings)
|
||||
md.overlay( cv_md )
|
||||
@ -181,7 +179,8 @@ def cli_mode( opts, settings ):
|
||||
match_results = OnlineMatchResults()
|
||||
|
||||
for f in opts.file_list:
|
||||
f = f.decode(filename_encoding, 'replace')
|
||||
if type(f) == str:
|
||||
f = f.decode(filename_encoding, 'replace')
|
||||
process_file_cli( f, opts, settings, match_results )
|
||||
sys.stdout.flush()
|
||||
|
||||
@ -210,9 +209,7 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
|
||||
batch_mode = len( opts.file_list ) > 1
|
||||
|
||||
ca = ComicArchive(filename)
|
||||
if settings.rar_exe_path != "":
|
||||
ca.setExternalRarProgram( settings.rar_exe_path )
|
||||
ca = ComicArchive(filename, settings)
|
||||
|
||||
if not os.path.lexists( filename ):
|
||||
print >> sys.stderr,"Cannot find "+ filename
|
||||
@ -241,7 +238,7 @@ def process_file_cli( filename, opts, settings, match_results ):
|
||||
brief = ""
|
||||
|
||||
if batch_mode:
|
||||
brief = "{0}: ".format(filename)
|
||||
brief = u"{0}: ".format(filename)
|
||||
|
||||
if ca.isZip(): brief += "ZIP archive "
|
||||
elif ca.isRar(): brief += "RAR archive "
|
||||
|
@ -40,7 +40,6 @@ sys.path.insert(0, os.path.abspath(".") )
|
||||
import UnRAR2
|
||||
from UnRAR2.rar_exceptions import *
|
||||
|
||||
from options import Options, MetaDataStyle
|
||||
from comicinfoxml import ComicInfoXml
|
||||
from comicbookinfo import ComicBookInfo
|
||||
from comet import CoMet
|
||||
@ -48,6 +47,12 @@ from genericmetadata import GenericMetadata, PageType
|
||||
from filenameparser import FileNameParser
|
||||
from settings import ComicTaggerSettings
|
||||
|
||||
class MetaDataStyle:
|
||||
CBI = 0
|
||||
CIX = 1
|
||||
COMET = 2
|
||||
name = [ 'ComicBookLover', 'ComicRack', 'CoMet' ]
|
||||
|
||||
class ZipArchiver:
|
||||
|
||||
def __init__( self, path ):
|
||||
@ -227,9 +232,9 @@ class ZipArchiver:
|
||||
class RarArchiver:
|
||||
|
||||
devnull = None
|
||||
def __init__( self, path ):
|
||||
def __init__( self, path, settings ):
|
||||
self.path = path
|
||||
self.rar_exe_path = None
|
||||
self.settings = settings
|
||||
|
||||
if RarArchiver.devnull is None:
|
||||
RarArchiver.devnull = open(os.devnull, "w")
|
||||
@ -252,7 +257,7 @@ class RarArchiver:
|
||||
|
||||
def setArchiveComment( self, comment ):
|
||||
|
||||
if self.rar_exe_path is not None:
|
||||
if self.settings.rar_exe_path is not None:
|
||||
try:
|
||||
# write comment to temp file
|
||||
tmp_fd, tmp_name = tempfile.mkstemp()
|
||||
@ -263,7 +268,7 @@ class RarArchiver:
|
||||
working_dir = os.path.dirname( os.path.abspath( self.path ) )
|
||||
|
||||
# use external program to write comment to Rar archive
|
||||
subprocess.call([self.rar_exe_path, 'c', '-w' + working_dir , '-c-', '-z' + tmp_name, self.path],
|
||||
subprocess.call([self.settings.rar_exe_path, 'c', '-w' + working_dir , '-c-', '-z' + tmp_name, self.path],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -321,7 +326,7 @@ class RarArchiver:
|
||||
|
||||
def writeArchiveFile( self, archive_file, data ):
|
||||
|
||||
if self.rar_exe_path is not None:
|
||||
if self.settings.rar_exe_path is not None:
|
||||
try:
|
||||
tmp_folder = tempfile.mkdtemp()
|
||||
|
||||
@ -336,7 +341,7 @@ class RarArchiver:
|
||||
f.close()
|
||||
|
||||
# use external program to write file to Rar archive
|
||||
subprocess.call([self.rar_exe_path, 'a', '-w' + working_dir ,'-c-', '-ep', self.path, tmp_file],
|
||||
subprocess.call([self.settings.rar_exe_path, 'a', '-w' + working_dir ,'-c-', '-ep', self.path, tmp_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -352,10 +357,10 @@ class RarArchiver:
|
||||
return False
|
||||
|
||||
def removeArchiveFile( self, archive_file ):
|
||||
if self.rar_exe_path is not None:
|
||||
if self.settings.rar_exe_path is not None:
|
||||
try:
|
||||
# use external program to remove file from Rar archive
|
||||
subprocess.call([self.rar_exe_path, 'd','-c-', self.path, archive_file],
|
||||
subprocess.call([self.settings.rar_exe_path, 'd','-c-', self.path, archive_file],
|
||||
startupinfo=self.startupinfo,
|
||||
stdout=RarArchiver.devnull)
|
||||
|
||||
@ -503,11 +508,12 @@ class ComicArchive:
|
||||
class ArchiveType:
|
||||
Zip, Rar, Folder, Unknown = range(4)
|
||||
|
||||
def __init__( self, path ):
|
||||
def __init__( self, path, settings ):
|
||||
self.path = path
|
||||
self.ci_xml_filename = 'ComicInfo.xml'
|
||||
self.comet_default_filename = 'CoMet.xml'
|
||||
self.resetCache()
|
||||
self.settings = settings
|
||||
|
||||
if self.zipTest():
|
||||
self.archive_type = self.ArchiveType.Zip
|
||||
@ -515,7 +521,7 @@ class ComicArchive:
|
||||
|
||||
elif self.rarTest():
|
||||
self.archive_type = self.ArchiveType.Rar
|
||||
self.archiver = RarArchiver( self.path )
|
||||
self.archiver = RarArchiver( self.path, settings )
|
||||
|
||||
elif os.path.isdir( self.path ):
|
||||
self.archive_type = self.ArchiveType.Folder
|
||||
@ -527,7 +533,7 @@ class ComicArchive:
|
||||
if ComicArchive.logo_data is None:
|
||||
fname = ComicTaggerSettings.getGraphic('nocover.png')
|
||||
with open(fname, 'rb') as fd:
|
||||
ComicArchive.logo_data = fd.read()
|
||||
ComicArchive.logo_data = fd.read()
|
||||
|
||||
# Clears the cached data
|
||||
def resetCache( self ):
|
||||
@ -548,10 +554,6 @@ class ComicArchive:
|
||||
def rename( self, path ):
|
||||
self.path = path
|
||||
self.archiver.path = path
|
||||
|
||||
def setExternalRarProgram( self, rar_exe_path ):
|
||||
if self.isRar():
|
||||
self.archiver.rar_exe_path = rar_exe_path
|
||||
|
||||
def zipTest( self ):
|
||||
return zipfile.is_zipfile( self.path )
|
||||
@ -578,7 +580,7 @@ class ComicArchive:
|
||||
if self.archive_type == self.ArchiveType.Unknown :
|
||||
return False
|
||||
|
||||
elif check_rar_status and self.isRar() and self.archiver.rar_exe_path is None:
|
||||
elif check_rar_status and self.isRar() and self.settings.rar_exe_path is None:
|
||||
return False
|
||||
|
||||
elif not os.access(self.path, os.W_OK):
|
||||
@ -603,7 +605,7 @@ class ComicArchive:
|
||||
ext = os.path.splitext(self.path)[1].lower()
|
||||
|
||||
if (
|
||||
( self.isZip() or self.isRar() or self.isFolder() )
|
||||
( self.isZip() or self.isRar() ) #or self.isFolder() )
|
||||
and
|
||||
( self.getNumberOfPages() > 2)
|
||||
|
||||
|
@ -89,36 +89,21 @@ class ComicInfoXml:
|
||||
if md_entry is not None:
|
||||
ET.SubElement(root, cix_entry).text = u"{0}".format(md_entry)
|
||||
|
||||
assign( 'Title', md.title )
|
||||
assign( 'Series', md.series )
|
||||
assign( 'Number', md.issue )
|
||||
assign( 'Title', md.title )
|
||||
assign( 'Count', md.issueCount )
|
||||
assign( 'Volume', md.volume )
|
||||
assign( 'AlternateSeries', md.alternateSeries )
|
||||
assign( 'AlternateNumber', md.alternateNumber )
|
||||
assign( 'StoryArc', md.storyArc )
|
||||
assign( 'SeriesGroup', md.seriesGroup )
|
||||
assign( 'AlternateCount', md.alternateCount )
|
||||
assign( 'Summary', md.comments )
|
||||
assign( 'Notes', md.notes )
|
||||
assign( 'Year', md.year )
|
||||
assign( 'Month', md.month )
|
||||
assign( 'Publisher', md.publisher )
|
||||
assign( 'Imprint', md.imprint )
|
||||
assign( 'Genre', md.genre )
|
||||
assign( 'Web', md.webLink )
|
||||
assign( 'PageCount', md.pageCount )
|
||||
assign( 'Format', md.format )
|
||||
assign( 'LanguageISO', md.language )
|
||||
assign( 'Manga', md.manga )
|
||||
assign( 'Characters', md.characters )
|
||||
assign( 'Teams', md.teams )
|
||||
assign( 'Locations', md.locations )
|
||||
assign( 'ScanInformation', md.scanInfo )
|
||||
assign( 'StoryArc', md.storyArc )
|
||||
assign( 'SeriesGroup', md.seriesGroup )
|
||||
assign( 'AgeRating', md.maturityRating )
|
||||
|
||||
if md.blackAndWhite is not None and md.blackAndWhite:
|
||||
ET.SubElement(root, 'BlackAndWhite').text = "Yes"
|
||||
assign( 'Day', md.day )
|
||||
|
||||
# need to specially process the credits, since they are structured differently than CIX
|
||||
credit_writer_list = list()
|
||||
@ -181,7 +166,23 @@ class ComicInfoXml:
|
||||
if len( credit_editor_list ) > 0:
|
||||
node = ET.SubElement(root, 'Editor')
|
||||
node.text = utils.listToString( credit_editor_list )
|
||||
|
||||
|
||||
assign( 'Publisher', md.publisher )
|
||||
assign( 'Imprint', md.imprint )
|
||||
assign( 'Genre', md.genre )
|
||||
assign( 'Web', md.webLink )
|
||||
assign( 'PageCount', md.pageCount )
|
||||
assign( 'LanguageISO', md.language )
|
||||
assign( 'Format', md.format )
|
||||
assign( 'AgeRating', md.maturityRating )
|
||||
if md.blackAndWhite is not None and md.blackAndWhite:
|
||||
ET.SubElement(root, 'BlackAndWhite').text = "Yes"
|
||||
assign( 'Manga', md.manga )
|
||||
assign( 'Characters', md.characters )
|
||||
assign( 'Teams', md.teams )
|
||||
assign( 'Locations', md.locations )
|
||||
assign( 'ScanInformation', md.scanInfo )
|
||||
|
||||
# loop and add the page entries under pages node
|
||||
if len( md.pages ) > 0:
|
||||
pages_node = ET.SubElement(root, 'Pages')
|
||||
@ -229,6 +230,7 @@ class ComicInfoXml:
|
||||
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' )
|
||||
|
@ -361,7 +361,7 @@ class ComicVineCacher:
|
||||
row = cur.fetchone()
|
||||
|
||||
details = dict()
|
||||
if row[0] is None :
|
||||
if row is None or row[0] is None :
|
||||
details['image_url'] = None
|
||||
details['thumb_image_url'] = None
|
||||
details['publish_month'] = None
|
||||
|
@ -26,7 +26,7 @@ from PyQt4 import uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
from imagefetcher import ImageFetcher
|
||||
from pageloader import PageLoader
|
||||
|
@ -1,3 +1,3 @@
|
||||
# This file should contan only these comments, and the line below.
|
||||
# Used by packaging makefiles and app
|
||||
version="1.1.1-beta"
|
||||
version="1.1.2a-beta"
|
||||
|
@ -30,8 +30,11 @@ import os
|
||||
from urllib import unquote
|
||||
|
||||
class FileNameParser:
|
||||
def fixSpaces( self, string ):
|
||||
placeholders = ['[-_]',' +']
|
||||
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()
|
||||
@ -151,7 +154,7 @@ class FileNameParser:
|
||||
# finding it easier
|
||||
|
||||
filename = filename.replace("+", " ")
|
||||
tmpstr = self.fixSpaces(filename)
|
||||
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("#", " ")
|
||||
|
@ -29,8 +29,8 @@ from PyQt4.QtCore import pyqtSignal
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from comicarchive import ComicArchive
|
||||
from comicarchive import MetaDataStyle
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from options import MetaDataStyle
|
||||
import utils
|
||||
|
||||
class FileTableWidget( QTableWidget ):
|
||||
@ -159,20 +159,8 @@ class FileSelectionList(QWidget):
|
||||
self.listCleared.emit()
|
||||
|
||||
def addPathList( self, pathlist ):
|
||||
filelist = []
|
||||
for p in pathlist:
|
||||
# if path is a folder, walk it recursivly, and all files underneath
|
||||
if type(p) == str:
|
||||
#make sure string is unicode
|
||||
filename_encoding = sys.getfilesystemencoding()
|
||||
p = p.decode(filename_encoding, 'replace')
|
||||
|
||||
if os.path.isdir( unicode(p)):
|
||||
for root,dirs,files in os.walk( unicode(p) ):
|
||||
for f in files:
|
||||
filelist.append(os.path.join(root,unicode(f)))
|
||||
else:
|
||||
filelist.append(unicode(p))
|
||||
|
||||
filelist = utils.get_recursive_filelist( pathlist )
|
||||
|
||||
# we now have a list of files to add
|
||||
|
||||
@ -231,12 +219,9 @@ class FileSelectionList(QWidget):
|
||||
if self.isListDupe(path):
|
||||
return None
|
||||
|
||||
ca = ComicArchive( path )
|
||||
if self.settings.rar_exe_path != "":
|
||||
ca.setExternalRarProgram( self.settings.rar_exe_path )
|
||||
ca = ComicArchive( path, self.settings )
|
||||
|
||||
if ca.seemsToBeAComicArchive() :
|
||||
|
||||
row = self.twList.rowCount()
|
||||
self.twList.insertRow( row )
|
||||
|
||||
|
@ -63,6 +63,7 @@ class GenericMetadata:
|
||||
self.publisher = None
|
||||
self.month = None
|
||||
self.year = None
|
||||
self.day = None
|
||||
self.issueCount = None
|
||||
self.volume = None
|
||||
self.genre = None
|
||||
@ -125,6 +126,7 @@ class GenericMetadata:
|
||||
assign( "issueCount", new_md.issueCount )
|
||||
assign( "title", new_md.title )
|
||||
assign( "publisher", new_md.publisher )
|
||||
assign( "day", new_md.day )
|
||||
assign( "month", new_md.month )
|
||||
assign( "year", new_md.year )
|
||||
assign( "volume", new_md.volume )
|
||||
@ -256,8 +258,9 @@ class GenericMetadata:
|
||||
add_attr_string( "issueCount" )
|
||||
add_attr_string( "title" )
|
||||
add_attr_string( "publisher" )
|
||||
add_attr_string( "month" )
|
||||
add_attr_string( "year" )
|
||||
add_attr_string( "month" )
|
||||
add_attr_string( "day" )
|
||||
add_attr_string( "volume" )
|
||||
add_attr_string( "volumeCount" )
|
||||
add_attr_string( "genre" )
|
||||
|
@ -82,6 +82,7 @@ class IssueIdentifier:
|
||||
self.coverUrlCallback = None
|
||||
self.search_result = self.ResultNoMatches
|
||||
self.cover_page_index = 0
|
||||
self.cancel = False
|
||||
|
||||
def setScoreMinThreshold( self, thresh ):
|
||||
self.min_score_thresh = thresh
|
||||
@ -223,7 +224,7 @@ class IssueIdentifier:
|
||||
if newline:
|
||||
self.output_function("\n")
|
||||
|
||||
def getIssueCoverMatchScore( self, comicVine, issue_id, localCoverHashList, useRemoteAlternates = False , use_log=True):
|
||||
def getIssueCoverMatchScore( self, comicVine, issue_id, localCoverHashList, useRemoteAlternates = False , useLog=True):
|
||||
|
||||
# localHashes is a list of pre-calculated hashs.
|
||||
# useRemoteAlternates - indicates to use alternate covers from CV
|
||||
@ -279,9 +280,9 @@ class IssueIdentifier:
|
||||
if self.cancel == True:
|
||||
raise IssueIdentifierCancelled
|
||||
|
||||
if use_log and useRemoteAlternates:
|
||||
if useLog and useRemoteAlternates:
|
||||
self.log_msg( "[{0} alt. covers]".format(len(remote_cover_list)-1), False )
|
||||
if use_log:
|
||||
if useLog:
|
||||
self.log_msg( "[ ", False )
|
||||
|
||||
score_list = []
|
||||
@ -294,7 +295,7 @@ class IssueIdentifier:
|
||||
score_item['url'] = remote_cover_item['url']
|
||||
score_item['hash'] = remote_cover_item['hash']
|
||||
score_list.append( score_item )
|
||||
if use_log:
|
||||
if useLog:
|
||||
self.log_msg( "{0} ".format(score), False )
|
||||
|
||||
if score <= self.strong_score_thresh:
|
||||
@ -304,7 +305,7 @@ class IssueIdentifier:
|
||||
if done:
|
||||
break
|
||||
|
||||
if use_log:
|
||||
if useLog:
|
||||
self.log_msg( " ]", False )
|
||||
|
||||
best_score_item = min(score_list, key=lambda x:x['score'])
|
||||
@ -348,7 +349,6 @@ class IssueIdentifier:
|
||||
right_side_image_data = self.cropCover( cover_image_data )
|
||||
if right_side_image_data is not None:
|
||||
narrow_cover_hash = self.calculateHash( right_side_image_data )
|
||||
self.log_msg(unicode(str(narrow_cover_hash)))
|
||||
|
||||
#self.log_msg( "Cover hash = {0:016x}".format(cover_hash) )
|
||||
|
||||
|
@ -23,8 +23,6 @@ import signal
|
||||
import os
|
||||
import traceback
|
||||
import platform
|
||||
import locale
|
||||
import codecs
|
||||
|
||||
import utils
|
||||
import cli
|
||||
@ -40,21 +38,12 @@ except ImportError as e:
|
||||
#---------------------------------------
|
||||
|
||||
def ctmain():
|
||||
# try to make stdout encodings happy for unicode
|
||||
if platform.system() == "Darwin":
|
||||
preferred_encoding = "utf-8"
|
||||
else:
|
||||
preferred_encoding = locale.getpreferredencoding()
|
||||
sys.stdout = codecs.getwriter(preferred_encoding)(sys.stdout)
|
||||
sys.stderr = codecs.getwriter(preferred_encoding)(sys.stderr)
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
opts = Options()
|
||||
opts.parseCmdLineArgs()
|
||||
|
||||
settings = ComicTaggerSettings()
|
||||
# make sure unrar program is in the path for the UnRAR class
|
||||
utils.addtopath(os.path.dirname(settings.unrar_exe_path))
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
if not qt_available and not opts.no_gui:
|
||||
|
@ -26,7 +26,7 @@ from PyQt4.QtCore import QUrl, pyqtSignal, QByteArray
|
||||
|
||||
from imagefetcher import ImageFetcher
|
||||
from settings import ComicTaggerSettings
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
from coverimagewidget import CoverImageWidget
|
||||
from comicvinetalker import ComicVineTalker
|
||||
import utils
|
||||
|
@ -22,22 +22,11 @@ import sys
|
||||
import getopt
|
||||
import platform
|
||||
import os
|
||||
|
||||
import traceback
|
||||
import ctversion
|
||||
import utils
|
||||
from genericmetadata import GenericMetadata
|
||||
|
||||
class Enum(set):
|
||||
def __getattr__(self, name):
|
||||
if name in self:
|
||||
return name
|
||||
raise AttributeError
|
||||
|
||||
class MetaDataStyle:
|
||||
CBI = 0
|
||||
CIX = 1
|
||||
COMET = 2
|
||||
name = [ 'ComicBookLover', 'ComicRack', 'CoMet' ]
|
||||
|
||||
from comicarchive import MetaDataStyle
|
||||
|
||||
class Options:
|
||||
help_text = """
|
||||
@ -82,7 +71,10 @@ If no options are given, {0} will run in windowed mode
|
||||
-e, --export-to-zip Export RAR archive to Zip format
|
||||
--delete-rar Delete original RAR archive after successful export to Zip
|
||||
--abort-on-conflict Don't export to zip if intended new filename exists (Otherwise, creates
|
||||
a new unique filename)
|
||||
a new unique filename)
|
||||
-S, --script=FILE Run an "add-on" python script that uses the comictagger library for custom
|
||||
processing. Script arguments can follow the script name
|
||||
-R, --recursive Recursively include files in sub-folders
|
||||
-v, --verbose Be noisy when doing what it does
|
||||
--terse Don't say much (for print mode)
|
||||
--version Display version
|
||||
@ -116,6 +108,9 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.no_overwrite = False
|
||||
self.interactive = False
|
||||
self.issue_id = None
|
||||
self.recursive = False
|
||||
self.run_script = False
|
||||
self.script = None
|
||||
self.file_list = []
|
||||
|
||||
def display_msg_and_quit( self, msg, code, show_help=False ):
|
||||
@ -188,10 +183,10 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
# parse command line options
|
||||
try:
|
||||
opts, args = getopt.getopt( input_args,
|
||||
"hpdt:fm:vonsrc:ie",
|
||||
"hpdt:fm:vonsrc:ieRS:",
|
||||
[ "help", "print", "delete", "type=", "copy=", "parsefilename", "metadata=", "verbose",
|
||||
"online", "dryrun", "save", "rename" , "raw", "noabort", "terse", "nooverwrite",
|
||||
"interactive", "nosummary", "version", "id="
|
||||
"interactive", "nosummary", "version", "id=" , "recursive", "script=",
|
||||
"export-to-zip", "delete-rar", "abort-on-conflict" ] )
|
||||
|
||||
except getopt.GetoptError as err:
|
||||
@ -203,6 +198,11 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.display_msg_and_quit( None, 0, show_help=True )
|
||||
if o in ("-v", "--verbose"):
|
||||
self.verbose = True
|
||||
if o in ("-S", "--script"):
|
||||
self.run_script = True
|
||||
self.script = a
|
||||
if o in ("-R", "--recursive"):
|
||||
self.recursive = True
|
||||
if o in ("-p", "--print"):
|
||||
self.print_tags = True
|
||||
if o in ("-d", "--delete"):
|
||||
@ -262,11 +262,12 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.data_style = MetaDataStyle.COMET
|
||||
else:
|
||||
self.display_msg_and_quit( "Invalid tag type", 1 )
|
||||
|
||||
|
||||
if self.print_tags or self.delete_tags or self.save_tags or self.copy_tags or self.rename_file or self.export_to_zip:
|
||||
self.no_gui = True
|
||||
|
||||
count = 0
|
||||
if self.run_script: count += 1
|
||||
if self.print_tags: count += 1
|
||||
if self.delete_tags: count += 1
|
||||
if self.save_tags: count += 1
|
||||
@ -275,7 +276,42 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
if self.export_to_zip: count +=1
|
||||
|
||||
if count > 1:
|
||||
self.display_msg_and_quit( "Must choose only one action of print, delete, save, copy, rename, or export", 1 )
|
||||
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)
|
||||
|
||||
if len(args) > 0:
|
||||
if platform.system() == "Windows":
|
||||
@ -284,7 +320,8 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
self.file_list = []
|
||||
for item in args:
|
||||
self.file_list.extend(glob.glob(item))
|
||||
self.filename = self.file_list[0]
|
||||
if len(self.file_list) > 0:
|
||||
self.filename = self.file_list[0]
|
||||
else:
|
||||
self.filename = args[0]
|
||||
self.file_list = args
|
||||
@ -304,3 +341,5 @@ For more help visit the wiki at: http://code.google.com/p/comictagger/
|
||||
#if self.rename_file and self.data_style is None:
|
||||
# self.display_msg_and_quit( "Please specify the type to use for renaming with -t", 1 )
|
||||
|
||||
if self.recursive:
|
||||
self.file_list = utils.get_recursive_filelist( self.file_list )
|
||||
|
@ -26,7 +26,7 @@ from PyQt4 import uic
|
||||
|
||||
from settings import ComicTaggerSettings
|
||||
from genericmetadata import GenericMetadata, PageType
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
from pageloader import PageLoader
|
||||
from coverimagewidget import CoverImageWidget
|
||||
|
||||
|
@ -23,7 +23,7 @@ from PyQt4 import QtCore, QtGui, uic
|
||||
from settings import ComicTaggerSettings
|
||||
from settingswindow import SettingsWindow
|
||||
from filerenamer import FileRenamer
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
|
||||
import os
|
||||
import utils
|
||||
|
@ -35,13 +35,18 @@ class ComicTaggerSettings:
|
||||
else:
|
||||
return os.path.join( os.path.expanduser('~') , '.ComicTagger')
|
||||
|
||||
frozen_win_exe_path = None
|
||||
|
||||
@staticmethod
|
||||
def baseDir():
|
||||
if getattr(sys, 'frozen', None):
|
||||
if platform.system() == "Darwin":
|
||||
return sys._MEIPASS
|
||||
else: # Windows
|
||||
return os.path.dirname( os.path.abspath( sys.argv[0] ) )
|
||||
#Preserve this value, in case sys.argv gets changed importing a plugin script
|
||||
if ComicTaggerSettings.frozen_win_exe_path is None:
|
||||
ComicTaggerSettings.frozen_win_exe_path = os.path.dirname( os.path.abspath( sys.argv[0] ) )
|
||||
return ComicTaggerSettings.frozen_win_exe_path
|
||||
else:
|
||||
return os.path.dirname( os.path.abspath( __file__) )
|
||||
|
||||
@ -143,6 +148,10 @@ class ComicTaggerSettings:
|
||||
self.unrar_exe_path = utils.which("unrar")
|
||||
if self.unrar_exe_path != "":
|
||||
self.save()
|
||||
|
||||
# make sure unrar/rar program is now in the path for the UnRAR class to use
|
||||
utils.addtopath(os.path.dirname(self.unrar_exe_path))
|
||||
utils.addtopath(os.path.dirname(self.rar_exe_path))
|
||||
|
||||
def reset( self ):
|
||||
os.unlink( self.settings_file )
|
||||
@ -274,3 +283,5 @@ class ComicTaggerSettings:
|
||||
with open( self.settings_file, 'wb') as configfile:
|
||||
self.config.write(configfile)
|
||||
|
||||
#make sure the basedir is cached, in case we're on windows running a script from frozen binary
|
||||
ComicTaggerSettings.baseDir()
|
||||
|
@ -153,8 +153,9 @@ class SettingsWindow(QtGui.QDialog):
|
||||
self.settings.rar_exe_path = str(self.leRarExePath.text())
|
||||
self.settings.unrar_exe_path = str(self.leUnrarExePath.text())
|
||||
|
||||
# make sure unrar program is now in the path for the UnRAR class
|
||||
# make sure unrar/rar program is now in the path for the UnRAR class
|
||||
utils.addtopath(os.path.dirname(self.settings.unrar_exe_path))
|
||||
utils.addtopath(os.path.dirname(self.settings.rar_exe_path))
|
||||
|
||||
if not str(self.leNameLengthDeltaThresh.text()).isdigit():
|
||||
self.leNameLengthDeltaThresh.setText("0")
|
||||
|
@ -32,7 +32,7 @@ import webbrowser
|
||||
import re
|
||||
|
||||
from volumeselectionwindow import VolumeSelectionWindow
|
||||
from options import MetaDataStyle
|
||||
from comicarchive import MetaDataStyle
|
||||
from comicinfoxml import ComicInfoXml
|
||||
from genericmetadata import GenericMetadata
|
||||
from comicvinetalker import ComicVineTalker, ComicVineTalkerException
|
||||
@ -72,9 +72,6 @@ class MultipleMatch():
|
||||
def __init__( self, ca, match_list):
|
||||
self.ca = ca
|
||||
self.matches = match_list
|
||||
|
||||
# this reads the environment and inits the right locale
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
|
||||
class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
@ -103,11 +100,6 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
self.fileSelectionList.selectionChanged.connect( self.fileListSelectionChanged )
|
||||
self.fileSelectionList.listCleared.connect( self.fileListCleared )
|
||||
|
||||
# ATB: Disable the list...
|
||||
#self.splitter.setSizes([100,0])
|
||||
#self.splitter.setHandleWidth(0)
|
||||
#self.splitter.handle(1).setDisabled(True)
|
||||
|
||||
# we can't specify relative font sizes in the UI designer, so
|
||||
# walk through all the lablels in the main form, and make them
|
||||
@ -151,7 +143,13 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
#TODO set up an RE validator for issueNum that allows
|
||||
# for all sorts of wacky things
|
||||
|
||||
|
||||
# tweak some control fonts
|
||||
utils.reduceWidgetFontSize( self.lblFilename, 1 )
|
||||
utils.reduceWidgetFontSize( self.lblArchiveType )
|
||||
utils.reduceWidgetFontSize( self.lblTagList )
|
||||
utils.reduceWidgetFontSize( self.lblPageCount )
|
||||
|
||||
#make sure some editable comboboxes don't take drop actions
|
||||
self.cbFormat.lineEdit().setAcceptDrops(False)
|
||||
self.cbMaturityRating.lineEdit().setAcceptDrops(False)
|
||||
@ -163,6 +161,8 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
self.btnAddCredit.clicked.connect(self.addCredit)
|
||||
self.btnRemoveCredit.clicked.connect(self.removeCredit)
|
||||
self.twCredits.cellDoubleClicked.connect(self.editCredit)
|
||||
self.connectDirtyFlagSignals()
|
||||
self.pageListEditor.modified.connect(self.setDirtyFlag)
|
||||
self.pageListEditor.firstFrontCoverChanged.connect( self.frontCoverChanged )
|
||||
self.pageListEditor.listOrderChanged.connect( self.pageListOrderChanged )
|
||||
self.tabWidget.currentChanged.connect( self.tabChanged )
|
||||
@ -560,7 +560,7 @@ class TaggerWindow( QtGui.QMainWindow):
|
||||
|
||||
filename = os.path.basename( ca.path )
|
||||
filename = os.path.splitext(filename)[0]
|
||||
filename = FileNameParser().fixSpaces(filename)
|
||||
filename = FileNameParser().fixSpaces(filename, False)
|
||||
|
||||
self.lblFilename.setText( filename )
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1096</width>
|
||||
<height>575</height>
|
||||
<height>621</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -91,23 +91,26 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>220</width>
|
||||
<width>230</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>220</width>
|
||||
<width>230</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
<enum>QFrame::Sunken</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="margin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="lblFilename">
|
||||
<property name="sizePolicy">
|
||||
@ -1092,7 +1095,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1096</width>
|
||||
<height>21</height>
|
||||
<height>22</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuComicTagger">
|
||||
|
@ -19,9 +19,62 @@ 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 platform
|
||||
import locale
|
||||
import codecs
|
||||
|
||||
class UtilsVars:
|
||||
already_fixed_encoding = False
|
||||
|
||||
def fix_output_encoding( ):
|
||||
if not UtilsVars.already_fixed_encoding:
|
||||
# this reads the environment and inits the right locale
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
|
||||
# try to make stdout/stderr encodings happy for unicode printing
|
||||
preferred_encoding = locale.getpreferredencoding()
|
||||
if getattr(sys, 'frozen', None) and platform.system() == "Darwin":
|
||||
preferred_encoding = "utf-8"
|
||||
sys.stdout = codecs.getwriter(preferred_encoding)(sys.stdout)
|
||||
sys.stderr = codecs.getwriter(preferred_encoding)(sys.stderr)
|
||||
elif platform.system() == "Windows":
|
||||
sys.stdout = codecs.getwriter(preferred_encoding)(sys.stdout)
|
||||
sys.stderr = codecs.getwriter(preferred_encoding)(sys.stderr)
|
||||
UtilsVars.already_fixed_encoding = True
|
||||
|
||||
def get_recursive_filelist( pathlist ):
|
||||
"""
|
||||
Get a recursive list of of all files under all path items in the list
|
||||
"""
|
||||
filename_encoding = sys.getfilesystemencoding()
|
||||
filelist = []
|
||||
for p in pathlist:
|
||||
# if path is a folder, walk it recursivly, and all files underneath
|
||||
if type(p) == str:
|
||||
#make sure string is unicode
|
||||
p = p.decode(filename_encoding) #, 'replace')
|
||||
elif type(p) != unicode:
|
||||
#it's probably a QString
|
||||
p = unicode(p)
|
||||
|
||||
if os.path.isdir( p ):
|
||||
for root,dirs,files in os.walk( p ):
|
||||
for f in files:
|
||||
if type(f) == str:
|
||||
#make sure string is unicode
|
||||
f = f.decode(filename_encoding, 'replace')
|
||||
elif type(f) != unicode:
|
||||
#it's probably a QString
|
||||
f = unicode(f)
|
||||
filelist.append(os.path.join(root,f))
|
||||
else:
|
||||
filelist.append(p)
|
||||
|
||||
return filelist
|
||||
|
||||
def listToString( l ):
|
||||
string = ""
|
||||
if l is not None:
|
||||
@ -31,10 +84,16 @@ def listToString( l ):
|
||||
string += item
|
||||
return string
|
||||
|
||||
def addtopath( dir ):
|
||||
# TODO only add if not there already
|
||||
if dir is not None and dir != "":
|
||||
os.environ['PATH'] = dir + os.pathsep + os.environ['PATH']
|
||||
def addtopath( dirname ):
|
||||
if dirname is not None and dirname != "":
|
||||
|
||||
# verify that path doesn't already contain the given dirname
|
||||
tmpdirname = re.escape(dirname)
|
||||
pattern = r"{sep}{dir}$|^{dir}{sep}|{sep}{dir}{sep}|^{dir}$".format( dir=tmpdirname, sep=os.pathsep)
|
||||
|
||||
match = re.search(pattern, os.environ['PATH'])
|
||||
if not match:
|
||||
os.environ['PATH'] = dirname + os.pathsep + os.environ['PATH']
|
||||
|
||||
# returns executable path, if it exists
|
||||
def which(program):
|
||||
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
mv /usr/bin/comictagger.py /usr/bin/comictagger
|
@ -1,2 +0,0 @@
|
||||
#!/bin/bash
|
||||
mv /usr/bin/comictagger /usr/bin/comictagger.py
|
@ -19,6 +19,8 @@ dist:
|
||||
cp -a $(TAGGER_SRC)/ui $(APP_BUNDLE)/Contents/MacOS
|
||||
cp -a $(TAGGER_SRC)/graphics $(APP_BUNDLE)/Contents/MacOS
|
||||
cp $(MAC_BASE)/app.icns $(APP_BUNDLE)/Contents/Resources/icon-windowed.icns
|
||||
# fix the version string in the Info.plist
|
||||
sed -i -e 's/0\.0\.0/$(VERSION_STR)/' $(MAC_BASE)/dist/ComicTagger.app/Contents/Info.plist
|
||||
|
||||
clean:
|
||||
rm -rf $(DIST_DIR) $(MAC_BASE)/build
|
||||
@ -59,5 +61,6 @@ diskimage:
|
||||
rm -f raw-$(DMG_FILE)
|
||||
|
||||
#move finished product to release folder
|
||||
mkdir -p $(TAGGER_BASE)/release
|
||||
mv $(DMG_FILE) $(TAGGER_BASE)/release
|
||||
|
||||
|
@ -1,3 +1,13 @@
|
||||
---------------------------------
|
||||
1.1.2-beta - 14-Feb-2013
|
||||
---------------------------------
|
||||
Changes:
|
||||
* Source is now packaged using Python distutils
|
||||
* Recursive mode for CLI
|
||||
* Run custom add-on scripts from CLI
|
||||
* Minor UI tweaks
|
||||
* Misc bug fixes
|
||||
|
||||
---------------------------------
|
||||
1.1.0-beta - 06-Feb-2013
|
||||
---------------------------------
|
||||
@ -8,7 +18,7 @@ Changes:
|
||||
double-clicked for embiggened view
|
||||
* Export-to-zip in CLI (very handy in scripts!)
|
||||
* More rename template variables
|
||||
* Misc GUI & CLI Tweaks
|
||||
* Misc GUI & CLI Tweaks
|
||||
|
||||
---------------------------------
|
||||
1.0.3-beta - 31-Jan-2013
|
||||
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
beautifulsoup4 >= 4.1
|
||||
PIL >= 1.1.6
|
30
scripts/README.txt
Normal file
30
scripts/README.txt
Normal file
@ -0,0 +1,30 @@
|
||||
This folder contains a set of example scripts that be used to extend the
|
||||
capabilities of the ComicTagger app. They can be run either directly through
|
||||
the python interpreter, or via the ComicTagger app.
|
||||
|
||||
To run via python directly, install ComicTagger source on your system using
|
||||
the setup.py file.
|
||||
|
||||
To run via the ComicTagger app, invoke:
|
||||
|
||||
# comictagger.py -S script.py [script args]
|
||||
|
||||
(This will work also for binary distributions on Mac and Windows. No need for
|
||||
an extra python install.)
|
||||
|
||||
The script must have an entry point function called "main()" to be invoked
|
||||
via the app.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
This feature is UNSUPPORTED, and is for the convenience of development-minded
|
||||
users of ComicTagger. The comictaggerlib module will remain largely
|
||||
undocumented, and it will up to the crafty script developer to look through
|
||||
the code to discern APIs and such.
|
||||
|
||||
That said, if there are questions, please post in the forums, and hopefully we
|
||||
can get your add-on scripts working!
|
||||
|
||||
http://comictagger.forumotion.com/
|
||||
|
||||
|
84
scripts/find_dupes.py
Executable file
84
scripts/find_dupes.py
Executable file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
find all duplicate comics
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
from comictaggerlib.comicarchive import *
|
||||
from comictaggerlib.settings import *
|
||||
from comictaggerlib.issuestring import *
|
||||
import comictaggerlib.utils
|
||||
|
||||
|
||||
def main():
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
style = MetaDataStyle.CIX
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print >> sys.stderr, "usage: {0} comic_folder ".format(sys.argv[0])
|
||||
return
|
||||
|
||||
filelist = utils.get_recursive_filelist( sys.argv[1:] )
|
||||
|
||||
#first find all comics with metadata
|
||||
print >> sys.stderr, "reading in all comics..."
|
||||
comic_list = []
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
if ca.seemsToBeAComicArchive() and ca.hasMetadata( style ):
|
||||
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()
|
||||
comic_list.append((filename, ca.readMetadata( style )))
|
||||
|
||||
print >> sys.stderr, fmt_str.format( "" ) + "\r",
|
||||
print "-----------------------------------------------"
|
||||
print "Found {0} comics with {1} tags".format( len(comic_list), MetaDataStyle.name[style])
|
||||
print "-----------------------------------------------"
|
||||
|
||||
#sort the list by series+issue+year, to put all the dupes together
|
||||
def makeKey(x):
|
||||
return "<" + unicode(x[1].series) + u" #" + unicode( x[1].issue ) + u" - " + unicode( x[1].year ) + ">"
|
||||
comic_list.sort(key=makeKey, reverse=False)
|
||||
|
||||
# look for duplicate blocks
|
||||
dupe_set_list = list()
|
||||
dupe_set = list()
|
||||
prev_key = ""
|
||||
for filename, md in comic_list:
|
||||
print >> sys.stderr, fmt_str.format( filename ) + "\r",
|
||||
sys.stderr.flush()
|
||||
|
||||
new_key = makeKey((filename, md))
|
||||
|
||||
#if the new key same as the last, add to to dupe set
|
||||
if new_key == prev_key:
|
||||
dupe_set.append(filename)
|
||||
|
||||
#else we're on a new potential block
|
||||
else:
|
||||
# only add if the dupe list has 2 or more
|
||||
if len (dupe_set) > 1:
|
||||
dupe_set_list.append( dupe_set )
|
||||
dupe_set = list()
|
||||
dupe_set.append(filename)
|
||||
|
||||
prev_key = new_key
|
||||
|
||||
print >> sys.stderr, fmt_str.format( "" ) + "\r",
|
||||
print "Found {0} duplicate sets".format( len(dupe_set_list))
|
||||
|
||||
for dupe_set in dupe_set_list:
|
||||
ca = ComicArchive(dupe_set[0], settings )
|
||||
md = ca.readMetadata( style )
|
||||
print "{0} #{1} ({2})".format( md.series, md.issue, md.year )
|
||||
for filename in dupe_set:
|
||||
print "------------->{0}".format( filename )
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
84
scripts/inventory.py
Executable file
84
scripts/inventory.py
Executable file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
Print out a line-by-line list of basic tag info from all comics
|
||||
"""
|
||||
|
||||
"""
|
||||
Copyright 2012 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
|
||||
|
||||
from comictaggerlib.comicarchive import *
|
||||
from comictaggerlib.settings import *
|
||||
from comictaggerlib.issuestring import *
|
||||
import comictaggerlib.utils
|
||||
|
||||
def main():
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
style = MetaDataStyle.CIX
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print >> sys.stderr, "usage: {0} comic_folder ".format(sys.argv[0])
|
||||
return
|
||||
|
||||
filelist = utils.get_recursive_filelist( sys.argv[1:] )
|
||||
|
||||
#first read in metadata from all files
|
||||
metadata_list = []
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
if ca.hasMetadata( style ):
|
||||
#make a list of paired filenames and metadata objects
|
||||
metadata_list.append((filename, ca.readMetadata( style )))
|
||||
|
||||
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( "" ) + "\r",
|
||||
print "-----------------------------------------------"
|
||||
print "Found {0} comics with {1} tags".format( len(metadata_list), MetaDataStyle.name[style])
|
||||
print "-----------------------------------------------"
|
||||
|
||||
# now, figure out column widths
|
||||
w0 = 4
|
||||
w1 = 4
|
||||
for filename,md in metadata_list:
|
||||
if not md.isEmpty:
|
||||
w0 = max( len((os.path.split(filename)[1])), w0)
|
||||
if md.series is not None:
|
||||
w1 = max( len(md.series), w1)
|
||||
w0 += 2
|
||||
|
||||
# build a format string
|
||||
fmt_str = u"{0:" + str(w0) + "} {1:" + str(w1) + "} #{2:6} ({3})"
|
||||
|
||||
# now sort the list by issue, and then series
|
||||
metadata_list.sort(key=lambda x: IssueString(x[1].issue).asString(3), reverse=False)
|
||||
metadata_list.sort(key=lambda x: unicode(x[1].series).lower()+str(x[1].year), reverse=False)
|
||||
|
||||
# now print
|
||||
for filename, md in metadata_list:
|
||||
if not md.isEmpty:
|
||||
print fmt_str.format(os.path.split(filename)[1]+":", md.series, md.issue, md.year), md.title
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
107
scripts/make_links.py
Executable file
107
scripts/make_links.py
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
make some tree structures and symbolic links to comic files based on metadata
|
||||
oragnizing by date and series, in different trees
|
||||
"""
|
||||
|
||||
"""
|
||||
Copyright 2012 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 platform
|
||||
|
||||
from comictaggerlib.comicarchive import *
|
||||
from comictaggerlib.settings import *
|
||||
from comictaggerlib.issuestring import *
|
||||
import comictaggerlib.utils
|
||||
|
||||
def make_folder( folder ):
|
||||
if not os.path.exists( folder ):
|
||||
try:
|
||||
os.makedirs(folder)
|
||||
except Exception as e:
|
||||
print "{0} Can't make {1} -- quitting".format(e, folder)
|
||||
quit()
|
||||
|
||||
def make_link( source, link ):
|
||||
if not os.path.exists( link ):
|
||||
os.symlink( os.path.abspath(source) , link )
|
||||
|
||||
def main():
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
style = MetaDataStyle.CIX
|
||||
|
||||
if platform.system() == "Windows":
|
||||
print >> sys.stderr, "Sorry, this script works only on UNIX systems"
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print >> sys.stderr, "usage: {0} comic_root link_root".format(sys.argv[0])
|
||||
return
|
||||
|
||||
comic_root = sys.argv[1]
|
||||
link_root = sys.argv[2]
|
||||
|
||||
print "root is : ", comic_root
|
||||
filelist = utils.get_recursive_filelist( [ comic_root ] )
|
||||
make_folder( link_root )
|
||||
|
||||
#first find all comics with metadata
|
||||
print "reading in all comics..."
|
||||
comic_list = []
|
||||
max_name_len = 2
|
||||
for filename in filelist:
|
||||
ca = ComicArchive(filename, settings )
|
||||
if ca.seemsToBeAComicArchive() and ca.hasMetadata( style ):
|
||||
|
||||
comic_list.append((filename, ca.readMetadata( style )))
|
||||
|
||||
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} tagged comics.".format( len(comic_list))
|
||||
|
||||
# walk through the comic list and add subdirs and links for each one
|
||||
for filename, md in comic_list:
|
||||
print >> sys.stderr, fmt_str.format( filename ) + "\r",
|
||||
sys.stderr.flush()
|
||||
|
||||
#do date organizing:
|
||||
if md.month is not None:
|
||||
month_str = "{0:02d}".format(int(md.month))
|
||||
else:
|
||||
month_str = "00"
|
||||
date_folder = os.path.join(link_root, "date", str(md.year), month_str)
|
||||
make_folder( date_folder )
|
||||
make_link( filename, os.path.join(date_folder, os.path.basename(filename)) )
|
||||
|
||||
#do publisher/series organizing:
|
||||
fixed_series_name = md.series
|
||||
if fixed_series_name is not None:
|
||||
# some tweaks to keep various filesystems happy
|
||||
fixed_series_name = fixed_series_name.replace("/", "-")
|
||||
fixed_series_name = fixed_series_name.replace("?", "")
|
||||
series_folder = os.path.join(link_root, "series", str(md.publisher), unicode(fixed_series_name))
|
||||
make_folder( series_folder )
|
||||
make_link( filename, os.path.join(series_folder, os.path.basename(filename)) )
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
149
scripts/remove_ads.py
Executable file
149
scripts/remove_ads.py
Executable file
@ -0,0 +1,149 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
Create new comic archives from old one, removing pages marked as ads
|
||||
and deleted. Walks recursivly through the given folders. Originals
|
||||
are kept in a subfolder at the level of the original
|
||||
"""
|
||||
|
||||
"""
|
||||
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 tempfile
|
||||
import zipfile
|
||||
import shutil
|
||||
|
||||
import comictaggerlib.utils
|
||||
from comictaggerlib.settings import *
|
||||
from comictaggerlib.comicarchive import *
|
||||
|
||||
subfolder_name = "PRE_AD_REMOVAL"
|
||||
unwanted_types = [ 'Deleted', 'Advertisment' ]
|
||||
|
||||
def main():
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
# this can only work with files with ComicRack tags
|
||||
style = MetaDataStyle.CIX
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print >> sys.stderr, "usage: {0} comic_folder ".format(sys.argv[0])
|
||||
return
|
||||
|
||||
filelist = utils.get_recursive_filelist( sys.argv[1:] )
|
||||
|
||||
#first read in CIX metadata from all files, make a list of candidates
|
||||
modify_list = []
|
||||
for filename in filelist:
|
||||
|
||||
ca = ComicArchive(filename, settings )
|
||||
if (ca.isZip or ca.isRar()) and ca.hasMetadata( style ):
|
||||
md = ca.readMetadata( style )
|
||||
if len(md.pages) != 0:
|
||||
for p in md.pages:
|
||||
if p.has_key('Type') and p['Type'] in unwanted_types:
|
||||
#This one has pages to remove. add to list!
|
||||
modify_list.append((filename, md))
|
||||
break
|
||||
|
||||
#now actually process those files
|
||||
for filename,md in modify_list:
|
||||
ca = ComicArchive(filename, settings )
|
||||
curr_folder = os.path.dirname( filename )
|
||||
curr_subfolder = os.path.join( curr_folder, subfolder_name )
|
||||
|
||||
#skip any of our generated subfolders...
|
||||
if os.path.basename(curr_folder) == subfolder_name:
|
||||
continue
|
||||
sys.stdout.write("Removing unwanted pages from " + filename)
|
||||
|
||||
# verify that we can write to current folder
|
||||
if not os.access(filename, os.W_OK):
|
||||
print "Can't move: {0}: skipped!".format(filename)
|
||||
continue
|
||||
if not os.path.exists( curr_subfolder ) and not os.access(curr_folder, os.W_OK):
|
||||
print "Can't create subfolder here: {0}: skipped!".format(filename)
|
||||
continue
|
||||
if not os.path.exists( curr_subfolder ):
|
||||
os.mkdir( curr_subfolder )
|
||||
if not os.access(curr_subfolder, os.W_OK):
|
||||
print "Can't write to the subfolder here: {0}: skipped!".format(filename)
|
||||
continue
|
||||
|
||||
# generate a new file with temp name
|
||||
tmp_fd, tmp_name = tempfile.mkstemp( dir=os.path.dirname(filename) )
|
||||
os.close( tmp_fd )
|
||||
|
||||
try:
|
||||
zout = zipfile.ZipFile (tmp_name, 'w')
|
||||
|
||||
# now read in all the pages from the old one, except the ones we want to skip
|
||||
new_num = 0
|
||||
new_pages = list()
|
||||
for p in md.pages:
|
||||
if p.has_key('Type') and p['Type'] in unwanted_types:
|
||||
continue
|
||||
else:
|
||||
pageNum = int(p['Image'])
|
||||
name = ca.getPageName( pageNum )
|
||||
buffer = ca.getPage( pageNum )
|
||||
sys.stdout.write('.')
|
||||
sys.stdout.flush()
|
||||
|
||||
#Generate a new name for the page file
|
||||
ext = os.path.splitext(name)[1]
|
||||
new_name = "page{0:04d}{1}".format(new_num,ext)
|
||||
zout.writestr(new_name, buffer)
|
||||
|
||||
# create new page entry
|
||||
new_p = dict()
|
||||
new_p['Image'] = str(new_num)
|
||||
if p.has_key('Type'):
|
||||
new_p['Type'] = p['Type']
|
||||
new_pages.append(new_p)
|
||||
new_num += 1
|
||||
|
||||
#preserve the old comment
|
||||
comment = ca.archiver.getArchiveComment()
|
||||
if comment is not None:
|
||||
zout.comment = ca.archiver.getArchiveComment()
|
||||
|
||||
except Exception as e:
|
||||
print "Failure creating new archive: {0}!".format(filename)
|
||||
print e, sys.exc_info()[0]
|
||||
zout.close()
|
||||
os.unlink( tmp_name )
|
||||
else:
|
||||
zout.close()
|
||||
|
||||
# Success! Now move the files
|
||||
shutil.move( filename, curr_subfolder )
|
||||
os.rename( tmp_name, filename )
|
||||
# TODO: We might have converted a rar to a zip, and should probably change
|
||||
# the extension, as needed.
|
||||
|
||||
print "Done!".format(filename)
|
||||
|
||||
# Create a new archive object for the new file, and write the old CIX data, with new page info
|
||||
ca = ComicArchive( filename, settings )
|
||||
md.pages = new_pages
|
||||
ca.writeMetadata( style, md )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
73
scripts/validate_cover.py
Executable file
73
scripts/validate_cover.py
Executable file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/python
|
||||
"""
|
||||
test archive cover against comicvine for a given issue ID
|
||||
"""
|
||||
|
||||
"""
|
||||
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 comictaggerlib.utils
|
||||
from comictaggerlib.settings import *
|
||||
from comictaggerlib.comicarchive import *
|
||||
from comictaggerlib.issueidentifier import *
|
||||
from comictaggerlib.comicvinetalker import *
|
||||
|
||||
def main():
|
||||
|
||||
utils.fix_output_encoding()
|
||||
settings = ComicTaggerSettings()
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print >> sys.stderr, "usage: {0} comicfile issueid".format(sys.argv[0])
|
||||
return
|
||||
|
||||
filename = sys.argv[1]
|
||||
issue_id = sys.argv[2]
|
||||
|
||||
if not os.path.exists(filename):
|
||||
print >> sys.stderr, filename + ": not found!"
|
||||
return
|
||||
|
||||
ca = ComicArchive(filename, settings )
|
||||
if not ca.seemsToBeAComicArchive():
|
||||
print >> sys.stderr, "Sorry, but "+ filename + " is not a comic archive!"
|
||||
return
|
||||
|
||||
ii = IssueIdentifier( ca, settings )
|
||||
|
||||
# calculate the hashes of the first two pages
|
||||
cover_image_data = ca.getPage( 0 )
|
||||
cover_hash0 = ii.calculateHash( cover_image_data )
|
||||
cover_image_data = ca.getPage( 1 )
|
||||
cover_hash1 = ii.calculateHash( cover_image_data )
|
||||
hash_list = [ cover_hash0, cover_hash1 ]
|
||||
|
||||
comicVine = ComicVineTalker( )
|
||||
result = ii.getIssueCoverMatchScore( comicVine, issue_id, hash_list, useRemoteAlternates=True, useLog=False)
|
||||
|
||||
print "Best cover match score is :", result['score']
|
||||
if result['score'] < ii.min_alternate_score_thresh:
|
||||
print "Looks like a match!"
|
||||
else:
|
||||
print "Bad score, maybe not a match?"
|
||||
print result['url']
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
122
setup.py
122
setup.py
@ -1,61 +1,61 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
import comictaggerlib.ctversion
|
||||
|
||||
setup(name = "comictagger",
|
||||
version = comictaggerlib.ctversion.version,
|
||||
description = "A cross-platorm GUI/CLI app for writing metadata to comic archives",
|
||||
author = "Anthony Beville",
|
||||
author_email = "comictagger@gmail.com",
|
||||
url = "http://code.google.com/p/comictagger/",
|
||||
download_url = "http://comictagger.googlecode.com/files/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
|
||||
packages = [ "comictaggerlib", "comictaggerlib/UnRAR2" ] ,
|
||||
package_data = {
|
||||
'comictaggerlib': ['ui/*.ui', 'graphics/*'] ,
|
||||
'comictaggerlib/UnRAR2': ['UnRARDLL/*.*', 'UnRARDLL/x64/*.*'] ,
|
||||
},
|
||||
scripts = ["comictagger.py"],
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics"
|
||||
],
|
||||
license = "Apache License 2.0",
|
||||
|
||||
long_description = """
|
||||
ComicTagger is a multi-platform app for writing metadata to comic archives, written in Python and PyQt.
|
||||
|
||||
Features:
|
||||
|
||||
* Runs on Mac OSX, Microsoft Windows, and Linux systems
|
||||
* Communicates with an online database (Comic Vine) for acquiring metadata
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover? and ComicRack?, with more planned).
|
||||
* Reads and writes RAR, Zip, and folder archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations.
|
||||
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* python imaging (PIL) >= 1.1.7
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
Optional requirement (for GUI):
|
||||
|
||||
* pyqt4
|
||||
"""
|
||||
)
|
||||
#!/usr/bin/env python
|
||||
|
||||
from distutils.core import setup
|
||||
import comictaggerlib.ctversion
|
||||
|
||||
setup(name = "comictagger",
|
||||
version = comictaggerlib.ctversion.version,
|
||||
description = "A cross-platform GUI/CLI app for writing metadata to comic archives",
|
||||
author = "Anthony Beville",
|
||||
author_email = "comictagger@gmail.com",
|
||||
url = "http://code.google.com/p/comictagger/",
|
||||
download_url = "http://comictagger.googlecode.com/files/comictagger-{0}.zip".format(comictaggerlib.ctversion.version),
|
||||
packages = [ "comictaggerlib", "comictaggerlib/UnRAR2" ] ,
|
||||
package_data = {
|
||||
'comictaggerlib': ['ui/*.ui', 'graphics/*'] ,
|
||||
'comictaggerlib/UnRAR2': ['UnRARDLL/*.*', 'UnRARDLL/x64/*.*'] ,
|
||||
},
|
||||
scripts = ["comictagger.py"],
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: Console",
|
||||
"Environment :: Win32 (MS Windows)",
|
||||
"Environment :: MacOS X",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Natural Language :: English",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
"Programming Language :: Python :: 2.6",
|
||||
"Programming Language :: Python :: 2.7",
|
||||
"Topic :: Utilities",
|
||||
"Topic :: Other/Nonlisted Topic",
|
||||
"Topic :: Multimedia :: Graphics"
|
||||
],
|
||||
license = "Apache License 2.0",
|
||||
|
||||
long_description = """
|
||||
ComicTagger is a multi-platform app for writing metadata to comic archives, written in Python and PyQt.
|
||||
|
||||
Features:
|
||||
|
||||
* Runs on Mac OSX, Microsoft Windows, and Linux systems
|
||||
* Communicates with an online database (Comic Vine) for acquiring metadata
|
||||
* Uses image processing to automatically match a given archive with the correct issue data
|
||||
* Batch processing in the GUI for tagging hundreds or more comics at a time
|
||||
* Reads and writes multiple tagging schemes ( ComicBookLover and ComicRack, with more planned).
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Command line interface (CLI) on all platforms (including Windows), which supports batch operations, and which can be used in native scripts for complex operations.
|
||||
|
||||
Requires:
|
||||
|
||||
* python 2.6 or 2.7
|
||||
* python imaging (PIL) >= 1.1.6
|
||||
* beautifulsoup > 4.1
|
||||
|
||||
Optional requirement (for GUI):
|
||||
|
||||
* pyqt4
|
||||
"""
|
||||
)
|
||||
|
153
todo.txt
153
todo.txt
@ -1,77 +1,76 @@
|
||||
-----------------------------------------------------
|
||||
Features
|
||||
-----------------------------------------------------
|
||||
|
||||
Rename dialog:
|
||||
check-box for rows?
|
||||
manual edit the preview?
|
||||
|
||||
Docs:
|
||||
Auto-Tagging Tips:
|
||||
Multiple Passes with different options
|
||||
|
||||
-----------------------------------------------------
|
||||
Bugs
|
||||
-----------------------------------------------------
|
||||
|
||||
Zip flakes out when filename differs from index (or whatever) i.e "\" vs "/". Python issue
|
||||
|
||||
-----------------------------------------------------
|
||||
Big Future Features
|
||||
-----------------------------------------------------
|
||||
|
||||
GCD scraper or DB reader
|
||||
|
||||
Batch Edit
|
||||
Form Mode: Single vs Batch
|
||||
|
||||
-----------------------------------------------------
|
||||
Small(er) Future Feature
|
||||
-----------------------------------------------------
|
||||
Parse out the rest of the scan info from filename
|
||||
|
||||
Style sheets for windows/mac/linux
|
||||
|
||||
CLI
|
||||
explicit metadata settings option format
|
||||
-- figure out how to add CBI "tags"
|
||||
-- delete CBI "tags"
|
||||
-- set primary credit flags
|
||||
-- set frontcover and others?
|
||||
|
||||
Archive function to detect tag blocks out of sync
|
||||
|
||||
Settings
|
||||
Add setting to dis-allow writing CBI to RAR
|
||||
|
||||
Google App engine to store hashes
|
||||
Content Hashes, Image hashes, who knows?
|
||||
|
||||
Filename parsing:
|
||||
Rework how series name is separated from issue
|
||||
|
||||
Support marvel's "AU" issues...
|
||||
Mostly done, gotta wait and see what CV does
|
||||
|
||||
Internal GenericMetadata - Make Characters, Genre into lists?
|
||||
|
||||
-----------------------------------------------------
|
||||
Config Mgmt check list
|
||||
-----------------------------------------------------
|
||||
|
||||
Release Process
|
||||
Optionally, make screen shots, upload to wiki
|
||||
Update release notes and wiki
|
||||
Update ctversion.py
|
||||
Build packages
|
||||
Make exe on Windows
|
||||
Make dmg on Mac
|
||||
Make zip on Mac or Linux
|
||||
Tag the repository
|
||||
Upload packages
|
||||
Announce on Forum and Main Page
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
rename 's/([A-Za-z]+)(\d+)(.cb[rz])/$1 $2$3/' *.cb?
|
||||
-----------------------------------------------------
|
||||
Features
|
||||
-----------------------------------------------------
|
||||
Rename dialog:
|
||||
check-box for rows?
|
||||
manual edit the preview?
|
||||
|
||||
Docs:
|
||||
Auto-Tagging Tips:
|
||||
Multiple Passes with different options
|
||||
|
||||
-----------------------------------------------------
|
||||
Bugs
|
||||
-----------------------------------------------------
|
||||
|
||||
Zip flakes out when filename differs from index (or whatever) i.e "\" vs "/". Python issue
|
||||
|
||||
-----------------------------------------------------
|
||||
Big Future Features
|
||||
-----------------------------------------------------
|
||||
|
||||
GCD scraper or DB reader
|
||||
|
||||
Batch Edit
|
||||
Form Mode: Single vs Batch
|
||||
|
||||
-----------------------------------------------------
|
||||
Small(er) Future Feature
|
||||
-----------------------------------------------------
|
||||
Parse out the rest of the scan info from filename
|
||||
|
||||
Style sheets for windows/mac/linux
|
||||
|
||||
CLI
|
||||
explicit metadata settings option format
|
||||
-- figure out how to add CBI "tags"
|
||||
-- delete CBI "tags"
|
||||
-- set primary credit flags
|
||||
-- set frontcover and others?
|
||||
|
||||
Archive function to detect tag blocks out of sync
|
||||
|
||||
Settings
|
||||
Add setting to dis-allow writing CBI to RAR
|
||||
|
||||
Google App engine to store hashes
|
||||
Content Hashes, Image hashes, who knows?
|
||||
|
||||
Filename parsing:
|
||||
Rework how series name is separated from issue
|
||||
|
||||
Support marvel's "AU" issues...
|
||||
Mostly done, gotta wait and see what CV does
|
||||
|
||||
Internal GenericMetadata - Make Characters, Genre into lists?
|
||||
|
||||
-----------------------------------------------------
|
||||
Config Mgmt check list
|
||||
-----------------------------------------------------
|
||||
|
||||
Release Process
|
||||
Optionally, make screen shots, upload to wiki
|
||||
Update release notes and wiki
|
||||
Update ctversion.py
|
||||
Build packages
|
||||
Make exe on Windows
|
||||
Make dmg on Mac
|
||||
Make zip on Mac or Linux
|
||||
Tag the repository
|
||||
Upload packages
|
||||
Announce on Forum and Main Page
|
||||
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
rename 's/([A-Za-z]+)(\d+)(.cb[rz])/$1 $2$3/' *.cb?
|
||||
|
@ -1,11 +1,12 @@
|
||||
# This Makefile expects that certain GNU utils are available:
|
||||
# rm, cp, grep, cut, cat
|
||||
|
||||
TAGGER_BASE:= c:\Users\tony\Dropbox\tagger\comictagger
|
||||
TAGGER_SRC := $(TAGGER_BASE)\comictaggerlib
|
||||
HOMEPATH ?= $(HOME)
|
||||
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"
|
||||
VERSION := $(shell grep version "$(TAGGER_SRC)\ctversion.py" | cut -d= -f2)
|
||||
VERSION := $(shell grep version "$(TAGGER_SRC)/ctversion.py" | cut -d= -f2)
|
||||
|
||||
all: clean dist package
|
||||
|
||||
@ -28,9 +29,4 @@ package:
|
||||
|
||||
clean:
|
||||
-rm -rf dist
|
||||
-rm -f "*~" *.pyc *.pyo
|
||||
-rm -f "$(TAGGER_BASE)\windows\*.exe"
|
||||
|
||||
test:
|
||||
echo !define RELEASE_STR $(VERSION) > test.nsh
|
||||
|
||||
-rm -rf nsis/release.nsh
|
||||
|
Reference in New Issue
Block a user