Compare commits

...
Author SHA1 Message Date
Jim Miller 125e29091f Update CLI download zip. 2014-05-23 20:17:27 -05:00
Jim Miller d0c73d5444 Bump versions. 2014-05-23 20:15:49 -05:00
Jim Miller bd8e54edcf Fixes for literotica.com: URLs using //site, allow https, ch01 as storyId, multi ch only. 2014-05-17 21:12:43 -05:00
Jim Miller d680a86f0c Fix for dark-solace.org Rating. 2014-05-17 15:55:20 -05:00
Jim Miller f075ae582d Added tag FanFictionDownLoader-4.5.01 for changeset b3a1ca11a76d 2014-05-13 20:18:11 -05:00
Jim Miller 928ebb9751 Added tag calibre-plugin-1.8.20 for changeset b3a1ca11a76d 2014-05-13 20:17:57 -05:00
Jim Miller ce262be162 Update CLI download zip. 2014-05-13 20:17:44 -05:00
Jim Miller 6faa6850af Bump versions. 2014-05-13 20:16:22 -05:00
Jim Miller f392c6dd77 Fix for dark-solace.org metadata parsing. 2014-05-13 12:00:58 -05:00
Jim Miller af0dff28b4 Fix for fictionpad.com removing 'dislikes' in some(all?) cases. 2014-05-10 14:30:53 -05:00
Jim Miller 02a75a821f Fix for storiesonline.net changing urls. 2014-05-10 14:30:29 -05:00
Jim Miller c06028b498 Fix for AO3 story not found. 2014-05-10 14:30:06 -05:00
Jim Miller de4b95af9b Added tag FanFictionDownLoader-4.5.00 for changeset fbdadac26cf9 2014-05-05 12:54:38 -05:00
Jim Miller 5366355d96 Added tag calibre-plugin-1.8.19 for changeset fbdadac26cf9 2014-05-05 12:54:23 -05:00
Jim Miller 264853d768 Update CLI download zip. 2014-05-05 12:54:11 -05:00
Jim Miller d82b399738 Bump versions. 2014-05-05 12:50:30 -05:00
Jim Miller 59446d23dc in/exclude_metadata_pre/post feature. 2014-05-05 12:21:55 -05:00
Jim Miller 6fcbdd8a8d https for squidge.org/peja, allow story url change http to https on any site. 2014-05-03 11:27:39 -05:00
Jim Miller 1348525d25 Fix for some stories' summaries on onedirectionfanfiction.com. 2014-05-02 11:46:59 -05:00
Jim Miller 40bac62c5e Change CLI -m output slightly. 2014-05-02 11:25:13 -05:00
Jim Miller 362f15f9fa Slightly improved conn refused handling. 2014-04-22 13:01:33 -05:00
Jim Miller b833041dc4 Allow https for FimF, still use http for canonical. 2014-04-22 13:01:26 -05:00
Jim Miller fa67220b86 Added tag FanFictionDownLoader-4.4.99 for changeset fdbc5eed9810 2014-04-18 14:18:55 -05:00
Jim Miller 4f412eb89f Added tag calibre-plugin-1.8.18 for changeset fdbc5eed9810 2014-04-18 14:18:41 -05:00
Jim Miller 6a4aa4340e Update CLI download zip. 2014-04-18 14:18:20 -05:00
Jim Miller dc785b911e Add fix_fimf_blockquotes feature, bump versions. 2014-04-18 14:17:26 -05:00
Jim Miller d403f916a9 Added tag FanFictionDownLoader-4.4.98 for changeset ec9ec0e9a813 2014-04-09 08:05:47 -05:00
Jim Miller 29fe1a6e24 Added tag calibre-plugin-1.8.17 for changeset ec9ec0e9a813 2014-04-09 08:05:32 -05:00
Jim Miller 131a08c0dc Update CLI download zip. 2014-04-09 08:05:14 -05:00
Jim Miller fbd26c16e0 Bump versions. 2014-04-09 08:03:16 -05:00
Jim Miller 34ebba40d0 Fix for a problem with literotica.com multi-page chapters on Kobo readers. 2014-04-08 09:32:36 -05:00
Jim Miller 7ce8436208 Added tag FanFictionDownLoader-4.4.97 for changeset 52e576f78b3d 2014-03-29 17:22:01 -05:00
Jim Miller b247e4fc7b Added tag calibre-plugin-1.8.16 for changeset 52e576f78b3d 2014-03-29 17:21:49 -05:00
18 changed files with 266 additions and 53 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# ffd-retief-hrd fanfictiondownloader
application: fanfictiondownloader
version: 4-4-97
version: 4-5-02
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, 16)
version = (1, 8, 21)
minimum_calibre_version = (1, 13, 0)
#: This field defines the GUI plugin class that contains all the code
+2 -1
View File
@@ -970,7 +970,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
if book_id and mi: # book_id and mi only set if matched by title/author.
liburl = self.get_story_url(db,book_id)
if book['url'] != liburl and prefs['checkforurlchange'] and \
not (book['url'].replace('https','http') == liburl and 'fanfiction.net' in liburl):
not (book['url'].replace('https','http') == liburl): # several sites have been changing to
# https now. Don't flag when that's the only change.
# special case for ffnet urls change to https.
if not question_dialog(self.gui, _('Change Story URL?'),'''
<h3>%s</h3>
+31 -1
View File
@@ -219,7 +219,32 @@ connect_timeout:60.0
# .*-Centered=>
# characters=>Sam W\.=>Sam Witwicky&&category=>Transformers
# characters=>Sam W\.=>Sam Winchester&&category=>Supernatural
## Include/Exclude metadata
##
## You can use the include/exclude metadata features to either limit
## the values of particular metadata lists to specific values or to
## exclude specific values. Further, you can conditionally apply each
## line depending on other metadata, use exact strings or regular
## expressions(regex) to match values, and negate matches.
##
## The settings are:
## include_metadata_pre
## exclude_metadata_pre
## include_metadata_post
## exclude_metadata_post
##
## The form of each line is:
## metakey[,metakey]==exactvalue
## metakey[,metakey]=~regex
## metakey[,metakey]==exactvalue&&conditionalkey==exactcondvalue
## metakey[,metakey]=~regex&&conditionalkey==exactcondvalue
## metakey[,metakey]==exactvalue&&conditionalkey=~condregex
##
## This is fairly complicated, so it's documented on its own wiki
## page:
## https://code.google.com/p/fanficdownloader/wiki/InExcludeMetadataFeature
## Some readers don't show horizontal rule (<hr />) tags correctly.
## This replaces them all with a centered '* * *'. (Note centering
## doesn't work on some devices either.)
@@ -1314,6 +1339,11 @@ extra_valid_entries:reviews,favs,follows
## when updating to enforce accurate chapters.
#do_update_hook:false
## fimfiction.net is reported to misinterprete some BBCode with
## blockquotes incorrectly. This fixes those instances and defaults
## to on, but can be switched off if it is found to cause problems.
fix_fimf_blockquotes:true
## Site dedicated to these categories/characters/ships
extracategories:My Little Pony: Friendship is Magic
+1 -1
View File
@@ -292,7 +292,7 @@ def main(argv,
else:
# regular download
if options.metaonly:
print adapter.getStoryMetadataOnly()
print adapter.getStoryMetadataOnly().getAllMetadata()
output_filename=writeStory(configuration,adapter,options.format,options.metaonly)
Binary file not shown.
@@ -145,10 +145,13 @@ class ArchiveOfOurOwnOrgAdapter(BaseSiteAdapter):
except urllib2.HTTPError, e:
if e.code == 404:
raise exceptions.StoryDoesNotExist(self.meta)
raise exceptions.StoryDoesNotExist(self.url)
else:
raise e
if "Sorry, we couldn&#x27;t find the work you were looking for." in data:
raise exceptions.StoryDoesNotExist(self.url)
if self.needToLoginCheck(data):
# need to log in for this one.
self.performLogin(url,data)
@@ -182,6 +182,11 @@ class DarkSolaceOrgAdapter(BaseSiteAdapter):
# first a tag in pagetitle is title
self.story.setMetadata('title',stripHTML(div.find('a')))
div.find('a').extract()
# only thing left in div(pagetitle) now should be 'by' and rating.
rating = stripHTML(div)
if '[' in rating:
self.story.setMetadata('rating', rating[rating.index('[')+1:-1])
for chapa in soup.findAll('a', href=re.compile(r'viewstory.php\?sid='+
self.story.getMetadata('storyId')+'&chapter=\d+')):
@@ -234,31 +239,28 @@ class DarkSolaceOrgAdapter(BaseSiteAdapter):
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
if 'Rated' in label:
self.story.setMetadata('rating', value[:len(value)-2])
if 'Word count' in label:
self.story.setMetadata('numWords', value)
if 'Categories' in label:
cats = labelspan.parent.findAll('a',href=re.compile(r'categories.php\?catid=\d+'))
cats = labelspan.parent.findAll('a',href=re.compile(r'browse.php\?type=categories'))
for cat in cats:
self.story.addToList('category',cat.string)
if 'Characters' in label:
for char in value.string.split(', '):
if not 'None' in char:
self.story.addToList('characters',char)
chars = labelspan.parent.findAll('a',href=re.compile(r'browse.php\?type=characters'))
for char in chars:
self.story.addToList('characters',char.string)
if 'Genre' in label:
for genre in value.string.split(', '):
if not 'None' in genre:
self.story.addToList('genre',genre)
genres = labelspan.parent.findAll('a',href=re.compile(r'browse.php\?type=class&type_id=1'))
for genre in genres:
self.story.addToList('genre',genre.string)
if 'Warnings' in label:
for warning in value.string.split(', '):
if not 'None' in warning:
self.story.addToList('warnings',warning)
warnings = labelspan.parent.findAll('a',href=re.compile(r'browse.php\?type=class&type_id=2'))
for warning in warnings:
self.story.addToList('warnings',warning.string)
if 'Completed' in label:
if 'Yes' in value:
@@ -133,6 +133,7 @@ class FictionPadSiteAdapter(BaseSiteAdapter):
author = tables['users'][0]
story = tables['stories'][0]
story_ver = tables['story_versions'][0]
print("story:%s"%story)
self.story.setMetadata('authorId',author['id'])
self.story.setMetadata('author',author['display_name'])
@@ -151,7 +152,8 @@ class FictionPadSiteAdapter(BaseSiteAdapter):
self.story.setMetadata('comments',story['comments_count'])
self.story.setMetadata('views',story['views_count'])
self.story.setMetadata('likes',int(story['likes'])) # no idea why they floated these.
self.story.setMetadata('dislikes',int(story['dislikes']))
if 'dislikes' in story:
self.story.setMetadata('dislikes',int(story['dislikes']))
if story_ver['is_complete']:
self.story.setMetadata('status', 'Completed')
@@ -59,7 +59,7 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter):
return "http://www.fimfiction.net/story/1234/story-title-here http://www.fimfiction.net/story/1234/ http://www.fimfiction.com/story/1234/1/ http://mobile.fimfiction.net/story/1234/1/story-title-here/chapter-title-here"
def getSiteURLPattern(self):
return r"http://(www|mobile)\.fimfiction\.(net|com)/story/\d+/?.*"
return r"https?://(www|mobile)\.fimfiction\.(net|com)/story/\d+/?.*"
def extractChapterUrlsAndMetadata(self):
@@ -85,7 +85,7 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter):
# Unfortunately, we still need to load the story index
# page to parse the characters. And chapters, now, too.
data = self._fetchUrl(self.url)
data = self.do_fix_blockquotes(self._fetchUrl(self.url))
soup = bs.BeautifulSoup(data)
except urllib2.HTTPError, e:
if e.code == 404:
@@ -283,12 +283,21 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter):
print("Existing epub has %s chapters\nNewest chapter is %s. Discarding old chapters from there on."%(len(self.oldchapters), self.newestChapterNum+1))
self.oldchapters = self.oldchapters[:self.newestChapterNum]
return len(self.oldchapters)
def do_fix_blockquotes(self,data):
if self.getConfig('fix_fimf_blockquotes'):
# <p class="double"><blockquote>
# </blockquote></p>
# include > in re groups so there's always something in the group.
data = re.sub(r'<p([^>]*>\s*)<blockquote([^>]*>)',r'<blockquote\2<p\1',data)
data = re.sub(r'</blockquote(>\s*)</p>',r'</p\1</blockquote>',data)
return data
def getChapterText(self, url):
logger.debug('Getting chapter text from: %s' % url)
soup = bs.BeautifulSoup(self._fetchUrl(url),selfClosingTags=('br','hr')).find('div', {'class' : 'chapter_content'})
data = self.do_fix_blockquotes(self._fetchUrl(url))
soup = bs.BeautifulSoup(data,selfClosingTags=('br','hr')).find('div', {'class' : 'chapter_content'})
if soup == None:
raise exceptions.FailedToDownload("Error downloading Chapter: %s! Missing required element!" % url)
return self.utf8FromSoup(url,soup)
+24 -14
View File
@@ -42,16 +42,19 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
self.story.setMetadata('siteabbrev','litero')
# get storyId from url--url validation guarantees query is only sid=1234
self.story.setMetadata('storyId',self.parsedUrl.path.split('/',)[2])
# normalize to first chapter. Not sure if they ever have more than 2 digits.
storyid = self.parsedUrl.path.split('/',)[2]
if re.match(r'-ch\d\d$',storyid):
storyid = storyid[:-2]+'01'
self.story.setMetadata('storyId',storyid)
self.origurl = url
if "http://www.i." in self.origurl:
if "//www.i." in self.origurl:
## accept m(mobile)url, but use www.
self.origurl = self.origurl.replace("http://www.i.","http://www.")
self.origurl = self.origurl.replace("//www.i.","//www.")
# normalized story URL.
self._setURL("http://"+self.getSiteDomain()\
self._setURL(url[:url.index('//')+2]+self.getSiteDomain()\
+"/s/"+self.story.getMetadata('storyId'))
# The date format will vary from site to site.
@@ -69,10 +72,10 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
@classmethod
def getSiteExampleURLs(self):
#return "http://www.literotica.com/s/story-title http://www.literotica.com/stories/showstory.php?id=1234 http://www.i.literotica.com/stories/showstory.php?id=1234"
return "http://www.literotica.com/s/story-title"
return "http://www.literotica.com/s/story-title https://www.literotica.com/s/story-title"
def getSiteURLPattern(self):
return r"http://www(\.i)?\.literotica\.com/s/([a-zA-Z0-9_-]+)"
return r"https?://www(\.i)?\.literotica\.com/s/([a-zA-Z0-9_-]+)"
def extractChapterUrlsAndMetadata(self):
@@ -97,20 +100,24 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
# author
a = soup1.find("span", "b-story-user-y")
self.story.setMetadata('authorId', urlparse.parse_qs(a.a['href'].split('?')[1])['uid'])
self.story.setMetadata('authorUrl', a.a['href'])
authorurl = a.a['href']
if authorurl.startswith('//'):
authorurl = self.parsedUrl.scheme+':'+authorurl
self.story.setMetadata('authorUrl', authorurl)
self.story.setMetadata('author', a.text)
# get the author page
try:
dataAuth = self._fetchUrl(a.a['href'])
dataAuth = self._fetchUrl(authorurl)
soupAuth = bs.BeautifulSoup(dataAuth)
except urllib2.HTTPError, e:
if e.code == 404:
raise exceptions.StoryDoesNotExist(a.a['href'])
raise exceptions.StoryDoesNotExist(authorurl)
else:
raise e
storyLink = soupAuth.find('a', href=url1)
## site has started using //domain.name/asdf urls remove https?: from front
storyLink = soupAuth.find('a', href=url1[url1.index(':')+1:])
if storyLink is not None:
# pull the published date from the author page
@@ -166,7 +173,10 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
self.story.setMetadata('datePublished',makeDate(stripHTML(row.find('td',{'class':'dt'})), self.dateformat))
while row['class'] == 'sl':
# pages include full URLs.
self.chapterUrls.append((row.a.string,row.a['href']))
chapurl = row.a['href']
if chapurl.startswith('//'):
chapurl = self.parsedUrl.scheme+':'+chapurl
self.chapterUrls.append((row.a.string,chapurl))
if not row.nextSibling:
break
row = row.nextSibling
@@ -203,7 +213,7 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
# get story text
story1 = soup1.find('div', 'b-story-body-x').p
story1.name='div'
story1.append('<br>')
story1.append('<br />')
storytext = self.utf8FromSoup(url,story1)
# find num pages
@@ -220,7 +230,7 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
[comment.extract() for comment in soup2.findAll(text=lambda text:isinstance(text, bs.Comment))]
story2 = soup2.find('div', 'b-story-body-x').p
story2.name='div'
story2.append('<br>')
story2.append('<br />')
storytext += self.utf8FromSoup(url,story2)
except urllib2.HTTPError, e:
if e.code == 404:
@@ -192,7 +192,7 @@ class OneDirectionFanfictionComAdapter(BaseSiteAdapter):
if 'Summary' in label:
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
while value and not defaultGetattr(value,'class') == 'label':
svalue += str(value)
value = value.nextSibling
self.setDescription(url,svalue)
@@ -62,7 +62,7 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
# normalized story URL.
self._setURL('http://' + self.getSiteDomain() + '/peja/cgi-bin/viewstory.php?sid='+self.story.getMetadata('storyId'))
self._setURL('https://' + self.getSiteDomain() + '/peja/cgi-bin/viewstory.php?sid='+self.story.getMetadata('storyId'))
# Each adapter needs to have a unique site abbreviation.
self.story.setMetadata('siteabbrev','wwomb')
@@ -83,10 +83,10 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
@classmethod
def getSiteExampleURLs(self):
return "http://"+self.getSiteDomain()+"/peja/cgi-bin/viewstory.php?sid=1234"
return "https://"+self.getSiteDomain()+"/peja/cgi-bin/viewstory.php?sid=1234"
def getSiteURLPattern(self):
return re.escape("http://"+self.getSiteDomain()+"/")+"~?"+re.escape("peja/cgi-bin/viewstory.php?sid=")+r"\d+$"
return r"https?"+re.escape("://"+self.getSiteDomain()+"/")+r"~?"+re.escape("peja/cgi-bin/viewstory.php?sid=")+r"\d+$"
## Getting the chapter list and the meta data, plus 'is adult' checking.
def extractChapterUrlsAndMetadata(self):
@@ -116,7 +116,7 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
# Find authorid and URL from... author url.
author = soup.find('div', {'id':"pagetitle"}).find('a')
self.story.setMetadata('authorId',author['href'].split('=')[1])
self.story.setMetadata('authorUrl','http://'+self.host+'/peja/cgi-bin/'+author['href'])
self.story.setMetadata('authorUrl','https://'+self.host+'/peja/cgi-bin/'+author['href'])
self.story.setMetadata('author',author.string)
authorSoup = bs.BeautifulSoup(self._fetchUrl(self.story.getMetadata('authorUrl')))
@@ -131,7 +131,7 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
chapterselect=soup.find('select',{'name':'chapter'})
if chapterselect:
for ch in chapterselect.findAll('option'):
self.chapterUrls.append((stripHTML(ch),'http://'+self.host+'/peja/cgi-bin/viewstory.php?sid='+self.story.getMetadata('storyId')+'&chapter='+ch['value']))
self.chapterUrls.append((stripHTML(ch),'https://'+self.host+'/peja/cgi-bin/viewstory.php?sid='+self.story.getMetadata('storyId')+'&chapter='+ch['value']))
else:
self.chapterUrls.append((title,url))
@@ -207,7 +207,7 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
# http://www.squidge.org/peja/cgi-bin/series.php?seriesid=254
a = titleblock.find('a', href=re.compile(r"series.php\?seriesid=\d+"))
series_name = a.string
series_url = 'http://'+self.host+'/peja/cgi-bin/'+a['href']
series_url = 'https://'+self.host+'/peja/cgi-bin/'+a['href']
# use BeautifulSoup HTML parser to make everything easier to find.
seriessoup = bs.BeautifulSoup(self._fetchUrl(series_url))
@@ -66,7 +66,7 @@ class StoriesOnlineNetAdapter(BaseSiteAdapter):
return "http://"+self.getSiteDomain()+"/s/1234 http://"+self.getSiteDomain()+"/s/1234:4010"
def getSiteURLPattern(self):
return re.escape("http://"+self.getSiteDomain())+r"/s/\d+((:\d+)?(;\d+)?$|(:i)?$)"
return re.escape("http://"+self.getSiteDomain())+r"/s/\d+((:\d+)?(;\d+)?$|(:i)?$)?"
## Login seems to be reasonably standard across eFiction sites.
def needToLoginCheck(self, data):
@@ -171,7 +171,7 @@ class StoriesOnlineNetAdapter(BaseSiteAdapter):
a = asoup.findAll('td', {'class' : 'lc2'})
for lc2 in a:
if lc2.find('a')['href'] == '/s/'+self.story.getMetadata('storyId'):
if lc2.find('a', href=re.compile(r'^/s/'+self.story.getMetadata('storyId'))):
i=1
break
if a[len(a)-1] == lc2:
+1 -1
View File
@@ -188,9 +188,9 @@ class BaseSiteAdapter(Configurable):
try:
return self._decode(self._fetchUrlRaw(url,parameters))
except u2.HTTPError, he:
excpt=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
+125
View File
@@ -221,6 +221,65 @@ langs = {
"Devanagari":"hi",
}
class InExMatch:
keys = []
regex = None
match = None
negate = False
def __init__(self,line):
if "=~" in line:
(self.keys,self.match) = line.split("=~")
self.match = self.match.replace('\s',' ')
self.regex = re.compile(self.match)
elif "!~" in line:
(self.keys,self.match) = line.split("!~")
self.match = self.match.replace('\s',' ')
self.regex = re.compile(self.match)
self.negate = True
elif "==" in line:
(self.keys,self.match) = line.split("==")
self.match = self.match.replace('\s',' ')
elif "!=" in line:
(self.keys,self.match) = line.split("!=")
self.match = self.match.replace('\s',' ')
self.negate = True
self.keys = map( lambda x: x.strip(), self.keys.split(",") )
# For conditional, only one key
def is_key(self,key):
return key == self.keys[0]
# For conditional, only one key
def key(self):
return self.keys[0]
def in_keys(self,key):
return key in self.keys
def is_match(self,value):
retval = False
if self.regex:
if self.regex.search(value):
retval = True
#print(">>>>>>>>>>>>>%s=~%s r: %s,%s=%s"%(self.match,value,self.negate,retval,self.negate != retval))
else:
retval = self.match == value
#print(">>>>>>>>>>>>>%s==%s r: %s,%s=%s"%(self.match,value,self.negate,retval, self.negate != retval))
return self.negate != retval
def __str__(self):
if self.negate:
f='!'
else:
f='='
if self.regex:
s='~'
else:
s='='
return u'InExMatch(%s %s%s %s)'%(self.keys,f,s,self.match)
class Story(Configurable):
def __init__(self, configuration):
@@ -231,6 +290,7 @@ class Story(Configurable):
except:
self.metadata = {'version':'4.4'}
self.replacements = []
self.in_ex_cludes = {}
self.chapters = [] # chapters will be tuples of (title,html)
self.imgurls = []
self.imgtuples = []
@@ -252,6 +312,15 @@ class Story(Configurable):
self.setReplace(self.getConfig('replace_metadata'))
in_ex_clude_list = ['include_metadata_pre','exclude_metadata_pre',
'include_metadata_post','exclude_metadata_post']
for ie in in_ex_clude_list:
ies = self.getConfig(ie)
# print("%s %s"%(ie,ies))
if ies:
iel = []
self.in_ex_cludes[ie] = self.set_in_ex_clude(ies)
def setMetadata(self, key, value, condremoveentities=True):
## still keeps &lt; &lt; and &amp;
if condremoveentities:
@@ -269,6 +338,56 @@ class Story(Configurable):
self.addToList('lastupdate',value.strftime("Last Update Year/Month: %Y/%m"))
self.addToList('lastupdate',value.strftime("Last Update: %Y/%m/%d"))
## metakey[,metakey]=~pattern
## metakey[,metakey]==string
## *for* part lines. Effect only when trailing conditional key=~regexp matches
## metakey[,metakey]=~pattern[&&metakey=~regexp]
## metakey[,metakey]==string[&&metakey=~regexp]
## metakey[,metakey]=~pattern[&&metakey==string]
## metakey[,metakey]==string[&&metakey==string]
def set_in_ex_clude(self,setting):
dest = []
# print("set_in_ex_clude:"+setting)
for line in setting.splitlines():
if line:
(match,condmatch)=(None,None)
if "&&" in line:
(line,conditional) = line.split("&&")
condmatch = InExMatch(conditional)
match = InExMatch(line)
dest.append([match,condmatch])
return dest
def do_in_ex_clude(self,which,value,key):
if value and which in self.in_ex_cludes:
include = 'include' in which
keyfound = False
found = False
for (match,condmatch) in self.in_ex_cludes[which]:
keyfndnow = False
if match.in_keys(key):
# key in keys and either no conditional, or conditional matched
if condmatch == None or condmatch.is_key(key):
keyfndnow = True
else:
condval = self.getMetadata(condmatch.key())
keyfndnow = condmatch.is_match(condval)
keyfound |= keyfndnow
# print("match:%s %s\ncondmatch:%s %s\n\tkeyfound:%s\n\tfound:%s"%(
# match,value,condmatch,condval,keyfound,found))
if keyfndnow:
found = isinstance(value,basestring) and match.is_match(value)
if found:
# print("match:%s %s\n\tkeyfndnow:%s\n\tfound:%s"%(
# match,value,keyfndnow,found))
if not include:
value = None
break
if include and keyfound and not found:
value = None
return value
## Two or three part lines. Two part effect everything.
## Three part effect only those key(s) lists.
@@ -301,6 +420,10 @@ class Story(Configurable):
self.replacements.append([metakeys,regexp,replacement,condkey,condregexp])
def doReplacements(self,value,key):
value = self.do_in_ex_clude('include_metadata_pre',value,key)
value = self.do_in_ex_clude('exclude_metadata_pre',value,key)
# if key=='characters' and value=='Bilbo':
# value = ''
for (metakeys,regexp,replacement,condkey,condregexp) in self.replacements:
if (metakeys == None or key in metakeys) \
and isinstance(value,basestring) \
@@ -312,6 +435,8 @@ class Story(Configurable):
if doreplace:
value = regexp.sub(replacement,value)
value = self.do_in_ex_clude('include_metadata_post',value,key)
value = self.do_in_ex_clude('exclude_metadata_post',value,key)
return value
def getMetadataRaw(self,key):
+3 -2
View File
@@ -64,7 +64,8 @@
<h3>Changes:</h3>
<p>
<ul>
<li>Add Pairing for hpfanficarchive.com</li>
<li>Fix for dark-solace.org Rating.</li>
<li>Fix for literotica.com URLs.</li>
</ul>
</p>
<p>
@@ -75,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-96.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
<a href="http://4-5-01.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
</p>
<div id='error'>
{{ error_message }}
+31 -1
View File
@@ -186,7 +186,32 @@ connect_timeout:60.0
# .*-Centered=>
# characters=>Sam W\.=>Sam Witwicky&&category=>Transformers
# characters=>Sam W\.=>Sam Winchester&&category=>Supernatural
## Include/Exclude metadata
##
## You can use the include/exclude metadata features to either limit
## the values of particular metadata lists to specific values or to
## exclude specific values. Further, you can conditionally apply each
## line depending on other metadata, use exact strings or regular
## expressions(regex) to match values, and negate matches.
##
## The settings are:
## include_metadata_pre
## exclude_metadata_pre
## include_metadata_post
## exclude_metadata_post
##
## The form of each line is:
## metakey[,metakey]==exactvalue
## metakey[,metakey]=~regex
## metakey[,metakey]==exactvalue&&conditionalkey==exactcondvalue
## metakey[,metakey]=~regex&&conditionalkey==exactcondvalue
## metakey[,metakey]==exactvalue&&conditionalkey=~condregex
##
## This is fairly complicated, so it's documented on its own wiki
## page:
## https://code.google.com/p/fanficdownloader/wiki/InExcludeMetadataFeature
## Some readers don't show horizontal rule (<hr />) tags correctly.
## This replaces them all with a centered '* * *'. (Note centering
## doesn't work on some devices either.)
@@ -1307,6 +1332,11 @@ extra_valid_entries:reviews,favs,follows
## when updating to enforce accurate chapters.
#do_update_hook:false
## fimfiction.net is reported to misinterprete some BBCode with
## blockquotes incorrectly. This fixes those instances and defaults
## to on, but can be switched off if it is found to cause problems.
fix_fimf_blockquotes:true
## Site dedicated to these categories/characters/ships
extracategories:My Little Pony: Friendship is Magic