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 2022-11-22 16:10:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-fanficfare (Old)
and /work/SRC/openSUSE:Factory/.python-fanficfare.new.1597 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-fanficfare"
Tue Nov 22 16:10:41 2022 rev:45 rq:1037251 version:4.18.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-fanficfare/python-fanficfare.changes
2022-10-25 11:20:29.182200339 +0200
+++
/work/SRC/openSUSE:Factory/.python-fanficfare.new.1597/python-fanficfare.changes
2022-11-22 16:10:48.958222689 +0100
@@ -1,0 +2,17 @@
+Tue Nov 22 07:50:20 UTC 2022 - Matej Cepl <[email protected]>
+
+- Update to 4.18.0:
+ - Update metadata caching with dependency invalidating
+ - Still allow images with use_flaresolverr_proxy if
+ use_browser_cache
+ - Support classic AND modern (and minimalist) theme for
+ storiesonline, finestories and scifistories - thanks, mvlcek
+ - adapter_tenhawkpresents: Change site to t.evancurrie.ca -
+ tenhawk domain semi-broken
+ - remove_class_chapter missing from config lists
+ - adapter_adultfanfictionorg: Fixes for site changes, thanks
+ cryosaur.
+ - Remove Calibre Update Cover option entirely(was deprecated)
+ #878
+
+-------------------------------------------------------------------
Old:
----
FanFicFare-4.17.0.tar.gz
New:
----
FanFicFare-4.18.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-fanficfare.spec ++++++
--- /var/tmp/diff_new_pack.Soy2qb/_old 2022-11-22 16:10:49.534225612 +0100
+++ /var/tmp/diff_new_pack.Soy2qb/_new 2022-11-22 16:10:49.542225652 +0100
@@ -21,7 +21,7 @@
%define skip_python2 1
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
Name: python-fanficfare
-Version: 4.17.0
+Version: 4.18.0
Release: 0
Summary: Tool for making eBooks from stories on fanfiction and other
web sites
License: GPL-3.0-only
++++++ FanFicFare-4.17.0.tar.gz -> FanFicFare-4.18.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/__init__.py
new/FanFicFare-4.18.0/calibre-plugin/__init__.py
--- old/FanFicFare-4.17.0/calibre-plugin/__init__.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/__init__.py 2022-11-22
02:04:53.000000000 +0100
@@ -33,7 +33,7 @@
from calibre.customize import InterfaceActionBase
# pulled out from FanFicFareBase for saving in prefs.py
-__version__ = (4, 17, 0)
+__version__ = (4, 18, 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.17.0/calibre-plugin/config.py
new/FanFicFare-4.18.0/calibre-plugin/config.py
--- old/FanFicFare-4.17.0/calibre-plugin/config.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/config.py 2022-11-22
02:04:53.000000000 +0100
@@ -70,8 +70,7 @@
from calibre_plugins.fanficfare_plugin.dialogs import (
UPDATE, UPDATEALWAYS, collision_order, save_collisions, RejectListDialog,
- EditTextDialog, IniTextDialog, RejectUrlEntry,
- updateepubcover_warning)
+ EditTextDialog, IniTextDialog, RejectUrlEntry)
from fanficfare.adapters import getSiteSections, get_section_url
@@ -280,7 +279,6 @@
prefs['collision'] =
save_collisions[unicode(self.basic_tab.collision.currentText())]
prefs['updatemeta'] = self.basic_tab.updatemeta.isChecked()
prefs['bgmeta'] = self.basic_tab.bgmeta.isChecked()
- prefs['updateepubcover'] =
self.basic_tab.updateepubcover.isChecked()
prefs['keeptags'] = self.basic_tab.keeptags.isChecked()
prefs['mark'] = self.basic_tab.mark.isChecked()
prefs['mark_success'] = self.basic_tab.mark_success.isChecked()
@@ -492,22 +490,6 @@
self.l.addLayout(horz)
- label = QLabel(_("The <i><b>Update EPUB Cover</b></i> option is
DEPRECATED and in future will always be ON.<br>"
- "For now you can still uncheck it here, but please
see <a href='https://github.com/JimmXinu/FanFicFare/issues/878'>this issue</a>
for more information."))
- label.setWordWrap(True)
- label.setOpenExternalLinks(True)
- self.l.addWidget(label)
-
- self.updateepubcover = QCheckBox(_('Default Update EPUB Cover when
Updating EPUB?'),self)
- self.updateepubcover.setToolTip(_("On each download, FanFicFare offers
an option to update the book cover image <i>inside</i> the EPUB from the web
site when the EPUB is updated.<br />This sets whether that will default to on
or off."))
- self.updateepubcover.setChecked(prefs['updateepubcover'])
- self.l.addWidget(self.updateepubcover)
-
- def updateepubcover_changed():
- if not self.updateepubcover.isChecked():
- updateepubcover_warning()
- self.updateepubcover.stateChanged.connect(updateepubcover_changed)
-
cali_gb = groupbox = QGroupBox(_("Updating Calibre Options"))
self.l = QVBoxLayout()
groupbox.setLayout(self.l)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/dialogs.py
new/FanFicFare-4.18.0/calibre-plugin/dialogs.py
--- old/FanFicFare-4.17.0/calibre-plugin/dialogs.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/dialogs.py 2022-11-22
02:04:53.000000000 +0100
@@ -314,14 +314,6 @@
self.mergehide.append(self.updatemeta)
self.mergeupdateshow.append(self.updatemeta)
- self.updateepubcover = QCheckBox(_('Update EPUB Cover?'),self)
- # self.updateepubcover.setToolTip(_('Update book cover image from site
or defaults (if found) <i>inside</i> the EPUB when EPUB is updated.'))
- self.updateepubcover.setToolTip(_('This feature is being removed. See
FanFicFare > Config > General.'))
- self.updateepubcover.setChecked(self.prefs['updateepubcover'])
- self.updateepubcover.setEnabled(False)
- horz.addWidget(self.updateepubcover)
- self.mergehide.append(self.updateepubcover)
-
self.gbl.addLayout(horz)
## bgmeta not used with Add New because of stories that change
@@ -451,11 +443,6 @@
self.updatemeta.setChecked(self.prefs['updatemeta'])
# self.bgmeta.setChecked(self.prefs['bgmeta'])
- if not self.merge:
- self.updateepubcover.setChecked(self.prefs['updateepubcover'])
- if not self.prefs['updateepubcover']:
- updateepubcover_warning()
-
self.url.setText(url_list_text)
if url_list_text:
self.button_box.button(QDialogButtonBox.Ok).setFocus()
@@ -493,14 +480,12 @@
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
'bgmeta': False, # self.bgmeta.isChecked(),
- 'updateepubcover': self.updateepubcover.isChecked(),
'smarten_punctuation':self.prefs['smarten_punctuation'],
'do_wordcount':self.prefs['do_wordcount'],
}
if self.merge:
retval['fileform']=='epub'
- retval['updateepubcover']=True
if self.newmerge:
retval['updatemeta']=True
retval['collision']=ADDNEW
@@ -912,15 +897,6 @@
self.updatemeta.setChecked(self.prefs['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) <i>inside</i> the EPUB when EPUB is updated.'))
- self.updateepubcover.setToolTip(_('This feature is being removed. See
FanFicFare > Config > General.'))
- self.updateepubcover.setEnabled(False)
- self.updateepubcover.setChecked(self.prefs['updateepubcover'])
- horz.addWidget(self.updateepubcover)
- if not self.prefs['updateepubcover']:
- updateepubcover_warning()
-
self.bgmeta = QCheckBox(_('Background Metadata?'),self)
self.bgmeta.setToolTip(_("Collect Metadata from sites in a Background
process.<br />This returns control to you quicker while updating, but you won't
be asked for username/passwords or if you are an adult--stories that need those
will just fail."))
self.bgmeta.setChecked(self.prefs['bgmeta'])
@@ -972,7 +948,6 @@
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
'bgmeta': self.bgmeta.isChecked(),
- 'updateepubcover': self.updateepubcover.isChecked(),
'smarten_punctuation':self.prefs['smarten_punctuation'],
'do_wordcount':self.prefs['do_wordcount'],
}
@@ -1713,12 +1688,3 @@
gprefs.set('questions_to_auto_skip', list(auto_skip))
return ret
-
-from calibre.gui2.ui import get_gui
-def updateepubcover_warning():
- return confirm('<p>'+_("FanFicFare's <i><b>Update EPUB Cover</b></i>
Download Option is being removed.")+'<\p>'+
- '<p>'+_("It was a very old setting that didn't quite do
what users expected.")+'<\p>'+
- '<p>'+_("You are getting this warning because you have
<i><b>Default Update EPUB Cover when Updating EPUB?</b></i> unchecked in
FanFicFare > Config > General.")+'<\p>'+
- '<p>'+_("See <a
href='https://github.com/JimmXinu/FanFicFare/issues/878'>this issue</a> for
more information.")+'<\p>',
- 'fff_updateepubcover_warning',
- get_gui(), show_cancel_button=False, title=_("FanFicFare
Warning"))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/fff_plugin.py
new/FanFicFare-4.18.0/calibre-plugin/fff_plugin.py
--- old/FanFicFare-4.17.0/calibre-plugin/fff_plugin.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/fff_plugin.py 2022-11-22
02:04:53.000000000 +0100
@@ -113,8 +113,7 @@
LoopProgressDialog, UserPassDialog, AboutDialog, CollectURLDialog,
RejectListDialog, EmailPassDialog,
save_collisions, question_dialog_all,
- NotGoingToDownload, RejectUrlEntry,
- updateepubcover_warning)
+ NotGoingToDownload, RejectUrlEntry)
# 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,
@@ -591,15 +590,12 @@
if prefs['download_from_email_immediately']:
## do imap fetch w/o GUI elements
if url_list:
- if prefs['updateepubcover'] == False:
- updateepubcover_warning()
self.prep_downloads({
'fileform': prefs['fileform'],
# save_collisions==convert from save value to local
lang value
'collision':
extraoptions.get('collision',save_collisions[prefs['collision']]),
'updatemeta': prefs['updatemeta'],
'bgmeta': False,
- 'updateepubcover': prefs['updateepubcover'],
'smarten_punctuation':prefs['smarten_punctuation'],
'do_wordcount':prefs['do_wordcount'],
'add_tag':prefs['imaptags'],
@@ -1237,8 +1233,7 @@
options={'fileform':'epub',
'collision':ADDNEW,
'updatemeta':True,
- 'bgmeta':False,
- 'updateepubcover':True},
+ 'bgmeta':False},
merge=False):
'''
Update passed in book dict with metadata from website and
@@ -1260,7 +1255,6 @@
collision = book['collision'] = options['collision']
updatemeta= options['updatemeta']
bgmeta= options['bgmeta']
- updateepubcover= options['updateepubcover']
## Check reject list. Redundant with below for when story URL
## changes, but also kept here to avoid network hit in most
@@ -1668,8 +1662,7 @@
options={'fileform':'epub',
'collision':ADDNEW,
'updatemeta':True,
- 'bgmeta':False,
- 'updateepubcover':True},
+ 'bgmeta':False},
merge=False):
'''
Called by LoopProgressDialog to start story downloads BG processing.
@@ -1798,8 +1791,7 @@
options={'fileform':'epub',
'collision':ADDNEW,
'updatemeta':True,
- 'bgmeta':False,
- 'updateepubcover':True},
+ 'bgmeta':False},
errorcol_label=None,
lastcheckedcol_label=None):
@@ -2149,7 +2141,6 @@
lastcheckedcol_label =
self.get_custom_col_label(prefs['lastcheckedcol'])
if prefs['mark'] or errorcol_label or lastcheckedcol_label:
self.previous = self.gui.library_view.currentIndex() # used by
update_books_finish.
- self.gui.status_bar.show_message(_('Adding/Updating %s BAD
books.')%len(book_list))
LoopProgressDialog(self.gui,
book_list,
partial(self.update_error_column_loop,
db=self.gui.current_db, errorcol_label=errorcol_label,
lastcheckedcol_label=lastcheckedcol_label),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/jobs.py
new/FanFicFare-4.18.0/calibre-plugin/jobs.py
--- old/FanFicFare-4.17.0/calibre-plugin/jobs.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/jobs.py 2022-11-22
02:04:53.000000000 +0100
@@ -222,9 +222,6 @@
options['fileform'],
options['personal.ini'])
- if not options['updateepubcover'] and 'epub_for_update' in book
and book['collision'] in (UPDATE, UPDATEALWAYS):
- configuration.set("overrides","never_make_cover","true")
-
# images only for epub, html, even if the user mistakenly
# turned it on else where.
if options['fileform'] not in ("epub","html"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/plugin-defaults.ini
new/FanFicFare-4.18.0/calibre-plugin/plugin-defaults.ini
--- old/FanFicFare-4.17.0/calibre-plugin/plugin-defaults.ini 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/plugin-defaults.ini 2022-11-22
02:04:53.000000000 +0100
@@ -143,6 +143,16 @@
## confidence required to use the chardet detected.
#chardet_confidence_limit:0.9
+## Normally, try to make the filenames 'safe' by removing invalid
+## filename chars. Applies to default_cover_image, force_cover_image,
+## output_filename & zip_filename.
+allow_unsafe_filename: false
+
+## The regex pattern of 'unsafe' filename chars for above. First
+## character . OR any one or more characters that are NOT a letter,
+## number, or one of _. []()&'-
+output_filename_safepattern:(^\.|/\.|[^a-zA-Z0-9_\. \[\]\(\)&'-]+)
+
## entries to make epub subjects and calibre tags
## lastupdate creates two tags: "Last Update Year/Month: %Y/%m" and "Last
Update: %Y/%m/%d"
include_subject_tags: extratags, genre, category, characters, ships, status
@@ -1182,7 +1192,7 @@
## default_cover_image is a python string Template string with
## ${title}, ${author} etc, same as titlepage_entries. Unless
## allow_unsafe_filename is true, invalid filename chars will be
-## removed from metadata fields
+## removed from metadata fields and spaces are allowed.
#default_cover_image:file:///C:/Users/username/Desktop/nook/images/icon.png
#default_cover_image:file:///C:/Users/username/Desktop/nook/images/${title}/icon.png
#default_cover_image:http://www.somesite.com/someimage.gif
@@ -1194,7 +1204,7 @@
## force_cover_image is a python string Template string with
## ${title}, ${author} etc, same as titlepage_entries. Unless
## allow_unsafe_filename is true, invalid filename chars will be
-## removed from metadata fields
+## removed from metadata fields and spaces are allowed.
#force_cover_image:file:///C:/Users/username/Desktop/nook/images/icon.png
#force_cover_image:file:///C:/Users/username/Desktop/nook/images/${title}/icon.png
#force_cover_image:http://www.somesite.com/someimage.gif
@@ -1761,17 +1771,6 @@
website_encodings:Windows-1252,utf8
-[fanfiction.tenhawkpresents.ink]
-use_basic_cache:true
-## Some sites require login (or login for some rated stories) The
-## program can prompt you, or you can save it in config. In
-## commandline version, this should go in your personal.ini, not
-## defaults.ini.
-#username:YourName
-#password:yourpassword
-
-website_encodings:Windows-1252,utf8
-
[fanficauthors.net]
use_basic_cache:true
## Some sites also require the user to confirm they are adult for
@@ -2696,6 +2695,18 @@
## for those not expecting it.
#append_datepublished_to_storyurl:false
+[t.evancurrie.ca]
+# was fanfiction.tenhawkpresents.ink
+use_basic_cache:true
+## Some sites require login (or login for some rated stories) The
+## program can prompt you, or you can save it in config. In
+## commandline version, this should go in your personal.ini, not
+## defaults.ini.
+#username:YourName
+#password:yourpassword
+
+website_encodings:Windows-1252,utf8
+
[tgstorytime.com]
## Site dedicated to these categories/characters/ships
extracategories:Transgender
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/calibre-plugin/prefs.py
new/FanFicFare-4.18.0/calibre-plugin/prefs.py
--- old/FanFicFare-4.17.0/calibre-plugin/prefs.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/calibre-plugin/prefs.py 2022-11-22
02:04:53.000000000 +0100
@@ -120,7 +120,7 @@
default_prefs['updatemeta'] = True
default_prefs['bgmeta'] = False
-default_prefs['updateepubcover'] = True # removed in favor of always True Sep
2022
+#default_prefs['updateepubcover'] = True # removed in favor of always True Oct
2022
default_prefs['keeptags'] = False
default_prefs['suppressauthorsort'] = False
default_prefs['suppresstitlesort'] = False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.17.0/fanficfare/adapters/adapter_adultfanfictionorg.py
new/FanFicFare-4.18.0/fanficfare/adapters/adapter_adultfanfictionorg.py
--- old/FanFicFare-4.17.0/fanficfare/adapters/adapter_adultfanfictionorg.py
2022-10-18 18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/adapters/adapter_adultfanfictionorg.py
2022-11-22 02:04:53.000000000 +0100
@@ -217,7 +217,7 @@
self.story.setMetadata('title',stripHTML(a).replace('\\','').replace('
',' ').replace(' ',' ').replace(' ',' ').strip())
# Find the chapters:
- chapters = soup.find('div',{'class':'dropdown-content'})
+ chapters = soup.find('ul',{'class':'dropdown-content'})
for i, chapter in enumerate(chapters.findAll('a')):
self.add_chapter(chapter,self.url+'&chapter='+unicode(i+1))
@@ -373,7 +373,7 @@
logger.debug('Getting chapter text from: %s' % url)
soup = self.make_soup(self.get_request(url))
- chaptertag = soup.find('div',{'class' :
'pagination'}).parent.findNext('td')
+ chaptertag =
soup.find('ul',{'class':'pagination'}).parent.parent.parent.findNextSibling('li')
if None == chaptertag:
raise exceptions.FailedToDownload("Error downloading Chapter: {0}!
Missing required element!".format(url))
# Change td to a div.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.17.0/fanficfare/adapters/adapter_finestoriescom.py
new/FanFicFare-4.18.0/fanficfare/adapters/adapter_finestoriescom.py
--- old/FanFicFare-4.17.0/fanficfare/adapters/adapter_finestoriescom.py
2022-10-18 18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/adapters/adapter_finestoriescom.py
2022-11-22 02:04:53.000000000 +0100
@@ -38,29 +38,3 @@
def getSiteDomain():
# The site domain. Does have www here, if it uses it.
return 'finestories.com'
-
- @classmethod
- def getTheme(cls):
- ## only one theme is supported.
- return "Modern"
-
- ## Login seems to be reasonably standard across eFiction sites.
- def needToLoginCheck(self, data):
- if 'Free Registration' in data \
- or "Log In" in data \
- or "Invalid Password!" in data \
- or "Invalid User Name!" in data:
- return True
- else:
- return False
-
- def getStoryMetadataFromAuthorPage(self):
- # surprisingly, the detailed page does not give enough details, so go
to author's page
- story_row = self.findStoryRow('div')
-
- description_element = story_row.find('div', {'class' : 'sdesc'})
-
- self.parseDescriptionField(description_element)
-
- misc_element = story_row.find('div', {'class' : 'misc'})
- self.parseOtherAttributes(misc_element)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.17.0/fanficfare/adapters/adapter_storiesonlinenet.py
new/FanFicFare-4.18.0/fanficfare/adapters/adapter_storiesonlinenet.py
--- old/FanFicFare-4.17.0/fanficfare/adapters/adapter_storiesonlinenet.py
2022-10-18 18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/adapters/adapter_storiesonlinenet.py
2022-11-22 02:04:53.000000000 +0100
@@ -91,7 +91,7 @@
@classmethod
def getTheme(cls):
- ## only one theme is supported.
+ # preferred theme
return "Classic"
def needToLoginCheck(self, data):
@@ -267,29 +267,40 @@
def getStoryMetadataFromAuthorPage(self):
# surprisingly, the detailed page does not give enough details, so go
to author's page
- story_row = self.findStoryRow('tr')
- self.has_universes = False
+ story_row = self.findStoryRow()
- title_cell = story_row.find('td', {'class' : 'lc2'})
- for cat in title_cell.findAll('div', {'class' : 'typediv'}):
- self.story.addToList('genre',cat.text)
+ if story_row.name == 'tr':
+ # classic theme
+ self.has_universes = False
- # in lieu of word count.
- self.story.setMetadata('size', story_row.find('td', {'class' :
'num'}).text)
+ title_cell = story_row.find('td', {'class' : 'lc2'})
+ for cat in title_cell.findAll('div', {'class' : 'typediv'}):
+ self.story.addToList('genre',cat.text)
- score = story_row.findNext('th', {'class' : 'ynum'}).text
- if re.match(r"[\d,\.]+",score):
- self.story.setMetadata('score', score)
+ # in lieu of word count.
+ self.story.setMetadata('size', story_row.find('td', {'class' :
'num'}).text)
- description_element = story_row.findNext('td', {'class' : 'lc4'})
- # logger.debug(description_element)
+ score = story_row.findNext('th', {'class' : 'ynum'}).text
+ if re.match(r"[\d,\.]+",score):
+ self.story.setMetadata('score', score)
- self.parseDescriptionField(description_element)
+ description_element = story_row.findNext('td', {'class' : 'lc4'})
+ # logger.debug(description_element)
- self.parseOtherAttributes(description_element)
+ self.parseDescriptionField(description_element)
+
+ self.parseOtherAttributes(description_element)
+ else:
+ # modern theme (or minimalist theme, should also work)
+ description_element = story_row.find('div', {'class' : 'sdesc'})
+
+ self.parseDescriptionField(description_element)
+
+ misc_element = story_row.find('div', {'class' : 'misc'})
+ self.parseOtherAttributes(misc_element)
- def findStoryRow(self, row_class='tr'):
+ def findStoryRow(self):
page=0
story_found = False
while not story_found:
@@ -301,7 +312,14 @@
raise exceptions.FailedToDownload("Story not found in
Author's list--Set Access Level to Full Access and change Listings Theme back
to "+self.getTheme())
asoup = self.make_soup(data)
- story_row = asoup.find(row_class, {'id' : 'sr' +
self.story.getMetadata('storyId')})
+ story_row = asoup.find('tr', {'id' : 'sr' +
self.story.getMetadata('storyId')})
+ if story_row:
+ logger.debug("Found story row on page %d" % page)
+ story_found = True
+ self.has_universes = "/universes" in data
+ break
+
+ story_row = asoup.find('div', {'id' : 'sr' +
self.story.getMetadata('storyId')})
if story_row:
logger.debug("Found story row on page %d" % page)
story_found = True
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/FanFicFare-4.17.0/fanficfare/adapters/adapter_tenhawkpresents.py
new/FanFicFare-4.18.0/fanficfare/adapters/adapter_tenhawkpresents.py
--- old/FanFicFare-4.17.0/fanficfare/adapters/adapter_tenhawkpresents.py
2022-10-18 18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/adapters/adapter_tenhawkpresents.py
2022-11-22 02:04:53.000000000 +0100
@@ -48,7 +48,7 @@
@staticmethod
def getSiteDomain():
- return 'fanfiction.tenhawkpresents.ink'
+ return 't.evancurrie.ca'
@classmethod
def getSiteExampleURLs(cls):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/fanficfare/cli.py
new/FanFicFare-4.18.0/fanficfare/cli.py
--- old/FanFicFare-4.17.0/fanficfare/cli.py 2022-10-18 18:47:27.000000000
+0200
+++ new/FanFicFare-4.18.0/fanficfare/cli.py 2022-11-22 02:04:53.000000000
+0100
@@ -28,7 +28,7 @@
import os, sys, platform
-version="4.17.0"
+version="4.18.0"
os.environ['CURRENT_VERSION_ID']=version
global_cache = 'global_cache'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/fanficfare/configurable.py
new/FanFicFare-4.18.0/fanficfare/configurable.py
--- old/FanFicFare-4.17.0/fanficfare/configurable.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/configurable.py 2022-11-22
02:04:53.000000000 +0100
@@ -192,6 +192,7 @@
'replace_hr':(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),
@@ -425,6 +426,7 @@
'keep_style_attr',
'keep_title_attr',
'keep_html_attrs',
+ 'remove_class_chapter',
'replace_tags_with_spans',
'keep_empty_tags',
'remove_tags',
@@ -458,6 +460,7 @@
'rating_titles',
'remove_transparency',
'replace_br_with_p',
+ 'replace_chapter_text',
'replace_hr',
'replace_xbr_with_hr',
'replace_metadata',
@@ -900,7 +903,8 @@
clude_metadata_re =
re.compile(r'(add_to_)?(in|ex)clude_metadata_(pre|post)$')
replace_metadata_re = re.compile(r'(add_to_)?replace_metadata$')
- from .story import set_in_ex_clude, make_replacements
+ replace_chapter_text_re =
re.compile(r'(add_to_)?replace_chapter_text$')
+ from .story import set_in_ex_clude, make_replacements,
make_chapter_text_replacements
custom_columns_settings_re =
re.compile(r'(add_to_)?custom_columns_settings$')
custom_columns_flags_re = re.compile(r'^[rna](_anthaver)?')
@@ -939,6 +943,9 @@
if replace_metadata_re.match(keyword):
make_replacements(value)
+ if replace_chapter_text_re.match(keyword):
+ make_chapter_text_replacements(value)
+
if generate_cover_settings_re.match(keyword):
make_generate_cover_settings(value)
@@ -1023,7 +1030,7 @@
if self.getConfig('use_flaresolverr_proxy',False):
logger.debug("use_flaresolverr_proxy:%s"%self.getConfig('use_flaresolverr_proxy'))
fetchcls = flaresolverr_proxy.FlareSolverr_ProxyFetcher
- if self.getConfig('use_flaresolverr_proxy') != 'withimages':
+ if self.getConfig('use_flaresolverr_proxy') != 'withimages'
and not self.getConfig('use_browser_cache'):
logger.warning("FlareSolverr v2+ doesn't work with images:
include_images automatically set false")
logger.warning("Set use_flaresolverr_proxy:withimages if
your are using FlareSolver v1 and want images")
self.set('overrides', 'include_images', 'false')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/fanficfare/defaults.ini
new/FanFicFare-4.18.0/fanficfare/defaults.ini
--- old/FanFicFare-4.17.0/fanficfare/defaults.ini 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/defaults.ini 2022-11-22
02:04:53.000000000 +0100
@@ -164,8 +164,8 @@
zip_filename: ${title}-${siteabbrev}_${storyId}${formatext}.zip
## Normally, try to make the filenames 'safe' by removing invalid
-## filename chars. Applies to default_cover_image, output_filename &
-## zip_filename.
+## filename chars. Applies to default_cover_image, force_cover_image,
+## output_filename & zip_filename.
allow_unsafe_filename: false
## The regex pattern of 'unsafe' filename chars for above. First
@@ -1782,17 +1782,6 @@
website_encodings:Windows-1252,utf8
-[fanfiction.tenhawkpresents.ink]
-use_basic_cache:true
-## Some sites require login (or login for some rated stories) The
-## program can prompt you, or you can save it in config. In
-## commandline version, this should go in your personal.ini, not
-## defaults.ini.
-#username:YourName
-#password:yourpassword
-
-website_encodings:Windows-1252,utf8
-
[fanficauthors.net]
use_basic_cache:true
## Some sites also require the user to confirm they are adult for
@@ -2717,6 +2706,18 @@
## for those not expecting it.
#append_datepublished_to_storyurl:false
+[t.evancurrie.ca]
+# was fanfiction.tenhawkpresents.ink
+use_basic_cache:true
+## Some sites require login (or login for some rated stories) The
+## program can prompt you, or you can save it in config. In
+## commandline version, this should go in your personal.ini, not
+## defaults.ini.
+#username:YourName
+#password:yourpassword
+
+website_encodings:Windows-1252,utf8
+
[tgstorytime.com]
## Site dedicated to these categories/characters/ships
extracategories:Transgender
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/fanficfare/exceptions.py
new/FanFicFare-4.18.0/fanficfare/exceptions.py
--- old/FanFicFare-4.17.0/fanficfare/exceptions.py 2022-10-18
18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/fanficfare/exceptions.py 2022-11-22
02:04:53.000000000 +0100
@@ -114,6 +114,13 @@
def __str__(self):
return self.error
+class CacheCleared(Exception):
+ def __init__(self,error):
+ self.error=error
+
+ def __str__(self):
+ return self.error
+
class HTTPErrorFFF(Exception):
def __init__(self,
url,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/fanficfare/story.py
new/FanFicFare-4.18.0/fanficfare/story.py
--- old/FanFicFare-4.17.0/fanficfare/story.py 2022-10-18 18:47:27.000000000
+0200
+++ new/FanFicFare-4.18.0/fanficfare/story.py 2022-11-22 02:04:53.000000000
+0100
@@ -454,6 +454,32 @@
# print("replace lines:%s"%len(retval))
return retval
+def make_chapter_text_replacements(replace):
+ retval=[]
+ for repl_line in replace.splitlines():
+ line=repl_line
+ try:
+ (regexp,replacement)=(None,None)
+ if "=>" in line:
+ parts = line.split("=>")
+ (regexp,replacement)=parts
+
+ if regexp:
+ regexp = re_compile(regexp,line)
+ # A way to explicitly include spaces in the
+ # replacement string. The .ini parser eats any
+ # trailing spaces.
+ replacement=replacement\
+ .replace(SPACE_REPLACE,' ')
+
+ retval.append([repl_line,regexp,replacement])
+ except Exception as e:
+ logger.error("Problem with Chapter Text Replacement
Line:%s"%repl_line)
+ raise exceptions.PersonalIniFailed(e,'replace_chapter_text
unpacking failed',repl_line)
+# raise
+ # print("replace lines:%s"%len(retval))
+ return retval
+
class StoryImage(dict):
pass
@@ -512,6 +538,83 @@
# logger.debug(self.size_index.keys())
# logger.debug("\n"+("\n".join([ x['newsrc'] for x in self.infos])))
+
+class MetadataCache:
+ def __init__(self):
+ # save processed metadata, dicts keyed by 'key', then
(removeentities,dorepl)
+ # {'key':{(removeentities,dorepl):"value",(...):"value"},'key':... }
+ self.processed_metadata_cache = {}
+ ## not entirely sure now why lists are separate, but I assume
+ ## there was a reason.
+ self.processed_metadata_list_cache = {}
+
+ ## lists of entries that depend on key value--IE, the ones
+ ## that should also be cache invalided when key is.
+ # {'key':['name','name',...]
+ self.dependent_entries = {}
+
+ def clear(self):
+ self.processed_metadata_cache = {}
+ self.processed_metadata_list_cache = {}
+
+ def invalidate(self,key,seen_list={}):
+ # logger.debug("invalidate(%s)"%key)
+ # logger.debug("seen_list(%s)"%seen_list)
+ if key in seen_list:
+ raise exceptions.CacheCleared('replace all')
+ try:
+ new_seen_list = dict(seen_list)
+ new_seen_list[key]=True
+ if key in self.processed_metadata_cache:
+ del self.processed_metadata_cache[key]
+ if key in self.processed_metadata_list_cache:
+ del self.processed_metadata_list_cache[key]
+
+ for entry in self.dependent_entries.get(key,[]):
+ ## replace_metadata lines without keys apply to all
+ ## entries--special key '' used to clear deps on *all*
+ ## cache sets.
+ if entry == '':
+ # logger.debug("clear in invalidate(%s)"%key)
+ raise exceptions.CacheCleared('recursed')
+ self.invalidate(entry,new_seen_list)
+ except exceptions.CacheCleared as e:
+ # logger.debug(e)
+ self.clear()
+ # logger.debug(self.dependent_entries)
+
+ def add_dependencies(self,include_key,list_keys):
+ for key in list_keys:
+ if key not in self.dependent_entries:
+ self.dependent_entries[key] = set()
+ self.dependent_entries[key].add(include_key)
+
+ def set_cached_scalar(self,key,removeallentities,doreplacements,value):
+ if key not in self.processed_metadata_cache:
+ self.processed_metadata_cache[key] = {}
+ self.processed_metadata_cache[key][(removeallentities,doreplacements)]
= value
+
+ def is_cached_scalar(self,key,removeallentities,doreplacements):
+ return key in self.processed_metadata_cache \
+ and (removeallentities,doreplacements) in
self.processed_metadata_cache[key]
+
+ def get_cached_scalar(self,key,removeallentities,doreplacements):
+ return
self.processed_metadata_cache[key][(removeallentities,doreplacements)]
+
+
+ def set_cached_list(self,key,removeallentities,doreplacements,value):
+ if key not in self.processed_metadata_list_cache:
+ self.processed_metadata_list_cache[key] = {}
+
self.processed_metadata_list_cache[key][(removeallentities,doreplacements)] =
value
+
+ def is_cached_list(self,key,removeallentities,doreplacements):
+ return key in self.processed_metadata_list_cache \
+ and (removeallentities,doreplacements) in
self.processed_metadata_list_cache[key]
+
+ def get_cached_list(self,key,removeallentities,doreplacements):
+ return
self.processed_metadata_list_cache[key][(removeallentities,doreplacements)]
+
+
class Story(Requestable):
def __init__(self, configuration):
@@ -523,6 +626,7 @@
self.metadata = {'version':'unknown'}
self.metadata['python_version']=sys.version
self.replacements = []
+ self.chapter_text_replacements = []
self.in_ex_cludes = {}
self.chapters = [] # chapters will be dict
containing(url,title,html,etc)
self.chapter_first = None
@@ -530,10 +634,13 @@
self.img_store = ImageStore()
- # save processed metadata, dicts keyed by 'key', then
(removeentities,dorepl)
- # {'key':{(removeentities,dorepl):"value",(...):"value"},'key':... }
- self.processed_metadata_cache = {}
- self.processed_metadata_list_cache = {}
+ self.metadata_cache = MetadataCache()
+
+ ## set include_in_ cache dependencies
+ for entry in self.getValidMetaList():
+ if self.hasConfig("include_in_"+entry):
+ self.metadata_cache.add_dependencies(entry,
+ [ k.replace('.NOREPL','') for k in
self.getConfigList("include_in_"+entry) ])
self.cover=None # *href* of new cover image--need to create html.
self.oldcover=None #
(oldcoverhtmlhref,oldcoverhtmltype,oldcoverhtmldata,oldcoverimghref,oldcoverimgtype,oldcoverimgdata)
@@ -541,6 +648,7 @@
self.logfile=None # cheesy way to carry log file forward across update.
self.replacements_prepped = False
+ self.chapter_text_replacements_prepped = False
self.chapter_error_count = 0
@@ -560,6 +668,19 @@
self.replacements =
make_replacements(self.getConfig('replace_metadata'))
+ ## set replace_metadata conditional key cache dependencies
+ for replaceline in self.replacements:
+ (repl_line,metakeys,regexp,replacement,cond_match) =
replaceline
+ ## replace_metadata lines without keys apply to all
+ ## entries--special key '' used to clear deps on *all*
+ ## cache sets.
+ if not metakeys:
+ metakeys = ['']
+ for key in metakeys:
+ if cond_match:
+
self.metadata_cache.add_dependencies(key.replace('_LIST',''),
+ [
cond_match.key() ])
+
in_ex_clude_list = ['include_metadata_pre','exclude_metadata_pre',
'include_metadata_post','exclude_metadata_post']
for ie in in_ex_clude_list:
@@ -570,9 +691,15 @@
self.in_ex_cludes[ie] = set_in_ex_clude(ies)
self.replacements_prepped = True
+ for which in self.in_ex_cludes.values():
+ for (line,match,cond_match) in which:
+ for key in match.keys:
+ if cond_match:
+
self.metadata_cache.add_dependencies(key.replace('_LIST',''),
+ [
cond_match.key() ])
+
def clear_processed_metadata_cache(self):
- self.processed_metadata_cache = {}
- self.processed_metadata_list_cache = {}
+ self.metadata_cache.clear()
def set_chapters_range(self,first=None,last=None):
self.chapter_first=first
@@ -584,8 +711,8 @@
def setMetadata(self, key, value, condremoveentities=True):
# delete cached replace'd value.
- if key in self.processed_metadata_cache:
- del self.processed_metadata_cache[key]
+ self.metadata_cache.invalidate(key)
+
# Fixing everything downstream to handle bool primatives is a
# pain.
if isinstance(value,bool):
@@ -683,7 +810,7 @@
# huuuge replace list cause a problem. Also allows dict()
# instead of list() for quicker lookups.
if repl_line in seen_list:
- logger.info("Skipping replace_metadata line %s to prevent
infinite recursion."%repl_line)
+ logger.info("Skipping replace_metadata line '%s' on %s to
prevent infinite recursion."%(repl_line,key))
continue
doreplace=True
if cond_match and cond_match.key() != key: # prevent infinite
recursion.
@@ -824,9 +951,8 @@
doreplacements=True,
seen_list={}):
# check for a cached value to speed processing
- if key in self.processed_metadata_cache \
- and (removeallentities,doreplacements) in
self.processed_metadata_cache[key]:
- return
self.processed_metadata_cache[key][(removeallentities,doreplacements)]
+ if
self.metadata_cache.is_cached_scalar(key,removeallentities,doreplacements):
+ return
self.metadata_cache.get_cached_scalar(key,removeallentities,doreplacements)
value = None
if not self.isValidMetaEntry(key):
@@ -870,9 +996,7 @@
value = self.getConfig("default_value_"+key)
# save a cached value to speed processing
- if key not in self.processed_metadata_cache:
- self.processed_metadata_cache[key] = {}
- self.processed_metadata_cache[key][(removeallentities,doreplacements)]
= value
+
self.metadata_cache.set_cached_scalar(key,removeallentities,doreplacements,value)
return value
@@ -983,8 +1107,9 @@
self.addToList(listname,v.strip())
def addToList(self,listname,value,condremoveentities=True,clear=False):
- if listname in self.processed_metadata_list_cache:
- del self.processed_metadata_list_cache[listname]
+ # delete cached replace'd value.
+ self.metadata_cache.invalidate(listname)
+
if value==None:
return
if condremoveentities:
@@ -1012,9 +1137,8 @@
retlist = []
# check for a cached value to speed processing
- if not skip_cache and listname in self.processed_metadata_list_cache \
- and (removeallentities,doreplacements) in
self.processed_metadata_list_cache[listname]:
- return
self.processed_metadata_list_cache[listname][(removeallentities,doreplacements)]
+ if not skip_cache and
self.metadata_cache.is_cached_list(listname,removeallentities,doreplacements):
+ return
self.metadata_cache.get_cached_list(listname,removeallentities,doreplacements)
if not self.isValidMetaEntry(listname):
retlist = []
@@ -1135,9 +1259,7 @@
retlist = []
if not skip_cache:
- if listname not in self.processed_metadata_list_cache:
- self.processed_metadata_list_cache[listname] = {}
-
self.processed_metadata_list_cache[listname][(removeallentities,doreplacements)]
= retlist
+
self.metadata_cache.set_cached_list(listname,removeallentities,doreplacements,retlist)
return retlist
@@ -1243,11 +1365,26 @@
chapter['toctitle'] = toctempl.substitute(chapter)
# set after, otherwise changes origtitle and toctitle
chapter['title'] = chapter['chapter']
- ## XXX -- add chapter text replacement here?
- ## chapter['html'] is a soup or soup part?
+ ## chapter['html'] is a string.
+ chapter['html'] =
self.do_chapter_text_replacements(chapter['html'])
retval.append(chapter)
return retval
+ def do_chapter_text_replacements(self,data):
+ '''
+ 'Undocumented' feature. This is a shotgun with a stirrup on
+ the end--you *will* shoot yourself in the foot a lot with it.
+ '''
+ # only compile chapter_text_replacements once.
+ if not self.chapter_text_replacements and
self.getConfig('replace_chapter_text'):
+ self.chapter_text_replacements =
make_chapter_text_replacements(self.getConfig('replace_chapter_text'))
+ logger.debug(self.chapter_text_replacements)
+ for replaceline in self.chapter_text_replacements:
+ (repl_line,regexp,replacement) = replaceline
+ if regexp.search(data):
+ data = regexp.sub(replacement,data)
+ return data
+
def get_filename_safe_metadata(self,pattern=None):
origvalues = self.getAllMetadata()
values={}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/FanFicFare-4.17.0/setup.py
new/FanFicFare-4.18.0/setup.py
--- old/FanFicFare-4.17.0/setup.py 2022-10-18 18:47:27.000000000 +0200
+++ new/FanFicFare-4.18.0/setup.py 2022-11-22 02:04:53.000000000 +0100
@@ -26,7 +26,7 @@
name=package_name,
# Versions should comply with PEP440.
- version="4.17.0",
+ version="4.18.0",
description='A tool for downloading fanfiction to eBook formats',
long_description=long_description,