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-02-03 21:35:51
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fanficfare (Old)
and /work/SRC/openSUSE:Factory/.python-fanficfare.new.1995 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-fanficfare"
Tue Feb 3 21:35:51 2026 rev:75 rq:1330811 version:4.53.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-fanficfare/python-fanficfare.changes
2025-12-02 13:21:33.190370556 +0100
+++
/work/SRC/openSUSE:Factory/.python-fanficfare.new.1995/python-fanficfare.changes
2026-02-03 21:36:41.265695223 +0100
@@ -1,0 +2,22 @@
+Tue Jan 27 16:44:19 UTC 2026 - Matej Cepl <[email protected]>
+
+- Update to the version 4.53.0:
+ - Fix for rare 'false' as INI list corner case
+ - base_xenforo2forum_adapter: Add include_nonauthor_poster
+ option
+ - adapter_storiesonlinenet: Improve inject_chapter_title
+ - writer_epub: Add option as page_progression_direction_rtl
+ - Changes for #1292 for normalizing different series URL forms.
+ - adapter_literotica: Remove unused chapter_categories_use_all
+ option, fix other site options for better defaults.ini
+ - Calc words_added even if not in logpage_entries.
+ - AO3: Site changed 'don't have permission' string
+ - adapter_literotica: Get series ID from data object
+ - adapter_literotica: Add chapter descriptions to summary
+ - adapter_literotica: Don't require tags_from_chapters for old
+ eroticatags collection
+ - adapter_fictionlive: create self.chapter_id_to_api earlier
+ for normalize_chapterurl
+ - adapter_literotica: Fix for one-shot aver_rating
+
+-------------------------------------------------------------------
Old:
----
FanFicFare-4.52.0.tar.gz
New:
----
FanFicFare-4.53.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-fanficfare.spec ++++++
--- /var/tmp/diff_new_pack.cLn9Pj/_old 2026-02-03 21:36:41.793717438 +0100
+++ /var/tmp/diff_new_pack.cLn9Pj/_new 2026-02-03 21:36:41.793717438 +0100
@@ -1,7 +1,7 @@
#
# spec file for package python-fanficfare
#
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -20,7 +20,7 @@
%define modnamedown fanficfare
%define skip_python2 1
Name: python-fanficfare
-Version: 4.52.0
+Version: 4.53.0
Release: 0
Summary: Tool for making eBooks from stories on fanfiction and other
web sites
License: GPL-3.0-only
++++++ FanFicFare-4.52.0.tar.gz -> FanFicFare-4.53.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/calibre-plugin/__init__.py
new/FanFicFare-4.53.0/calibre-plugin/__init__.py
--- old/FanFicFare-4.52.0/calibre-plugin/__init__.py 2025-12-01
15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/calibre-plugin/__init__.py 2026-01-01
16:18:34.000000000 +0100
@@ -33,7 +33,7 @@
from calibre.customize import InterfaceActionBase
# pulled out from FanFicFareBase for saving in prefs.py
-__version__ = (4, 52, 0)
+__version__ = (4, 53, 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.52.0/calibre-plugin/plugin-defaults.ini
new/FanFicFare-4.53.0/calibre-plugin/plugin-defaults.ini
--- old/FanFicFare-4.52.0/calibre-plugin/plugin-defaults.ini 2025-12-01
15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/calibre-plugin/plugin-defaults.ini 2026-01-01
16:18:34.000000000 +0100
@@ -1158,6 +1158,11 @@
## NOTE: SV requires login (always_login:true) to see dice rolls.
#include_dice_rolls:false
+## If the poster is *not* the author (IE, original poster), include a
+## poster link prepended to the chapter text.
+## Eg: "<p>Chapter by: <a...>ThePoster</a></p>"
+#include_nonauthor_poster:false
+
[epub]
## Each output format has a section that overrides [defaults]
@@ -1411,6 +1416,10 @@
## under [defaults] or [epub].
#force_update_epub_always:false
+## Mark epub as having right-to-left page progression direction.
+## Useful for RtL languages such as Japanese.
+#page_progression_direction_rtl:false
+
[html]
## include images from img tags in the body and summary of
@@ -2293,21 +2302,17 @@
averrating_label:Average Rating
extra_titlepage_entries:eroticatags,averrating
-## Chapters can be in different categories. Default to not using all
-## to be consistent with previous version.
-chapter_categories_use_all: false
-
## For multiple chapter stories, attempt to clean up the chapter title. This
will
## remove the story title and change "Ch. 01" to "Chapter 1", "Pt. 01" to
"Part 1"
## or just use the text. If this can't be done, the full title is used.
-clean_chapter_titles: false
+#clean_chapter_titles: false
## For stories, collect tags from individual chapter pages in addition to the
## series page tags. This allows collection of tags beyond the top 10 on the
series but
## if the author updates tags on a chapter and not the series, those tags may
persist even if
## the chapter is not fetched during an update.
## Default is false to maintain previous behavior.
-tags_from_chapters: false
+#tags_from_chapters: false
## For multi-chapter stories (series), use the chapter approval dates for
datePublished
## and dateUpdated instead of the series metadata dates. This provides more
accurate dates
@@ -2316,7 +2321,7 @@
## approved, if chapters are approved out of order, or if the works were
approved/updated
## before literotica's current series system was implemented.
## Default is false to maintain previous behavior.
-dates_from_chapters: false
+#dates_from_chapters: false
## Some stories mistakenly include 'Ch' or 'Pt' at the end of the
## story title. Appears to be a site bug or common author error. Copy
@@ -2325,11 +2330,13 @@
# title=> (Ch|Pt)$=>
## Add the chapter description at the start of each chapter.
-description_in_chapter: false
+#description_in_chapter: false
## Author's stories are now hidden behind 'Show More' button.
## This option will attemph to fetch the stories.
-fetch_stories_from_api: true
+#fetch_stories_from_api: true
+
+#include_chapter_descriptions_in_summary: false
## Clear FanFiction from defaults, site is original fiction.
extratags:Erotica
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.52.0/fanficfare/adapters/adapter_fictionlive.py
new/FanFicFare-4.53.0/fanficfare/adapters/adapter_fictionlive.py
--- old/FanFicFare-4.52.0/fanficfare/adapters/adapter_fictionlive.py
2025-12-01 15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/adapters/adapter_fictionlive.py
2026-01-01 16:18:34.000000000 +0100
@@ -55,6 +55,8 @@
self.story_id = self.parsedUrl.path.split('/')[3]
self.story.setMetadata('storyId', self.story_id)
+ self.chapter_id_to_api = {}
+
# normalize URL. omits title in the url
self._setURL("https://fiction.live/stories//{s_id}".format(s_id =
self.story_id));
@@ -222,8 +224,6 @@
## api url to get content of a multi route chapter. requires only the
route id and no timestamps
route_chunkrange_url =
"https://fiction.live/api/anonkun/route/{c_id}/chapters"
- self.chapter_id_to_api = {}
-
def add_chapter_url(title, bounds):
"Adds a chapter url based on the start/end chunk-range timestamps."
start, end = bounds
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.52.0/fanficfare/adapters/adapter_literotica.py
new/FanFicFare-4.53.0/fanficfare/adapters/adapter_literotica.py
--- old/FanFicFare-4.52.0/fanficfare/adapters/adapter_literotica.py
2025-12-01 15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/adapters/adapter_literotica.py
2026-01-01 16:18:34.000000000 +0100
@@ -19,6 +19,7 @@
import logging
logger = logging.getLogger(__name__)
import re
+import json
from bs4.element import Comment
from ..htmlcleanup import stripHTML
@@ -194,8 +195,18 @@
isSingleStory = '/series/se' not in self.url
- ## common between one-shots and multi-chapters
+ if not isSingleStory:
+ # Normilize the url?
+ state = re.findall(r"prefix\=\"/series/\",state='(.+?)'</script>",
data)
+ json_state =
json.loads(state[0].replace("\\'","'").replace("\\\\","\\"))
+ url_series_id =
unicode(re.match(self.getSiteURLPattern(),self.url).group('storyseriesid'))
+ json_series_id = unicode(json_state['series']['data']['id'])
+ if json_series_id != url_series_id:
+ res = re.sub(url_series_id, json_series_id, unicode(self.url))
+ logger.debug("Normalized url: %s"%res)
+ self._setURL(res)
+ ## common between one-shots and multi-chapters
# title
self.story.setMetadata('title', stripHTML(soup.select_one('h1')))
# logger.debug(self.story.getMetadata('title'))
@@ -225,31 +236,29 @@
else: # if all else fails
self.story.setMetadata('authorId', stripHTML(authora))
- ## Collect tags from series/story page if tags_from_chapters is enabled
- if self.getConfig("tags_from_chapters"):
- if soup.select('div#tabpanel-tags'):
- # logger.debug("tags1")
- 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_"]') ])
+ if soup.select('div#tabpanel-tags'):
+ # logger.debug("tags1")
+ 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_"]') ])
# logger.debug(self.story.getList('eroticatags'))
## look first for 'Series Introduction', then Info panel short desc
## series can have either, so put in common code.
+ desc = []
introtag = soup.select_one('div.bp_rh')
- descdiv = soup.select_one('div#tabpanel-info div.bn_B')
- if not descdiv:
- descdiv = soup.select_one('div[class^="_tab__pane_"]
div[class^="_widget__info_"]')
+ descdiv = soup.select_one('div#tabpanel-info div.bn_B') or \
+ soup.select_one('div[class^="_tab__pane_"]
div[class^="_widget__info_"]')
if introtag and stripHTML(introtag):
# make sure there's something in the tag.
# logger.debug("intro %s"%introtag)
- self.setDescription(self.url,introtag)
+ desc.append(unicode(introtag))
elif descdiv and stripHTML(descdiv):
# make sure there's something in the tag.
# logger.debug("desc %s"%descdiv)
- self.setDescription(self.url,descdiv)
- else:
+ desc.append(unicode(descdiv))
+ if not desc or
self.getConfig("include_chapter_descriptions_in_summary"):
## Only for backward compatibility with 'stories' that
## don't have an intro or short desc.
descriptions = []
@@ -259,7 +268,9 @@
descriptions.append("%d. %s" % (i + 1,
stripHTML(chapterdesctag)))
# now put it back--it's used below
chapterdesctag.append(a)
-
self.setDescription(authorurl,"<p>"+"</p>\n<p>".join(descriptions)+"</p>")
+ desc.append(unicode("<p>"+"</p>\n<p>".join(descriptions)+"</p>"))
+
+ self.setDescription(self.url,u''.join(desc))
if isSingleStory:
## one-shots don't *display* date info, but they have it
@@ -276,6 +287,12 @@
self.story.setMetadata('datePublished', dateval)
self.story.setMetadata('dateUpdated', dateval)
+ ## one-shots don't have same json data to get aver_rating
+ ## from below. This kludge matches the data_approve
+ rateall = re.search(r'rate_all:([\d\.]+)',data)
+ if rateall:
+ self.story.setMetadata('averrating', '%4.2f' %
float(rateall.group(1)))
+
## one-shots assumed completed.
self.story.setMetadata('status','Completed')
@@ -341,15 +358,10 @@
state =
data[i+len(state_start):data.index(state_end,i)].replace("\\'","'").replace("\\\\","\\")
if state:
# logger.debug(state)
- import json
json_state = json.loads(state)
# logger.debug(json.dumps(json_state,
sort_keys=True,indent=2, separators=(',', ':')))
all_rates = []
- ## one-shot
- if 'story' in json_state:
- all_rates = [
float(json_state['story']['data']['rate_all']) ]
- ## series
- elif 'series' in json_state:
+ if 'series' in json_state:
all_rates = [ float(x['rate_all']) for x in
json_state['series']['works'] ]
## Extract dates from chapter approval dates if
dates_from_chapters is enabled
@@ -372,7 +384,7 @@
## alternate chapters from JSON
if self.num_chapters() < 1:
logger.debug("Getting Chapters from series JSON")
- seriesid =
json_state.get('series',{}).get('coversSeriesId',None)
+ seriesid =
json_state.get('series',{}).get('data',{}).get('id',None)
if seriesid:
logger.info("Fetching chapter data from JSON")
logger.debug(seriesid)
@@ -471,8 +483,8 @@
page_urls = get_urls_from_html(soup, url,
configuration=self.configuration, normalize=normalize)
- if not self.getConfig("fetch_stories_from_api"):
- logger.debug('Not enabled')
+ if not self.getConfig("fetch_stories_from_api",True):
+ logger.debug('fetch_stories_from_api Not enabled')
return {'urllist': page_urls}
user_story_list =
re.search(r'literotica\.com/authors/.+?/lists\?listid=(?P<list_id>\d+)', url)
@@ -543,7 +555,6 @@
logger.debug("Type of works not found")
return {'urllist': urls}
- import json
last_page = int(js_story_list.group('last_page'))
current_page = int(js_story_list.group('current_page')) + 1
# Fetching the remaining urls from api. Can't trust the number given
about the pages left from a website. Sometimes even the api returns outdated
number of pages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.52.0/fanficfare/adapters/adapter_storiesonlinenet.py
new/FanFicFare-4.53.0/fanficfare/adapters/adapter_storiesonlinenet.py
--- old/FanFicFare-4.52.0/fanficfare/adapters/adapter_storiesonlinenet.py
2025-12-01 15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/adapters/adapter_storiesonlinenet.py
2026-01-01 16:18:34.000000000 +0100
@@ -628,11 +628,13 @@
chapter_title = None
if self.getConfig('inject_chapter_title'):
- h2tag = pagetag.find('h2')
- if h2tag:
- # I'm seeing an h1 now, but it's not logged in?
- # Something's broken...
- chapter_title = h2tag.extract()
+ if self.num_chapters() > 1:
+ cttag = pagetag.find('h2')
+ else:
+ ## single chapter stories formatted a little differently.
+ cttag = pagetag.find('h1')
+ if cttag:
+ chapter_title = cttag.extract()
# Strip te header section
tag = pagetag.find('header')
@@ -714,4 +716,5 @@
# inject_chapter_title
if chapter_title:
chapter_title.name='h3'
+ chapter_title['class']='inject_chapter_title'
pagetag.insert(0,chapter_title)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.52.0/fanficfare/adapters/base_otw_adapter.py
new/FanFicFare-4.53.0/fanficfare/adapters/base_otw_adapter.py
--- old/FanFicFare-4.52.0/fanficfare/adapters/base_otw_adapter.py
2025-12-01 15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/adapters/base_otw_adapter.py
2026-01-01 16:18:34.000000000 +0100
@@ -237,7 +237,7 @@
if 'This work is part of an ongoing challenge and will be revealed
soon!' in meta:
raise exceptions.FailedToDownload('Site says: "This work is part
of an ongoing challenge and will be revealed soon!"')
- if '<div class="flash error">Sorry, you don't have permission to
access the page you were trying to reach.</div>' in data:
+ if re.search(r'<div class="flash error">Sorry, you don(\'|')t have
permission to access the page you were trying to reach.</div>', data):
# note that it's not *actually* a 503 code...
raise exceptions.FailedToDownload('Site says: "Sorry, you don\'t
have permission to access the page you were trying to reach."')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.52.0/fanficfare/adapters/base_xenforo2forum_adapter.py
new/FanFicFare-4.53.0/fanficfare/adapters/base_xenforo2forum_adapter.py
--- old/FanFicFare-4.52.0/fanficfare/adapters/base_xenforo2forum_adapter.py
2025-12-01 15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/adapters/base_xenforo2forum_adapter.py
2026-01-01 16:18:34.000000000 +0100
@@ -966,6 +966,28 @@
postbody = self.get_post_body(souptag)
+ if self.getConfig("include_nonauthor_poster"):
+ poster_atag = souptag.select_one('div.message-userDetails
a.username')
+ # logger.debug(stripHTML(poster_atag))
+ if True or stripHTML(poster_atag) not in
self.story.getList('author'):
+ ## <div class="message-userDetails"> <h4
+ ## class="message-name"><a class="username"
+ ##
href="https://forums.spacebattles.com/members/stargazingseraph.561651/"><span
+ ## class="username--style476">StargazingSeraph</span></a></h4>
+ if not topsoup:
+ ## only top of soup has new_tag, and parents is a
+ ## generator not a list.
+ topsoup = [x for x in souptag.parents][-1]
+ poster = topsoup.new_tag('p')
+ poster['class']='poster'
+ poster.string="Chapter by: "
+ poster.append(poster_atag)
+
+ # logger.debug(poster)
+ postbody.insert(0,"\n")
+ postbody.insert(0,poster)
+ postbody.insert(0,"\n")
+
# XenForo uses <base href="https://forums.spacebattles.com/" />
return self.utf8FromSoup(self.getURLPrefix(),postbody)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/fanficfare/cli.py
new/FanFicFare-4.53.0/fanficfare/cli.py
--- old/FanFicFare-4.52.0/fanficfare/cli.py 2025-12-01 15:25:22.000000000
+0100
+++ new/FanFicFare-4.53.0/fanficfare/cli.py 2026-01-01 16:18:34.000000000
+0100
@@ -28,7 +28,7 @@
import os, sys, platform
-version="4.52.0"
+version="4.53.0"
os.environ['CURRENT_VERSION_ID']=version
global_cache = 'global_cache'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/fanficfare/configurable.py
new/FanFicFare-4.53.0/fanficfare/configurable.py
--- old/FanFicFare-4.52.0/fanficfare/configurable.py 2025-12-01
15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/configurable.py 2026-01-01
16:18:34.000000000 +0100
@@ -208,7 +208,6 @@
'remove_empty_p':(None,None,boollist),
'sort_ships':(None,None,boollist),
'strip_chapter_numbers':(None,None,boollist),
- 'remove_class_chapter':(None,None,boollist),
'mark_new_chapters':(None,None,boollist+['latestonly']),
'titlepage_use_table':(None,None,boollist),
'decode_emails':(None,None,boollist),
@@ -264,13 +263,13 @@
'romancecat_to_characters_ships':(['tthfanfic.org'],None,boollist),
'use_meta_keywords':(['literotica.com'],None,boollist),
- 'chapter_categories_use_all':(['literotica.com'],None,boollist),
'clean_chapter_titles':(['literotica.com'],None,boollist),
'description_in_chapter':(['literotica.com'],None,boollist),
'fetch_stories_from_api':(['literotica.com'],None,boollist),
'order_chapters_by_date':(['literotica.com'],None,boollist),
'tags_from_chapters':(['literotica.com'],None,boollist),
'dates_from_chapters':(['literotica.com'],None,boollist),
+
'include_chapter_descriptions_in_summary':(['literotica.com'],None,boollist),
'inject_chapter_title':(['asianfanfics.com']+wpc_list,None,boollist),
'inject_chapter_image':(['asianfanfics.com'],None,boollist),
@@ -288,6 +287,7 @@
'calibre_series_meta':(None,['epub'],boollist),
'force_update_epub_always':(None,['epub'],boollist),
+ 'page_progression_direction_rtl':(None,['epub'],boollist),
'windows_eol':(None,['txt'],boollist),
@@ -325,6 +325,7 @@
'use_threadmarks_cover':(base_xenforo2_list,None,boollist),
'skip_sticky_first_posts':(base_xenforo2_list,None,boollist),
'include_dice_rolls':(base_xenforo2_list,None,boollist+['svg']),
+ 'include_nonauthor_poster':(base_xenforo2_list,None,boollist),
'include_chapter_banner_images':(['wattpad.com'],None,boollist),
'dateUpdated_method':(['wattpad.com'],None,['modifyDate',
'lastPublishedPart']),
'fix_excess_space': (['novelonlinefull.com', 'novelall.com'],
['epub', 'html'], boollist),
@@ -461,6 +462,7 @@
'logpage_at_end',
'calibre_series_meta',
'force_update_epub_always',
+ 'page_progression_direction_rtl',
'include_subject_tags',
'include_titlepage',
'include_tocpage',
@@ -520,7 +522,6 @@
'pairingcat_to_characters_ships',
'romancecat_to_characters_ships',
'use_meta_keywords',
- 'chapter_categories_use_all',
'clean_chapter_titles',
'conditionals_use_lists',
'description_in_chapter',
@@ -528,6 +529,7 @@
'fetch_stories_from_api',
'tags_from_chapters',
'dates_from_chapters',
+ 'include_chapter_descriptions_in_summary',
'inject_chapter_title',
'inject_chapter_image',
'append_datepublished_to_storyurl',
@@ -606,6 +608,7 @@
'use_threadmarks_cover',
'skip_sticky_first_posts',
'include_dice_rolls',
+ 'include_nonauthor_poster',
'include_chapter_banner_images',
'dateUpdated_method',
'datethreadmark_format',
@@ -817,7 +820,8 @@
# split and strip each.
def get_config_list(self, sections, key, default=[]):
- vlist = re.split(r'(?<!\\),',self.get_config(sections,key)) # don't
split on \,
+ ## "%s" to make false > "false" Rare corner case, probably accidental
+ vlist = re.split(r'(?<!\\),',"%s"%self.get_config(sections,key)) #
don't split on \,
vlist = [x for x in [ v.strip().replace(r'\,',',') for v in vlist ] if
x !='']
if not vlist:
return default
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/fanficfare/defaults.ini
new/FanFicFare-4.53.0/fanficfare/defaults.ini
--- old/FanFicFare-4.52.0/fanficfare/defaults.ini 2025-12-01
15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/defaults.ini 2026-01-01
16:18:34.000000000 +0100
@@ -1148,6 +1148,11 @@
## NOTE: SV requires login (always_login:true) to see dice rolls.
#include_dice_rolls:false
+## If the poster is *not* the author (IE, original poster), include a
+## poster link prepended to the chapter text.
+## Eg: "<p>Chapter by: <a...>ThePoster</a></p>"
+#include_nonauthor_poster:false
+
[epub]
## Each output format has a section that overrides [defaults]
@@ -1399,6 +1404,10 @@
## under [defaults] or [epub].
#force_update_epub_always:false
+## Mark epub as having right-to-left page progression direction.
+## Useful for RtL languages such as Japanese.
+#page_progression_direction_rtl:false
+
[html]
## include images from img tags in the body and summary of
@@ -2286,21 +2295,17 @@
averrating_label:Average Rating
extra_titlepage_entries:eroticatags,averrating
-## Chapters can be in different categories. Default to not using all
-## to be consistent with previous version.
-chapter_categories_use_all: false
-
## For multiple chapter stories, attempt to clean up the chapter title. This
will
## remove the story title and change "Ch. 01" to "Chapter 1", "Pt. 01" to
"Part 1"
## or just use the text. If this can't be done, the full title is used.
-clean_chapter_titles: false
+#clean_chapter_titles: false
## For stories, collect tags from individual chapter pages in addition to the
## series page tags. This allows collection of tags beyond the top 10 on the
series but
## if the author updates tags on a chapter and not the series, those tags may
persist even if
## the chapter is not fetched during an update.
## Default is false to maintain previous behavior.
-tags_from_chapters: false
+#tags_from_chapters: false
## For multi-chapter stories (series), use the chapter approval dates for
datePublished
## and dateUpdated instead of the series metadata dates. This provides more
accurate dates
@@ -2309,7 +2314,7 @@
## approved, if chapters are approved out of order, or if the works were
approved/updated
## before literotica's current series system was implemented.
## Default is false to maintain previous behavior.
-dates_from_chapters: false
+#dates_from_chapters: false
## Some stories mistakenly include 'Ch' or 'Pt' at the end of the
## story title. Appears to be a site bug or common author error. Copy
@@ -2318,11 +2323,13 @@
# title=> (Ch|Pt)$=>
## Add the chapter description at the start of each chapter.
-description_in_chapter: false
+#description_in_chapter: false
## Author's stories are now hidden behind 'Show More' button.
## This option will attemph to fetch the stories.
-fetch_stories_from_api: true
+#fetch_stories_from_api: true
+
+#include_chapter_descriptions_in_summary: false
## Clear FanFiction from defaults, site is original fiction.
extratags:Erotica
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/fanficfare/writers/writer_epub.py
new/FanFicFare-4.53.0/fanficfare/writers/writer_epub.py
--- old/FanFicFare-4.52.0/fanficfare/writers/writer_epub.py 2025-12-01
15:25:22.000000000 +0100
+++ new/FanFicFare-4.53.0/fanficfare/writers/writer_epub.py 2026-01-01
16:18:34.000000000 +0100
@@ -294,13 +294,12 @@
retval = START.substitute(self.story.getAllMetadata())
- ## words_added is only used in logpage because it's the only
- ## place we know the previous version's word count.
- if 'words_added' in (self.getConfigList("logpage_entries") +
self.getConfigList("extra_logpage_entries")):
- new_words = self.story.getMetadata('numWords')
- old_words = oldvalues.get('numWords',None)
- if new_words and old_words:
-
self.story.setMetadata('words_added',commaGroups(unicode(int(new_words.replace(',',''))-int(old_words.replace(',','')))))
+ ## words_added is calculated from logpage because it's the
+ ## only place we know the previous version's word count.
+ new_words = self.story.getMetadata('numWords')
+ old_words = oldvalues.get('numWords',None)
+ if new_words and old_words:
+
self.story.setMetadata('words_added',commaGroups(unicode(int(new_words.replace(',',''))-int(old_words.replace(',','')))))
for entry in self.getConfigList("logpage_entries") +
self.getConfigList("extra_logpage_entries"):
if self.isValidMetaEntry(entry):
@@ -816,6 +815,8 @@
spine = newTag(contentdom,"spine",attrs={"toc":"ncx"})
+ if self.getConfig('page_progression_direction_rtl'):
+ spine.setAttribute("page-progression-direction","rtl")
package.appendChild(spine)
for itemref in itemrefs:
spine.appendChild(newTag(contentdom,"itemref",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.52.0/pyproject.toml
new/FanFicFare-4.53.0/pyproject.toml
--- old/FanFicFare-4.52.0/pyproject.toml 2025-12-01 15:25:22.000000000
+0100
+++ new/FanFicFare-4.53.0/pyproject.toml 2026-01-01 16:18:34.000000000
+0100
@@ -16,7 +16,7 @@
#
# For a discussion on single-sourcing the version, see
# https://packaging.python.org/guides/single-sourcing-package-version/
-version = "4.52.0"
+version = "4.53.0"
# This is a one-line description or tagline of what your project does. This
# corresponds to the "Summary" metadata field: