diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py
index 7d2d955..4535577 100644
--- a/calibre-plugin/__init__.py
+++ b/calibre-plugin/__init__.py
@@ -27,7 +27,7 @@ class FanFictionDownLoaderBase(InterfaceActionBase):
description = 'UI plugin to download FanFiction stories from various sites.'
supported_platforms = ['windows', 'osx', 'linux']
author = 'Jim Miller'
- version = (1, 5, 26)
+ version = (1, 5, 27)
minimum_calibre_version = (0, 8, 30)
#: This field defines the GUI plugin class that contains all the code
diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py
index ad68bf8..a2d0e8a 100644
--- a/calibre-plugin/config.py
+++ b/calibre-plugin/config.py
@@ -39,6 +39,7 @@ all_prefs.defaults['personal.ini'] = get_resources('plugin-example.ini')
all_prefs.defaults['updatemeta'] = True
all_prefs.defaults['updatecover'] = False
+all_prefs.defaults['updateepubcover'] = False
all_prefs.defaults['keeptags'] = False
all_prefs.defaults['urlsfromclip'] = True
all_prefs.defaults['updatedefault'] = True
@@ -67,6 +68,7 @@ all_prefs.defaults['custom_cols'] = {}
copylist = ['personal.ini',
'updatemeta',
'updatecover',
+ 'updateepubcover',
'keeptags',
'urlsfromclip',
'updatedefault',
@@ -176,6 +178,7 @@ class ConfigWidget(QWidget):
prefs['collision'] = unicode(self.basic_tab.collision.currentText())
prefs['updatemeta'] = self.basic_tab.updatemeta.isChecked()
prefs['updatecover'] = self.basic_tab.updatecover.isChecked()
+ prefs['updateepubcover'] = self.basic_tab.updateepubcover.isChecked()
prefs['keeptags'] = self.basic_tab.keeptags.isChecked()
prefs['urlsfromclip'] = self.basic_tab.urlsfromclip.isChecked()
prefs['updatedefault'] = self.basic_tab.updatedefault.isChecked()
@@ -245,9 +248,11 @@ class BasicTab(QWidget):
label.setWordWrap(True)
self.l.addWidget(label)
self.l.addSpacing(5)
-
+
+ tooltip = "On each download, FFDL offers an option to select the output format.
This sets what that option will default to."
horz = QHBoxLayout()
label = QLabel('Default Output &Format:')
+ label.setToolTip(tooltip)
horz.addWidget(label)
self.fileform = QComboBox(self)
self.fileform.addItem('epub')
@@ -255,15 +260,16 @@ class BasicTab(QWidget):
self.fileform.addItem('html')
self.fileform.addItem('txt')
self.fileform.setCurrentIndex(self.fileform.findText(prefs['fileform']))
- self.fileform.setToolTip('Choose output format to create. May set default from plugin configuration.')
+ self.fileform.setToolTip(tooltip)
self.fileform.activated.connect(self.set_collisions)
label.setBuddy(self.fileform)
horz.addWidget(self.fileform)
self.l.addLayout(horz)
+ tooltip = "On each download, FFDL offers an option of what happens if that story already exists.
This sets what that option will default to."
horz = QHBoxLayout()
label = QLabel('Default If Story Already Exists?')
- label.setToolTip("What to do if there's already an existing story with the same title and author.")
+ label.setToolTip(tooltip)
horz.addWidget(label)
self.collision = QComboBox(self)
# add collision options
@@ -271,18 +277,23 @@ class BasicTab(QWidget):
i = self.collision.findText(prefs['collision'])
if i > -1:
self.collision.setCurrentIndex(i)
- # self.collision.setToolTip('Overwrite will replace the existing story. Add New will create a new story with the same title and author.')
+ self.collision.setToolTip(tooltip)
label.setBuddy(self.collision)
horz.addWidget(self.collision)
self.l.addLayout(horz)
self.updatemeta = QCheckBox('Default Update Calibre &Metadata?',self)
- self.updatemeta.setToolTip('Update title, author, URL, tags, custom columns, etc for story in Calibre from web site.')
+ self.updatemeta.setToolTip("On each download, FFDL offers an option to update Calibre's metadata (title, author, URL, tags, custom columns, etc) from the web site.
This sets whether that will default to on or off.")
self.updatemeta.setChecked(prefs['updatemeta'])
self.l.addWidget(self.updatemeta)
- self.updatecover = QCheckBox('Update Cover when Updating Metadata?',self)
- self.updatecover.setToolTip("Update cover image from EPUB when metadata is updated. (EPUB only.)\nDoesn't go looking for new images on 'Update Calibre Metadata Only'.")
+ self.updateepubcover = QCheckBox('Default Update EPUB Cover when Updating EPUB?',self)
+ self.updateepubcover.setToolTip("On each download, FFDL offers an option to update the book cover image inside the EPUB from the web site when the EPUB is updated.
This sets whether that will default to on or off.")
+ self.updateepubcover.setChecked(prefs['updateepubcover'])
+ self.l.addWidget(self.updateepubcover)
+
+ self.updatecover = QCheckBox('Update Calibre Cover when Updating Metadata?',self)
+ self.updatecover.setToolTip("Update calibre book cover image from EPUB when metadata is updated. (EPUB only.)\nDoesn't go looking for new images on 'Update Calibre Metadata Only'.")
self.updatecover.setChecked(prefs['updatecover'])
self.l.addWidget(self.updatecover)
@@ -514,7 +525,7 @@ class GenerateCoverTab(QWidget):
if site == u"Default":
s = "On Metadata update, run Generate Cover with this setting, if not selected for specific site."
else:
- s = "On Metadata update, run Generate Cover with this setting for site (%s)."%site
+ s = "On Metadata update, run Generate Cover with this setting for %s stories."%site
label.setToolTip(s)
horz.addWidget(label)
@@ -536,7 +547,7 @@ class GenerateCoverTab(QWidget):
self.l.addWidget(self.gcnewonly)
self.allow_gc_from_ini = QCheckBox('Allow generate_cover_settings from personal.ini to override.',self)
- self.allow_gc_from_ini.setToolTip("The INI parameter generate_cover_settings allows you to choose a GC setting based on metadata rather than site,\nbut it's much more complex. generate_cover_settings is ignored when this is off.")
+ self.allow_gc_from_ini.setToolTip("The personal.ini parameter generate_cover_settings allows you to choose a GC setting based on metadata rather than site, but it's much more complex.
generate_cover_settings is ignored when this is off.")
self.allow_gc_from_ini.setChecked(prefs['allow_gc_from_ini'])
self.l.addWidget(self.allow_gc_from_ini)
diff --git a/calibre-plugin/dialogs.py b/calibre-plugin/dialogs.py
index 87c8e41..12d9349 100644
--- a/calibre-plugin/dialogs.py
+++ b/calibre-plugin/dialogs.py
@@ -123,10 +123,18 @@ class AddNewDialog(SizePersistedDialog):
horz.addWidget(self.collision)
self.l.addLayout(horz)
+ horz = QHBoxLayout()
self.updatemeta = QCheckBox('Update Calibre &Metadata?',self)
self.updatemeta.setToolTip('Update metadata for story in Calibre from web site?')
self.updatemeta.setChecked(prefs['updatemeta'])
- self.l.addWidget(self.updatemeta)
+ horz.addWidget(self.updatemeta)
+
+ self.updateepubcover = QCheckBox('Update EPUB Cover?',self)
+ self.updateepubcover.setToolTip('Update book cover image from site or defaults (if found) inside the EPUB when EPUB is updated.')
+ self.updateepubcover.setChecked(prefs['updateepubcover'])
+ horz.addWidget(self.updateepubcover)
+
+ self.l.addLayout(horz)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
@@ -155,6 +163,7 @@ class AddNewDialog(SizePersistedDialog):
'fileform': unicode(self.fileform.currentText()),
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
+ 'updateepubcover': self.updateepubcover.isChecked(),
}
def get_urlstext(self):
@@ -402,6 +411,11 @@ class UpdateExistingDialog(SizePersistedDialog):
self.updatemeta.setChecked(prefs['updatemeta'])
options_layout.addWidget(self.updatemeta)
+ self.updateepubcover = QCheckBox('Update EPUB Cover?',self)
+ self.updateepubcover.setToolTip('Update book cover image from site or defaults (if found) inside the EPUB when EPUB is updated.')
+ self.updateepubcover.setChecked(prefs['updateepubcover'])
+ options_layout.addWidget(self.updateepubcover)
+
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
@@ -435,6 +449,7 @@ class UpdateExistingDialog(SizePersistedDialog):
'fileform': unicode(self.fileform.currentText()),
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
+ 'updateepubcover': self.updateepubcover.isChecked(),
}
def display_story_list(gui, header, prefs, icon, books,
diff --git a/calibre-plugin/ffdl_plugin.py b/calibre-plugin/ffdl_plugin.py
index 46e66cf..c00f2f9 100644
--- a/calibre-plugin/ffdl_plugin.py
+++ b/calibre-plugin/ffdl_plugin.py
@@ -386,7 +386,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
def get_metadata_for_book(self,book,
options={'fileform':'epub',
'collision':ADDNEW,
- 'updatemeta':True}):
+ 'updatemeta':True,
+ 'updateepubcover':True}):
'''
Update passed in book dict with metadata from website and
necessary data. To be called from LoopProgressDialog
@@ -402,6 +403,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
fileform = options['fileform']
collision = options['collision']
updatemeta= options['updatemeta']
+ updateepubcover= options['updateepubcover']
if not book['good']:
# book has already been flagged bad for whatever reason.
@@ -593,7 +595,8 @@ make_firstimage_cover:true
def start_download_list(self,book_list,
options={'fileform':'epub',
'collision':ADDNEW,
- 'updatemeta':True}):
+ 'updatemeta':True,
+ 'updateepubcover':True}):
'''
Called by LoopProgressDialog to start story downloads BG processing.
adapter_list is a list of tuples of (url,adapter)
@@ -641,7 +644,8 @@ make_firstimage_cover:true
def _update_book(self,book,db=None,
options={'fileform':'epub',
'collision':ADDNEW,
- 'updatemeta':True}):
+ 'updatemeta':True,
+ 'updateepubcover':True}):
print("add/update %s %s"%(book['title'],book['url']))
mi = self._make_mi_from_book(book)
diff --git a/calibre-plugin/jobs.py b/calibre-plugin/jobs.py
index 39156ba..d724f09 100644
--- a/calibre-plugin/jobs.py
+++ b/calibre-plugin/jobs.py
@@ -107,6 +107,9 @@ def do_download_for_worker(book,options):
ffdlconfig.readfp(StringIO(get_resources("plugin-defaults.ini")))
ffdlconfig.readfp(StringIO(options['personal.ini']))
+ if not options['updateepubcover'] and 'epub_for_update' in book and options['collision'] in (UPDATE, UPDATEALWAYS):
+ ffdlconfig.set("overrides","never_make_cover","true")
+
adapter = adapters.getAdapter(ffdlconfig,book['url'],options['fileform'])
adapter.is_adult = book['is_adult']
adapter.username = book['username']
@@ -136,13 +139,15 @@ def do_download_for_worker(book,options):
## checks were done earlier, just update it.
elif 'epub_for_update' in book and options['collision'] in (UPDATE, UPDATEALWAYS):
-
+
# update now handled by pre-populating the old images and
# chapters in the adapter rather than merging epubs.
urlchaptercount = int(story.getMetadata('numChapters'))
(url,chaptercount,
adapter.oldchapters,
- adapter.oldimgs) = get_update_data(book['epub_for_update'])
+ adapter.oldimgs,
+ adapter.oldcover,
+ adapter.calibrebookmark) = get_update_data(book['epub_for_update'])
print("Do update - epub(%d) vs url(%d)" % (chaptercount, urlchaptercount))
print("write to %s"%outfile)
diff --git a/downloader.py b/downloader.py
index 673d75c..8f405e8 100644
--- a/downloader.py
+++ b/downloader.py
@@ -64,6 +64,9 @@ def main():
parser.add_option("-u", "--update-epub",
action="store_true", dest="update",
help="Update an existing epub with new chapter, give epub filename instead of storyurl.",)
+ parser.add_option("--update-cover",
+ action="store_true", dest="updatecover",
+ help="Update cover in an existing epub, otherwise existing cover (if any) is used on update. Only valid with --update-epub.",)
parser.add_option("--force",
action="store_true", dest="force",
help="Force overwrite or update of an existing epub, download and overwrite all chapters.",)
@@ -104,6 +107,9 @@ def main():
if options.force:
config.set("overrides","always_overwrite","true")
+
+ if options.update and not options.updatecover:
+ config.set("overrides","never_make_cover","true")
if options.options:
for opt in options.options:
@@ -167,7 +173,9 @@ def main():
# merging epubs.
(url,chaptercount,
adapter.oldchapters,
- adapter.oldimgs) = get_update_data(args[0])
+ adapter.oldimgs,
+ adapter.oldcover,
+ adapter.calibrebookmark) = get_update_data(args[0])
writeStory(config,adapter,"epub")
diff --git a/fanficdownloader/adapters/base_adapter.py b/fanficdownloader/adapters/base_adapter.py
index b1d41f3..be06130 100644
--- a/fanficdownloader/adapters/base_adapter.py
+++ b/fanficdownloader/adapters/base_adapter.py
@@ -87,6 +87,8 @@ class BaseSiteAdapter(Configurable):
self.chapterLast = None
self.oldchapters = None
self.oldimgs = None
+ self.oldcover = None # (data of existing cover html, data of existing cover image)
+ self.calibrebookmark = None
## order of preference for decoding.
self.decode = ["utf8",
"Windows-1252"] # 1252 is a superset of
@@ -218,6 +220,15 @@ class BaseSiteAdapter(Configurable):
self.getConfig('allow_unsafe_filename')),
self._fetchUrlRaw,
cover=True)
+
+ # no new cover, set old cover, if there is one.
+ if not self.story.cover and self.oldcover:
+ self.story.oldcover = self.oldcover
+
+ # cheesy way to carry calibre bookmark file forward across update.
+ if self.calibrebookmark:
+ self.story.calibrebookmark = self.calibrebookmark
+
return self.story
def getStoryMetadataOnly(self):
diff --git a/fanficdownloader/epubutils.py b/fanficdownloader/epubutils.py
index 845f359..ebb74e9 100644
--- a/fanficdownloader/epubutils.py
+++ b/fanficdownloader/epubutils.py
@@ -40,6 +40,50 @@ def get_update_data(inputio,
## Save the path to the .opf file--hrefs inside it are relative to it.
relpath = get_path_part(rootfilename)
+ oldcover = None
+ calibrebookmark = None
+ # Looking for pre-existing cover.
+ for item in contentdom.getElementsByTagName("reference"):
+ if item.getAttribute("type") == "cover":
+ # there is a cover (x)html file, save the soup for it.
+ href=relpath+item.getAttribute("href")
+ oldcoverhtmlhref = href
+ oldcoverhtmldata = epub.read(href)
+ oldcoverhtmltype = "application/xhtml+xml"
+ for item in contentdom.getElementsByTagName("item"):
+ if( relpath+item.getAttribute("href") == oldcoverhtmlhref ):
+ oldcoverhtmltype = item.getAttribute("media-type")
+ break
+ soup = bs.BeautifulSoup(oldcoverhtmldata.decode("utf-8"))
+ src = None
+ # first img or image tag.
+ imgs = soup.findAll('img')
+ if imgs:
+ src = get_path_part(href)+imgs[0]['src']
+ else:
+ imgs = soup.findAll('image')
+ if imgs:
+ src=get_path_part(href)+imgs[0]['xlink:href']
+
+ if not src:
+ continue
+ try:
+ # remove all .. and the path part above it, if present.
+ # Mostly for epubs edited by Sigil.
+ src = re.sub(r"([^/]+/\.\./)","",src)
+ print("epubutils: found pre-existing cover image:%s"%src)
+ oldcoverimghref = src
+ oldcoverimgdata = epub.read(src)
+ for item in contentdom.getElementsByTagName("item"):
+ if( relpath+item.getAttribute("href") == oldcoverimghref ):
+ oldcoverimgtype = item.getAttribute("media-type")
+ break
+ oldcover = (oldcoverhtmlhref,oldcoverhtmltype,oldcoverhtmldata,oldcoverimghref,oldcoverimgtype,oldcoverimgdata)
+ except Exception as e:
+ print("Cover Image %s not found"%src)
+ print("Exception: %s"%(unicode(e)))
+ traceback.print_exc()
+
filecount = 0
soups = [] # list of xhmtl blocks
images = {} # dict() longdesc->data
@@ -61,7 +105,7 @@ def get_update_data(inputio,
try:
newsrc=get_path_part(href)+img['src']
# remove all .. and the path part above it, if present.
- # Most for epubs edited by Sigil.
+ # Mostly for epubs edited by Sigil.
newsrc = re.sub(r"([^/]+/\.\./)","",newsrc)
longdesc=img['longdesc']
data = epub.read(newsrc)
@@ -85,9 +129,14 @@ def get_update_data(inputio,
filecount+=1
+ try:
+ calibrebookmark = epub.read("META-INF/calibre_bookmarks.txt")
+ except:
+ pass
+
for k in images.keys():
print("\tlongdesc:%s\n\tData len:%s\n"%(k,len(images[k])))
- return (source,filecount,soups,images)
+ return (source,filecount,soups,images,oldcover,calibrebookmark)
def get_path_part(n):
relpath = os.path.dirname(n)
diff --git a/fanficdownloader/story.py b/fanficdownloader/story.py
index 5e94164..7dc7bdb 100644
--- a/fanficdownloader/story.py
+++ b/fanficdownloader/story.py
@@ -197,7 +197,9 @@ class Story:
self.imgurls = []
self.imgtuples = []
self.listables = {} # some items (extratags, category, warnings & genres) are also kept as lists.
- self.cover=None
+ self.cover=None # *href* of new cover image--need to create html.
+ self.oldcover=None # (oldcoverhtmlhref,oldcoverhtmltype,oldcoverhtmldata,oldcoverimghref,oldcoverimgtype,oldcoverimgdata)
+ self.calibrebookmark=None # cheesy way to carry calibre bookmark file forward across update.
def setMetadata(self, key, value):
## still keeps < < and &
diff --git a/fanficdownloader/writers/writer_epub.py b/fanficdownloader/writers/writer_epub.py
index bbe47fe..da734e9 100644
--- a/fanficdownloader/writers/writer_epub.py
+++ b/fanficdownloader/writers/writer_epub.py
@@ -262,6 +262,37 @@ ${value}
items.append(("ncx","toc.ncx","application/x-dtbncx+xml",None)) ## we'll generate the toc.ncx file,
## but it needs to be in the items manifest.
+ guide = None
+ coverIO = None
+
+ imgid = "image0000"
+ if not self.story.cover and self.story.oldcover:
+ print("writer_epub: no new cover, has old cover, write image.")
+ (oldcoverhtmlhref,
+ oldcoverhtmltype,
+ oldcoverhtmldata,
+ oldcoverimghref,
+ oldcoverimgtype,
+ oldcoverimgdata) = self.story.oldcover
+ outputepub.writestr(oldcoverhtmlhref,oldcoverhtmldata)
+ outputepub.writestr(oldcoverimghref,oldcoverimgdata)
+
+ imgid = "image0"
+ items.append((imgid,
+ oldcoverimghref,
+ oldcoverimgtype,
+ None))
+ items.append(("cover",oldcoverhtmlhref,oldcoverhtmltype,None))
+ itemrefs.append("cover")
+ metadata.appendChild(newTag(contentdom,"meta",{"content":"image0",
+ "name":"cover"}))
+ guide = newTag(contentdom,"guide")
+ guide.appendChild(newTag(contentdom,"reference",attrs={"type":"cover",
+ "title":"Cover",
+ "href":oldcoverhtmlhref}))
+
+
+
if self.getConfig('include_images'):
imgcount=0
for imgmap in self.story.getImgUrls():
@@ -276,9 +307,6 @@ ${value}
items.append(("style","OEBPS/stylesheet.css","text/css",None))
- guide = None
- coverIO = None
-
if self.story.cover:
# Note that the id of the cover xhmtl *must* be 'cover'
# for it to work on Nook.
@@ -347,8 +375,8 @@ div { margin: 0pt; padding: 0pt; }
contentxml = contentdom.toxml(encoding='utf-8')
# tweak for brain damaged Nook STR. Nook insists on name before content.
- contentxml = contentxml.replace('',
- '')
+ contentxml = contentxml.replace(''%imgid,
+ ''%imgid)
outputepub.writestr("content.opf",contentxml)
contentdom.unlink()
@@ -458,6 +486,9 @@ div { margin: 0pt; padding: 0pt; }
outputepub.writestr("OEBPS/file%04d.xhtml"%(index+1),fullhtml.encode('utf-8'))
del fullhtml
+ if self.story.calibrebookmark:
+ outputepub.writestr("META-INF/calibre_bookmarks.txt",self.story.calibrebookmark)
+
# declares all the files created by Windows. otherwise, when
# it runs in appengine, windows unzips the files as 000 perms.
for zf in outputepub.filelist: