mirror of
https://github.com/wassname/FanFicFare.git
synced 2026-09-16 11:47:12 +08:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a325ffa3 | ||
|
|
29f900199e | ||
|
|
f56fb0efed | ||
|
|
a703011aef | ||
|
|
a635272bcf | ||
|
|
1079ed565c |
@@ -1,6 +1,6 @@
|
||||
# ffd-retief-hrd fanfictiondownloader
|
||||
application: fanfictiondownloader
|
||||
version: 4-4-57
|
||||
version: 4-4-58
|
||||
runtime: python27
|
||||
api_version: 1
|
||||
threadsafe: true
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import (unicode_literals, division, absolute_import,
|
||||
print_function)
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2011, Jim Miller'
|
||||
__copyright__ = '2013, Jim Miller'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
# The class that all Interface Action plugin wrappers must inherit from
|
||||
@@ -26,7 +26,7 @@ class FanFictionDownLoaderBase(InterfaceActionBase):
|
||||
description = 'UI plugin to download FanFiction stories from various sites.'
|
||||
supported_platforms = ['windows', 'osx', 'linux']
|
||||
author = 'Jim Miller'
|
||||
version = (1, 7, 24)
|
||||
version = (1, 7, 25)
|
||||
minimum_calibre_version = (0, 8, 57)
|
||||
|
||||
#: This field defines the GUI plugin class that contains all the code
|
||||
|
||||
+49
-1
@@ -1,4 +1,4 @@
|
||||
# Copyright 2012 Fanficdownloader team
|
||||
# Copyright 2013 Fanficdownloader team
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@@ -191,6 +191,10 @@ extratags: FanFiction
|
||||
## summary.
|
||||
keep_summary_html:true
|
||||
|
||||
## If set true, any style attributes on tags in the story HTML will be
|
||||
## kept. Useful for keeping extra colors & formatting from original.
|
||||
#keep_style_attr: false
|
||||
|
||||
## Don't like the numbers at the start of chapter titles on some
|
||||
## sites? You can use strip_chapter_numbers to strip them off. Just
|
||||
## want to make them all look the same? Strip them off, then add them
|
||||
@@ -1383,3 +1387,47 @@ extracategories:Stargate: Atlantis
|
||||
## extratags params in all other sections. Only commandline options
|
||||
## beat overrides.
|
||||
#extratags:fanficdownloader
|
||||
|
||||
|
||||
[teststory:defaults]
|
||||
valid_entries:title,author_list,authorId_list,authorUrl_list,storyUrl,
|
||||
datePublished,dateUpdated,numWords,status,language,series,seriesUrl,
|
||||
rating,category_list,genre_list,warnings_list,characters_list
|
||||
|
||||
# {{storyId}} is a special case--it's the only one that works.
|
||||
title:Test Story Title {{storyId}}
|
||||
author_list:Test Author aa
|
||||
authorId_list:1
|
||||
authorUrl_list:http://test1.com?authid=1
|
||||
storyUrl:http://test1.com?sid={{storyId}}
|
||||
datePublished:1975-03-15
|
||||
dateUpdated:1975-04-15
|
||||
numWords:123,456
|
||||
status:In-Progress
|
||||
language:English
|
||||
|
||||
chaptertitles:Prologue
|
||||
|
||||
## Add additional sections with different numbers to get different
|
||||
## parameters for different story urls.
|
||||
## test1.com?sid=1000
|
||||
[teststory:1000]
|
||||
# note the leading commas when doing add_to_ with valid_entries and *_list
|
||||
add_to_valid_entries:,favs
|
||||
title:Testing New Feature {{storyId}}
|
||||
author_list:Bob Smith
|
||||
authorId_list:45
|
||||
authorUrl_list:http://test1.com?authid=45
|
||||
datePublished:2013-03-15
|
||||
dateUpdated:2013-04-15
|
||||
numWords:1456
|
||||
favs:56
|
||||
series:The Great Test [4]
|
||||
seriesUrl:http://test1.com?seriesid=1
|
||||
rating:Tweenie
|
||||
category_list:Harry Potter,Furbie,Crossover,Puella Magi Madoka Magica/魔法少女まどか★マギカ,Magical Girl Lyrical Nanoha
|
||||
genre_list:Fantasy,Comedy,Sci-Fi,Noir
|
||||
warnings_list:Swearing,Violence
|
||||
characters_list:Bob Smith,George Johnson,Fred Smythe
|
||||
|
||||
chaptertitles:Prologue,Chapter 1\, Xenos on Cinnabar,Chapter 2\, Sinmay on Kintikin,3. Chapter 3
|
||||
|
||||
@@ -47,31 +47,63 @@ class TestSiteAdapter(BaseSiteAdapter):
|
||||
return BaseSiteAdapter.getSiteURLPattern(self)+r'/?\?sid=\d+$'
|
||||
|
||||
def extractChapterUrlsAndMetadata(self):
|
||||
idstr = self.story.getMetadata('storyId')
|
||||
idnum = int(idstr)
|
||||
|
||||
if self.story.getMetadata('storyId') == '665' and not (self.is_adult or self.getConfig("is_adult")):
|
||||
if idnum >= 1000:
|
||||
logger.warn("storyId:%s - Custom INI data will be used."%idstr)
|
||||
|
||||
sections = ['teststory:%s'%idstr,'teststory:defaults']
|
||||
#print("self.get_config_list(sections,'valid_entries'):%s"%self.get_config_list(sections,'valid_entries'))
|
||||
for key in self.get_config_list(sections,'valid_entries'):
|
||||
if key.endswith("_list"):
|
||||
nkey = key[:-len("_list")]
|
||||
#print("addList:%s"%(nkey))
|
||||
for val in self.get_config_list(sections,key):
|
||||
#print("addList:%s->%s"%(nkey,val))
|
||||
self.story.addToList(nkey,val.decode('utf-8').replace('{{storyId}}',idstr))
|
||||
else:
|
||||
# Special cases:
|
||||
if key in ['datePublished','dateUpdated']:
|
||||
self.story.setMetadata(key,makeDate(self.get_config(sections,key),"%Y-%m-%d"))
|
||||
else:
|
||||
self.story.setMetadata(key,self.get_config(sections,key).decode('utf-8').replace('{{storyId}}',idstr))
|
||||
#print("set:%s->%s"%(key,self.story.getMetadata(key)))
|
||||
|
||||
self.chapterUrls = []
|
||||
for (j,chap) in enumerate(self.get_config_list(sections,'chaptertitles'),start=1):
|
||||
self.chapterUrls.append( (chap,self.url+"&chapter=%d"%j) )
|
||||
# self.chapterUrls = [(u'Prologue '+self.crazystring,self.url+"&chapter=1"),
|
||||
# ('Chapter 1, Xenos on Cinnabar',self.url+"&chapter=2"),
|
||||
# ]
|
||||
self.story.setMetadata('numChapters',len(self.chapterUrls))
|
||||
|
||||
return
|
||||
|
||||
if idstr == '665' and not (self.is_adult or self.getConfig("is_adult")):
|
||||
logger.warn("self.is_adult:%s"%self.is_adult)
|
||||
raise exceptions.AdultCheckRequired(self.url)
|
||||
|
||||
if self.story.getMetadata('storyId') == '666':
|
||||
if idstr == '666':
|
||||
raise exceptions.StoryDoesNotExist(self.url)
|
||||
|
||||
if self.story.getMetadata('storyId').startswith('670'):
|
||||
if idstr.startswith('670'):
|
||||
time.sleep(1.0)
|
||||
|
||||
if self.story.getMetadata('storyId').startswith('671'):
|
||||
if idstr.startswith('671'):
|
||||
time.sleep(1.0)
|
||||
|
||||
if self.getConfig("username"):
|
||||
self.username = self.getConfig("username")
|
||||
|
||||
if self.story.getMetadata('storyId') == '668' and self.username != "Me" :
|
||||
if idstr == '668' and self.username != "Me" :
|
||||
raise exceptions.FailedToLogin(self.url,self.username)
|
||||
|
||||
if self.story.getMetadata('storyId') == '664':
|
||||
self.story.setMetadata(u'title',"Test Story Title "+self.story.getMetadata('storyId')+self.crazystring)
|
||||
if idstr == '664':
|
||||
self.story.setMetadata(u'title',"Test Story Title "+idstr+self.crazystring)
|
||||
self.story.setMetadata('author','Test Author aa bare amp(&) quote(') amp(&)')
|
||||
else:
|
||||
self.story.setMetadata(u'title',"Test Story Title "+self.story.getMetadata('storyId'))
|
||||
self.story.setMetadata(u'title',"Test Story Title "+idstr)
|
||||
self.story.setMetadata('author','Test Author aa')
|
||||
self.story.setMetadata('storyUrl',self.url)
|
||||
self.setDescription(self.url,u'Description '+self.crazystring+u''' Done
|
||||
@@ -79,13 +111,12 @@ class TestSiteAdapter(BaseSiteAdapter):
|
||||
Some more longer description. "I suck at summaries!" "Better than it sounds!" "My first fic"
|
||||
''')
|
||||
self.story.setMetadata('datePublished',makeDate("1975-03-15","%Y-%m-%d"))
|
||||
if self.story.getMetadata('storyId') == '669':
|
||||
if idstr == '669':
|
||||
self.story.setMetadata('dateUpdated',datetime.datetime.now())
|
||||
else:
|
||||
self.story.setMetadata('dateUpdated',makeDate("1975-04-15","%Y-%m-%d"))
|
||||
self.story.setMetadata('numWords','123456')
|
||||
|
||||
idnum = int(self.story.getMetadata('storyId'))
|
||||
if idnum % 2 == 1:
|
||||
self.story.setMetadata('status','In-Progress')
|
||||
else:
|
||||
@@ -108,7 +139,7 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
|
||||
|
||||
self.story.setMetadata('rating','Tweenie')
|
||||
|
||||
if self.story.getMetadata('storyId') == '673':
|
||||
if idstr == '673':
|
||||
self.story.addToList('author','Author From List')
|
||||
self.story.addToList('author','Author From List 2')
|
||||
|
||||
@@ -128,19 +159,19 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
|
||||
self.story.addToList('warnings','Swearing')
|
||||
self.story.addToList('warnings','Violence')
|
||||
|
||||
if self.story.getMetadata('storyId') == '80':
|
||||
if idstr == '80':
|
||||
self.story.addToList('category',u'Rizzoli & Isles')
|
||||
self.story.addToList('characters','J. Rizzoli')
|
||||
elif self.story.getMetadata('storyId') == '81':
|
||||
elif idstr == '81':
|
||||
self.story.addToList('category',u'Pitch Perfect')
|
||||
self.story.addToList('characters','Chloe B.')
|
||||
elif self.story.getMetadata('storyId') == '83':
|
||||
elif idstr == '83':
|
||||
self.story.addToList('category',u'Rizzoli & Isles')
|
||||
self.story.addToList('characters','J. Rizzoli')
|
||||
self.story.addToList('category',u'Pitch Perfect')
|
||||
self.story.addToList('characters','Chloe B.')
|
||||
self.story.addToList('ships','Chloe B. & J. Rizzoli')
|
||||
elif self.story.getMetadata('storyId') == '82':
|
||||
elif idstr == '82':
|
||||
self.story.addToList('characters','Henry (Once Upon a Time)')
|
||||
self.story.addToList('category',u'Once Upon a Time (TV)')
|
||||
else:
|
||||
@@ -218,6 +249,9 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
|
||||
<div>
|
||||
<h3>Prologue</h3>
|
||||
<p>This is a fake adapter for testing purposes. Different sid's will give different errors:</p>
|
||||
<h4>Config(personal.ini)</h4>
|
||||
<p>sid>=1000 will use custom test story data from your configuration(personal.ini)</p>
|
||||
<p>Hard coded ids:</p>
|
||||
<p>http://test1.com?sid=664 - Crazy string title</p>
|
||||
<p>http://test1.com?sid=665 - raises AdultCheckRequired</p>
|
||||
<p>http://test1.com?sid=666 - raises StoryDoesNotExist</p>
|
||||
@@ -225,10 +259,6 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
|
||||
<p>http://test1.com?sid=668 - raises FailedToLogin unless username='Me'</p>
|
||||
<p>http://test1.com?sid=669 - Succeeds with Updated Date=now</p>
|
||||
<p>http://test1.com?sid=670 - Succeeds, but sleeps 2sec on each chapter</p>
|
||||
|
||||
|
||||
|
||||
|
||||
<p>http://test1.com?sid=671 - Succeeds, but sleeps 2sec metadata only</p>
|
||||
<p>http://test1.com?sid=672 - Succeeds, quick meta, sleeps 2sec chapters only</p>
|
||||
<p>http://test1.com?sid=673 - Succeeds, multiple authors, extra categories, genres</p>
|
||||
|
||||
@@ -315,6 +315,8 @@ class BaseSiteAdapter(Configurable):
|
||||
fetch=self._fetchUrlRaw
|
||||
|
||||
acceptable_attributes = ['href','name','class','id']
|
||||
if self.getConfig("keep_style_attr"):
|
||||
acceptable_attributes.append('style')
|
||||
#print("include_images:"+self.getConfig('include_images'))
|
||||
if self.getConfig('include_images'):
|
||||
acceptable_attributes.extend(('src','alt','longdesc'))
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import ConfigParser
|
||||
import ConfigParser, re
|
||||
|
||||
# All of the writers(epub,html,txt) and adapters(ffnet,twlt,etc)
|
||||
# inherit from Configurable. The config file(s) uses ini format:
|
||||
@@ -99,8 +99,12 @@ class Configuration(ConfigParser.SafeConfigParser):
|
||||
def getValidMetaList(self):
|
||||
return self.validEntries + self.getConfigList("extra_valid_entries")
|
||||
|
||||
# used by adapters & writers, non-convention naming style
|
||||
def hasConfig(self, key):
|
||||
for section in self.sectionslist:
|
||||
return self.has_config(self.sectionslist, key)
|
||||
|
||||
def has_config(self, sections, key):
|
||||
for section in sections:
|
||||
try:
|
||||
self.get(section,key)
|
||||
#print("found %s in section [%s]"%(key,section))
|
||||
@@ -114,10 +118,14 @@ class Configuration(ConfigParser.SafeConfigParser):
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# used by adapters & writers, non-convention naming style
|
||||
def getConfig(self, key, default=""):
|
||||
return self.get_config(self.sectionslist,key,default)
|
||||
|
||||
def get_config(self, sections, key, default=""):
|
||||
val = default
|
||||
for section in self.sectionslist:
|
||||
for section in sections:
|
||||
try:
|
||||
val = self.get(section,key)
|
||||
if val and val.lower() == "false":
|
||||
@@ -127,7 +135,7 @@ class Configuration(ConfigParser.SafeConfigParser):
|
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError), e:
|
||||
pass
|
||||
|
||||
for section in self.sectionslist[::-1]:
|
||||
for section in sections[::-1]:
|
||||
# 'martian smiley' [::-1] reverses list by slicing whole list with -1 step.
|
||||
try:
|
||||
val = val + self.get(section,"add_to_"+key)
|
||||
@@ -138,11 +146,15 @@ class Configuration(ConfigParser.SafeConfigParser):
|
||||
return val
|
||||
|
||||
# split and strip each.
|
||||
def getConfigList(self, key):
|
||||
vlist = self.getConfig(key).split(',')
|
||||
vlist = filter( lambda x : x !='', [ v.strip() for v in vlist ])
|
||||
def get_config_list(self, sections, key):
|
||||
vlist = re.split(r'(?<!\\),',self.get_config(sections,key)) # don't split on \,
|
||||
vlist = filter( lambda x : x !='', [ v.strip().replace('\,',',') for v in vlist ])
|
||||
#print "vlist("+key+"):"+str(vlist)
|
||||
return vlist
|
||||
|
||||
# used by adapters & writers, non-convention naming style
|
||||
def getConfigList(self, key):
|
||||
return self.get_config_list(self.sectionslist, key)
|
||||
|
||||
# extended by adapter, writer and story for ease of calling configuration.
|
||||
class Configurable(object):
|
||||
@@ -157,11 +169,19 @@ class Configurable(object):
|
||||
return self.configuration.getValidMetaList()
|
||||
|
||||
def hasConfig(self, key):
|
||||
return self.configuration.hasConfig(key)
|
||||
return self.configuration.hasConfig(key)
|
||||
|
||||
def has_config(self, sections, key):
|
||||
return self.configuration.has_config(sections, key)
|
||||
|
||||
def getConfig(self, key, default=""):
|
||||
return self.configuration.getConfig(key,default)
|
||||
|
||||
def get_config(self, sections, key, default=""):
|
||||
return self.configuration.get_config(sections,key,default)
|
||||
|
||||
def getConfigList(self, key):
|
||||
return self.configuration.getConfigList(key)
|
||||
|
||||
|
||||
def get_config_list(self, sections, key):
|
||||
return self.configuration.get_config_list(sections,key)
|
||||
|
||||
@@ -300,7 +300,7 @@ class Story(Configurable):
|
||||
replacement=replacement.replace('\s',' ')
|
||||
self.replacements.append([metakeys,regexp,replacement,condkey,condregexp])
|
||||
|
||||
def doReplacments(self,value,key):
|
||||
def doReplacements(self,value,key):
|
||||
for (metakeys,regexp,replacement,condkey,condregexp) in self.replacements:
|
||||
if (metakeys == None or key in metakeys) \
|
||||
and isinstance(value,basestring) \
|
||||
@@ -340,7 +340,7 @@ class Story(Configurable):
|
||||
value = value.strftime(self.getConfig(key+"_format","%Y-%m-%d"))
|
||||
|
||||
if doreplacements:
|
||||
value=self.doReplacments(value,key)
|
||||
value=self.doReplacements(value,key)
|
||||
if removeallentities and value != None:
|
||||
return removeAllEntities(value)
|
||||
else:
|
||||
@@ -364,8 +364,8 @@ class Story(Configurable):
|
||||
auth = v
|
||||
# make sure doreplacements & removeallentities are honored.
|
||||
if doreplacements:
|
||||
aurl=self.doReplacments(aurl,'authorUrl')
|
||||
auth=self.doReplacments(auth,'author')
|
||||
aurl=self.doReplacements(aurl,'authorUrl')
|
||||
auth=self.doReplacements(auth,'author')
|
||||
if removeallentities:
|
||||
aurl=removeAllEntities(aurl)
|
||||
auth=removeAllEntities(auth)
|
||||
@@ -438,7 +438,7 @@ class Story(Configurable):
|
||||
if retlist:
|
||||
if doreplacements:
|
||||
retlist = filter( lambda x : x!=None and x!='' ,
|
||||
map(partial(self.doReplacments,key=listname),retlist) )
|
||||
map(partial(self.doReplacements,key=listname),retlist) )
|
||||
if removeallentities:
|
||||
retlist = filter( lambda x : x!=None and x!='' ,
|
||||
map(removeAllEntities,retlist) )
|
||||
|
||||
+1417
-1369
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user