[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [krita/krita/4.1] plugins/python/comics_project_management_tools: Fix several issues with the EPUB m
From: Boudewijn Rempt <null () kde ! org>
Date: 2018-09-07 14:15:55
Message-ID: E1fyHYB-0003Xd-OD () code ! kde ! org
[Download RAW message or body]
Git commit e8fc7211894132621926d80df9ba54359c752231 by Boudewijn Rempt, on behalf of Wolthera \
van Hövell tot Westerflier. Committed on 07/09/2018 at 12:49.
Pushed by rempt into branch 'krita/4.1'.
Fix several issues with the EPUB metadata export.
* Add MARC-relators for use with the 'refines'.
* Add Series definitions and series number.
* Add UUID sharing between acbf and epub.
* Add a modiied and proper date stuff.
M +5 -9 plugins/python/comics_project_management_tools/comics_export_dialog.py
M +25 -1 plugins/python/comics_project_management_tools/comics_metadata_dialog.py
M +10 -0 plugins/python/comics_project_management_tools/comics_project_manager_docker.py
M +2 -1 plugins/python/comics_project_management_tools/comics_project_setup_wizard.py
M +88 -8 plugins/python/comics_project_management_tools/exporters/CPMT_EPUB_exporter.py
https://commits.kde.org/krita/e8fc7211894132621926d80df9ba54359c752231
diff --git a/plugins/python/comics_project_management_tools/comics_export_dialog.py \
b/plugins/python/comics_project_management_tools/comics_export_dialog.py index \
c62d64149bb..b821644d378 100644
--- a/plugins/python/comics_project_management_tools/comics_export_dialog.py
+++ b/plugins/python/comics_project_management_tools/comics_export_dialog.py
@@ -294,8 +294,6 @@ class comic_export_setting_dialog(QDialog):
ACBFdocInfo = QGroupBox()
ACBFdocInfo.setTitle(i18n("ACBF Document Info"))
ACBFdocInfo.setLayout(ACBFform)
- self.lnACBFSource = QLineEdit()
- self.lnACBFSource.setToolTip(i18n("Whether the acbf file is an adaption of an existing \
source, and if so, how to find information about that source. So for example, for an adapted \
webcomic, the official website url should go here.")) self.lnACBFID = QLabel()
self.lnACBFID.setToolTip(i18n("By default this will be filled with a generated \
universal unique identifier. The ID by itself is merely so that comic book library management \
programs can figure out if this particular comic is already in their database and whether it \
has been rated. Of course, the UUID can be changed into something else by manually changing the \
JSON, but this is advanced usage.")) self.spnACBFVersion = QSpinBox()
@@ -309,7 +307,6 @@ class comic_export_setting_dialog(QDialog):
self.chkIncludeTranslatorComments.setToolTip(i18n("A PO file can contain translator's \
comments. If this is checked, the translations comments will be added as references into the \
ACBF file.")) self.lnTranslatorHeader = QLineEdit()
- ACBFform.addRow(i18n("Source:"), self.lnACBFSource)
ACBFform.addRow(i18n("ACBF UID:"), self.lnACBFID)
ACBFform.addRow(i18n("Version:"), self.spnACBFVersion)
ACBFform.addRow(i18n("Version history:"), acbfHistoryList)
@@ -627,12 +624,13 @@ class comic_export_setting_dialog(QDialog):
listItems.append(QStandardItem()) # First name
self.ACBFauthorModel.appendRow(listItems)
- if "acbfSource" in config.keys():
- self.lnACBFSource.setText(config["acbfSource"])
- if "acbfID" in config.keys():
+ if "uuid" in config.keys():
+ self.lnACBFID.setText(config["uuid"])
+ elif "acbfID" in config.keys():
self.lnACBFID.setText(config["acbfID"])
else:
- self.lnACBFID.setText(QUuid.createUuid().toString())
+ config["uuid"] = QUuid.createUuid().toString()
+ self.lnACBFID.setText(config["uuid"])
if "acbfVersion" in config.keys():
self.spnACBFVersion.setValue(config["acbfVersion"])
if "acbfHistory" in config.keys():
@@ -726,8 +724,6 @@ class comic_export_setting_dialog(QDialog):
author.pop(listEntries[i])
authorList.append(author)
config["acbfAuthor"] = authorList
- config["acbfSource"] = self.lnACBFSource.text()
- config["acbfID"] = self.lnACBFID.text()
config["acbfVersion"] = self.spnACBFVersion.value()
versionList = []
for r in range(self.ACBFhistoryModel.rowCount()):
diff --git a/plugins/python/comics_project_management_tools/comics_metadata_dialog.py \
b/plugins/python/comics_project_management_tools/comics_metadata_dialog.py index \
d1cf198a697..0cd609b2f99 100644
--- a/plugins/python/comics_project_management_tools/comics_metadata_dialog.py
+++ b/plugins/python/comics_project_management_tools/comics_metadata_dialog.py
@@ -28,7 +28,7 @@ import types
from pathlib import Path # For reading all the files in a directory.
from PyQt5.QtGui import QStandardItem, QStandardItemModel, QImage, QIcon, QPixmap, QPainter, \
QPalette, QFontDatabase from PyQt5.QtWidgets import QComboBox, QCompleter, \
QStyledItemDelegate, QLineEdit, QDialog, QDialogButtonBox, QVBoxLayout, QFormLayout, \
QTabWidget, QWidget, QPlainTextEdit, QHBoxLayout, QSpinBox, QDateEdit, QPushButton, QLabel, \
QTableView
-from PyQt5.QtCore import QDir, QLocale, QStringListModel, Qt, QDate, QSize
+from PyQt5.QtCore import QDir, QLocale, QStringListModel, Qt, QDate, QSize, QUuid
"""
multi entry completer cobbled together from the two examples on stackoverflow:3779720
@@ -397,6 +397,10 @@ class comic_meta_data_editor(QDialog):
self.cmb_entry_type = QComboBox()
self.cmb_entry_type.addItems(["IssueID", "SeriesID", "URL"])
self.cmb_entry_type.setEditable(True)
+ self.ln_source = QLineEdit()
+ self.ln_source.setToolTip(i18n("Whether the comic is an adaption of an existing \
source, and if so, how to find information about that source. So for example, for an adapted \
webcomic, the official website url should go here.")) + self.label_uuid = QLabel()
+ self.label_uuid.setToolTip(i18n("By default this will be filled with a generated \
universal unique identifier. The ID by itself is merely so that comic book library management \
programs can figure out if this particular comic is already in their database and whether it \
has been rated. Of course, the UUID can be changed into something else by manually changing the \
JSON, but this is advanced usage.")) self.ln_database_entry = QLineEdit()
dbHorizontal = QHBoxLayout()
dbHorizontal.addWidget(self.ln_database_name)
@@ -407,6 +411,8 @@ class comic_meta_data_editor(QDialog):
publisherLayout.addRow(i18n("City:"), self.publishCity)
publisherLayout.addRow(i18n("Date:"), publishDateLayout)
publisherLayout.addRow(i18n("ISBN:"), self.isbn)
+ publisherLayout.addRow(i18n("Source:"), self.ln_source)
+ publisherLayout.addRow(i18n("UUID:"), self.label_uuid)
publisherLayout.addRow(i18n("License:"), self.license)
publisherLayout.addRow(i18n("Database:"), dataBaseReference)
@@ -619,6 +625,23 @@ class comic_meta_data_editor(QDialog):
self.publishDate.setDate(QDate.fromString(config["publishingDate"], Qt.ISODate))
if "isbn-number" in config.keys():
self.isbn.setText(config["isbn-number"])
+ if "source" in config.keys():
+ self.ln_source.setText(config["source"])
+ elif "acbfSource" in config.keys():
+ self.ln_source.setText(config["acbfSource"])
+ if "uuid" in config.keys():
+ self.label_uuid.setText(config["uuid"])
+ else:
+ uuid = str()
+ if "acbfID" in config.keys():
+ uuid = config["acbfID"]
+ uuid = uuid.strip("{")
+ uuid = uuid.strip("}")
+ uuidVerify = uuid.split("-")
+ if len(uuidVerify[0])!=8 or len(uuidVerify[1])!=4 or len(uuidVerify[2])!=4 or \
len(uuidVerify[3])!=4 or len(uuidVerify[4])!=12: + uuid = \
QUuid.createUuid().toString() + self.label_uuid.setText(uuid)
+ config["uuid"] = uuid
if "license" in config.keys():
self.license.setCurrentText(config["license"])
else:
@@ -746,6 +769,7 @@ class comic_meta_data_editor(QDialog):
config["publisherCity"] = self.publishCity.text()
config["publishingDate"] = self.publishDate.date().toString(Qt.ISODate)
config["isbn-number"] = self.isbn.text()
+ config["source"] = self.ln_source.text()
config["license"] = self.license.currentText()
if self.ln_database_name.text().isalnum() and self.ln_database_entry.text().isalnum():
dbRef = {}
diff --git a/plugins/python/comics_project_management_tools/comics_project_manager_docker.py \
b/plugins/python/comics_project_management_tools/comics_project_manager_docker.py index \
f3640ac9611..608d96a7458 100644
--- a/plugins/python/comics_project_management_tools/comics_project_manager_docker.py
+++ b/plugins/python/comics_project_management_tools/comics_project_manager_docker.py
@@ -780,6 +780,16 @@ class comics_project_manager_docker(DockWidget):
"""
def slot_export(self):
+
+ #ensure there is a unique identifier
+ if "uuid" not in self.setupDictionary.keys():
+ uuid = str()
+ if "acbfID" in self.setupDictionary.keys():
+ uuid = str(self.setupDictionary["acbfID"])
+ else:
+ uuid = QUuid.createUuid().toString()
+ self.setupDictionary["uuid"] = uuid
+
exporter = comics_exporter.comicsExporter()
exporter.set_config(self.setupDictionary, self.projecturl)
exportSuccess = exporter.export()
diff --git a/plugins/python/comics_project_management_tools/comics_project_setup_wizard.py \
b/plugins/python/comics_project_management_tools/comics_project_setup_wizard.py index \
7312b246368..003be44cee3 100644
--- a/plugins/python/comics_project_management_tools/comics_project_setup_wizard.py
+++ b/plugins/python/comics_project_management_tools/comics_project_setup_wizard.py
@@ -26,7 +26,7 @@ import os # For finding the script location.
from pathlib import Path # For reading all the files in a directory.
import random # For selecting two random words from a list.
from PyQt5.QtWidgets import QWidget, QWizard, QWizardPage, QHBoxLayout, QFormLayout, \
QFileDialog, QLineEdit, QPushButton, QCheckBox, QLabel, QDialog
-from PyQt5.QtCore import QDate, QLocale
+from PyQt5.QtCore import QDate, QLocale, QUuid
from krita import *
from . import comics_metadata_dialog
@@ -171,6 +171,7 @@ class ComicsProjectSetupWizard():
self.setupDictionary["exportLocation"] = self.exportDirectory
self.setupDictionary["templateLocation"] = self.templateLocation
self.setupDictionary["translationLocation"] = self.translationLocation
+ self.setupDictionary["uuid"] = QUuid.createUuid().toString()
# Finally, write the dictionary into the json file.
self.writeConfig()
diff --git a/plugins/python/comics_project_management_tools/exporters/CPMT_EPUB_exporter.py \
b/plugins/python/comics_project_management_tools/exporters/CPMT_EPUB_exporter.py index \
7965cf8a8f4..4128692952e 100644
--- a/plugins/python/comics_project_management_tools/exporters/CPMT_EPUB_exporter.py
+++ b/plugins/python/comics_project_management_tools/exporters/CPMT_EPUB_exporter.py
@@ -25,6 +25,7 @@ import shutil
import os
from pathlib import Path
from PyQt5.QtXml import QDomDocument, QDomElement, QDomText, QDomNodeList
+from PyQt5.QtCore import Qt, QDateTime
def export(configDictionary = {}, projectURL = str(), pagesLocationList = []):
path = Path(os.path.join(projectURL, configDictionary["exportLocation"]))
@@ -128,6 +129,10 @@ Write OPF metadata file
def write_opf_file(path, configDictionary, htmlFiles, pagesList, coverpageurl, coverpagehtml):
+ # marc relators
+ # This has several entries removed to reduce it to the most relevant entries.
+ marcRelators = {"abr":i18n("Abridger"), "acp":i18n("Art copyist"), "act":i18n("Actor"), \
"adi":i18n("Art director"), "adp":i18n("Adapter"), "ann":i18n("Annotator"), \
"ant":i18n("Bibliographic antecedent"), "arc":i18n("Architect"), "ard":i18n("Artistic \
director"), "art":i18n("Artist"), "asn":i18n("Associated name"), "ato":i18n("Autographer"), \
"att":i18n("Attributed name"), "aud":i18n("Author of dialog"), "aut":i18n("Author"), \
"bdd":i18n("Binding designer"), "bjd":i18n("Bookjacket designer"), "bkd":i18n("Book designer"), \
"bkp":i18n("Book producer"), "blw":i18n("Blurb writer"), "bnd":i18n("Binder"), \
"bpd":i18n("Bookplate designer"), "bsl":i18n("Bookseller"), "cll":i18n("Calligrapher"), \
"clr":i18n("Colorist"), "cns":i18n("Censor"), "cov":i18n("Cover designer"), \
"cph":i18n("Copyright holder"), "cre":i18n("Creator"), "ctb":i18n("Contributor"), \
"cur":i18n("Curator"), "cwt":i18n("Commentator for written text"), "drm":i18n("Draftsman"), \
"dsr":i18n("Designer"), "dub":i18n("Dubious author"), "edt":i18n("Editor"), \
"etr":i18n("Etcher"), "exp":i18n("Expert"), "fnd":i18n("Funder"), "ill":i18n("Illustrator"), \
"ilu":i18n("Illuminator"), "ins":i18n("Inscriber"), "lse":i18n("Licensee"), \
"lso":i18n("Licensor"), "ltg":i18n("Lithographer"), "mdc":i18n("Metadata contact"), \
"oth":i18n("Other"), "own":i18n("Owner"), "pat":i18n("Patron"), "pbd":i18n("Publishing \
director"), "pbl":i18n("Publisher"), "prt":i18n("Printer"), "sce":i18n("Scenarist"), \
"scr":i18n("Scribe"), "spn":i18n("Sponsor"), "stl":i18n("Storyteller"), \
"trc":i18n("Transcriber"), "trl":i18n("Translator"), "tyd":i18n("Type designer"), \
"tyg":i18n("Typographer"), "wac":i18n("Writer of added commentary"), "wal":i18n("Writer of \
added lyrics"), "wam":i18n("Writer of accompanying material"), "wat":i18n("Writer of added \
text"), "win":i18n("Writer of introduction"), "wpr":i18n("Writer of preface"), \
"wst":i18n("Writer of supplementary textual content")} +
# opf file
opfFile = QDomDocument()
opfRoot = opfFile.createElement("package")
@@ -139,17 +144,72 @@ def write_opf_file(path, configDictionary, htmlFiles, pagesList, \
coverpageurl, c opfMeta = opfFile.createElement("metadata")
opfMeta.setAttribute("xmlns:dc", "http://purl.org/dc/elements/1.1/")
+ # EPUB metadata requires a title, language and uuid
+
+ langString = "en-US"
if "language" in configDictionary.keys():
- bookLang = opfFile.createElement("dc:language")
- bookLang.appendChild(opfFile.createTextNode(configDictionary["language"]))
- opfMeta.appendChild(bookLang)
+ langString = configDictionary["language"]
+
+ bookLang = opfFile.createElement("dc:language")
+ bookLang.appendChild(opfFile.createTextNode(langString))
+ opfMeta.appendChild(bookLang)
+
bookTitle = opfFile.createElement("dc:title")
if "title" in configDictionary.keys():
bookTitle.appendChild(opfFile.createTextNode(str(configDictionary["title"])))
else:
bookTitle.appendChild(opfFile.createTextNode("Comic with no Name"))
opfMeta.appendChild(bookTitle)
-
+
+ # Generate series title and the like here too.
+ if "seriesName" in configDictionary.keys():
+ bookTitle.setAttribute("id", "#main")
+
+ refine = opfFile.createElement("meta")
+ refine.setAttribute("refines", "#main")
+ refine.setAttribute("property", "title-type")
+ refine.appendChild(opfFile.createTextNode("main"))
+ opfMeta.appendChild(refine)
+
+ refine2 = opfFile.createElement("meta")
+ refine2.setAttribute("refines", "#main")
+ refine2.setAttribute("property", "display-seq")
+ refine2.appendChild(opfFile.createTextNode("1"))
+ opfMeta.appendChild(refine2)
+
+ seriesTitle = opfFile.createElement("dc:title")
+ seriesTitle.appendChild(opfFile.createTextNode(str(configDictionary["seriesName"])))
+ seriesTitle.setAttribute("id", "#series")
+ opfMeta.appendChild(seriesTitle)
+
+ refineS = opfFile.createElement("meta")
+ refineS.setAttribute("refines", "#series")
+ refineS.setAttribute("property", "title-type")
+ refineS.appendChild(opfFile.createTextNode("collection"))
+ opfMeta.appendChild(refineS)
+
+ refineS2 = opfFile.createElement("meta")
+ refineS2.setAttribute("refines", "#series")
+ refineS2.setAttribute("property", "display-seq")
+ refineS2.appendChild(opfFile.createTextNode("2"))
+ opfMeta.appendChild(refineS2)
+
+ if "seriesNumber" in configDictionary.keys():
+ refineS3 = opfFile.createElement("meta")
+ refineS3.setAttribute("refines", "#series")
+ refineS3.setAttribute("property", "group-position")
+ refineS3.appendChild(opfFile.createTextNode(str(configDictionary["seriesNumber"])))
+ opfMeta.appendChild(refineS3)
+
+ uuid = str(configDictionary["uuid"])
+ uuid = uuid.strip("{")
+ uuid = uuid.strip("}")
+
+ # Append the id, and assign it as the bookID.
+ uniqueID = opfFile.createElement("dc:identifier")
+ uniqueID.appendChild(opfFile.createTextNode("urn:uuid:"+uuid))
+ uniqueID.setAttribute("id", "BookID")
+ opfMeta.appendChild(uniqueID)
if "authorList" in configDictionary.keys():
for authorE in range(len(configDictionary["authorList"])):
@@ -176,13 +236,31 @@ def write_opf_file(path, configDictionary, htmlFiles, pagesList, \
coverpageurl, c role.setAttribute("refines", "cre" + str(authorE))
role.setAttribute("scheme", "marc:relators")
role.setAttribute("property", "role")
- role.appendChild(opfFile.createTextNode(str(authorDict["role"])))
+ roleString = str(authorDict["role"])
+ if roleString in marcRelators.values():
+ i = list(marcRelators.values()).index(roleString)
+ roleString = list(marcRelators.keys())[i]
+ else:
+ roleString = "oth"
+ role.appendChild(opfFile.createTextNode(roleString))
opfMeta.appendChild(role)
if "publishingDate" in configDictionary.keys():
date = opfFile.createElement("dc:date")
date.appendChild(opfFile.createTextNode(configDictionary["publishingDate"]))
opfMeta.appendChild(date)
+
+ #Creation date
+ modified = opfFile.createElement("meta")
+ modified.setAttribute("property", "dcterms:modified")
+ modified.appendChild(opfFile.createTextNode(QDateTime.currentDateTimeUtc().toString(Qt.ISODate)))
+ opfMeta.appendChild(modified)
+
+ if "source" in configDictionary.keys():
+ source = opfFile.createElement("dc:source")
+ date.appendChild(opfFile.createTextNode(configDictionary["source"]))
+ opfMeta.appendChild(source)
+
description = opfFile.createElement("dc:description")
if "summary" in configDictionary.keys():
description.appendChild(opfFile.createTextNode(configDictionary["summary"]))
@@ -190,13 +268,15 @@ def write_opf_file(path, configDictionary, htmlFiles, pagesList, \
coverpageurl, c
description.appendChild(opfFile.createTextNode("There was no summary upon generation \
of this file.")) opfMeta.appendChild(description)
- typeE = opfFile.createElement("dc:type")
- typeE.appendChild(opfFile.createTextNode("Comic"))
- opfMeta.appendChild(typeE)
+ # Type can be dictionary or index, or one of those edupub thingies. Not necessary for \
comics. + # typeE = opfFile.createElement("dc:type")
+ # opfMeta.appendChild(typeE)
if "publisherName" in configDictionary.keys():
publisher = opfFile.createElement("dc:publisher")
publisher.appendChild(opfFile.createTextNode(configDictionary["publisherName"]))
opfMeta.appendChild(publisher)
+
+
if "isbn-number" in configDictionary.keys():
publishISBN = opfFile.createElement("dc:identifier")
publishISBN.appendChild(opfFile.createTextNode(str("urn:isbn:") + \
configDictionary["isbn-number"]))
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic