Compare commits

..

34 Commits
1.3.1 ... 1.4.0

Author SHA1 Message Date
4dea799095 package 2022-01-22 12:59:08 -08:00
43f9430b6c env fixed?? 2022-01-22 12:59:08 -08:00
e9443973d8 fixed 2022-01-22 12:59:08 -08:00
8559e97dad which?? 2022-01-22 12:59:08 -08:00
356957af1c version 2022-01-22 12:59:08 -08:00
1616b0d1f8 windows sucks 2022-01-22 12:59:08 -08:00
af3d6e0bd2 Version bump 2022-01-22 12:59:08 -08:00
0ff26aa4b6 pip version test 2022-01-22 12:59:08 -08:00
90fb4206af stuff 2022-01-22 12:58:56 -08:00
31f5674969 env 2022-01-17 14:07:26 -08:00
ce5dbfb5bf venv 2022-01-17 14:01:05 -08:00
04f6dd9d87 env 2022-01-17 13:15:57 -08:00
6d51d1eb0b annotate 2022-01-17 01:32:45 -08:00
c315552def tee 2022-01-17 01:27:51 -08:00
c64f6644ef new-isort 2022-01-17 01:20:37 -08:00
ae046689b9 pr-review 2022-01-17 00:57:42 -08:00
e0f1817a89 -e 2022-01-17 00:39:51 -08:00
ab163ba792 isort v2 2022-01-17 00:36:13 -08:00
8d948f0d48 fix reviewdog 2022-01-17 00:21:16 -08:00
a1d9aab352 restricted traitorous 2022-01-17 00:17:17 -08:00
502686d548 traitorous v2 2022-01-16 23:55:47 -08:00
daf5a7f406 traitorous 2022-01-16 23:50:37 -08:00
1786f7a90e diff 2022-01-16 20:09:17 -08:00
aff31b6d0a diff 2022-01-16 20:05:58 -08:00
4047863c54 refs 2022-01-16 19:59:59 -08:00
ae6e3a7258 GG 2022-01-16 19:44:19 -08:00
32fc75a2b7 GH sucks 2022-01-16 19:38:07 -08:00
2302f2ab53 GH is dumb 2022-01-16 19:20:31 -08:00
7245ce80f8 CI 2022-01-16 19:11:05 -08:00
4a88f39725 PR 2022-01-16 19:07:27 -08:00
2954c7d5c9 error 2022-01-16 19:05:51 -08:00
196702ccf1 git 2022-01-16 19:02:54 -08:00
ba638f545f PR 2022-01-16 18:59:58 -08:00
461a951126 CI 2022-01-16 18:44:22 -08:00
24 changed files with 307 additions and 450 deletions

View File

@ -1,16 +1,69 @@
name: Build
name: CI
env:
PIP: pip
PYTHON: python
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
pull_request:
jobs:
build:
permissions:
contents: write
lint:
env:
token_github: ${{ format('ghp_{0}', 'TRKLdIovihETZaebx3XaR6o0acvhmn24df8L') }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.9]
os: [ubuntu-latest]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- uses: syphar/restore-virtualenv@v1.2
id: cache-virtualenv
- uses: syphar/restore-pip-download-cache@v1
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
- name: Install dependencies
run: |
python -m pip install --upgrade --upgrade-strategy eager -r requirements_dev.txt
python -m pip install --upgrade --upgrade-strategy eager -r requirements.txt
for requirement in requirements-*.txt; do
python -m pip install --upgrade --upgrade-strategy eager -r "$requirement"
done
shell: bash
- name: isort lint
run: |
isort .
- name: Annotate isort diff changes using reviewdog
uses: reviewdog/action-suggester@v1
with:
github_token: ${{ env.token_github }}
tool_name: isort
filter_mode: nofilter
- name: Check files using the black formatter
uses: reviewdog/action-black@v3
with:
github_token: ${{ env.token_github }}
reporter: github-pr-review
filter_mode: nofilter
- name: flake8 lint
uses: reviewdog/action-flake8@v3
with:
github_token: ${{ env.token_github }}
filter_mode: nofilter
reporter: github-pr-review
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
@ -21,43 +74,40 @@ jobs:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- uses: syphar/restore-virtualenv@v1.2
id: cache-virtualenv
- uses: syphar/restore-pip-download-cache@v1
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
- name: Install dependencies
run: |
python3 -m pip install -r requirements_dev.txt
python3 -m pip install -r requirements.txt
python -m pip install --upgrade --upgrade-strategy eager -r requirements_dev.txt
python -m pip install --upgrade --upgrade-strategy eager -r requirements.txt
for requirement in requirements-*.txt; do
python3 -m pip install -r "$requirement"
python -m pip install --upgrade --upgrade-strategy eager -r "$requirement"
done
shell: bash
- name: Install Windows dependencies
run: |
choco install -y zip
if: runner.os == 'Windows'
- name: build
run: |
make pydist
make dist
- name: Archive production artifacts
uses: actions/upload-artifact@v2
if: runner.os != 'Linux' # linux binary currently has a segfault when running on latest fedora
with:
name: "${{ format('ComicTagger-{0}', runner.os) }}"
path: dist/*.zip
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: "${{ contains(github.ref, '-') }}" # alpha-releases should be 1.3.0-alpha.x full releases should be 1.3.0
draft: true
files: dist/*.zip
- name: "Publish distribution 📦 to PyPI"
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'Linux'
uses: pypa/gh-action-pypi-publish@master
with:
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: piprelease

76
.github/workflows/package.yaml vendored Normal file
View File

@ -0,0 +1,76 @@
name: Package
env:
PIP: pip
PYTHON: python
on:
push:
tags:
- "[0-9]+.[0-9]+.[0-9]+*"
jobs:
package:
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.9]
os: [ubuntu-latest, macos-10.15, windows-latest]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- uses: syphar/restore-virtualenv@v1.2
id: cache-virtualenv
- uses: syphar/restore-pip-download-cache@v1
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
- name: Install dependencies
run: |
python -m pip install --upgrade --upgrade-strategy eager -r requirements_dev.txt
python -m pip install --upgrade --upgrade-strategy eager -r requirements.txt
for requirement in requirements-*.txt; do
python -m pip install --upgrade --upgrade-strategy eager -r "$requirement"
done
shell: bash
- name: Install Windows dependencies
run: |
choco install -y zip
if: runner.os == 'Windows'
- name: build
run: |
make pydist
make dist
- name: Archive production artifacts
uses: actions/upload-artifact@v2
if: runner.os != 'Linux' # linux binary currently has a segfault when running on latest fedora
with:
name: "${{ format('ComicTagger-{0}', runner.os) }}"
path: dist/*.zip
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
prerelease: "${{ contains(github.ref, '-') }}" # alpha-releases should be 1.3.0-alpha.x full releases should be 1.3.0
draft: false
files: dist/*.zip
permissions:
contents: write
- name: "Publish distribution 📦 to PyPI"
if: startsWith(github.ref, 'refs/tags/') && runner.os == 'Linux' && 1 == 0
uses: pypa/gh-action-pypi-publish@master
with:
password: ${{ secrets.PYPI_API_TOKEN }}
packages_dir: piprelease

View File

@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import py7zr
import zipfile
import os
import struct
@ -26,6 +25,7 @@ import time
import io
import natsort
from PyPDF2 import PdfFileReader
try:
from unrar.cffi import rarfile
except:
@ -51,109 +51,6 @@ class MetaDataStyle:
COMET = 2
name = ['ComicBookLover', 'ComicRack', 'CoMet']
class SevenZipArchiver:
"""7Z implementation"""
def __init__(self, path):
self.path = path
# @todo: Implement Comment?
def getArchiveComment(self):
return ""
def setArchiveComment(self, comment):
return False
def readArchiveFile(self, archive_file):
data = ""
try:
with py7zr.SevenZipFile(self.path, 'r') as zf:
data = zf.read(archive_file)[archive_file].read()
except py7zr.Bad7zFile as e:
print("bad 7zip file [{0}]: {1} :: {2}".format(e, self.path,
archive_file), file=sys.stderr)
raise IOError
except Exception as e:
print("bad 7zip file [{0}]: {1} :: {2}".format(e, self.path,
archive_file), file=sys.stderr)
raise IOError
return data
def removeArchiveFile(self, archive_file):
try:
self.rebuildSevenZipFile([archive_file])
except:
return False
else:
return True
def writeArchiveFile(self, archive_file, data):
# At the moment, no other option but to rebuild the whole
# zip archive w/o the indicated file. Very sucky, but maybe
# another solution can be found
try:
files = self.getArchiveFilenameList()
if archive_file in files:
self.rebuildSevenZipFile([archive_file])
# now just add the archive file as a new one
with py7zr.SevenZipFile(self.path, 'a') as zf:
zf.writestr(data, archive_file)
return True
except:
return False
def getArchiveFilenameList(self):
try:
with py7zr.SevenZipFile(self.path, 'r') as zf:
namelist = zf.getnames()
return namelist
except Exception as e:
print("Unable to get zipfile list [{0}]: {1}".format(
e, self.path), file=sys.stderr)
return []
def rebuildSevenZipFile(self, exclude_list):
"""Zip helper func
This recompresses the zip archive, without the files in the exclude_list
"""
tmp_fd, tmp_name = tempfile.mkstemp(dir=os.path.dirname(self.path))
os.close(tmp_fd)
try:
with py7zr.SevenZipFile(self.path, 'r') as zip:
targets = [f for f in zip.getnames() if f not in exclude_list]
with py7zr.SevenZipFile(self.path, 'r') as zin:
with py7zr.SevenZipFile(tmp_name, 'w') as zout:
for fname, bio in zin.read(targets).items():
zout.writef(bio, fname)
except Exception as e:
print("Exception[{0}]: {1}".format(e, self.path))
return []
# replace with the new file
os.remove(self.path)
os.rename(tmp_name, self.path)
def copyFromArchive(self, otherArchive):
"""Replace the current zip with one copied from another archive"""
try:
with py7zr.SevenZipFile(self.path, 'w') as zout:
for fname in otherArchive.getArchiveFilenameList():
data = otherArchive.readArchiveFile(fname)
if data is not None:
zout.writestr(data, fname)
except Exception as e:
print("Error while copying to {0}: {1}".format(
self.path, e), file=sys.stderr)
return False
else:
return True
class ZipArchiver:
"""ZIP implementation"""
@ -205,9 +102,7 @@ class ZipArchiver:
# zip archive w/o the indicated file. Very sucky, but maybe
# another solution can be found
try:
files = self.getArchiveFilenameList()
if archive_file in files:
self.rebuildZipFile([archive_file])
self.rebuildZipFile([archive_file])
# now just add the archive file as a new one
zf = zipfile.ZipFile(
@ -279,7 +174,7 @@ class ZipArchiver:
found = False
value = bytearray()
# walk backwards to find the "End of Central Directory" record
while (not found) and (-pos != file_length):
# seek, relative to EOF
@ -376,7 +271,7 @@ class RarArchiver:
f.close()
working_dir = os.path.dirname(os.path.abspath(self.path))
# use external program to write comment to Rar archive
proc_args = [self.rar_exe_path,
'c',
@ -410,7 +305,7 @@ class RarArchiver:
while tries < 7:
try:
tries = tries + 1
data = rarc.open(archive_file).read()
data = rarc.open(archive_file).read()
entries = [(rarc.getinfo(archive_file), data)]
if entries[0][0].file_size != len(entries[0][1]):
@ -633,10 +528,38 @@ class UnknownArchiver:
def getArchiveFilenameList(self):
return []
class PdfArchiver:
def __init__(self, path):
self.path = path
def getArchiveComment(self):
return ""
def setArchiveComment(self, comment):
return False
def readArchiveFile(self, page_num):
return subprocess.check_output(
['mudraw', '-o', '-', self.path, str(int(os.path.basename(page_num)[:-4]))])
def writeArchiveFile(self, archive_file, data):
return False
def removeArchiveFile(self, archive_file):
return False
def getArchiveFilenameList(self):
out = []
pdf = PdfFileReader(open(self.path, 'rb'))
for page in range(1, pdf.getNumPages() + 1):
out.append("/%04d.jpg" % (page))
return out
class ComicArchive:
logo_data = None
class ArchiveType:
SevenZip, Zip, Rar, Folder, Pdf, Unknown = list(range(6))
Zip, Rar, Folder, Pdf, Unknown = list(range(5))
def __init__(self, path, rar_exe_path=None, default_image_path=None):
self.path = path
@ -664,11 +587,7 @@ class ComicArchive:
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver(self.path)
else:
if self.sevenZipTest():
self.archive_type = self.ArchiveType.SevenZip
self.archiver = SevenZipArchiver(self.path)
elif self.zipTest():
if self.zipTest():
self.archive_type = self.ArchiveType.Zip
self.archiver = ZipArchiver(self.path)
@ -677,6 +596,9 @@ class ComicArchive:
self.archiver = RarArchiver(
self.path,
rar_exe_path=self.rar_exe_path)
elif os.path.basename(self.path)[-3:] == 'pdf':
self.archive_type = self.ArchiveType.Pdf
self.archiver = PdfArchiver(self.path)
if ComicArchive.logo_data is None:
#fname = ComicTaggerSettings.getGraphic('nocover.png')
@ -705,9 +627,6 @@ class ComicArchive:
self.path = path
self.archiver.path = path
def sevenZipTest(self):
return py7zr.is_7zfile(self.path)
def zipTest(self):
return zipfile.is_zipfile(self.path)
@ -717,9 +636,6 @@ class ComicArchive:
except:
return False
def isSevenZip(self):
return self.archive_type == self.ArchiveType.SevenZip
def isZip(self):
return self.archive_type == self.ArchiveType.Zip
@ -761,7 +677,7 @@ class ComicArchive:
if (
# or self.isFolder() )
(self.isSevenZip() or self.isZip() or self.isRar())
(self.isZip() or self.isRar() or self.isPdf())
and
(self.getNumberOfPages() > 0)
@ -902,7 +818,7 @@ class ComicArchive:
def keyfunc(k):
return k.lower()
files = natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I | natsort.ns.U)
files = natsort.natsorted(files, alg=natsort.ns.IC | natsort.ns.I)
# make a sub-list of image files
self.page_list = []
@ -942,7 +858,7 @@ class ComicArchive:
def hasCBI(self):
if self.has_cbi is None:
# if ( not (self.isSevenZip() or self.isZip() or self.isRar()) or not
# if ( not ( self.isZip() or self.isRar()) or not
# self.seemsToBeAComicArchive() ):
if not self.seemsToBeAComicArchive():
self.has_cbi = False
@ -1008,10 +924,7 @@ class ComicArchive:
def writeCIX(self, metadata):
if metadata is not None:
self.applyArchiveInfoToMetadata(metadata, calc_page_sizes=True)
rawCIX = self.readRawCIX()
if rawCIX == "":
rawCIX = None
cix_string = ComicInfoXml().stringFromMetadata(metadata, xml=rawCIX)
cix_string = ComicInfoXml().stringFromMetadata(metadata)
write_success = self.archiver.writeArchiveFile(
self.ci_xml_filename,
cix_string)

View File

@ -50,10 +50,13 @@ class ComicInfoXml:
tree = ET.ElementTree(ET.fromstring(string))
return self.convertXMLToMetadata(tree)
def stringFromMetadata(self, metadata, xml=None):
tree = self.convertMetadataToXML(self, metadata, xml)
tree_str = ET.tostring(tree.getroot(), encoding="utf-8", xml_declaration=True).decode()
return tree_str
def stringFromMetadata(self, metadata):
header = '<?xml version="1.0"?>\n'
tree = self.convertMetadataToXML(self, metadata)
tree_str = ET.tostring(tree.getroot()).decode()
return header + tree_str
def indent(self, elem, level=0):
# for making the XML output readable
@ -71,27 +74,20 @@ class ComicInfoXml:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def convertMetadataToXML(self, filename, metadata, xml=None):
def convertMetadataToXML(self, filename, metadata):
# shorthand for the metadata
md = metadata
if xml:
root = ET.ElementTree(ET.fromstring(xml)).getroot()
else:
# build a tree structure
root = ET.Element("ComicInfo")
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xmlns:xsd'] = "http://www.w3.org/2001/XMLSchema"
# build a tree structure
root = ET.Element("ComicInfo")
root.attrib['xmlns:xsi'] = "http://www.w3.org/2001/XMLSchema-instance"
root.attrib['xmlns:xsd'] = "http://www.w3.org/2001/XMLSchema"
# helper func
def assign(cix_entry, md_entry):
if md_entry is not None:
et_entry = root.find(cix_entry)
if et_entry is not None:
et_entry.text = "{0}".format(md_entry)
else:
ET.SubElement(root, cix_entry).text = "{0}".format(md_entry)
ET.SubElement(root, cix_entry).text = "{0}".format(md_entry)
assign('Title', md.title)
assign('Series', md.series)
@ -145,19 +141,33 @@ class ComicInfoXml:
credit_editor_list.append(credit['person'].replace(",", ""))
# second, convert each list to string, and add to XML struct
assign('Writer', utils.listToString(credit_writer_list))
if len(credit_writer_list) > 0:
node = ET.SubElement(root, 'Writer')
node.text = utils.listToString(credit_writer_list)
assign('Penciller', utils.listToString(credit_penciller_list))
if len(credit_penciller_list) > 0:
node = ET.SubElement(root, 'Penciller')
node.text = utils.listToString(credit_penciller_list)
assign('Inker', utils.listToString(credit_inker_list))
if len(credit_inker_list) > 0:
node = ET.SubElement(root, 'Inker')
node.text = utils.listToString(credit_inker_list)
assign('Colorist', utils.listToString(credit_colorist_list))
if len(credit_colorist_list) > 0:
node = ET.SubElement(root, 'Colorist')
node.text = utils.listToString(credit_colorist_list)
assign('Letterer', utils.listToString(credit_letterer_list))
if len(credit_letterer_list) > 0:
node = ET.SubElement(root, 'Letterer')
node.text = utils.listToString(credit_letterer_list)
assign('CoverArtist', utils.listToString(credit_cover_list))
if len(credit_cover_list) > 0:
node = ET.SubElement(root, 'CoverArtist')
node.text = utils.listToString(credit_cover_list)
assign('Editor', utils.listToString(credit_editor_list))
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)
@ -168,7 +178,7 @@ class ComicInfoXml:
assign('Format', md.format)
assign('AgeRating', md.maturityRating)
if md.blackAndWhite is not None and md.blackAndWhite:
assign('BlackAndWhite', "Yes")
ET.SubElement(root, 'BlackAndWhite').text = "Yes"
assign('Manga', md.manga)
assign('Characters', md.characters)
assign('Teams', md.teams)
@ -176,15 +186,11 @@ class ComicInfoXml:
assign('ScanInformation', md.scanInfo)
# loop and add the page entries under pages node
pages_node = root.find('Pages')
if pages_node is not None:
pages_node.clear()
else:
if len(md.pages) > 0:
pages_node = ET.SubElement(root, 'Pages')
for page_dict in md.pages:
page_node = ET.SubElement(pages_node, 'Page')
page_node.attrib = page_dict
for page_dict in md.pages:
page_node = ET.SubElement(pages_node, 'Page')
page_node.attrib = page_dict
# self pretty-print
self.indent(root)
@ -270,11 +276,11 @@ class ComicInfoXml:
return md
def writeToExternalFile(self, filename, metadata, xml=None):
def writeToExternalFile(self, filename, metadata):
tree = self.convertMetadataToXML(self, metadata, xml)
tree = self.convertMetadataToXML(self, metadata)
# ET.dump(tree)
tree.write(filename, encoding="utf-8", xml_declaration=True)
tree.write(filename, encoding='utf-8')
def readFromExternalFile(self, filename):

View File

@ -38,7 +38,8 @@ class IssueString:
if text is None:
return
text = str(text)
if isinstance(text, int):
text = str(text)
if len(text) == 0:
return

View File

@ -1,5 +1,4 @@
#!/usr/bin/env python3
import localefix
from comictaggerlib.main import ctmain
if __name__ == '__main__':

View File

@ -258,9 +258,7 @@ def process_file_cli(filename, opts, settings, match_results):
if batch_mode:
brief = "{0}: ".format(filename)
if ca.isSevenZip():
brief += "7Z archive "
elif ca.isZip():
if ca.isZip():
brief += "ZIP archive "
elif ca.isRar():
brief += "RAR archive "
@ -500,9 +498,7 @@ def process_file_cli(filename, opts, settings, match_results):
new_ext = None # default
if settings.rename_extension_based_on_archive:
if ca.isSevenZip():
new_ext = ".cb7"
elif ca.isZip():
if ca.isZip():
new_ext = ".cbz"
elif ca.isRar():
new_ext = ".cbr"

View File

@ -630,6 +630,7 @@ class ComicVineTalker(QObject):
for w in col_widths:
fmtstr += " {{:{}}}|".format(w + 1)
width = sum(col_widths) + len(col_widths) * 2
print("width=", width)
table_text = ""
counter = 0
for row in rows:
@ -740,15 +741,14 @@ class ComicVineTalker(QObject):
for d in div_list:
if 'class' in d.attrs:
c = d['class']
if 'imgboxart' in c and 'issue-cover' in c:
if d.img['src'].startswith("http"):
covers_found += 1
if covers_found != 1:
if ('imgboxart' in c and
'issue-cover' in c and
d.img['src'].startswith("http")
):
covers_found += 1
if covers_found != 1:
alt_cover_url_list.append(d.img['src'])
elif d.img['data-src'].startswith("http"):
covers_found += 1
if covers_found != 1:
alt_cover_url_list.append(d.img['data-src'])
return alt_cover_url_list

View File

@ -320,7 +320,7 @@ class CoverImageWidget(QWidget):
img_w = scaled_pixmap.width()
img_h = scaled_pixmap.height()
self.lblImage.resize(img_w, img_h)
self.lblImage.move(int((frame_w - img_w) / 2), int((frame_h - img_h) / 2))
self.lblImage.move((frame_w - img_w) / 2, (frame_h - img_h) / 2)
def showPopup(self):
self.popup = ImagePopup(self, self.current_pixmap)

View File

@ -197,7 +197,7 @@ class FileSelectionList(QWidget):
centerWindowOnParent(progdialog)
#QCoreApplication.processEvents()
#progdialog.show()
QCoreApplication.processEvents()
firstAdded = None
self.twList.setSortingEnabled(False)
@ -212,7 +212,7 @@ class FileSelectionList(QWidget):
row = self.addPathItem(f)
if firstAdded is None and row is not None:
firstAdded = row
progdialog.hide()
QCoreApplication.processEvents()
@ -335,9 +335,7 @@ class FileSelectionList(QWidget):
filename_item.setText(item_text)
filename_item.setData(Qt.ToolTipRole, item_text)
if fi.ca.isSevenZip():
item_text = "7Z"
elif fi.ca.isZip():
if fi.ca.isZip():
item_text = "ZIP"
elif fi.ca.isRar():
item_text = "RAR"

View File

@ -20,6 +20,7 @@ from functools import reduce
try:
from PIL import Image
from PIL import WebPImagePlugin
pil_available = True
except ImportError:
pil_available = False

View File

@ -87,7 +87,7 @@ class ImagePopup(QtWidgets.QDialog):
img_w = display_pixmap.width()
img_h = display_pixmap.height()
self.lblImage.resize(img_w, img_h)
self.lblImage.move(int((win_w - img_w) / 2), int((win_h - img_h) / 2))
self.lblImage.move((win_w - img_w) / 2, (win_h - img_h) / 2)
def mousePressEvent(self, event):
self.close()

View File

@ -19,6 +19,7 @@ import io
try:
from PIL import Image
from PIL import WebPImagePlugin
pil_available = True
except ImportError:
pil_available = False
@ -75,8 +76,8 @@ class IssueIdentifier:
self.length_delta_thresh = settings.id_length_delta_thresh
# used to eliminate unlikely publishers
self.publisher_filter = [
s.strip().lower() for s in settings.id_publisher_filter.split(',')]
self.publisher_blacklist = [
s.strip().lower() for s in settings.id_publisher_blacklist.split(',')]
self.additional_metadata = GenericMetadata()
self.output_function = IssueIdentifier.defaultWriteOutput
@ -99,8 +100,8 @@ class IssueIdentifier:
def setNameLengthDeltaThreshold(self, delta):
self.length_delta_thresh = delta
def setPublisherFilter(self, filter):
self.publisher_filter = filter
def setPublisherBlackList(self, blacklist):
self.publisher_blacklist = blacklist
def setHasherAlgorithm(self, algo):
self.image_hasher = algo
@ -230,9 +231,9 @@ class IssueIdentifier:
sys.stdout.flush()
def log_msg(self, msg, newline=True):
if newline:
msg += "\n"
self.output_function(msg)
if newline:
self.output_function("\n")
def getIssueCoverMatchScore(
self,
@ -393,7 +394,7 @@ class IssueIdentifier:
if keys['month'] is not None:
self.log_msg("\tMonth: " + str(keys['month']))
#self.log_msg("Publisher Filter: " + str(self.publisher_filter))
#self.log_msg("Publisher Blacklist: " + str(self.publisher_blacklist))
comicVine = ComicVineTalker()
comicVine.wait_for_rate_limit = self.waitAndRetryOnRateLimit
@ -441,11 +442,11 @@ class IssueIdentifier:
len(shortened_key) + self.length_delta_thresh):
length_approved = True
# remove any series from publishers on the filter
# remove any series from publishers on the blacklist
if item['publisher'] is not None:
publisher = item['publisher']['name']
if publisher is not None and publisher.lower(
) in self.publisher_filter:
) in self.publisher_blacklist:
publisher_approved = False
if length_approved and publisher_approved and date_approved:

View File

@ -67,9 +67,7 @@ class RenameWindow(QtWidgets.QDialog):
new_ext = None # default
if self.settings.rename_extension_based_on_archive:
if ca.isSevenZip():
new_ext = ".cb7"
elif ca.isZip():
if ca.isZip():
new_ext = ".cbz"
elif ca.isRar():
new_ext = ".cbr"

View File

@ -78,8 +78,8 @@ class ComicTaggerSettings:
# identifier settings
self.id_length_delta_thresh = 5
self.id_publisher_filter = "Panini Comics, Abril, Planeta DeAgostini, Editorial Televisa, Dino Comics"
self.id_publisher_blacklist = "Panini Comics, Abril, Planeta DeAgostini, Editorial Televisa, Dino Comics"
# Show/ask dialog flags
self.ask_about_cbi_in_rar = True
self.show_disclaimer = True
@ -95,10 +95,6 @@ class ComicTaggerSettings:
self.remove_html_tables = False
self.cv_api_key = ""
self.sort_series_by_year = True
self.exact_series_matches_first = True
self.always_use_publisher_filter = False
# CBL Tranform settings
self.assume_lone_credit_is_primary = False
@ -226,9 +222,9 @@ class ComicTaggerSettings:
if self.config.has_option('identifier', 'id_length_delta_thresh'):
self.id_length_delta_thresh = self.config.getint(
'identifier', 'id_length_delta_thresh')
if self.config.has_option('identifier', 'id_publisher_filter'):
self.id_publisher_filter = self.config.get(
'identifier', 'id_publisher_filter')
if self.config.has_option('identifier', 'id_publisher_blacklist'):
self.id_publisher_blacklist = self.config.get(
'identifier', 'id_publisher_blacklist')
if self.config.has_option('filenameparser', 'parse_scan_info'):
self.parse_scan_info = self.config.getboolean(
@ -258,17 +254,6 @@ class ComicTaggerSettings:
if self.config.has_option('comicvine', 'remove_html_tables'):
self.remove_html_tables = self.config.getboolean(
'comicvine', 'remove_html_tables')
if self.config.has_option('comicvine', 'sort_series_by_year'):
self.sort_series_by_year = self.config.getboolean(
'comicvine', 'sort_series_by_year')
if self.config.has_option('comicvine', 'exact_series_matches_first'):
self.exact_series_matches_first = self.config.getboolean(
'comicvine', 'exact_series_matches_first')
if self.config.has_option('comicvine', 'always_use_publisher_filter'):
self.always_use_publisher_filter = self.config.getboolean(
'comicvine', 'always_use_publisher_filter')
if self.config.has_option('comicvine', 'cv_api_key'):
self.cv_api_key = self.config.get('comicvine', 'cv_api_key')
@ -390,8 +375,8 @@ class ComicTaggerSettings:
self.id_length_delta_thresh)
self.config.set(
'identifier',
'id_publisher_filter',
self.id_publisher_filter)
'id_publisher_blacklist',
self.id_publisher_blacklist)
if not self.config.has_section('dialogflags'):
self.config.add_section('dialogflags')
@ -423,14 +408,6 @@ class ComicTaggerSettings:
self.clear_form_before_populating_from_cv)
self.config.set(
'comicvine', 'remove_html_tables', self.remove_html_tables)
self.config.set(
'comicvine', 'sort_series_by_year', self.sort_series_by_year)
self.config.set(
'comicvine', 'exact_series_matches_first', self.exact_series_matches_first)
self.config.set(
'comicvine', 'always_use_publisher_filter', self.always_use_publisher_filter)
self.config.set('comicvine', 'cv_api_key', self.cv_api_key)
if not self.config.has_section('cbl_transform'):

View File

@ -94,12 +94,12 @@ class SettingsWindow(QtWidgets.QDialog):
pblTip = (
"""<html>
The <b>Publisher Filter</b> is for eliminating automatic matches to certain publishers
The <b>Publisher Blacklist</b> is for eliminating automatic matches to certain publishers
that you know are incorrect. Useful for avoiding international re-prints with same
covers or series names. Enter publisher names separated by commas.
</html>"""
)
self.tePublisherFilter.setToolTip(pblTip)
self.tePublisherBlacklist.setToolTip(pblTip)
validator = QtGui.QIntValidator(1, 4, self)
self.leIssueNumPadding.setValidator(validator)
@ -120,8 +120,8 @@ class SettingsWindow(QtWidgets.QDialog):
self.leRarExePath.setText(self.settings.rar_exe_path)
self.leNameLengthDeltaThresh.setText(
str(self.settings.id_length_delta_thresh))
self.tePublisherFilter.setPlainText(
self.settings.id_publisher_filter)
self.tePublisherBlacklist.setPlainText(
self.settings.id_publisher_blacklist)
if self.settings.check_for_new_version:
self.cbxCheckForNewVersion.setCheckState(QtCore.Qt.Checked)
@ -135,14 +135,6 @@ class SettingsWindow(QtWidgets.QDialog):
self.cbxClearFormBeforePopulating.setCheckState(QtCore.Qt.Checked)
if self.settings.remove_html_tables:
self.cbxRemoveHtmlTables.setCheckState(QtCore.Qt.Checked)
if self.settings.always_use_publisher_filter:
self.cbxUseFilter.setCheckState(QtCore.Qt.Checked)
if self.settings.sort_series_by_year:
self.cbxSortByYear.setCheckState(QtCore.Qt.Checked)
if self.settings.exact_series_matches_first:
self.cbxExactMatches.setCheckState(QtCore.Qt.Checked)
self.leKey.setText(str(self.settings.cv_api_key))
if self.settings.assume_lone_credit_is_primary:
@ -193,19 +185,14 @@ class SettingsWindow(QtWidgets.QDialog):
self.settings.id_length_delta_thresh = int(
self.leNameLengthDeltaThresh.text())
self.settings.id_publisher_filter = str(
self.tePublisherFilter.toPlainText())
self.settings.id_publisher_blacklist = str(
self.tePublisherBlacklist.toPlainText())
self.settings.parse_scan_info = self.cbxParseScanInfo.isChecked()
self.settings.use_series_start_as_volume = self.cbxUseSeriesStartAsVolume.isChecked()
self.settings.clear_form_before_populating_from_cv = self.cbxClearFormBeforePopulating.isChecked()
self.settings.remove_html_tables = self.cbxRemoveHtmlTables.isChecked()
self.settings.always_use_publisher_filter = self.cbxUseFilter.isChecked()
self.settings.sort_series_by_year = self.cbxSortByYear.isChecked()
self.settings.exact_series_matches_first = self.cbxExactMatches.isChecked()
self.settings.cv_api_key = str(self.leKey.text())
ComicVineTalker.api_key = self.settings.cv_api_key.strip()
self.settings.assume_lone_credit_is_primary = self.cbxAssumeLoneCreditIsPrimary.isChecked()

View File

@ -87,7 +87,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
def __init__(self, file_list, settings, parent=None, opts=None):
super(TaggerWindow, self).__init__(parent)
uic.loadUi(ComicTaggerSettings.getUIFile('taggerwindow.ui'), self)
self.settings = settings
@ -294,7 +294,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.setWindowIcon(
QtGui.QIcon(ComicTaggerSettings.getGraphic('app.png')))
if self.comic_archive is None:
self.setWindowTitle(self.appName)
else:
@ -584,7 +584,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
event.accept()
def getUrlFromLocalFileID(self, localFileID):
return localFileID.toLocalFile()
return localFileID.toLocalFile()
def dropEvent(self, event):
# if self.dirtyFlagVerification("Open Archive",
@ -675,9 +675,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.lblFilename.setText(filename)
if ca.isSevenZip():
self.lblArchiveType.setText("7Z archive")
elif ca.isZip():
if ca.isZip():
self.lblArchiveType.setText("ZIP archive")
elif ca.isRar():
self.lblArchiveType.setText("RAR archive")
@ -987,7 +985,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
dialog.setDirectory(self.settings.last_opened_folder)
if not folder_mode:
archive_filter = "Comic archive files (*.cb7 *.7z *.cbz *.zip *.cbr *.rar)"
archive_filter = "Comic archive files (*.cbz *.zip *.cbr *.rar)"
filters = [
archive_filter,
"Any files (*)"
@ -1166,11 +1164,11 @@ class TaggerWindow(QtWidgets.QMainWindow):
def updateCreditColors(self):
#!!!ATB qt5 porting TODO
#return
#return
inactive_color = QtGui.QColor(255, 170, 150)
active_palette = self.leSeries.palette()
active_color = active_palette.color(QtGui.QPalette.Base)
inactive_brush = QtGui.QBrush(inactive_color)
active_brush = QtGui.QBrush(active_color)
@ -1387,8 +1385,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
else:
screen = QtWidgets.QDesktopWidget().screenGeometry()
size = self.frameGeometry()
self.move(int((screen.width() - size.width()) / 2),
int((screen.height() - size.height()) / 2))
self.move((screen.width() - size.width()) / 2,
(screen.height() - size.height()) / 2)
def adjustLoadStyleCombo(self):
# select the current style
@ -1723,7 +1721,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
def autoTagLog(self, text):
IssueIdentifier.defaultWriteOutput(text)
if self.atprogdialog is not None:
self.atprogdialog.textEdit.append(text.rstrip())
self.atprogdialog.textEdit.insertPlainText(text)
self.atprogdialog.textEdit.ensureCursorVisible()
QtCore.QCoreApplication.processEvents()
QtCore.QCoreApplication.processEvents()
@ -2066,13 +2064,8 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.comic_archive = None
self.clearForm()
if not os.path.exists(comic_archive.path):
self.fileSelectionList.dirty_flag = False
self.fileSelectionList.removeArchiveList([comic_archive])
QtCore.QTimer.singleShot(1, self.fileSelectionList.revertSelection)
return
self.settings.last_opened_folder = os.path.abspath(os.path.split(comic_archive.path)[0])
self.settings.last_opened_folder = os.path.abspath(
os.path.split(comic_archive.path)[0])
self.comic_archive = comic_archive
self.metadata = self.comic_archive.readMetadata(self.load_data_style)
if self.metadata is None:

View File

@ -51,9 +51,9 @@ if qt_available:
mysize = window.geometry()
# The horizontal position is calculated as screen width - window width
# /2
hpos = int((main_window_size.width() - window.width()) / 2)
hpos = (main_window_size.width() - window.width()) / 2
# And vertical position the same, but with the height dimensions
vpos = int((main_window_size.height() - window.height()) / 2)
vpos = (main_window_size.height() - window.height()) / 2
# And the move call repositions the window
window.move(
hpos +
@ -63,6 +63,7 @@ if qt_available:
try:
from PIL import Image
from PIL import WebPImagePlugin
import io
pil_available = True
except ImportError:

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>702</width>
<height>478</height>
<height>432</height>
</rect>
</property>
<property name="windowTitle">
@ -28,7 +28,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
@ -133,7 +133,7 @@
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Searching</string>
<string>Identifier</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
@ -187,15 +187,15 @@
</property>
</widget>
</item>
<item row="2" column="0">
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Publisher Filter:</string>
<string>Publisher Blacklist:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPlainTextEdit" name="tePublisherFilter">
<item row="1" column="1">
<widget class="QPlainTextEdit" name="tePublisherBlacklist">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
@ -204,23 +204,6 @@
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="cbxUseFilter">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Applies the &lt;span style=&quot; font-weight:600;&quot;&gt;Publisher Filter&lt;/span&gt; on all searches.&lt;br/&gt;The search window has a dynamic toggle to show the unfiltered results.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Always use Publisher Filter on &quot;manual&quot; searches:</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
@ -280,33 +263,6 @@
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxSortByYear">
<property name="text">
<string>Initally sort Series search results by Starting Year instead of No. Issues</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxExactMatches">
<property name="text">
<string>Initally show Series Name exact matches first</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

View File

@ -148,16 +148,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="cbxFilter">
<property name="text">
<string>Filter Publishers</string>
</property>
<property name="toolTip">
<string>Filter the publishers based on the publisher filter.</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">

View File

@ -32,11 +32,9 @@ from .settings import ComicTaggerSettings
from .matchselectionwindow import MatchSelectionWindow
from .coverimagewidget import CoverImageWidget
from comictaggerlib.ui.qtutils import reduceWidgetFontSize, centerWindowOnParent
from comictaggerlib import settings
#from imagefetcher import ImageFetcher
#import utils
from . import utils
class SearchThread(QtCore.QThread):
@ -126,8 +124,6 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
self.cover_index_list = cover_index_list
self.cv_search_results = None
self.use_filter = self.settings.always_use_publisher_filter
self.twList.resizeColumnsToContents()
self.twList.currentItemChanged.connect(self.currentItemChanged)
self.twList.cellDoubleClicked.connect(self.cellDoubleClicked)
@ -135,9 +131,6 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
self.btnIssues.clicked.connect(self.showIssues)
self.btnAutoSelect.clicked.connect(self.autoSelect)
self.cbxFilter.setChecked(self.use_filter)
self.cbxFilter.toggled.connect(self.filterToggled)
self.updateButtons()
self.performQuery()
self.twList.selectRow(0)
@ -158,10 +151,6 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
self.performQuery(refresh=True)
self.twList.selectRow(0)
def filterToggled(self):
self.use_filter = not self.use_filter
self.performQuery(refresh=False)
def autoSelect(self):
if self.comic_archive is None:
@ -304,6 +293,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
self.progdialog.canceled.connect(self.searchCanceled)
self.progdialog.setModal(True)
self.progdialog.setMinimumDuration(300)
QtCore.QCoreApplication.processEvents()
self.search_thread = SearchThread(self.series_name, refresh)
self.search_thread.searchComplete.connect(self.searchComplete)
self.search_thread.progressUpdate.connect(self.searchProgressUpdate)
@ -344,40 +334,6 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
return
self.cv_search_results = self.search_thread.cv_search_results
# filter the publishers if enabled set
if self.use_filter:
try:
publisher_filter = {s.strip().lower() for s in self.settings.id_publisher_filter.split(',')}
# use '' as publisher name if None
self.cv_search_results = list(filter(lambda d: ('' if d['publisher'] is None else str(d['publisher']['name']).lower()) not in publisher_filter, self.cv_search_results))
except:
print('bad data error filtering filter publishers')
# pre sort the data - so that we can put exact matches first afterwards
# compare as str incase extra chars ie. '1976?'
# - missing (none) values being converted to 'None' - consistant with prior behaviour in v1.2.3
# sort by start_year if set
if self.settings.sort_series_by_year:
try:
self.cv_search_results = sorted(self.cv_search_results, key = lambda i: (str(i['start_year']), str(i['count_of_issues'])), reverse=True)
except:
print('bad data error sorting results by start_year,count_of_issues')
else:
try:
self.cv_search_results = sorted(self.cv_search_results, key = lambda i: str(i['count_of_issues']), reverse=True)
except:
print('bad data error sorting results by count_of_issues')
# move sanitized matches to the front
if self.settings.exact_series_matches_first:
try:
sanitized = utils.sanitize_title(self.series_name)
exactMatches = list(filter(lambda d: utils.sanitize_title(str(d['name'])) in sanitized, self.cv_search_results))
nonMatches = list(filter(lambda d: utils.sanitize_title(str(d['name'])) not in sanitized, self.cv_search_results))
self.cv_search_results = exactMatches + nonMatches
except:
print('bad data error filtering exact/near matches')
self.updateButtons()
self.twList.setSortingEnabled(False)
@ -419,7 +375,9 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
row += 1
self.twList.resizeColumnsToContents()
self.twList.setSortingEnabled(True)
self.twList.sortItems(2, QtCore.Qt.DescendingOrder)
self.twList.selectRow(0)
self.twList.resizeColumnsToContents()
@ -448,7 +406,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
return
self.volume_id = self.twList.item(curr.row(), 0).data(QtCore.Qt.UserRole)
# list selection was changed, update the info on the volume
for record in self.cv_search_results:
if record['id'] == self.volume_id:

View File

@ -1,44 +0,0 @@
import locale
import os
import subprocess
import sys
def _lang_code_mac():
"""
stolen from https://github.com/mu-editor/mu
Returns the user's language preference as defined in the Language & Region
preference pane in macOS's System Preferences.
"""
# Uses the shell command `defaults read -g AppleLocale` that prints out a
# language code to standard output. Assumptions about the command:
# - It exists and is in the shell's PATH.
# - It accepts those arguments.
# - It returns a usable language code.
#
# Reference documentation:
# - The man page for the `defaults` command on macOS.
# - The macOS underlying API:
# https://developer.apple.com/documentation/foundation/nsuserdefaults.
LANG_DETECT_COMMAND = 'defaults read -g AppleLocale'
status, output = subprocess.getstatusoutput(LANG_DETECT_COMMAND)
if status == 0:
# Command was successful.
lang_code = output
else:
_logger.warning('Language detection command failed: %r', output)
lang_code = ''
return lang_code
if sys.platform == "darwin" and "LANG" not in os.environ:
code = _lang_code_mac()
if code != "":
os.environ["LANG"] = f"{code}.utf-8"
locale.setlocale(locale.LC_ALL, '')
sys.stdout.reconfigure(encoding=sys.getdefaultencoding())
sys.stderr.reconfigure(encoding=sys.getdefaultencoding())
sys.stdin.reconfigure(encoding=sys.getdefaultencoding())

View File

@ -1,6 +1,6 @@
beautifulsoup4 >= 4.1
PyPDF2==1.24
configparser
natsort >=8.1.0
natsort
pillow>=4.3.0
requests
py7zr

View File

@ -1,4 +1,4 @@
pyinstaller>=4.10
pyinstaller==4.3
setuptools>=42
setuptools_scm[toml]>=3.4
wheel