From 18e45a403b7d6ab9f54f1f31f4db0c63753d7817 Mon Sep 17 00:00:00 2001 From: Jim Miller Date: Wed, 29 Apr 2026 13:01:22 -0500 Subject: [PATCH] PI Anthology: Reuse epub cover if there is one. --- calibre-plugin/fff_plugin.py | 55 +++++++++++++++++++++++------------- fanficfare/epubutils.py | 50 ++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 22 deletions(-) diff --git a/calibre-plugin/fff_plugin.py b/calibre-plugin/fff_plugin.py index 6249661f..cb44db60 100644 --- a/calibre-plugin/fff_plugin.py +++ b/calibre-plugin/fff_plugin.py @@ -64,7 +64,7 @@ from fanficfare import adapters, exceptions from fanficfare.epubutils import ( get_dcsource, get_dcsource_chaptercount, get_story_url_from_epub_html, - get_story_url_from_zip_html, reset_orig_chapters_epub, get_cover_data) + get_story_url_from_zip_html, reset_orig_chapters_epub, get_cover_img) from fanficfare.geturls import ( get_urls_from_page, get_urls_from_text,get_urls_from_imap, @@ -2213,30 +2213,45 @@ class FanFicFarePlugin(InterfaceAction): ## start with None. If no subbook covers, don't force one ## here. User can configure FFF to always create/polish a ## cover if they want. This is about when we force it. - coverpath = None + coverimgpath = None coverimgtype = None + had_cover = False - ## first, look for covers inside the subbooks. Stop at the - ## first one, which will be used if there isn't a pre-existing + # epubmerge wants a path to cover img on disk + def write_image(imgtype,imgdata): + tmp = PersistentTemporaryFile(prefix='cover_', + suffix='.'+imagetypes[imgtype], + dir=options['tdir']) + tmp.write(imgdata) + tmp.flush() + tmp.close() + return tmp.name + + ## if prior epub had a cover, we should use it again. + if mergebook['calibre_id'] and db.has_format(mergebook['calibre_id'],'EPUB',index_is_id=True): + (covertype,coverdata) = get_cover_img(db.format(mergebook['calibre_id'],'EPUB',index_is_id=True,as_file=True)) + if coverdata: + had_cover = True + coverimgpath = write_image(covertype,coverdata) + coverimgtype = covertype + logger.debug("prior anthology cover found") + + ## look for covers inside the subbooks. Stop at the first + ## one, which will be used if there isn't a pre-existing ## calibre cover. - if not coverpath: + if not coverimgpath: for book in good_list: - coverdata = get_cover_data(book['outfile']) + (covertype,coverdata) = get_cover_img(book['outfile']) if coverdata: # found a cover. - (coverimgtype,coverimgdata) = coverdata[4:6] - # logger.debug('coverimgtype:%s [%s]'%(coverimgtype,imagetypes[coverimgtype])) - tmpcover = PersistentTemporaryFile(suffix='.'+imagetypes[coverimgtype], - dir=options['tdir']) - tmpcover.write(coverimgdata) - tmpcover.flush() - tmpcover.close() - coverpath = tmpcover.name + coverimgpath = write_image(covertype,coverdata) + coverimgtype = covertype + logger.debug('from subbook coverimgpath:%s'%coverimgpath) break - # logger.debug('coverpath:%s'%coverpath) ## if updating an existing book and there is at least one ## subbook cover: - if coverpath and mergebook['calibre_id']: + if not had_cover and coverimgpath and mergebook['calibre_id']: + logger.debug("anth cover: using cal cover") # Couldn't find a better way to get the cover path. calcoverpath = os.path.join(db.library_path, db.path(mergebook['calibre_id'], index_is_id=True), @@ -2244,9 +2259,11 @@ class FanFicFarePlugin(InterfaceAction): ## if there's an existing cover, use it. Calibre will set ## it for us during lots of different actions anyway. if os.path.exists(calcoverpath): - coverpath = calcoverpath + coverimgpath = calcoverpath - # logger.debug('coverpath:%s'%coverpath) + ## Note that this cover will be replaced if 'inject + ## generated' cover is on + logger.debug('coverimgpath:%s'%coverimgpath) mrg_args = [tmp.name, [ x['outfile'] for x in good_list ],] mrg_kwargs = { @@ -2254,7 +2271,7 @@ class FanFicFarePlugin(InterfaceAction): 'titleopt':mergebook['title'], 'keepmetadatafiles':True, 'source':mergebook['url'], - 'coverjpgpath':coverpath + 'coverjpgpath':coverimgpath } logger.debug('anthology_merge_keepsingletocs:%s'% mergebook['anthology_merge_keepsingletocs']) diff --git a/fanficfare/epubutils.py b/fanficfare/epubutils.py index 2b9590b1..154336a8 100644 --- a/fanficfare/epubutils.py +++ b/fanficfare/epubutils.py @@ -33,9 +33,53 @@ def get_dcsource_chaptercount(inputio): ## getsoups=True to check for continue_on_chapter_error chapters. return get_update_data(inputio,getfilecount=True,getsoups=True)[:2] # (source,filecount) -def get_cover_data(inputio): - # (oldcoverhtmlhref,oldcoverhtmltype,oldcoverhtmldata,oldcoverimghref,oldcoverimgtype,oldcoverimgdata) - return get_update_data(inputio,getfilecount=True,getsoups=False)[4] +## only finds and returns cover image type and data, not cover page. +## should work on any epub. Added for anthology cover issues. +def get_cover_img(inputio): + # (oldcoverimgtype,oldcoverimgdata) + epub = ZipFile(inputio, 'r') # works equally well with inputio as a path or a blob + + ## Find the .opf file. + container = epub.read("META-INF/container.xml") + containerdom = parseString(container) + rootfilenodelist = containerdom.getElementsByTagName("rootfile") + rootfilename = rootfilenodelist[0].getAttribute("full-path") + + contentdom = parseString(epub.read(rootfilename)) + firstmetadom = contentdom.getElementsByTagName("metadata")[0] + + ## Save the path to the .opf file--hrefs inside it are relative to it. + relpath = get_path_part(rootfilename) + # logger.debug("relpath:%s"%relpath) + +# + + coverid = None + covertype = None + coverdata = None + + for metatag in firstmetadom.getElementsByTagName("meta"): + if metatag.getAttribute('name') == 'cover': + coverid = metatag.getAttribute('content') + # logger.debug("coverid:%s"%coverid) + break + if coverid: + for item in contentdom.getElementsByTagName("item"): + if item.getAttribute('id') == coverid: + coverhref = relpath+item.getAttribute("href") + ## remove .. and the part it obviates + coverhref = re.sub(r"([^/]+/\.\./)","",coverhref) + covertype = item.getAttribute('media-type') + # logger.debug("covertype:%s coverhref:%s"%( + covertype,coverhref)) + try: + coverdata = epub.read(coverhref) + # logger.debug("coverdatalen:%s"%len(coverdata)) + except Exception as e: + logger.info("Failed to read cover (%s): %s"%(coverhref,e)) + covertype, coverdata = None, None + break + return covertype, coverdata def get_oldcover(epub,relpath,contentdom,item): href=relpath+item.getAttribute("href")