Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-fanficfare for openSUSE:Factory checked in at 2026-04-04 19:08:02 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-fanficfare (Old) and /work/SRC/openSUSE:Factory/.python-fanficfare.new.21863 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-fanficfare" Sat Apr 4 19:08:02 2026 rev:78 rq:1344572 version:4.56.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-fanficfare/python-fanficfare.changes 2026-03-02 17:40:30.367796089 +0100 +++ /work/SRC/openSUSE:Factory/.python-fanficfare.new.21863/python-fanficfare.changes 2026-04-04 19:09:58.191404086 +0200 @@ -1,0 +2,14 @@ +Sat Apr 4 11:40:27 UTC 2026 - Matej Cepl <[email protected]> + +- Update to the version 4.56.0: + - Add Reject URLs: Accept story URLs drag/drop & paste like Add + Stories by URL + - Add top menu items for Add/Edit Reject URLs. + - Remove fanficfare_macmenuhack. + - Epub Update: Don't cache cover image with others, trips + dedup. + - adapter_literotica: Fix for site change (#1318), collect tags + on one-shots. + - Make seriesUrl mutable again. + +------------------------------------------------------------------- Old: ---- FanFicFare-4.55.0.tar.gz New: ---- FanFicFare-4.56.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-fanficfare.spec ++++++ --- /var/tmp/diff_new_pack.T7i5Eb/_old 2026-04-04 19:09:58.751427047 +0200 +++ /var/tmp/diff_new_pack.T7i5Eb/_new 2026-04-04 19:09:58.751427047 +0200 @@ -20,7 +20,7 @@ %define modnamedown fanficfare %define skip_python2 1 Name: python-fanficfare -Version: 4.55.0 +Version: 4.56.0 Release: 0 Summary: Tool for making eBooks from stories on fanfiction and other web sites License: GPL-3.0-only ++++++ FanFicFare-4.55.0.tar.gz -> FanFicFare-4.56.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/calibre-plugin/__init__.py new/FanFicFare-4.56.0/calibre-plugin/__init__.py --- old/FanFicFare-4.55.0/calibre-plugin/__init__.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/calibre-plugin/__init__.py 2026-04-02 17:03:42.000000000 +0200 @@ -33,7 +33,7 @@ from calibre.customize import InterfaceActionBase # pulled out from FanFicFareBase for saving in prefs.py -__version__ = (4, 55, 0) +__version__ = (4, 56, 0) ## Apparently the name for this class doesn't matter--it was still ## 'demo' for the first few versions. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/calibre-plugin/config.py new/FanFicFare-4.56.0/calibre-plugin/config.py --- old/FanFicFare-4.55.0/calibre-plugin/config.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/calibre-plugin/config.py 2026-04-02 17:03:42.000000000 +0200 @@ -760,6 +760,7 @@ tooltip=_("One URL per line:\n<b>http://...,note</b>\n<b>http://...,title by author - note</b>"), rejectreasons=rejecturllist.get_reject_reasons(), reasonslabel=_('Add this reason to all URLs added:'), + accept_storyurls=True, save_size_name='fff:Add Reject List') d.exec_() if d.result() == d.Accepted: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/calibre-plugin/dialogs.py new/FanFicFare-4.56.0/calibre-plugin/dialogs.py --- old/FanFicFare-4.55.0/calibre-plugin/dialogs.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/calibre-plugin/dialogs.py 2026-04-02 17:03:42.000000000 +0200 @@ -1328,6 +1328,7 @@ icon=None, title=None, label=None, tooltip=None, read_only=False, rejectreasons=[],reasonslabel=None, + accept_storyurls=False, save_size_name='fff:edit text dialog', ): SizePersistedDialog.__init__(self, parent, save_size_name) @@ -1341,7 +1342,10 @@ self.setWindowIcon(icon) self.l.addWidget(self.label) - self.textedit = QTextEdit(self) + if accept_storyurls: + self.textedit = DroppableQTextEdit(self) + else: + self.textedit = QTextEdit(self) self.textedit.setLineWrapMode(QTextEditNoWrap) self.textedit.setReadOnly(read_only) self.textedit.setText(text) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/calibre-plugin/fff_plugin.py new/FanFicFare-4.56.0/calibre-plugin/fff_plugin.py --- old/FanFicFare-4.55.0/calibre-plugin/fff_plugin.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/calibre-plugin/fff_plugin.py 2026-04-02 17:03:42.000000000 +0200 @@ -10,7 +10,9 @@ import fanficfare.six as six from fanficfare.six import ensure_text, string_types, text_type as unicode -# import cProfile +# from io import StringIO +# import cProfile, pstats +# from pstats import SortKey # def do_cprofile(func): # def profiled_func(*args, **kwargs): @@ -21,7 +23,12 @@ # profile.disable() # return result # finally: -# profile.print_stats() +# # profile.print_stats() +# s = StringIO() +# sortby = SortKey.CUMULATIVE +# ps = pstats.Stats(profile, stream=s).sort_stats(sortby) +# ps.print_stats(20) +# print(s.getvalue()) # return profiled_func import logging @@ -111,7 +118,8 @@ LoopProgressDialog, UserPassDialog, AboutDialog, CollectURLDialog, RejectListDialog, EmailPassDialog, TOTPDialog, save_collisions, question_dialog_all, - NotGoingToDownload, RejectUrlEntry, IniTextDialog) + NotGoingToDownload, RejectUrlEntry, IniTextDialog, + EditTextDialog) # because calibre immediately transforms html into zip and don't want # to have an 'if html'. db.has_format is cool with the case mismatch, @@ -205,20 +213,6 @@ prefs, self.qaction.icon()) - ## Kludgey, yes, but with the real configuration inside the - ## library now, how else would a user be able to change this - ## setting if it's crashing calibre? - def check_macmenuhack(self): - try: - return self.macmenuhack - except: - file_path = os.path.join(calibre_config_dir, - *("plugins/fanficfare_macmenuhack.txt".split('/'))) - file_path = os.path.abspath(file_path) - logger.debug("Plugin %s macmenuhack file_path:%s"%(self.name,file_path)) - self.macmenuhack = os.access(file_path, os.F_OK) - return self.macmenuhack - accepts_drops = True def accept_enter_event(self, event, mime_data): @@ -443,30 +437,38 @@ self.reject_list_action = self.create_menu_item_ex(self.menu, _('Reject Selected Books'), unique_name='Reject Selected Books', image='rotate-right.png', triggered=self.reject_list_urls) - # self.menu.addSeparator() - # print("platform.system():%s"%platform.system()) - # print("platform.mac_ver()[0]:%s"%platform.mac_ver()[0]) - if not self.check_macmenuhack(): # not platform.mac_ver()[0]: # Some macs crash on these menu items for unknown reasons. - self.menu.addSeparator() - self.editpersonalini_action = self.create_menu_item_ex(self.menu, _('Edit personal.ini'), - image= 'config.png', - unique_name='Edit personal.ini', - shortcut_name=_('Edit personal.ini'), - triggered=self.editpersonalini) - - self.config_action = self.create_menu_item_ex(self.menu, _('&Configure FanFicFare'), - image= 'config.png', - unique_name='Configure FanFicFare', - shortcut_name=_('Configure FanFicFare'), - triggered=do_user_config) - - self.about_action = self.create_menu_item_ex(self.menu, _('About FanFicFare'), - image= 'images/icon.png', - unique_name='About FanFicFare', - shortcut_name=_('About FanFicFare'), - triggered=self.about) + self.add_reject_urls_action = self.create_menu_item_ex(self.menu, _('Add Reject URLs'), + image='rotate-right.png', + unique_name='Add Reject URLs', + shortcut_name=_('Add Reject URLs'), + triggered=self.add_reject_urls) + + self.edit_reject_urls_action = self.create_menu_item_ex(self.menu, _('Edit Reject URLs'), + image='rotate-right.png', + unique_name='Edit Reject URLs', + shortcut_name=_('Edit Reject URLs'), + triggered=self.edit_reject_urls) + + self.menu.addSeparator() + self.editpersonalini_action = self.create_menu_item_ex(self.menu, _('Edit personal.ini'), + image= 'config.png', + unique_name='Edit personal.ini', + shortcut_name=_('Edit personal.ini'), + triggered=self.editpersonalini) + + self.config_action = self.create_menu_item_ex(self.menu, _('&Configure FanFicFare'), + image= 'config.png', + unique_name='Configure FanFicFare', + shortcut_name=_('Configure FanFicFare'), + triggered=do_user_config) + + self.about_action = self.create_menu_item_ex(self.menu, _('About FanFicFare'), + image= 'images/icon.png', + unique_name='About FanFicFare', + shortcut_name=_('About FanFicFare'), + triggered=self.about) self.gui.keyboard.finalize() def about(self,checked): @@ -502,6 +504,35 @@ prefs['personal.ini'] = get_resources('plugin-example.ini') prefs.save_to_db() + def add_reject_urls(self): + d = EditTextDialog(self.gui, + "http://example.com/story.php?sid=5,"+_("Reason why I rejected it")+"\nhttp://example.com/story.php?sid=6,"+_("Title by Author")+" - "+_("Reason why I rejected it"), + # icon=self.windowIcon(), + title=_("FanFicFare"), + label=_("Add Reject URLs. Use: <b>http://...,note</b> or <b>http://...,title by author - note</b><br>Invalid story URLs will be ignored."), + tooltip=_("One URL per line:\n<b>http://...,note</b>\n<b>http://...,title by author - note</b>"), + rejectreasons=rejecturllist.get_reject_reasons(), + reasonslabel=_('Add this reason to all URLs added:'), + accept_storyurls=True, + save_size_name='fff:Add Reject List') + d.exec_() + if d.result() == d.Accepted: + rejecturllist.add_text(d.get_plain_text(),d.get_reason_text()) + + def edit_reject_urls(self): + with busy_cursor(): + d = RejectListDialog(self.gui, + rejecturllist.get_list(), + rejectreasons=rejecturllist.get_reject_reasons(), + header=_("Edit Reject URLs List"), + show_delete=False, + show_all_reasons=False) + d.exec_() + if d.result() != d.Accepted: + return + with busy_cursor(): + rejecturllist.add(d.get_reject_list(),clear=True) + def create_menu_item_ex(self, parent_menu, menu_text, image=None, tooltip=None, shortcut=None, triggered=None, is_checked=None, shortcut_name=None, unique_name=None): @@ -1869,6 +1900,7 @@ else: return None + # @do_cprofile def update_books_loop(self,book,db=None, options={'fileform':'epub', 'collision':ADDNEW, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/calibre-plugin/jobs.py new/FanFicFare-4.56.0/calibre-plugin/jobs.py --- old/FanFicFare-4.55.0/calibre-plugin/jobs.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/calibre-plugin/jobs.py 2026-04-02 17:03:42.000000000 +0200 @@ -59,6 +59,7 @@ do_list = [] done_list = [] + logger.info("\n\n"+_("Downloading FanFiction Stories")+"\n%s\n"%("\n".join([ "%(status)s %(url)s %(comment)s" % book for book in book_list]))) ## pass failures from metadata through bg job so all results are ## together. for book in book_list: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/adapters/adapter_literotica.py new/FanFicFare-4.56.0/fanficfare/adapters/adapter_literotica.py --- old/FanFicFare-4.55.0/fanficfare/adapters/adapter_literotica.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/adapters/adapter_literotica.py 2026-04-02 17:03:42.000000000 +0200 @@ -241,7 +241,7 @@ self.story.extendList('eroticatags', [ stripHTML(t).title() for t in soup.select('div#tabpanel-tags a.av_as') ]) if soup.select('div[class^="_widget__tags_"]'): # logger.debug("tags2") - self.story.extendList('eroticatags', [ stripHTML(t).title() for t in soup.select('div[class^="_widget__tags_"] a[class^="_tags__link_"]') ]) + self.story.extendList('eroticatags', [ stripHTML(t).title() for t in soup.select('div[class^="_widget__tags_"] a[class^="_tag_item_"]') ]) # logger.debug(self.story.getList('eroticatags')) ## look first for 'Series Introduction', then Info panel short desc diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/adapters/base_adapter.py new/FanFicFare-4.56.0/fanficfare/adapters/base_adapter.py --- old/FanFicFare-4.55.0/fanficfare/adapters/base_adapter.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/adapters/base_adapter.py 2026-04-02 17:03:42.000000000 +0200 @@ -782,7 +782,7 @@ (img['src'],longdesc)=self.story.addImgUrl(url,self.img_url_trans(img['src']),fetch, coverexclusion=self.getConfig('cover_exclusion_regexp')) if longdesc: - logger.debug("---set longdesc:%s"%longdesc) + # logger.debug("---set longdesc:%s"%longdesc) img['longdesc'] = longdesc except AttributeError as ae: logger.info("Parsing for img tags failed--probably poor input HTML. Skipping img(%s)"%img) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/cli.py new/FanFicFare-4.56.0/fanficfare/cli.py --- old/FanFicFare-4.55.0/fanficfare/cli.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/cli.py 2026-04-02 17:03:42.000000000 +0200 @@ -28,7 +28,7 @@ import os, sys, platform -version="4.55.0" +version="4.56.0" os.environ['CURRENT_VERSION_ID']=version global_cache = 'global_cache' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/configurable.py new/FanFicFare-4.56.0/fanficfare/configurable.py --- old/FanFicFare-4.55.0/fanficfare/configurable.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/configurable.py 2026-04-02 17:03:42.000000000 +0200 @@ -526,7 +526,6 @@ return list([ 'authorId', 'authorUrl', - 'seriesUrl', 'storyId', 'storyUrl', 'langcode', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/epubutils.py new/FanFicFare-4.56.0/fanficfare/epubutils.py --- old/FanFicFare-4.55.0/fanficfare/epubutils.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/epubutils.py 2026-04-02 17:03:42.000000000 +0200 @@ -304,6 +304,10 @@ for item in contentdom.getElementsByTagName("item"): href=relpath+item.getAttribute("href") if item.getAttribute("media-type").startswith("image/") and getsoups: + if oldcover and href == oldcover[3]: + # don't include cover image, already handled by + # oldcover code and can trip de-dup unintentionally. + continue img_url = href.replace("OEBPS/","") # logger.debug("-->img img:%s"%img_url) if img_url not in images: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/fanficfare/story.py new/FanFicFare-4.56.0/fanficfare/story.py --- old/FanFicFare-4.55.0/fanficfare/story.py 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/fanficfare/story.py 2026-04-02 17:03:42.000000000 +0200 @@ -658,7 +658,7 @@ if failure: info['newsrc'] = 'failedtoload' info['actuallyused'] = False - logger.debug("add_img(%s,%s,%s,%s,%s,used:%s)"%(url,ext,mime,uuid,info['newsrc'],info['actuallyused'])) + # logger.debug("add_img(%s,%s,%s,%s,%s,used:%s)"%(url,ext,mime,uuid,info['newsrc'],info['actuallyused'])) return info def cache_failed_url(self,url): @@ -1639,7 +1639,7 @@ ## likely changed to jpg. (src,data)=oldimgs[url] ext = src.split('.')[-1] - logger.debug("load_oldimgs:(%s,%s,%s)"%(url,ext,imagetypes[ext])) + # logger.debug("load_oldimgs:(%s,%s,%s)"%(url,ext,imagetypes[ext])) self.img_store.add_img(url, ext, imagetypes[ext], @@ -1746,7 +1746,7 @@ (data,ext,mime) = no_convert_image(imgurl, imgdata) else: - logger.debug("Doing image processing on (%s)"%imgurl) + # logger.debug("Doing image processing on (%s)"%imgurl) try: sizes = [ int(x) for x in self.getConfigList('image_max_size',['580', '725']) ] except Exception as e: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/FanFicFare-4.55.0/pyproject.toml new/FanFicFare-4.56.0/pyproject.toml --- old/FanFicFare-4.55.0/pyproject.toml 2026-03-01 16:25:11.000000000 +0100 +++ new/FanFicFare-4.56.0/pyproject.toml 2026-04-02 17:03:42.000000000 +0200 @@ -16,7 +16,7 @@ # # For a discussion on single-sourcing the version, see # https://packaging.python.org/guides/single-sourcing-package-version/ -version = "4.55.0" +version = "4.56.0" # This is a one-line description or tagline of what your project does. This # corresponds to the "Summary" metadata field:
