Compare commits

...
Author SHA1 Message Date
Jim Miller 4718955ede Update CLI download zip. 2014-02-28 17:10:05 -06:00
Jim Miller d60ad9892d Fix for KeyError:'site' error and https with ffnet.
Also don't retry 404 errors and bump versions.
2014-02-28 17:08:07 -06:00
Jim Miller 35b5eb3dee Added tag FanFictionDownLoader-4.4.93 for changeset f603b2ffd630 2014-02-24 21:11:11 -06:00
Jim Miller f9d8e2f3db Added tag calibre-plugin-1.8.12 for changeset f603b2ffd630 2014-02-24 21:10:58 -06:00
Jim Miller f291625ae3 Update CLI download zip. 2014-02-24 21:10:46 -06:00
Jim Miller 6ef28cee6a Add tweak_fg_sleep feature set for ffnet, make ffnet chapter look ahead optional 2014-02-24 21:08:56 -06:00
Jim Miller fc3431098d Add slow_down_sleep_time to test1 adapter for... testing. 2014-02-21 22:59:03 -06:00
Jim Miller 8980eaf21b Set boolean custom columns from personal.ini when t/true/yes/y/1 vs
f/false/no/n/0.
2014-02-21 19:07:32 -06:00
Jim Miller c8a89fa8b2 More Fimf fixes from FaceDeer. 2014-02-21 19:05:37 -06:00
Jim Miller 683ad6182d Fix for calibre converted <body ...> tags. 2014-02-21 19:04:40 -06:00
Jim Miller 3267064576 Added tag FanFictionDownLoader-4.4.92 for changeset 43b0cca5b451 2014-02-15 09:45:07 -06:00
Jim Miller ab128c417c Added tag calibre-plugin-1.8.11 for changeset e3c25dc63ece 2014-02-15 09:44:35 -06:00
Jim Miller dbc9b5e250 Update CLI download zip. 2014-02-15 09:44:19 -06:00
Jim Miller fffdd89254 Update translations to include new strings (not yet translated). 2014-02-15 09:42:02 -06:00
Jim Miller cbb17aeba1 Bump versions. 2014-02-15 09:39:27 -06:00
Jim Miller 7433d2494f Increase default slow_down_sleep_time for ffnet. 2014-02-15 09:07:52 -06:00
Jim Miller d0fa72a002 Don't autoconvert on Update Calibre Only. 2014-02-11 10:48:48 -06:00
Jim Miller 7412acd007 Fix image download for ffnet, make buffynfaith.net use User-agent. 2014-02-10 13:27:33 -06:00
Jim Miller 196eff7f60 Add option to call Polish on GC to inject cover in epub. 2014-02-10 13:26:58 -06:00
Jim Miller 8f06a374cb Moved tag FanFictionDownLoader-4.4.91 to changeset 40b20c6d208b (from changeset 135173f84a14) 2014-02-05 19:07:23 -06:00
Jim Miller 4f5738108f Update CLI download zip. 2014-02-05 19:07:08 -06:00
Jim Miller 6f22a3c701 Added tag calibre-plugin-1.8.10 for changeset 135173f84a14 2014-02-05 18:58:20 -06:00
Jim Miller 589076eed9 Added tag FanFictionDownLoader-4.4.91 for changeset 135173f84a14 2014-02-05 18:58:06 -06:00
21 changed files with 1168 additions and 993 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# ffd-retief-hrd fanfictiondownloader
application: fanfictiondownloader
version: 4-4-91
version: 4-4-94
runtime: python27
api_version: 1
threadsafe: true
+1 -1
View File
@@ -42,7 +42,7 @@ class FanFictionDownLoaderBase(InterfaceActionBase):
description = _('UI plugin to download FanFiction stories from various sites.')
supported_platforms = ['windows', 'osx', 'linux']
author = 'Jim Miller'
version = (1, 8, 10)
version = (1, 8, 13)
minimum_calibre_version = (1, 13, 0)
#: This field defines the GUI plugin class that contains all the code
+7 -1
View File
@@ -55,7 +55,7 @@ from calibre_plugins.fanfictiondownloader_plugin.dialogs \
EditTextDialog, RejectUrlEntry)
from calibre_plugins.fanfictiondownloader_plugin.fanficdownloader.adapters \
import (getConfigSections, getNormalStoryURL)
import getConfigSections
from calibre_plugins.fanfictiondownloader_plugin.common_utils \
import ( KeyboardConfigDialog, PrefsViewerDialog )
@@ -249,6 +249,7 @@ class ConfigWidget(QWidget):
#print("gc_site_settings[%s]:%s"%(site,gc_site_settings[site]))
prefs['gc_site_settings'] = gc_site_settings
prefs['allow_gc_from_ini'] = self.generatecover_tab.allow_gc_from_ini.isChecked()
prefs['gc_polish_cover'] = self.generatecover_tab.gc_polish_cover.isChecked()
# Count Pages tab
countpagesstats = []
@@ -753,6 +754,11 @@ class GenerateCoverTab(QWidget):
self.allow_gc_from_ini.setChecked(prefs['allow_gc_from_ini'])
self.l.addWidget(self.allow_gc_from_ini)
self.gc_polish_cover = QCheckBox(_("Use calibre's Polish feature to inject/update the cover"),self)
self.gc_polish_cover.setToolTip(_("Calibre's Polish feature will be used to inject or update the generated cover into the ebook, EPUB only."))
self.gc_polish_cover.setChecked(prefs['gc_polish_cover'])
self.l.addWidget(self.gc_polish_cover)
class CountPagesTab(QWidget):
def __init__(self, parent_dialog, plugin_action):
+56 -9
View File
@@ -660,13 +660,13 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
#print("update_dialog()")
db = self.gui.current_db
book_list = map( self.make_book_id_only, id_list )
books = map( self.make_book_id_only, id_list )
for j, book in enumerate(book_list):
for j, book in enumerate(books):
book['listorder'] = j
LoopProgressDialog(self.gui,
book_list,
books,
partial(self.populate_book_from_calibre_id, db=self.gui.current_db),
self.update_dialog_finish,
init_label=_("Collecting stories for update..."),
@@ -718,6 +718,9 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
url_list = split_text_to_urls(books)
books = self.convert_urls_to_books(url_list)
## for tweak_fg_sleep
options['ffnetcount']=len(filter(lambda x : x['site']=='www.fanfiction.net', books))
options['version'] = self.version
logger.debug(self.version)
@@ -815,6 +818,18 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
options['personal.ini'] = get_ffdl_personalini()
adapter = get_ffdl_adapter(url,fileform)
# reduce foreground sleep time for ffnet when few books.
if 'ffnetcount' in options and \
adapter.getConfig('tweak_fg_sleep') and \
adapter.getSiteDomain() == 'www.fanfiction.net':
minslp = float(adapter.getConfig('min_fg_sleep'))
maxslp = float(adapter.getConfig('max_fg_sleep'))
dwnlds = float(adapter.getConfig('max_fg_sleep_at_downloads'))
m = (maxslp-minslp) / (dwnlds-1)
b = minslp - m
slp = min(maxslp,m*float(options['ffnetcount'])+b)
#print("m:%s b:%s = %s"%(m,b,slp))
adapter.set_sleep(slp)
## three tries, that's enough if both user/pass & is_adult needed,
## or a couple tries of one or the other
@@ -1138,6 +1153,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
func, args=args,
description=desc)
self.gui.jobs_pointer.start()
self.gui.status_bar.show_message(_('Starting %d FanFictionDownLoads')%len(book_list),3000)
def update_books_loop(self,book,db=None,
@@ -1232,7 +1248,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
cp_plugin = self.gui.iactions['Count Pages']
cp_plugin.count_statistics(all_ids,prefs['countpagesstats'])
if prefs['autoconvert']:
if prefs['autoconvert'] and options['collision'] != CALIBREONLY:
self.gui.status_bar.show_message(_('Starting auto conversion of %d books.')%(len(all_ids)), 3000)
self.gui.iactions['Convert Books'].auto_convert_auto_add(all_ids)
@@ -1339,7 +1355,6 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
bad_list = sorted(bad_list,key=lambda x : x['listorder'])
self.gui.status_bar.show_message(_('Merging %s books.')%total_good)
existingbook = None
if 'mergebook' in options:
@@ -1576,6 +1591,13 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
else:
val = val
if val != '':
if coldef['datatype'] == 'bool':
if val.lower() in ('t','true','1','yes','y'):
val = True
elif val.lower() in ('f','false','0','no','n'):
val = False
else:
val = None # for tri-state 'booleans'. Yes/No/Null
db.set_custom(book_id, val, label=label, commit=False)
if flag == 'a':
@@ -1622,7 +1644,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
if 'Generate Cover' in self.gui.iactions and (book['added'] or not prefs['gcnewonly']):
logger.debug("Do Generate Cover added:%s gcnewonly:%s"%(book['added'],prefs['gcnewonly']))
#logger.debug("Do Generate Cover added:%s gcnewonly:%s"%(book['added'],prefs['gcnewonly']))
# force a refresh if generating cover so complex composite
# custom columns are current and correct
@@ -1665,6 +1687,28 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
realmi = db.get_metadata(book_id, index_is_id=True)
gc_plugin.generate_cover_for_book(realmi,saved_setting_name=setting_name)
if prefs['gc_polish_cover'] and \
options['fileform'] == "epub" and calibre_version >= (0, 9, 39):
# set cover inside epub from calibre's polish feature
from calibre.ebooks.oeb.polish.main import polish, ALL_OPTS
from calibre.utils.logging import Log
from collections import namedtuple
# Couldn't find a better way to get the cover path.
cover_path = os.path.join(db.library_path, db.path(book_id, index_is_id=True), 'cover.jpg')
data = {'cover':cover_path}
#print("cover_path:%s"%cover_path)
opts = ALL_OPTS.copy()
opts.update(data)
O = namedtuple('Options', ' '.join(ALL_OPTS.iterkeys()))
opts = O(**opts)
log = Log(level=Log.DEBUG)
outfile = db.format_abspath(book_id, formmapping[options['fileform']], index_is_id=True)
#print("polish cover outfile:%s"%outfile)
polish({outfile:outfile}, opts, log, logger.info)
def get_clean_reading_lists(self,lists):
if lists == None or lists.strip() == "" :
return []
@@ -1753,6 +1797,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
book['end'] = None
book['comment'] = '' # note this is a comment on the d/l or update.
book['url'] = ''
book['site'] = ''
book['added'] = False
return book
@@ -1829,13 +1874,15 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
book['status'] = _('Not Found')
else:
# get normalized url or None.
book['url'] = self.is_good_downloader_url(url)
if book['url'] == None:
urlsitetuple = adapters.getNormalStoryURLSite(url)
if urlsitetuple == None:
book['url'] = url
book['comment'] = _("URL is not a valid story URL.")
book['good'] = False
book['icon']='dialog_error.png'
book['status'] = _('Bad URL')
else:
(book['url'],book['site'])=urlsitetuple
def get_story_url(self, db, book_id=None, path=None):
if book_id == None:
+1
View File
@@ -57,6 +57,7 @@ default_prefs['addtolistsonread'] = False
default_prefs['gcnewonly'] = False
default_prefs['gc_site_settings'] = {}
default_prefs['allow_gc_from_ini'] = True
default_prefs['gc_polish_cover'] = False
default_prefs['countpagesstats'] = []
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+13 -1
View File
@@ -1215,6 +1215,11 @@ user_agent:
## Change this to false to use them anyway.
never_make_cover: true
## fanfiction.net is blocking people more aggressively. If you
## download fewer stories less often you can likely get by with
## reducing this sleep.
slow_down_sleep_time:4
## Extra metadata that this adapter knows about. See [dramione.org]
## for examples of how to use them.
extra_valid_entries:reviews,favs,follows
@@ -1223,13 +1228,20 @@ extra_valid_entries:reviews,favs,follows
## to be romantic pairings.
ships_label:Pairings
## Date formats used by FFDL. Published and Update don't have time.
## Date formats used by FFDL. Published and Update don't usually have
## time, but they do now on ffnet.
## See http://docs.python.org/library/datetime.html#strftime-strptime-behavior
## Note that ini format requires % to be escaped as %%.
#dateCreated_format:%%Y-%%m-%%d %%H:%%M:%%S
datePublished_format:%%Y-%%m-%%d %%H:%%M:%%S
dateUpdated_format:%%Y-%%m-%%d %%H:%%M:%%S
## ffnet used to have a tendency to send out update notices in email
## before all their servers were showing the update on the first
## chapter. It generates another server request and doesn't seem to
## be needed lately, so now default it to off.
check_next_chapter:false
[www.fanfiktion.de]
## Some sites require login (or login for some rated stories) The
## program can prompt you, or you can save it in config. In
Binary file not shown.
+11 -3
View File
@@ -146,6 +146,13 @@ for x in imports():
__domain_map[site]=cls
def getNormalStoryURL(url):
r = getNormalStoryURLSite(url)
if r:
return r[0]
else:
return None
def getNormalStoryURLSite(url):
if not getNormalStoryURL.__dummyconfig:
getNormalStoryURL.__dummyconfig = Configuration("test1.com","EPUB")
# pulling up an adapter is pretty low over-head. If
@@ -153,10 +160,11 @@ def getNormalStoryURL(url):
try:
adapter = getAdapter(getNormalStoryURL.__dummyconfig,url)
url = adapter.url
site = adapter.getSiteDomain()
del adapter
return url
return (url,site)
except:
return None;
return None
# kludgey function static/singleton
getNormalStoryURL.__dummyconfig = None
@@ -193,7 +201,7 @@ def getConfigSectionFor(url):
def getClassFor(url):
## fix up leading protocol.
fixedurl = re.sub(r"(?i)^[htps]+[:/]+","http://",url.strip())
fixedurl = re.sub(r"(?i)^[htp]+(s?)[:/]+",r"http\1://",url.strip())
if not fixedurl.startswith("http"):
fixedurl = "http://%s"%url
## remove any trailing '#' locations.
@@ -87,7 +87,7 @@ class BuffyNFaithNetAdapter(BaseSiteAdapter):
def setHeader(self):
"buffynfaith.net wants a Referer for images. Used both above and below(after cookieproc added)"
self.opener.addheaders = [('Referer', 'http://'+self.getSiteDomain()+'/')]
self.opener.addheaders.append(('Referer', 'http://'+self.getSiteDomain()+'/'))
@classmethod
def getSiteExampleURLs(self):
@@ -57,6 +57,8 @@ class FanFictionNetSiteAdapter(BaseSiteAdapter):
## accept m(mobile)url, but use www.
self.origurl = self.origurl.replace("https://m.","https://www.")
self.opener.addheaders.append(('Referer',self.origurl))
@staticmethod
def getSiteDomain():
return 'www.fanfiction.net'
@@ -105,28 +107,31 @@ class FanFictionNetSiteAdapter(BaseSiteAdapter):
if "not found. Please check to see you are not using an outdated url." in data:
raise exceptions.FailedToDownload("Error downloading Chapter: %s! 'Chapter not found. Please check to see you are not using an outdated url.'" % url)
try:
# rather nasty way to check for a newer chapter. ffnet has a
# tendency to send out update notices in email before all
# their servers are showing the update on the first chapter.
if self.getConfig('check_next_chapter'):
try:
chapcount = len(soup.find('select', { 'name' : 'chapter' } ).findAll('option'))
# get chapter part of url.
## ffnet used to have a tendency to send out update
## notices in email before all their servers were
## showing the update on the first chapter. It
## generates another server request and doesn't seem
## to be needed lately, so now default it to off.
try:
chapcount = len(soup.find('select', { 'name' : 'chapter' } ).findAll('option'))
# get chapter part of url.
except:
chapcount = 1
chapter = url.split('/',)[5]
tryurl = "https://%s/s/%s/%d/"%(self.getSiteDomain(),
self.story.getMetadata('storyId'),
chapcount+1)
logger.debug('=Trying newer chapter: %s' % tryurl)
newdata = self._fetchUrl(tryurl)
if "not found. Please check to see you are not using an outdated url." \
not in newdata:
logger.debug('=======Found newer chapter: %s' % tryurl)
soup = bs.BeautifulSoup(newdata)
except:
chapcount = 1
chapter = url.split('/',)[5]
tryurl = "https://%s/s/%s/%d/"%(self.getSiteDomain(),
self.story.getMetadata('storyId'),
chapcount+1)
logger.debug('=Trying newer chapter: %s' % tryurl)
newdata = self._fetchUrl(tryurl)
if "not found. Please check to see you are not using an outdated url." \
not in newdata:
logger.debug('=======Found newer chapter: %s' % tryurl)
soup = bs.BeautifulSoup(newdata)
except:
pass
pass
# Find authorid and URL from... author url.
a = soup.find('a', href=re.compile(r"^/u/\d+"))
self.story.setMetadata('authorId',a['href'].split('/')[2])
@@ -101,7 +101,7 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter):
# if "/images/missing_story.png" in data:
# raise exceptions.StoryDoesNotExist(self.url)
if "This story has been marked as having adult content." in data:
if "This story has been marked as having adult content. Please click below to confirm you are of legal age to view adult material in your country." in data:
raise exceptions.AdultCheckRequired(self.url)
if self.password:
@@ -249,18 +249,20 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter):
value = unicode(value)
self.story.setMetadata(metakey, value)
#Sequel links and group links are each bundled into story_group_list containers.
#Rather than mess around examining the header text, which is outside the containers,
#one can tell the two link types apart by examining them directly.
## Groups and sequels code from FaceDeer
allGroupLists = soup.findAll('ul', {'id':'story_group_list'})
for groupList in allGroupLists:
for groupName in groupList.findAll('a', {'href':re.compile('^/group/')}):
self.story.addToList("groupsUrl", 'http://'+self.host+groupName["href"])
self.story.addToList("groups",stripHTML(groupName).replace(',', ';'))
for sequel in groupList.findAll('a', {'class':'story_link'}):
sequelStoryHeader = soup.find('h1', {'class':'header-stories'}, text="Sequels")
if not sequelStoryHeader == None:
sequelContainer = sequelStoryHeader.parent.parent
for sequel in sequelContainer.findAll('a', {'class':'story_link'}):
self.story.addToList("sequelsUrl", 'http://'+self.host+sequel["href"])
self.story.addToList("sequels", stripHTML(sequel).replace(',', ';'))
#The link to the prequel is embedded in the description text, so erring
#on the side of caution and wrapping this whole thing in a try block.
#If anything goes wrong this probably wasn't a valid prequel link.
@@ -50,6 +50,7 @@ class TestSiteAdapter(BaseSiteAdapter):
def extractChapterUrlsAndMetadata(self):
idstr = self.story.getMetadata('storyId')
idnum = int(idstr)
self.do_sleep()
if idnum >= 1000:
logger.warn("storyId:%s - Custom INI data will be used."%idstr)
@@ -296,6 +297,7 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
def getChapterText(self, url):
logger.debug('Getting chapter text from: %s' % url)
self.do_sleep()
if self.story.getMetadata('storyId').startswith('670') or \
self.story.getMetadata('storyId').startswith('672'):
time.sleep(1.0)
+20 -5
View File
@@ -76,6 +76,8 @@ class BaseSiteAdapter(Configurable):
self.username = "NoneGiven" # if left empty, site doesn't return any message at all.
self.password = ""
self.is_adult=False
self.override_sleep = None
self.opener = u2.build_opener(u2.HTTPCookieProcessor(),GZipProcessor())
## Specific UA because too many sites are blocking the default python UA.
@@ -147,8 +149,7 @@ class BaseSiteAdapter(Configurable):
# Assumes application/x-www-form-urlencoded. parameters, headers are dict()s
def _postUrl(self, url, parameters={}, headers={}):
if self.getConfig('slow_down_sleep_time'):
time.sleep(float(self.getConfig('slow_down_sleep_time')))
self.do_sleep()
## u2.Request assumes POST when data!=None. Also assumes data
## is application/x-www-form-urlencoded.
@@ -166,17 +167,31 @@ class BaseSiteAdapter(Configurable):
return self.opener.open(url.replace(' ','%20'),urllib.urlencode(parameters)).read()
else:
return self.opener.open(url.replace(' ','%20')).read()
def set_sleep(self,val):
print("\n===========\n set sleep time %s\n==========="%val)
self.override_sleep = val
def do_sleep(self):
if self.override_sleep:
time.sleep(float(self.override_sleep))
elif self.getConfig('slow_down_sleep_time'):
time.sleep(float(self.getConfig('slow_down_sleep_time')))
# parameters is a dict()
def _fetchUrl(self, url, parameters=None):
if self.getConfig('slow_down_sleep_time'):
time.sleep(float(self.getConfig('slow_down_sleep_time')))
self.do_sleep()
excpt=None
for sleeptime in [0, 0.5, 4, 9]:
time.sleep(sleeptime)
try:
return self._decode(self._fetchUrlRaw(url,parameters))
except u2.HTTPError, he:
if he.code == 404:
logger.warn("Caught an exception reading URL: %s Exception %s."%(unicode(url),unicode(he)))
excpt=he
break # break out on 404
except Exception, e:
excpt=e
logger.warn("Caught an exception reading URL: %s Exception %s."%(unicode(url),unicode(e)))
@@ -367,7 +382,7 @@ class BaseSiteAdapter(Configurable):
# Don't want body tags in chapter html--writers add them.
# This is primarily for epub updates.
retval = re.sub(r"</?body>\r?\n?","",retval)
retval = re.sub(r"</?body[^>]*>\r?\n?","",retval)
if self.getConfig("replace_br_with_p"):
# Apply heuristic processing to replace <br> paragraph
+7 -1
View File
@@ -573,7 +573,13 @@ class Story(Configurable):
imgurl = url
else:
parsedUrl = urlparse.urlparse(parenturl)
if url.startswith("/") :
if url.startswith("//") :
imgurl = urlparse.urlunparse(
(parsedUrl.scheme,
'',
url,
'','',''))
elif url.startswith("/") :
imgurl = urlparse.urlunparse(
(parsedUrl.scheme,
parsedUrl.netloc,
+4 -4
View File
@@ -60,14 +60,14 @@
<p>Hi, {{ nickname }}! This is FanFictionDownLoader, which makes reading stories from various websites
much easier. </p>
</div>
<!-- put announcements here, h3 is a good title size. -->
<!-- put announcements here, h3 is a good title size.
<h3>Changes:</h3>
<p>
<ul>
<li>Fix for possible divide-by-zero in replace_br_with_p.</li>
<li>Fix for cover image.</li>
</ul>
</p>
-->
<p>
Questions? Check out our
<a href="http://code.google.com/p/fanficdownloader/wiki/FanFictionDownloaderFAQs">FAQs</a>.
@@ -76,7 +76,7 @@
If you have any problems with this application, please
report them in
the <a href="http://groups.google.com/group/fanfic-downloader">FanFictionDownLoader Google Group</a>. The
<a href="http://4-4-90.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
<a href="http://4-4-92.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
</p>
<div id='error'>
{{ error_message }}
+22 -1
View File
@@ -1202,6 +1202,20 @@ user_agent:
## Change this to false to use them anyway.
never_make_cover: true
## fanfiction.net is blocking people more aggressively. If you
## download fewer stories less often you can likely get by with
## reducing this sleep.
slow_down_sleep_time:4
## ffnet is sensitive to too many hits. Users are sensitive to long
## waits during the initial metadata collection in the foreground.
## When used, these settings will speed up metadata downloads in the
## foreground linearly.
tweak_fg_sleep:true
min_fg_sleep:1.0
max_fg_sleep:4.0
max_fg_sleep_at_downloads: 10
## Extra metadata that this adapter knows about. See [dramione.org]
## for examples of how to use them.
extra_valid_entries:reviews,favs,follows
@@ -1210,13 +1224,20 @@ extra_valid_entries:reviews,favs,follows
## to be romantic pairings.
ships_label:Pairings
## Date formats used by FFDL. Published and Update don't have time.
## Date formats used by FFDL. Published and Update don't usually have
## time, but they do now on ffnet.
## See http://docs.python.org/library/datetime.html#strftime-strptime-behavior
## Note that ini format requires % to be escaped as %%.
#dateCreated_format:%%Y-%%m-%%d %%H:%%M:%%S
datePublished_format:%%Y-%%m-%%d %%H:%%M:%%S
dateUpdated_format:%%Y-%%m-%%d %%H:%%M:%%S
## ffnet used to have a tendency to send out update notices in email
## before all their servers were showing the update on the first
## chapter. It generates another server request and doesn't seem to
## be needed lately, so now default it to off.
check_next_chapter:false
[www.fanfiktion.de]
## Some sites require login (or login for some rated stories) The
## program can prompt you, or you can save it in config. In