Compare commits
15 Commits
untagged-0
...
1.2.0+2
Author | SHA1 | Date | |
---|---|---|---|
0fbaeb861e | |||
3dcc04a318 | |||
933e053df3 | |||
5f22a583e8 | |||
3174b49d94 | |||
93ce311359 | |||
bc43c5e329 | |||
9bf7aa20fb | |||
5416bb15c3 | |||
562a659195 | |||
1d3d6e2741 | |||
c9724527b5 | |||
2891209b4e | |||
5b87e19d3e | |||
674e24fc41 |
23
.travis.yml
Normal file
23
.travis.yml
Normal file
@ -0,0 +1,23 @@
|
||||
language: generic
|
||||
os: osx
|
||||
osx_image: xcode8.3
|
||||
install:
|
||||
- brew upgrade python3
|
||||
- pip3 install --upgrade setuptools
|
||||
- pip3 install -r requirements.txt
|
||||
script:
|
||||
- make dist
|
||||
- ls -l dist
|
||||
deploy:
|
||||
name: $TRAVIS_TAG
|
||||
body: New macOS Release
|
||||
provider: releases
|
||||
skip_cleanup: true
|
||||
api_key:
|
||||
secure: RgohcOJOfLhXXT12bMWaLwOqhe+ClSCYXjYuUJuWK4/E1fdd1xu1ebdQU+MI/R8cZ0Efz3sr2n3NkO/Aa8gN68xEfuF7RVRMm64P9oPrfZgGdsD6H43rU/6kN8bgaDRmCYpLTfXaJ+/gq0x1QDkhWJuceF2BYEGGvL0BvS/TUsLyjVxs8ujTplLyguXHNEv4/7Yz7SBNZZmUHjBuq/y+l8ds3ra9rSgAVAN1tMXoFKJPv+SNNkpTo5WUNMPzBnN041F1rzqHwYDLog2V7Krp9JkXzheRFdAr51/tJBYzEd8AtYVdYvaIvoO6A4PiTZ7MpsmcZZPAWqLQU00UTm/PhT/LVR+7+f8lOBG07RgNNHB+edjDRz3TAuqyuZl9wURWTZKTPuO49TkZMz7Wm0DRNZHvBm1IXLeSG7Tll2YL1+WpZNZg+Dhro2J1QD3vxDXafhMdTCB4z0q5aKpG93IT0p6oXOO0oEGOPZYbA2c5R3SXWSyqd1E1gdhbVjIZr59h++TEf1zz07tvWHqPuAF/Ly/j+dIcY2wj0EzRWaSASWgUpTnMljAkHtWhqDw4GXGDRkRUWRJl1d0/JyVqCeIdRzDQNl8/q7BcO3F1zqr1PgnYdz0lfwWxL1/ekw2vHOJE/GOdkyvX0aJrnaOV338mjJbfGHYv4ESc9ow1kdtIbiU=
|
||||
file_glob: true
|
||||
file: dist/ComicTagger*.zip
|
||||
draft: true
|
||||
on:
|
||||
branch: master
|
||||
tags: true
|
8
Makefile
8
Makefile
@ -1,11 +1,13 @@
|
||||
VERSION_STR := $(shell python -c 'import comictaggerlib.ctversion; print( comictaggerlib.ctversion.version)')
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
OS_VERSION=win-$(PROCESSOR_ARCHITECTURE)
|
||||
APP_NAME=comictagger.exe
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR).exe
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR)-$(OS_VERSION).exe
|
||||
else ifeq ($(shell uname -s),Darwin)
|
||||
OS_VERSION=osx-$(shell defaults read loginwindow SystemVersionStampAsString)-$(shell uname -m)
|
||||
APP_NAME=ComicTagger.app
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR).app
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR)-$(OS_VERSION).app
|
||||
else
|
||||
APP_NAME=comictagger
|
||||
FINAL_NAME=ComicTagger-$(VERSION_STR)
|
||||
@ -52,4 +54,4 @@ endif
|
||||
|
||||
dist: unrar
|
||||
pyinstaller -y comictagger.spec
|
||||
mv dist/$(APP_NAME) dist/$(FINAL_NAME)
|
||||
cd dist && zip -r $(FINAL_NAME).zip $(APP_NAME)
|
||||
|
16
README.md
16
README.md
@ -1,6 +1,18 @@
|
||||
A fork from the primary dev branch at https://github.com/davide-romanini/comictagger
|
||||
|
||||
Changes:
|
||||
ComicTagger is a multi-platform app for writing metadata to digital comics, 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).
|
||||
* Reads and writes RAR and Zip archives (external tools needed for writing RAR)
|
||||
* Can run without PyQt5 installed
|
||||
|
||||
|
||||
Recent changes:
|
||||
- Ported to Python 3
|
||||
- Ported to PyQt5
|
||||
- Added more application and GUI awareness of the unrar library, and removed references to the old scheme that used the unrar executable.
|
||||
|
15
appveyor.yml
15
appveyor.yml
@ -2,5 +2,16 @@ version: 1.0.{build}
|
||||
build_script:
|
||||
- cmd: powershell -exec bypass -File windows\fullbuild.ps1
|
||||
artifacts:
|
||||
- path: dist\*.exe
|
||||
name: ComicTagger
|
||||
- path: dist\*.zip
|
||||
name: ComicTagger
|
||||
deploy:
|
||||
description: New Windows Release
|
||||
provider: GitHub
|
||||
auth_token:
|
||||
secure: GftVXNVTfnfWxlCIWNsufQjgrLqGPbrhK7bddzZ+6onYbbx6vEKWku0QMZjcxq/D
|
||||
draft: true
|
||||
prerelease: false
|
||||
artifact: ComicTagger
|
||||
on:
|
||||
APPVEYOR_REPO_TAG: true
|
||||
|
@ -153,6 +153,7 @@ class ZipArchiver:
|
||||
zf = zipfile.ZipFile(
|
||||
self.path,
|
||||
mode='a',
|
||||
allowZip64=True,
|
||||
compression=zipfile.ZIP_DEFLATED)
|
||||
zf.writestr(archive_file, data)
|
||||
zf.close()
|
||||
@ -185,7 +186,7 @@ class ZipArchiver:
|
||||
os.close(tmp_fd)
|
||||
|
||||
zin = zipfile.ZipFile(self.path, 'r')
|
||||
zout = zipfile.ZipFile(tmp_name, 'w')
|
||||
zout = zipfile.ZipFile(tmp_name, 'w', allowZip64=True)
|
||||
for item in zin.infolist():
|
||||
buffer = zin.read(item.filename)
|
||||
if (item.filename not in exclude_list):
|
||||
@ -270,7 +271,7 @@ class ZipArchiver:
|
||||
"""Replace the current zip with one copied from another archive"""
|
||||
|
||||
try:
|
||||
zout = zipfile.ZipFile(self.path, 'w')
|
||||
zout = zipfile.ZipFile(self.path, 'w', allowZip64=True)
|
||||
for fname in otherArchive.getArchiveFilenameList():
|
||||
data = otherArchive.readArchiveFile(fname)
|
||||
if data is not None:
|
||||
|
@ -123,7 +123,7 @@ def which(program):
|
||||
|
||||
def removearticles(text):
|
||||
text = text.lower()
|
||||
articles = ['and', 'a', '&', 'issue']
|
||||
articles = ['and', 'a', '&', 'issue', 'the']
|
||||
newText = ''
|
||||
for word in text.split(' '):
|
||||
if word not in articles:
|
||||
|
@ -105,7 +105,7 @@ class ComicVineTalker(QObject):
|
||||
self.log_func = None
|
||||
|
||||
# always use a tls context for urlopen
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
|
||||
def setLogFunc(self, log_func):
|
||||
self.log_func = log_func
|
||||
@ -188,7 +188,7 @@ class ComicVineTalker(QObject):
|
||||
# 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
|
||||
#print("---", url)
|
||||
for tries in range(3):
|
||||
try:
|
||||
resp = urllib.request.urlopen(url, context=self.ssl)
|
||||
@ -226,10 +226,10 @@ class ComicVineTalker(QObject):
|
||||
|
||||
original_series_name = series_name
|
||||
|
||||
# Split and rejoin to remove extra internal spaces
|
||||
# Split and rejoin to remove extra internal spaces
|
||||
query_word_list = series_name.split()
|
||||
query_string = " ".join( query_word_list ).strip()
|
||||
#print "Query string = ", query_string
|
||||
#print ("Query string = ", query_string)
|
||||
|
||||
query_string = urllib.parse.quote_plus(query_string.encode("utf-8"))
|
||||
|
||||
@ -246,6 +246,19 @@ class ComicVineTalker(QObject):
|
||||
current_result_count = cv_response['number_of_page_results']
|
||||
total_result_count = cv_response['number_of_total_results']
|
||||
|
||||
# 8 Dec 2018 - Comic Vine changed query results again. Terms are now
|
||||
# ORed together, and we get thousands of results. Good news is the
|
||||
# results are sorted by relevance, so we can be smart about halting
|
||||
# the search.
|
||||
# 1. Don't fetch more than some sane amount of pages.
|
||||
max_results = 500
|
||||
# 2. Halt when not all of our search terms are present in a result
|
||||
# 3. Halt when the results contain more (plus threshold) words than
|
||||
# our search
|
||||
result_word_count_max = len(query_word_list) + 3
|
||||
|
||||
total_result_count = min(total_result_count, max_results)
|
||||
|
||||
if callback is None:
|
||||
self.writeLog(
|
||||
"Found {0} of {1} results\n".format(
|
||||
@ -258,7 +271,29 @@ class ComicVineTalker(QObject):
|
||||
callback(current_result_count, total_result_count)
|
||||
|
||||
# see if we need to keep asking for more pages...
|
||||
stop_searching = False
|
||||
while (current_result_count < total_result_count):
|
||||
|
||||
last_result = search_results[-1]['name']
|
||||
|
||||
# See if the last result's name has all the of the search terms.
|
||||
# if not, break out of this, loop, we're done.
|
||||
#print("Searching for {} in '{}'".format(query_word_list, last_result))
|
||||
for term in query_word_list:
|
||||
if term not in last_result.lower():
|
||||
#print("Term '{}' not in last result. Halting search result fetching".format(term))
|
||||
stop_searching = True
|
||||
break
|
||||
|
||||
# Also, stop searching when the word count of last results is too much longer
|
||||
# than our search terms list
|
||||
if len(utils.removearticles(last_result).split()) > result_word_count_max:
|
||||
#print("Last result '{}' is too long. Halting search result fetching".format(last_result))
|
||||
stop_searching = True
|
||||
|
||||
if stop_searching:
|
||||
break
|
||||
|
||||
if callback is None:
|
||||
self.writeLog(
|
||||
"getting another page of results {0} of {1}...\n".format(
|
||||
@ -274,6 +309,15 @@ class ComicVineTalker(QObject):
|
||||
if callback is not None:
|
||||
callback(current_result_count, total_result_count)
|
||||
|
||||
# Remove any search results that don't contain all the search terms
|
||||
# (iterate backwards for easy removal)
|
||||
for i in range(len(search_results) - 1, -1, -1):
|
||||
record = search_results[i]
|
||||
for term in query_word_list:
|
||||
if term not in record['name'].lower():
|
||||
del search_results[i]
|
||||
break
|
||||
|
||||
# for record in search_results:
|
||||
#print(u"{0}: {1} ({2})".format(record['id'], record['name'] , record['start_year']))
|
||||
# print(record)
|
||||
@ -328,16 +372,16 @@ class ComicVineTalker(QObject):
|
||||
limit = cv_response['limit']
|
||||
current_result_count = cv_response['number_of_page_results']
|
||||
total_result_count = cv_response['number_of_total_results']
|
||||
# print "ATB total_result_count", total_result_count
|
||||
#print("total_result_count", total_result_count)
|
||||
|
||||
#print("ATB Found {0} of {1} results".format(cv_response['number_of_page_results'], cv_response['number_of_total_results']))
|
||||
#print("Found {0} of {1} results".format(cv_response['number_of_page_results'], cv_response['number_of_total_results']))
|
||||
volume_issues_result = cv_response['results']
|
||||
page = 1
|
||||
offset = 0
|
||||
|
||||
# see if we need to keep asking for more pages...
|
||||
while (current_result_count < total_result_count):
|
||||
#print("ATB getting another page of issue results {0} of {1}...".format(current_result_count, total_result_count))
|
||||
#print("getting another page of issue results {0} of {1}...".format(current_result_count, total_result_count))
|
||||
page += 1
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
@ -380,16 +424,16 @@ class ComicVineTalker(QObject):
|
||||
limit = cv_response['limit']
|
||||
current_result_count = cv_response['number_of_page_results']
|
||||
total_result_count = cv_response['number_of_total_results']
|
||||
# print "ATB total_result_count", total_result_count
|
||||
#print("total_result_count", total_result_count)
|
||||
|
||||
#print("ATB Found {0} of {1} results\n".format(cv_response['number_of_page_results'], cv_response['number_of_total_results']))
|
||||
#print("Found {0} of {1} results\n".format(cv_response['number_of_page_results'], cv_response['number_of_total_results']))
|
||||
filtered_issues_result = cv_response['results']
|
||||
page = 1
|
||||
offset = 0
|
||||
|
||||
# see if we need to keep asking for more pages...
|
||||
while (current_result_count < total_result_count):
|
||||
#print("ATB getting another page of issue results {0} of {1}...\n".format(current_result_count, total_result_count))
|
||||
#print("getting another page of issue results {0} of {1}...\n".format(current_result_count, total_result_count))
|
||||
page += 1
|
||||
offset += cv_response['number_of_page_results']
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
# This file should contain only these comments, and the line below.
|
||||
# Used by packaging makefiles and app
|
||||
version = "1.1.30-rc1"
|
||||
version = "1.2.0+2"
|
@ -67,7 +67,7 @@ class ImageFetcher(QObject):
|
||||
self.create_image_db()
|
||||
|
||||
# always use a tls context for urlopen
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
|
||||
self.ssl = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
|
||||
def clearCache(self):
|
||||
os.unlink(self.db_file)
|
||||
|
@ -1 +1 @@
|
||||
1.1.16-beta-rc
|
||||
1.2.0+2
|
||||
|
@ -1,5 +0,0 @@
|
||||
# to be run inside comictagger folder
|
||||
# installs conda and pip dependencies
|
||||
activate comictagger
|
||||
conda install -y pyqt=4
|
||||
pip install -r .\requirements.txt
|
@ -1,3 +0,0 @@
|
||||
$env:PATH+=";C:\ProgramData\Miniconda2\Scripts;C:\ProgramData\Miniconda2"
|
||||
$env:PATH+=";C:\tools\mingw64\bin"
|
||||
Set-Alias make mingw32-make.exe -scope Global
|
@ -1,5 +1,5 @@
|
||||
# Script to be run inside appveyor for a full build
|
||||
choco install -y mingw
|
||||
choco install -y mingw zip
|
||||
refreshenv
|
||||
$env:PATH="C:\Python36-x64;$env:path"
|
||||
python -m venv venv
|
||||
|
@ -1,11 +0,0 @@
|
||||
# This script should be run into an admin PowerShell to install all required
|
||||
# packages needed for building comictagger on windows
|
||||
#
|
||||
# NOTE: this script has not been fully tested on a fresh windows VM.
|
||||
#
|
||||
# install chocolatey
|
||||
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
|
||||
choco install -y git mingw miniconda
|
||||
$env:PATH+=";C:\ProgramData\Miniconda2\Scripts;C:\ProgramData\Miniconda2"
|
||||
$env:PATH+=";C:\tools\mingw64\bin"
|
||||
conda create -y --name comictagger python=2
|
Reference in New Issue
Block a user