mirror of
https://github.com/wassname/FanFicFare.git
synced 2026-09-16 11:47:12 +08:00
Compare commits
13
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a325ffa3 | ||
|
|
29f900199e | ||
|
|
f56fb0efed | ||
|
|
a703011aef | ||
|
|
a635272bcf | ||
|
|
1079ed565c | ||
|
|
17ab4a3d25 | ||
|
|
2566e434ee | ||
|
|
91f5269453 | ||
|
|
235f00a5e6 | ||
|
|
aad4a26131 | ||
|
|
f20a02a2bc | ||
|
|
1ba1da4d65 |
@@ -1,6 +1,6 @@
|
||||
# ffd-retief-hrd fanfictiondownloader
|
||||
application: fanfictiondownloader
|
||||
version: 4-4-56
|
||||
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, 23)
|
||||
version = (1, 7, 25)
|
||||
minimum_calibre_version = (0, 8, 57)
|
||||
|
||||
#: This field defines the GUI plugin class that contains all the code
|
||||
|
||||
@@ -53,13 +53,14 @@ class RejectUrlEntry:
|
||||
matchpat=re.compile(r"^(?P<url>[^,]+)(,(?P<fullnote>(((?P<title>.+) by (?P<auth>.+?)( - (?P<note>.+))?)|.*)))?$")
|
||||
|
||||
def __init__(self,url_or_line,note=None,title=None,auth=None,
|
||||
addreasontext=None,fromline=False):
|
||||
|
||||
addreasontext=None,fromline=False,book_id=None):
|
||||
|
||||
self.url=url_or_line
|
||||
self.note=note
|
||||
self.title=title
|
||||
self.auth=auth
|
||||
self.valid=False
|
||||
self.book_id=book_id
|
||||
|
||||
if fromline:
|
||||
mc = re.match(self.matchpat,url_or_line)
|
||||
@@ -828,7 +829,9 @@ class RejectListTableWidget(QTableWidget):
|
||||
|
||||
def populate_table_row(self, row, rej):
|
||||
|
||||
self.setItem(row, 0, ReadOnlyTableWidgetItem(rej.url))
|
||||
url_cell = ReadOnlyTableWidgetItem(rej.url)
|
||||
url_cell.setData(Qt.UserRole, QVariant(rej.book_id))
|
||||
self.setItem(row, 0, url_cell)
|
||||
self.setItem(row, 1, ReadOnlyTableWidgetItem(rej.title))
|
||||
self.setItem(row, 2, ReadOnlyTableWidgetItem(rej.auth))
|
||||
|
||||
@@ -950,10 +953,19 @@ class RejectListDialog(SizePersistedDialog):
|
||||
rejectrows = []
|
||||
for row in range(self.rejects_table.rowCount()):
|
||||
url = unicode(self.rejects_table.item(row, 0).text()).strip()
|
||||
book_id = self.rejects_table.item(row, 0).data(Qt.UserRole).toPyObject()
|
||||
title = unicode(self.rejects_table.item(row, 1).text()).strip()
|
||||
auth = unicode(self.rejects_table.item(row, 2).text()).strip()
|
||||
note = unicode(self.rejects_table.cellWidget(row, 3).currentText()).strip()
|
||||
rejectrows.append(RejectUrlEntry(url,note,title,auth,self.get_reason_text()))
|
||||
rejectrows.append(RejectUrlEntry(url,note,title,auth,self.get_reason_text(),book_id=book_id))
|
||||
return rejectrows
|
||||
|
||||
def get_reject_list_ids(self):
|
||||
rejectrows = []
|
||||
for row in range(self.rejects_table.rowCount()):
|
||||
book_id = self.rejects_table.item(row, 0).data(Qt.UserRole).toPyObject()
|
||||
if book_id:
|
||||
rejectrows.append(book_id)
|
||||
return rejectrows
|
||||
|
||||
def get_reason_text(self):
|
||||
|
||||
@@ -408,12 +408,12 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
|
||||
def reject_list_urls_finish(self, book_list):
|
||||
|
||||
# construct reject list of tuples:
|
||||
# (calibre_id, url, "title, authors", old reject note).
|
||||
# construct reject list of objects
|
||||
reject_list = [ RejectUrlEntry(x['url'],
|
||||
x['oldrejnote'],
|
||||
x['title'],
|
||||
', '.join(x['author']))
|
||||
', '.join(x['author']),
|
||||
book_id=x['calibre_id'])
|
||||
for x in book_list if x['good'] ]
|
||||
if reject_list:
|
||||
d = RejectListDialog(self.gui,reject_list,
|
||||
@@ -426,7 +426,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
rejecturllist.add(d.get_reject_list())
|
||||
|
||||
if d.get_deletebooks():
|
||||
self.gui.iactions['Remove Books'].delete_books()
|
||||
self.gui.iactions['Remove Books'].do_library_delete(d.get_reject_list_ids())
|
||||
|
||||
else:
|
||||
message="<p>Rejecting FFDL URLs: None of the books selected have FanFiction URLs.</p><p>Proceed to Remove?</p>"
|
||||
@@ -661,6 +661,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
|
||||
url = book['url']
|
||||
print("url:%s"%url)
|
||||
mi = None
|
||||
|
||||
if not merge: # skip reject list when merging.
|
||||
if rejecturllist.check(url):
|
||||
@@ -845,6 +846,28 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
book['icon'] = 'edit-redo.png'
|
||||
book['status'] = 'Update'
|
||||
|
||||
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:
|
||||
if collision in (OVERWRITE,OVERWRITEALWAYS):
|
||||
updat="overwrit"
|
||||
else:
|
||||
updat="updat"
|
||||
if not question_dialog(self.gui, 'Change Story URL?',
|
||||
'<h3>Change Story URL?</h3>'+
|
||||
'<p><b>%s</b> by <b>%s</b> is already in your library with a different source URL:</p>'%
|
||||
(mi.title,', '.join(mi.author))+
|
||||
'<p>In library: <a href="%(liburl)s">%(liburl)s</a></p><p>New URL: <a href="%(newurl)s">%(newurl)s</a></p>'%
|
||||
{'liburl':liburl,'newurl':book['url']}+
|
||||
"<p>Click '<b>Yes</b>' to %se book with new URL.</p>"%updat+
|
||||
"<p>Click '<b>No</b>' to skip %sing this book.</p>"%updat,
|
||||
show_copy_button=False):
|
||||
book['comment'] = "Update declined by user due to differing story URL(%s)"%liburl
|
||||
book['good']=False
|
||||
book['icon']='rotate-right.png'
|
||||
book['status'] = 'Different URL'
|
||||
return
|
||||
|
||||
if book_id != None and collision != ADDNEW:
|
||||
if collision in (CALIBREONLY):
|
||||
book['comment'] = 'Metadata collected.'
|
||||
@@ -889,7 +912,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
db.copy_format_to(book_id,fileform,tmp,index_is_id=True)
|
||||
print("existing epub tmp:"+tmp.name)
|
||||
book['epub_for_update'] = tmp.name
|
||||
|
||||
|
||||
if book_id != None and prefs['injectseries']:
|
||||
mi = db.get_metadata(book_id,index_is_id=True)
|
||||
if not book['series'] and mi.series != None:
|
||||
@@ -1281,7 +1304,10 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
|
||||
existingepub = db.format(book_id,'EPUB',index_is_id=True, as_file=True)
|
||||
epubmi = get_metadata(existingepub,'EPUB')
|
||||
if epubmi.cover_data[1] is not None:
|
||||
db.set_cover(book_id, epubmi.cover_data[1])
|
||||
try:
|
||||
db.set_cover(book_id, epubmi.cover_data[1])
|
||||
except:
|
||||
print("Failed to set_cover, skipping")
|
||||
|
||||
# set author link if found. All current adapters have authorUrl, except anonymous on AO3.
|
||||
if 'authorUrl' in book['all_metadata']:
|
||||
|
||||
+52
-2
@@ -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.
|
||||
@@ -169,6 +169,8 @@ extratags: FanFiction
|
||||
## *Five* part lines. Effect only when trailing conditional key=>regexp matches
|
||||
## metakey[,metakey]=>pattern=>replacement[&&conditionalkey=>regexp]
|
||||
## Note that if metakey == conditionalkey the conditional is ignored.
|
||||
## You can use \s in the replacement to add explicit spaces. (The config parser
|
||||
## tends to discard trailing spaces.)
|
||||
#replace_metadata:
|
||||
# genre,category=>Sci-Fi=>SF
|
||||
# Puella Magi Madoka Magica.* => Madoka
|
||||
@@ -189,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
|
||||
@@ -273,7 +279,7 @@ output_css:
|
||||
|
||||
[txt]
|
||||
## Add URLs since there aren't links.
|
||||
titlepage_entries: series,seriesUrl,category,genre,language,status,datePublished,dateUpdated,dateCreated,rating,warnings,numChapters,numWords,site,storyUrl, authorUrl, description
|
||||
titlepage_entries: series,seriesUrl,category,genre,language,characters,ships,status,datePublished,dateUpdated,dateCreated,rating,warnings,numChapters,numWords,site,storyUrl, authorUrl, description
|
||||
|
||||
## Width to word wrap text output. 0 indicates no wrapping.
|
||||
wrap_width: 78
|
||||
@@ -1381,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,18 +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.')
|
||||
elif self.story.getMetadata('storyId') == '82':
|
||||
self.story.addToList('ships','Chloe B. & J. Rizzoli')
|
||||
elif idstr == '82':
|
||||
self.story.addToList('characters','Henry (Once Upon a Time)')
|
||||
self.story.addToList('category',u'Once Upon a Time (TV)')
|
||||
else:
|
||||
@@ -217,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>
|
||||
@@ -224,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)
|
||||
|
||||
@@ -280,10 +280,10 @@ class Story(Configurable):
|
||||
for line in replace.splitlines():
|
||||
(metakeys,regexp,replacement,condkey,condregexp)=(None,None,None,None,None)
|
||||
if "&&" in line:
|
||||
(line,conditional) = map( lambda x: x.strip(), line.split("&&") )
|
||||
(condkey,condregexp) = map( lambda x: x.strip(), conditional.split("=>") )
|
||||
(line,conditional) = line.split("&&")
|
||||
(condkey,condregexp) = conditional.split("=>")
|
||||
if "=>" in line:
|
||||
parts = map( lambda x: x.strip(), line.split("=>") )
|
||||
parts = line.split("=>")
|
||||
if len(parts) > 2:
|
||||
metakeys = map( lambda x: x.strip(), parts[0].split(",") )
|
||||
(regexp,replacement)=parts[1:]
|
||||
@@ -294,9 +294,13 @@ class Story(Configurable):
|
||||
regexp = re.compile(regexp)
|
||||
if condregexp:
|
||||
condregexp = re.compile(condregexp)
|
||||
# A way to explicitly include spaces in the
|
||||
# replacement string. The .ini parser eats any
|
||||
# trailing spaces.
|
||||
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) \
|
||||
@@ -336,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:
|
||||
@@ -360,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)
|
||||
@@ -434,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) )
|
||||
|
||||
+4
-11
@@ -57,14 +57,7 @@
|
||||
<h3>Changes:</h3>
|
||||
<p>
|
||||
<ul>
|
||||
<li>New site: netraptor.org</li>
|
||||
<li>New site: asr3.slashzone.org</li>
|
||||
<li>New site: tokra.fandomnet.com</li>
|
||||
<li>Remove defunct site: www.jlaunlimited.com</li>
|
||||
<li>Fix author URLs for several sites with leading 'dir' in URL.</li>
|
||||
<li>Fix for no chapter name for one chapter stories on TtH.</li>
|
||||
<li>Improved error handling for mobi issues.</li>
|
||||
<li>Add 'url' to chapter custom formats and class="skip_on_ffdl_update" for updates.</li>
|
||||
<li>Don't strip lead/trail whitespace from replace_metadata, add feature \s->' ' in replace_metadata replacements.</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
@@ -75,7 +68,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-55.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
|
||||
<a href="http://4-4-56.fanfictiondownloader.appspot.com">Previous Version</a> is also available for you to use if necessary.
|
||||
</p>
|
||||
<div id='error'>
|
||||
{{ error_message }}
|
||||
@@ -599,12 +592,12 @@
|
||||
Use the URL of the story's chapter list, such as
|
||||
<br /><a href="http://www.henneth-annun.net/stories/chapter.cfm?stid=1234">http://www.henneth-annun.net/stories/chapter.cfm?stid=1234</a>
|
||||
</dd>
|
||||
<dt>http://www.psychfic.com</dt>
|
||||
<dt>www.psychfic.com</dt>
|
||||
<dd>
|
||||
Use the URL of the story's chapter list, such as
|
||||
<br /><a href="http://www.psychfic.com/viewstory.php?sid=1234">http://www.psychfic.com/viewstory.php?sid=1234</a>
|
||||
</dd>
|
||||
<dt>http://tokra.fandomnet.com</dt>
|
||||
<dt>tokra.fandomnet.com</dt>
|
||||
<dd>
|
||||
Use the URL of the story's chapter list, such as
|
||||
<br /><a href="http://tokra.fandomnet.com/viewstory.php?sid=1234">http://tokra.fandomnet.com/viewstory.php?sid=1234</a>
|
||||
|
||||
+1417
-1367
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user