Compare commits

...
17 Commits
Author SHA1 Message Date
Jim Miller 6b28482514 Bump versions 2015-11-23 15:30:33 -06:00
Jim Miller b210007f59 Update translations 2015-11-23 15:30:18 -06:00
Jim Miller 08bc200c5c Updates for mediaminer.org. 2015-11-19 13:46:13 -06:00
Jim Miller 43ec9c0232 Remove some debug output. 2015-11-19 13:45:44 -06:00
Jim Miller a0c752c61f Make sort_ships honor doreplacements (and .NOREPL) flag. 2015-11-18 12:51:53 -06:00
Jim Miller ed84f10bfb Remove debug some output 2015-11-14 09:25:00 -06:00
Jim Miller 1d1e43f16f Change str() to unicode() many places. 2015-11-13 14:45:26 -06:00
Jim Miller ba4a29d646 Update translations. 2015-11-13 14:44:51 -06:00
Jim Miller 7ba523f20d Reduce over long error msgs when editing personal.ini(plugin). 2015-11-06 15:48:12 -06:00
Jim Miller 0e42f758ab Add sort_ships_splits and ships_CHARS. 2015-11-05 20:26:08 -06:00
Jim Miller e2995a7614 Add sort_ships_splits and ships_CHARS. 2015-11-05 13:56:07 -06:00
Jim Miller 3abb78a15e Fix to allow 'Update Calibre Metadata Only(Saved Column)' as default. 2015-11-04 10:58:47 -06:00
Jim Miller 3b9b9c1eb7 Fix for UnNew epubs with & or double quote in chap title. 2015-10-31 14:27:25 -05:00
Jim Miller 235c5ae62f Fix for whofic.com with replace_br_with_p 2015-10-31 14:26:51 -05:00
Jim Miller b2238e10e7 Add html5lib to more BS4 calls, convert another place to BS4. 2015-10-22 18:16:30 -05:00
Jim Miller a5bc7bd757 Comment out debug that can cause additional errors depending on content. 2015-10-22 18:15:42 -05:00
Jim Miller 36d848fde9 Add series00-04HTml to defaults.ini for AO3. 2015-10-21 18:43:16 -05:00
75 changed files with 261 additions and 205 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ class FanFicFareBase(InterfaceActionBase):
description = _('UI plugin to download FanFiction stories from various sites.')
supported_platforms = ['windows', 'osx', 'linux']
author = 'Jim Miller'
version = (2, 2, 14)
version = (2, 2, 15)
minimum_calibre_version = (1, 48, 0)
#: This field defines the GUI plugin class that contains all the code
+3
View File
@@ -108,6 +108,7 @@ save_collisions={
OVERWRITE:SAVE_OVERWRITE,
OVERWRITEALWAYS:SAVE_OVERWRITEALWAYS,
CALIBREONLY:SAVE_CALIBREONLY,
CALIBREONLYSAVECOL:SAVE_CALIBREONLYSAVECOL,
SAVE_SKIP:SKIP,
SAVE_ADDNEW:ADDNEW,
SAVE_UPDATE:UPDATE,
@@ -1456,6 +1457,8 @@ class ViewLog(SizePersistedDialog):
## error = (lineno, msg)
for (lineno, error_msg) in errors:
# print('adding label for error:%s: %s'%(lineno, error_msg))
if len(error_msg) > 200:
error_msg=error_msg[:200]+" ..."
label = QLabel('%s: %s'%(lineno, error_msg))
label.setWordWrap(True)
label.setStyleSheet("QLabel { margin-left: 2em; color : blue; } QLabel:hover { color: red; }");
+23 -6
View File
@@ -344,12 +344,21 @@ anthology_title_pattern:${title} Anthology
## any anthology tags.
anthology_tags:Anthology
## Reorder ships so b/a and c/b/a become a/b and a/b/c. Only separates
## on '/', so use replace_metadata to change separator first if
## needed. Something like: ships=>[ ]*(/|&|&)[ ]*=>/ You can use
## ships_LIST to change the / back to something else if you want.
## Reorder ships so b/a and c/b/a become a/b and a/b/c. '/' is no
## longer hard coded and can be changed and added to with
## sort_ships_splits.
sort_ships:false
## Each line indicates first a regex that should be used to split each
## ships entry and then, after => the string to use to merge the parts
## back together. \s == blank space.
## Each part will have replace_metadata with key ships_CHARS applied.
## By default, sort_ships_splits:/=>/
## Here's possible an example for AO3:
#sort_ships_splits:
# [ ]*/[ ]*=>/
# [ ]*&[ ]*=>\s&\s
## join_string_<entry> options -- FanFicFare list entries are comma
## separated by default. You can use this to change that. For example,
## if you want authors separated with ' & ' instead, use
@@ -709,7 +718,7 @@ extratags: FanFiction,Testing,HTML
## AO3 adapter defines a few extra metadata entries.
## If there's ever more than 4 series, add series04,series04Url etc.
extra_valid_entries:fandoms,freeformtags,freefromtags,ao3categories,comments,kudos,hits,bookmarks,collections,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url
extra_valid_entries:fandoms,freeformtags,freefromtags,ao3categories,comments,kudos,hits,bookmarks,collections,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url,series00HTML,series01HTML,series02HTML,series03HTML
fandoms_label:Fandoms
freeformtags_label:Freeform Tags
freefromtags_label:Freeform Tags
@@ -719,6 +728,14 @@ kudos_label:Kudos
hits_label:Hits
collections_label:Collections
bookmarks_label:Bookmarks
series00HTML_label:Series
series01HTML_label:Additional Series
series02HTML_label:Additional Series
series03HTML_label:Additional Series
## Assume entryUrl, apply to "<a class='%slink' href='%s'>%s</a>" to
## make entryHTML.
make_linkhtml_entries:series00,series01,series02,series03
## AO3 doesn't have anything it calls 'genre'. The adapter used to be
## hardcoded to include the site specific metadata freeformtags &
@@ -737,7 +754,7 @@ include_in_category:fandoms
include_in_freefromtags:freeformtags
## adds to titlepage_entries instead of replacing it.
#extra_titlepage_entries: fandoms,freeformtags,ao3categories,comments,kudos,hits,bookmarks,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url
#extra_titlepage_entries: fandoms,freeformtags,ao3categories,comments,kudos,hits,bookmarks,series01HTML,series02HTML,series03HTML
## adds to include_subject_tags instead of replacing it.
#extra_subject_tags:fandoms,freeformtags,ao3categories
+13 -13
View File
@@ -12,8 +12,8 @@ msgid ""
msgstr ""
"Project-Id-Version: calibre-plugins\n"
"POT-Creation-Date: 2015-09-30 10:40+Central Daylight Time\n"
"PO-Revision-Date: 2015-10-07 09:47+0000\n"
"Last-Translator: Valsu <valsu@hylia.de>\n"
"PO-Revision-Date: 2015-11-14 16:55+0000\n"
"Last-Translator: ILB\n"
"Language-Team: German (http://www.transifex.com/calibre/calibre-plugins/language/de/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -181,7 +181,7 @@ msgid ""
"adult--stories that need those will just fail.<br />Only available for "
"Update/Overwrite of existing books in case URL given isn't canonical or "
"matches to existing book by Title/Author."
msgstr ""
msgstr "Bei jedem Download bietet FanFicFare eine Option, um im Hintergrund Metadaten von Webseiten zu sammeln.<br />Während eines Updates gibt dies die Kontrolle schneller an Sie zurück, aber Sie werden nicht nach Nutzernamen/Passwörtern oder ihrem Alter gefragt--Geschichten, die diese Daten benötigen, können nicht geladen werden.<br />Das Aktualisieren/Überschreiben vorhandener Bücher ist nur möglich, wenn die URL dem vorgegebenen Schema folgt oder mit dem Titel/Autor eines vorhandenen Buchs übereinstimmt."
#: config.py:442
msgid "Updating Calibre Options"
@@ -238,7 +238,7 @@ msgid ""
"Check for existings Series Anthology books using each new story's series URL before downloading.\n"
"Offer to skip downloading if a Series Anthology is found.\n"
"Doesn't work when Collect Metadata in Background is selected."
msgstr ""
msgstr "Mit jeder neuen Serien-URL wird auf bereits vorhandene Serien-Sammelbände geprüft.\nFanFicFare bietet an, diese zu überspringen, wenn ein Sammelband gefunden wurde.\nFunktioniert nicht, wenn \"Aktualisiere Calibre Metadaten aus der gespeicherten Metadaten-Spalte\" selektiert ist."
#: config.py:471
msgid "Check for changed Story URL?"
@@ -270,7 +270,7 @@ msgid ""
"Mark added/updated books when finished. Use with option below.\n"
"You can also manually search for 'marked:fff_success'.\n"
"'marked:fff_failed' is also available, or search 'marked:fff' for both."
msgstr ""
msgstr "Markiere hinzugefügte/aktualisierte Bücher, wenn fertig. Verwendbar mit der nachfolgenden Option.\nSie können auch manuell nach 'marked:fff_success' (erfolgreich) suchen.\n'marked:fff_failed' (gescheitert) steht ebenfalls zur Verfügung, oder suche 'marked:fff' für beides."
#: config.py:486
msgid "Show Marked books when finished?"
@@ -281,7 +281,7 @@ msgid ""
"Show Marked added/updated books only when finished.\n"
"You can also manually search for 'marked:fff_success'.\n"
"'marked:fff_failed' is also available, or search 'marked:fff' for both."
msgstr ""
msgstr "Zeige markierte hinzugefügte/aktualisierte Bücher, wenn fertig. \nSie können auch manuell nach 'marked:fff_success' (erfolgreich) suchen.\n'marked:fff_failed' (gescheitert) steht ebenfalls zur Verfügung, oder suche 'marked:fff' für beides."
#: config.py:491
msgid "Smarten Punctuation (EPUB only)"
@@ -599,7 +599,7 @@ msgstr ""
#: config.py:857
msgid "Generate Calibre Cover:"
msgstr ""
msgstr "Erzeuge Calibre Cover:"
#: config.py:884
msgid "Plugin %(gc)s"
@@ -1317,7 +1317,7 @@ msgid ""
"control to you quicker while updating, but you won't be asked for "
"username/passwords or if you are an adult--stories that need those will just"
" fail."
msgstr ""
msgstr "Sammelt Metadaten von Webseiten im Hintergrund.\nWährend eines Updates gibt dies die Kontrolle schneller an Sie zurück, aber Sie werden nicht nach Nutzernamen/Passwörtern oder ihrem Alter gefragt--Geschichten, die diese Daten benötigen, können nicht geladen werden."
#: dialogs.py:913 fff_plugin.py:1434 fff_plugin.py:1633 fff_plugin.py:1663
msgid "Comment"
@@ -1894,7 +1894,7 @@ msgstr "Das existierende EPUB hat %d Kapitel, die Web-Seite nur %d. Benutzen Sie
msgid ""
"FanFicFare doesn't recognize chapters in existing epub, epub is probably "
"from a different source. Use Overwrite to force update."
msgstr ""
msgstr "FanFicFare kann keine Kapitel im bestehenden epub erkennen, epub ist wahrscheinlich aus einer anderen Quelle. Verwenden Sie überschreiben, um die Aktualisierung zu erzwingen."
#: fff_plugin.py:1312 jobs.py:208
msgid "Not Overwriting, web site is not newer."
@@ -2036,25 +2036,25 @@ msgstr "Fehler"
msgid ""
"You configured FanFicFare to automatically update Reading Lists, but you "
"don't have the %s plugin installed anymore?"
msgstr ""
msgstr "Sie konfigurierten FanFicFare Lese-Listen automatisch zu aktualisieren, aber Sie haben dieses % s Plugin nicht mehr installiert?"
#: fff_plugin.py:2118
msgid ""
"You configured FanFicFare to automatically update \"To Read\" Reading Lists,"
" but you don't have any lists set?"
msgstr ""
msgstr "Sie konfigurierten FanFicFare die \"zu lesen\"-Lese-Liste automatisch zu aktualisieren, aber Sie haben noch keine Listen festgelegt?"
#: fff_plugin.py:2128 fff_plugin.py:2146
msgid ""
"You configured FanFicFare to automatically update Reading List '%s', but you"
" don't have a list of that name?"
msgstr ""
msgstr "Sie konfigurierten FanFicFare Lese-Listen automatisch zu aktualisieren, aber Sie haben keine Listen mit diesem Namen?"
#: fff_plugin.py:2134
msgid ""
"You configured FanFicFare to automatically update \"Send to Device\" Reading"
" Lists, but you don't have any lists set?"
msgstr ""
msgstr "Sie konfigurierten FanFicFare die \"ans Gerät senden\"-Lese-Listen automatisch zu aktualisieren, aber Sie haben noch keine Listen festgelegt?"
#: fff_plugin.py:2257
msgid "No story URL found."
@@ -137,7 +137,7 @@ class AdAstraFanficComSiteAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ''
while value and not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
# sometimes poorly formated desc (<p> w/o </p>) leads
# to all labels being included.
@@ -194,7 +194,7 @@ class AshwinderSycophantHexComAdapter(BaseSiteAdapter):
val = val.nextSibling
val = val.nextSibling
while not defaultGetattr(val) == 'table':
svalue += str(val)
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
@@ -265,7 +265,7 @@ class BdsmGeschichtenAdapter(BaseSiteAdapter):
nextLinkMatch = _REGEX_TRAILING_DIGIT.match(nextLink)
if nextLinkMatch is not None:
curChap = nextLinkMatch.group(1)
nextLink = re.sub(_REGEX_TRAILING_DIGIT, str(int(curChap) + 1), nextLink)
nextLink = re.sub(_REGEX_TRAILING_DIGIT, unicode(int(curChap) + 1), nextLink)
else:
break
try:
@@ -289,7 +289,7 @@ class BdsmGeschichtenAdapter(BaseSiteAdapter):
curMax = self.maxChapter + step
lastHit = True
while True:
nextChapterUrl = re.sub(_REGEX_TRAILING_DIGIT, str(curMax), self.url)
nextChapterUrl = re.sub(_REGEX_TRAILING_DIGIT, unicode(curMax), self.url)
if nextChapterUrl == self.url:
logger.debug("Unable to guess next chapter because URL doesn't end in numbers")
break;
@@ -316,8 +316,8 @@ class BdsmGeschichtenAdapter(BaseSiteAdapter):
logger.debug(curMax)
for i in xrange(1, self.maxChapter):
nextChapterUrl = re.sub(_REGEX_TRAILING_DIGIT, str(i), self.url)
nextChapterTitle = re.sub("1", str(i), title1)
nextChapterUrl = re.sub(_REGEX_TRAILING_DIGIT, unicode(i), self.url)
nextChapterTitle = re.sub("1", unicode(i), title1)
self.chapterUrls.append((nextChapterTitle, nextChapterUrl))
def getChapterText(self, url):
@@ -244,7 +244,7 @@ class BloodTiesFansComAdapter(BaseSiteAdapter): # XXX
## Everything until the next strong tag.
svalue = ""
while not isinstance(value,bs.Tag) or value.name != 'strong':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -156,7 +156,7 @@ class ChaosSycophantHexComAdapter(BaseSiteAdapter):
val = value
value = value.previousSibling
while not defaultGetattr(val,'class') == 'label':
svalue += str(val)
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
@@ -167,7 +167,7 @@ class DestinysGatewayComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -217,7 +217,7 @@ class DracoAndGinnyComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
+1 -1
View File
@@ -230,7 +230,7 @@ class DramioneOrgAdapter(BaseSiteAdapter):
## Everything until the next strong tag.
svalue = ""
while not isinstance(value,bs.Tag) or value.name != 'strong':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
@@ -147,8 +147,8 @@ class EfictionEstelielDeAdapter(BaseSiteAdapter):
if 'Summary' in label:
## Everything until the next span class='label'
svalue = ""
while 'Rating' not in str(value):
svalue += str(value)
while 'Rating' not in unicode(value):
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -172,7 +172,7 @@ class ErosnSapphoSycophantHexComAdapter(BaseSiteAdapter):
val = value
value = value.previousSibling
while not defaultGetattr(val,'class') == 'label':
svalue += str(val)
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
@@ -228,7 +228,7 @@ class FanficCastleTVNetAdapter(BaseSiteAdapter): # XXX
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -165,7 +165,7 @@ class FicBookNetAdapter(BaseSiteAdapter):
i=0
for part in pr:
i=i+len(stripHTML(part).split(' '))
self.story.setMetadata('numWords', str(i))
self.story.setMetadata('numWords', unicode(i))
i=0
fandoms = table.findAll('a', href=re.compile(r'/fanfiction/\w+'))
+1 -1
View File
@@ -159,7 +159,7 @@ class FicwadComSiteAdapter(BaseSiteAdapter):
## perhaps not the most efficient way to parse this, using
## regexps for each rather than something more complex, but
## IMO, it's more readable and amenable to change.
metastr = stripHTML(str(metap)).replace('\n',' ').replace('\t',' ').replace(u'\u00a0',' ')
metastr = stripHTML(unicode(metap)).replace('\n',' ').replace('\t',' ').replace(u'\u00a0',' ')
m = re.match(r".*?Rating: (.+?) -.*?",metastr)
if m:
@@ -160,7 +160,7 @@ class FineStoriesComAdapter(BaseSiteAdapter):
skip=0
i=0
while i == 0:
asoup = bs.BeautifulSoup(self._fetchUrl(self.story.getMetadata('authorUrl')+"&skip="+str(skip)))
asoup = bs.BeautifulSoup(self._fetchUrl(self.story.getMetadata('authorUrl')+"&skip="+unicode(skip)))
a = asoup.findAll('td', {'class' : 'lc2'})
for lc2 in a:
@@ -113,7 +113,7 @@ class HarryPotterFanFictionComSiteAdapter(BaseSiteAdapter):
tdstr = tr.findAll('td')[2].string
if tdstr and tdstr.isdigit():
words+=int(tdstr)
self.story.setMetadata('numWords',str(words))
self.story.setMetadata('numWords',unicode(words))
# Find the chapters:
tablelist = soup.find('table',{'class':'text'})
@@ -135,7 +135,7 @@ class HarryPotterFanFictionComSiteAdapter(BaseSiteAdapter):
#for center in soup.findAll('center'):
table = soup.find('table',{'class':'storymaininfo'})
if table:
metastr = stripHTML(str(table)).replace('\n',' ').replace('\t',' ')
metastr = stripHTML(unicode(table)).replace('\n',' ').replace('\t',' ')
# Rating: 12+ Story Reviews: 3
# Chapters: 3
# Characters: Andromeda, Ted, Bellatrix, R. Lestrange, Lucius, Narcissa, OC
+1 -1
View File
@@ -147,7 +147,7 @@ class HLFictionNetAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'classification':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -222,7 +222,7 @@ class IkEternalNetAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -207,7 +207,7 @@ class ImagineEFicComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -195,7 +195,7 @@ class KSArchiveComAdapter(BaseSiteAdapter): # XXX
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
# poor HTML(unclosed <p> for one) can cause run on
# over the next label.
if '<span class="label">' in svalue:
+2 -2
View File
@@ -237,12 +237,12 @@ class LiteroticaSiteAdapter(BaseSiteAdapter):
# find num pages
pgs = int(soup1.find("span", "b-pager-caption-t r-d45").string.split(' ')[0])
logger.debug("pages: "+str(pgs))
logger.debug("pages: "+unicode(pgs))
# get all the pages
for i in xrange(2, pgs+1):
try:
logger.debug("fetching page "+str(i))
logger.debug("fetching page "+unicode(i))
time.sleep(0.5)
data2 = self._fetchUrl(url, {'page': i})
# brute force approach to replace the wrapping <p> tag. If
@@ -156,7 +156,7 @@ class LumosSycophantHexComAdapter(BaseSiteAdapter):
val = value
value = value.previousSibling
while not defaultGetattr(val,'class') == 'label':
svalue += str(val)
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
+1 -1
View File
@@ -218,7 +218,7 @@ class MassEffect2InAdapter(BaseSiteAdapter):
self.story.setMetadata('status', 'In Progress' if storyInProgress else 'Completed')
self.story.setMetadata('datePublished', datePublished)
self.story.setMetadata('dateUpdated', dateUpdated)
self.story.setMetadata('numWords', str(wordCount))
self.story.setMetadata('numWords', unicode(wordCount))
self.story.setMetadata('numChapters', chapterCount)
# Site-specific metadata.
+29 -26
View File
@@ -41,6 +41,7 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
# get storyId from url--url validation guarantees query correct
m = re.match(self.getSiteURLPattern(),url)
urltitle='urltitle'
cattitle='cattitle'
if m:
if m.group('id1'):
self.story.setMetadata('storyId',m.group('id1'))
@@ -50,13 +51,21 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
urltitle=m.group('urltitle2')
elif m.group('id3'):
self.story.setMetadata('storyId',m.group('id3'))
elif m.group('id4'):
self.story.setMetadata('storyId',m.group('id4'))
cattitle=m.group('cattitle4')
urltitle=m.group('urltitle4')
elif m.group('id5'):
self.story.setMetadata('storyId',m.group('id5'))
cattitle=m.group('cattitle5')
urltitle=m.group('urltitle5')
else:
raise InvalidStoryURL(url,
self.getSiteDomain(),
self.getSiteExampleURLs())
# normalized story URL.
self._setURL('http://' + self.getSiteDomain() + '/fanfic/s/'+urltitle+'/'+self.story.getMetadata('storyId'))
self._setURL('http://' + self.getSiteDomain() + '/fanfic/s/'+cattitle+'/'+urltitle+'/'+self.story.getMetadata('storyId'))
else:
raise exceptions.InvalidStoryURL(url,
self.getSiteDomain(),
@@ -72,7 +81,7 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
@classmethod
def getSiteExampleURLs(cls):
return "http://"+cls.getSiteDomain()+"/fanfic/s/story-title/123456 http://"+cls.getSiteDomain()+"/fanfic/c/story-title/chapter-title/123456/987612"
return "http://"+cls.getSiteDomain()+"/fanfic/s/category-name/story-title/123456 http://"+cls.getSiteDomain()+"/fanfic/c/category-name/story-title/123456/987612"
def getSiteURLPattern(self):
## old urls
@@ -82,8 +91,13 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
## http://www.mediaminer.org/fanfic/c/ghosts-from-the-past/chapter-2/72/174
## http://www.mediaminer.org/fanfic/s/robtech-final-missions/61553
## http://www.mediaminer.org/fanfic/c/robtech-final-missions/robotech-final-missions-oneshot/61553/189830
## even newer urls
## http://www.mediaminer.org/fanfic/s/gundam-wing-fan-fiction/the-preventer-operatives/171000
## http://www.mediaminer.org/fanfic/c/gundam-wing-fan-fiction/the-preventer-operatives/171000/608822
return re.escape("http://"+self.getSiteDomain())+r"/fanfic/"+\
r"((s/(?P<urltitle1>[^/]+)/(?P<id1>\d+))|"+\
r"((s/(?P<cattitle4>[^/]+)/(?P<urltitle4>[^/]+)/(?P<id4>\d+))|"+\
r"((c/(?P<cattitle5>[^/]+)/(?P<urltitle5>[^/]+)/(?P<id5>\d+))/\d+)|"+\
r"(s/(?P<urltitle1>[^/]+)/(?P<id1>\d+))|"+\
r"((c/(?P<urltitle2>[^/]+)/[^/]+/(?P<id2>\d+))/\d+)|"+\
r"(view_st\.php/(?P<id3>\d+)))"
@@ -109,40 +123,29 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
# use BeautifulSoup HTML parser to make everything easier to find.
soup = self.make_soup(data)
# [ A - All Readers ], strip '[' ']'
## title:
## <h1 id="post-title">A, A' Fan Fiction &#10095; Mmmmm</h1>
titletext = stripHTML(soup.find("h1",{"id":"post-title"}))
titletext = titletext[titletext.index(u'')+2:]
# print("title:(%s)"%titletext)
self.story.setMetadata('title',titletext)
# [ A - All Readers ], strip '[ ' ' ]'
## Above title because we remove the smtxt font to get title.
smtxt = soup.find("h3",{"id":"post-rating"})
smtxt = soup.find("div",{"id":"post-rating"})
if not smtxt:
logger.error("can't find rating")
raise exceptions.StoryDoesNotExist(self.url)
else:
rating = smtxt.string[1:-1]
rating = smtxt.string[2:-2]
self.story.setMetadata('rating',rating)
# Find authorid and URL from... author url.
a = soup.find('a', href=re.compile(r"/fanfic/src.php/u/\d+"))
a = soup.find('a', href=re.compile(r"/user_info.php/\d+"))
self.story.setMetadata('authorId',a['href'].split('/')[-1])
self.story.setMetadata('authorUrl','http://'+self.host+a['href'])
self.story.setMetadata('author',a.string)
## Title - Good grief. Title varies by chaptered, 1chapter and 'type=one shot'--and even 'one-shot's can have titled chapter.
## But, if colspan=2, there's no chapter title.
## <td class="ffh">Atmosphere: Chapter 1</b> <font class="smtxt">[ P - Pre-Teen ]</font></td>
## <td colspan=2 class="ffh">Hearts of Ice <font class="smtxt">[ P - Pre-Teen ]</font></td>
## <td colspan=2 class="ffh">Suzaku no Princess <font class="smtxt">[ P - Pre-Teen ]</font></td>
## <td class="ffh">The Kraut, The Bartender, and The Drunkard: Chapter 1</b> <font class="smtxt">[ P - Pre-Teen ]</font></td>
## <td class="ffh">Betrayal and Justice: A Cold Heart</b> <font size="-1">( Chapter 1 )</font> <font class="smtxt">[ A - All Readers ]</font></td>
## <td class="ffh">Question and Answer: Question and Answer</b> <font size="-1">( One-Shot )</font> <font class="smtxt">[ A - All Readers ]</font></td>
# title = soup.find('td',{'class':'ffh'})
# for font in title.findAll('font'):
# font.extract() # removes 'font' tags from inside the td.
# if title.has_attr('colspan'):
# titlet = stripHTML(title)
# else:
# ## No colspan, it's part chapter title--even if it's a one-shot.
# titlet = ':'.join(stripHTML(title).split(':')[:-1]) # strip trailing 'Chapter X' or chapter title
self.story.setMetadata('title',stripHTML(soup.find('h1',{'id':'post-title'})))
# save date from first for later.
firstdate=None
@@ -155,7 +158,7 @@ class MediaMinerOrgSiteAdapter(BaseSiteAdapter):
# category
# <a href="/fanfic/src.php/a/567">Ranma 1/2</a>
for a in soup.findAll('a',href=re.compile(r"^/fanfic/src.php/a/")):
for a in soup.findAll('a',href=re.compile(r"^/fanfic/a/")):
self.story.addToList('category',a.string)
# genre
@@ -206,7 +206,7 @@ class MerlinFicDtwinsCoUk(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -190,7 +190,7 @@ class MidnightwhispersCaAdapter(BaseSiteAdapter): # XXX
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -240,7 +240,7 @@ class MuggleNetComAdapter(BaseSiteAdapter): # XXX
# ## Everything until the next span class='label'
# svalue = ""
# while not defaultGetattr(value,'class') == 'label':
# svalue += str(value)
# svalue += unicode(value)
# value = value.nextSibling
# self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -194,7 +194,7 @@ class NfaCommunityComAdapter(BaseSiteAdapter): # XXX
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -196,7 +196,7 @@ class OcclumencySycophantHexComAdapter(BaseSiteAdapter):
val = val.nextSibling
val = val.nextSibling
while not defaultGetattr(val) == 'table':
svalue += str(val)
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
@@ -194,7 +194,7 @@ class OneDirectionFanfictionComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while value and not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
@@ -218,7 +218,7 @@ class PommeDeSangComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -181,7 +181,7 @@ class PonyFictionArchiveNetAdapter(BaseSiteAdapter):
value = section.nextSibling
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
+1 -1
View File
@@ -202,7 +202,7 @@ class PortkeyOrgAdapter(BaseSiteAdapter): # XXX
svalue = ""
value = labelspan.nextSibling
while not defaultGetattr(value,'class') == 'dark-small-bold':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
for genre in svalue.split("/"):
@@ -123,7 +123,7 @@ class PotionsAndSnitchesOrgSiteAdapter(BaseSiteAdapter):
## Everything until the next div class='listbox'
svalue = ""
while not defaultGetattr(value,'class') == 'listbox':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+2 -2
View File
@@ -245,7 +245,7 @@ class PotterFicsComAdapter(BaseSiteAdapter):
chnum = 0
for li in list:
chnum += 1
chTitle = str(chnum) + '. ' + li.a.b.string.strip()
chTitle = unicode(chnum) + '. ' + li.a.b.string.strip()
chURL = makeAbsoluteURL(li.a['href'])
chapters.append((chTitle,chURL))
#Get reviews, add to total
@@ -263,7 +263,7 @@ class PotterFicsComAdapter(BaseSiteAdapter):
mb = list.parent
#get the div before that, will either be the description, or the google ad bar
mb = mb.findPreviousSibling('div')
if 'google_ad_client' in str(mb):
if 'google_ad_client' in unicode(mb):
#couldn't find description, leaving it blank
pass
else:
@@ -217,7 +217,7 @@ class PotterHeadsAnonymousComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -171,7 +171,7 @@ class PretenderCenterComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -166,7 +166,7 @@ class PsychFicComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -179,7 +179,7 @@ class QafFicComAdapter(BaseSiteAdapter):
svalue = ""
while not defaultGetattr(value,'class') == 'classification' and value != None:
if "Featured Stories" not in value:
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -151,7 +151,7 @@ class SamDeanArchiveNuAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'classification':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+2 -2
View File
@@ -251,10 +251,10 @@ class ScarHeadNetAdapter(BaseSiteAdapter):
svalue = ""
while i == 0:
try:
b = str(a)
b = unicode(a)
svalue += b.split('Summary: ')[1]
except:
svalue += str(a)
svalue += unicode(a)
if a.nextSibling != None:
a = a.nextSibling
else:
@@ -175,7 +175,7 @@ class ScarvesAndCoffeeNetAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -176,7 +176,7 @@ class SG1HeliopolisComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -166,7 +166,7 @@ class SinfulDesireOrgAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -158,7 +158,7 @@ class SquidgeOrgPejaAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'classification':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -178,7 +178,7 @@ class StoriesOnlineNetAdapter(BaseSiteAdapter):
page=0
i=0
while i == 0:
asoup = self.make_soup(self._fetchUrl(self.story.getList('authorUrl')[0]+"/"+str(page)))
asoup = self.make_soup(self._fetchUrl(self.story.getList('authorUrl')[0]+"/"+unicode(page)))
a = asoup.findAll('td', {'class' : 'lc2'})
for lc2 in a:
@@ -172,7 +172,7 @@ class TenhawkPresentsComSiteAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -135,7 +135,7 @@ class TheAlphaGateComAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -215,7 +215,7 @@ class TheMasqueNetAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
@@ -161,7 +161,7 @@ class ThePetulantPoetessComAdapter(BaseSiteAdapter):
index = 1
found = 0
while found == 0:
asoup = bs.BeautifulSoup(self._fetchUrl(self.story.getMetadata('authorUrl')+"&page="+str(index)))
asoup = bs.BeautifulSoup(self._fetchUrl(self.story.getMetadata('authorUrl')+"&page="+unicode(index)))
for info in asoup.findAll('td', {'class' : 'highlightcolor1'}):
a = info.find('a', href=re.compile(r'viewstory.php\?sid='+self.story.getMetadata('storyId')+"$"))
@@ -193,7 +193,7 @@ class TolkienFanfictionAdapter(BaseSiteAdapter):
self.story.addToList('characters', character)
logger.debug("Characters: %s" % self.story.getMetadata('characters'))
logger.debug('Title as `str`: ' + str(title))
logger.debug('Title as `str`: ' + unicode(title))
# For publication date we need to search
try:
queryString = urllib.urlencode((
+1 -1
View File
@@ -217,7 +217,7 @@ class TrekiverseOrgAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ''
while value and not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
# sometimes poorly formated desc (<p> w/o </p>) leads
# to all labels being included.
@@ -170,7 +170,7 @@ class TheWritersCoffeeShopComSiteAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
# poor HTML(unclosed <p> for one) can cause run on
# over the next label.
if '<span class="label">' in svalue:
+1 -1
View File
@@ -162,7 +162,7 @@ class TwilightedNetSiteAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
@@ -150,7 +150,7 @@ class WalkingThePlankOrgAdapter(BaseSiteAdapter):
## Everything until the next span class='label'
svalue = ""
while not defaultGetattr(value,'class') == 'label':
svalue += str(value)
svalue += unicode(value)
value = value.nextSibling
self.setDescription(url,svalue)
#self.story.setMetadata('description',stripHTML(svalue))
+1 -1
View File
@@ -124,7 +124,7 @@ class WhoficComSiteAdapter(BaseSiteAdapter):
# link instead to find the appropriate metadata.
a = soup.find('a', href=re.compile(r'reviews.php\?sid='+self.story.getMetadata('storyId')))
metadata = a.findParent('td')
metadatachunks = self.utf8FromSoup(None,metadata).split('<br />')
metadatachunks = self.utf8FromSoup(None,metadata,allow_replace_br_with_p=False).split('<br />')
# process metadata for this story.
self.setDescription(url,metadatachunks[1])
#self.story.setMetadata('description', metadatachunks[1])
@@ -131,8 +131,8 @@ class WolverineAndRogueComAdapter(BaseSiteAdapter):
while value != None:
val = value
value = value.previousSibling
while "Categories" not in str(val):
svalue += str(val)
while "Categories" not in unicode(val):
svalue += unicode(val)
val = val.nextSibling
self.setDescription(url,svalue)
+2 -2
View File
@@ -557,7 +557,7 @@ class BaseSiteAdapter(Configurable):
# This gives us a unicode object, not just a string containing bytes.
# (I gave soup a unicode string, you'd think it could give it back...)
# Now also does a bunch of other common processing for us.
def utf8FromSoup(self,url,soup,fetch=None):
def utf8FromSoup(self,url,soup,fetch=None,allow_replace_br_with_p=True):
if not fetch:
fetch=self._fetchUrlRaw
@@ -616,7 +616,7 @@ class BaseSiteAdapter(Configurable):
# This is primarily for epub updates.
retval = re.sub(r"</?(html|head|body)[^>]*>\r?\n?","",retval)
if self.getConfig("replace_br_with_p"):
if self.getConfig("replace_br_with_p") and allow_replace_br_with_p:
# Apply heuristic processing to replace <br> paragraph
# breaks with <p> tags.
retval = replace_br_with_p(retval)
+4 -4
View File
@@ -338,7 +338,7 @@ class BaseEfictionAdapter(BaseSiteAdapter):
errorDiv = soup.find("div", "errortext")
self.triedLoggingIn = True
else:
raise exceptions.FailedToLogin(self.url, str(errorDiv))
raise exceptions.FailedToLogin(self.url, unicode(errorDiv))
else:
warningLink = errorDiv.find("a")
if warningLink is not None and ( \
@@ -354,9 +354,9 @@ class BaseEfictionAdapter(BaseSiteAdapter):
errorDiv = soup.find("div", "errortext")
self.triedAcceptWarnings = True
else:
raise exception.FailedToDownload(self.url, str(errorDiv))
raise exception.FailedToDownload(self.url, unicode(errorDiv))
else:
raise exception.FailedToDownload(self.url, str(errorDiv))
raise exception.FailedToDownload(self.url, unicode(errorDiv))
# title and author
pagetitleDiv = soup.find("div", {"id": "pagetitle"})
@@ -382,7 +382,7 @@ class BaseEfictionAdapter(BaseSiteAdapter):
if (type(nextEl) is bs.Tag):
valueStr += nextEl.prettify()
else:
valueStr += str(nextEl)
valueStr += unicode(nextEl)
nextEl = nextEl.nextSibling
key = labelSpan.text.strip()
+1 -1
View File
@@ -159,7 +159,7 @@ def main(argv=None, parser=None, passed_defaultsini=None, passed_personalini=Non
options,
passed_defaultsini,
passed_personalini)
print("pagecache:%s"%options.pagecache.keys())
#print("pagecache:%s"%options.pagecache.keys())
except Exception, e:
print "URL(%s) Failed: Exception (%s). Run URL individually for more detail."%(url,e)
else:
+1
View File
@@ -302,6 +302,7 @@ def get_valid_keywords():
'replace_metadata',
'slow_down_sleep_time',
'sort_ships',
'sort_ships_splits',
'strip_chapter_numbers',
'strip_chapter_numeral',
'strip_text_links',
+23 -6
View File
@@ -343,12 +343,21 @@ chapter_title_new_pattern:(new) ${title}
## (new) is just text and can be changed.
chapter_title_addnew_pattern:${index}. (new) ${title}
## Reorder ships so b/a and c/b/a become a/b and a/b/c. Only separates
## on '/', so use replace_metadata to change separator first if
## needed. Something like: ships=>[ ]*(/|&amp;|&)[ ]*=>/ You can use
## ships_LIST to change the / back to something else if you want.
## Reorder ships so b/a and c/b/a become a/b and a/b/c. '/' is no
## longer hard coded and can be changed and added to with
## sort_ships_splits.
sort_ships:false
## Each line indicates first a regex that should be used to split each
## ships entry and then, after => the string to use to merge the parts
## back together. \s == blank space.
## Each part will have replace_metadata with key ships_CHARS applied.
## By default, sort_ships_splits:/=>/
## Here's possible an example for AO3:
#sort_ships_splits:
# [ ]*/[ ]*=>/
# [ ]*&amp;[ ]*=>\s&amp;\s
## join_string_<entry> options -- FanFicFare list entries are comma
## separated by default. You can use this to change that. For example,
## if you want authors separated with ' & ' instead, use
@@ -715,7 +724,7 @@ extratags: FanFiction,Testing,HTML
## AO3 adapter defines a few extra metadata entries.
## If there's ever more than 4 series, add series04,series04Url etc.
extra_valid_entries:fandoms,freeformtags,freefromtags,ao3categories,comments,kudos,hits,bookmarks,collections,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url
extra_valid_entries:fandoms,freeformtags,freefromtags,ao3categories,comments,kudos,hits,bookmarks,collections,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url,series00HTML,series01HTML,series02HTML,series03HTML
fandoms_label:Fandoms
freeformtags_label:Freeform Tags
freefromtags_label:Freeform Tags
@@ -725,6 +734,14 @@ kudos_label:Kudos
hits_label:Hits
collections_label:Collections
bookmarks_label:Bookmarks
series00HTML_label:Series
series01HTML_label:Additional Series
series02HTML_label:Additional Series
series03HTML_label:Additional Series
## Assume entryUrl, apply to "<a class='%slink' href='%s'>%s</a>" to
## make entryHTML.
make_linkhtml_entries:series00,series01,series02,series03
## AO3 doesn't have anything it calls 'genre'. The adapter used to be
## hardcoded to include the site specific metadata freeformtags &
@@ -743,7 +760,7 @@ include_in_category:fandoms
include_in_freefromtags:freeformtags
## adds to titlepage_entries instead of replacing it.
#extra_titlepage_entries: fandoms,freeformtags,ao3categories,comments,kudos,hits,bookmarks,series00,series01,series02,series03,series00Url,series01Url,series02Url,series03Url
#extra_titlepage_entries: fandoms,freeformtags,ao3categories,comments,kudos,hits,bookmarks,series01HTML,series02HTML,series03HTML
## adds to include_subject_tags instead of replacing it.
#extra_subject_tags:fandoms,freeformtags,ao3categories
+22 -13
View File
@@ -57,7 +57,7 @@ def get_update_data(inputio,
if( relpath+item.getAttribute("href") == oldcoverhtmlhref ):
oldcoverhtmltype = item.getAttribute("media-type")
break
soup = bs.BeautifulSoup(oldcoverhtmldata.decode("utf-8"))
soup = bs.BeautifulSoup(oldcoverhtmldata.decode("utf-8"),"html5lib")
src = None
# first img or image tag.
imgs = soup.findAll('img')
@@ -208,7 +208,7 @@ def get_story_url_from_html(inputio,_is_good_url=None):
#print("---- item:%s"%item)
if( item.getAttribute("media-type") == "application/xhtml+xml" ):
filehref=relpath+item.getAttribute("href")
soup = bs.BeautifulSoup(epub.read(filehref).decode("utf-8"))
soup = bs.BeautifulSoup(epub.read(filehref).decode("utf-8"),"html5lib")
for link in soup.findAll('a',href=re.compile(r'^http.*')):
ahref=link['href']
#print("href:(%s)"%ahref)
@@ -265,34 +265,44 @@ def reset_orig_chapters_epub(inputio,outfile):
chapterorigtitle = None
tag = soup.find('meta',{'name':'chapterorigtitle'})
if tag:
chapterorigtitle = tag['content'].replace('&','&amp;').replace('"','&quot;')
chapterorigtitle = tag['content']
# toctitle is separate for add_chapter_numbers:toconly users.
chaptertoctitle = None
tag = soup.find('meta',{'name':'chaptertoctitle'})
if tag:
chaptertoctitle = tag['content'].replace('&','&amp;').replace('"','&quot;')
elif chapterorigtitle:
chaptertoctitle = tag['content']
chaptertoctitle = chapterorigtitle
chaptertitle = None
tag = soup.find('meta',{'name':'chaptertitle'})
if tag:
chaptertitle = tag['content'].replace('&','&amp;').replace('"','&quot;')
chaptertitle = tag['content']
chaptertitle_tag = tag
# logger.debug("chaptertitle:(%s) chapterorigtitle:(%s)"%(chaptertitle, chapterorigtitle))
#logger.debug("chaptertitle:(%s) chapterorigtitle:(%s)"%(chaptertitle, chapterorigtitle))
if chaptertitle and chapterorigtitle and chapterorigtitle != chaptertitle:
origdata = data
# print("\n%s\n%s\n"%(chapterorigtitle,chaptertitle))
data = data.replace(u'<meta name="chaptertitle" content="'+chaptertitle+u'"></meta>',
u'<meta name="chaptertitle" content="'+chapterorigtitle+u'"></meta>')
data = data.replace(u'<title>'+chaptertitle+u'</title>',u'<title>'+chapterorigtitle+u'</title>')
data = data.replace(u'<h3>'+chaptertitle+u'</h3>',u'<h3>'+chapterorigtitle+u'</h3>')
# data = data.replace(u'<meta name="chaptertitle" content="'+chaptertitle+u'"></meta>',
# u'<meta name="chaptertitle" content="'+chapterorigtitle+u'"></meta>')
# data = data.replace(u'<title>'+chaptertitle+u'</title>',u'<title>'+chapterorigtitle+u'</title>')
# data = data.replace(u'<h3>'+chaptertitle+u'</h3>',u'<h3>'+chapterorigtitle+u'</h3>')
chaptertitle_tag['content'] = chapterorigtitle
title_tag = soup.find('title')
if title_tag and title_tag.string == chaptertitle:
title_tag.string.replace_with(chapterorigtitle)
h3_tag = soup.find('h3')
if h3_tag and h3_tag.string == chaptertitle:
h3_tag.string.replace_with(chapterorigtitle)
data = unicode(soup)
entrychanged = ( origdata != data )
changed = changed or entrychanged
if entrychanged:
logger.debug("\nentrychanged:%s\n"%zf)
_replace_tocncx(tocncxdom,zf,chaptertoctitle)
## Also look for and update individual
## book toc.ncx files for anthology in case
@@ -331,7 +341,6 @@ def reset_orig_chapters_epub(inputio,outfile):
return changed
def _replace_tocncx(tocncxdom,zf,chaptertoctitle):
## go after the TOC entry, too.
# <navPoint id="file0005" playOrder="6">
+2 -2
View File
@@ -25,7 +25,7 @@ import urlparse
import logging
logger = logging.getLogger(__name__)
from BeautifulSoup import BeautifulSoup
from bs4 import BeautifulSoup
from gziphttp import GZipProcessor
import adapters
@@ -82,7 +82,7 @@ def get_urls_from_html(data,url=None,configuration=None,normalize=False,restrict
if not configuration:
configuration = Configuration("test1.com","EPUB")
soup = BeautifulSoup(data)
soup = BeautifulSoup(data,"html5lib")
if restrictsearch:
soup = soup.find(*restrictsearch)
#logger.debug("restrict search:%s"%soup)
+1 -1
View File
@@ -69,7 +69,7 @@ class HtmlProcessor:
'''Replace <pre> tags with HTML-ified text.'''
pres = self._soup.findAll('pre')
for pre in pres:
pre.replaceWith(self._FixPreContents(str(pre.contents[0])))
pre.replaceWith(self._FixPreContents(unicode(pre.contents[0])))
def _FixPreContents(self, text):
if self.unfill:
+57 -31
View File
@@ -77,7 +77,7 @@ try:
if removetrans and img.has_transparent_pixels():
canvas = Image()
canvas.create_canvas(int(img.size[0]), int(img.size[1]), str(background))
canvas.create_canvas(int(img.size[0]), int(img.size[1]), unicode(background))
canvas.compose(img)
img = canvas
export = True
@@ -380,29 +380,34 @@ def set_in_ex_clude(setting):
## metakey[,metakey]=>pattern=>replacement[&&metakey=>regexp]
def make_replacements(replace):
retval=[]
for line in replace.splitlines():
# print("replacement line:%s"%line)
(metakeys,regexp,replacement,condkey,condregexp)=(None,None,None,None,None)
if "&&" in line:
(line,conditional) = line.split("&&")
(condkey,condregexp) = conditional.split("=>")
if "=>" in line:
parts = line.split("=>")
if len(parts) > 2:
metakeys = map( lambda x: x.strip(), parts[0].split(",") )
(regexp,replacement)=parts[1:]
else:
(regexp,replacement)=parts
if regexp:
regexp = re_compile(regexp,line)
if condregexp:
condregexp = re_compile(condregexp,line)
# A way to explicitly include spaces in the
# replacement string. The .ini parser eats any
# trailing spaces.
replacement=replacement.replace(SPACE_REPLACE,' ')
retval.append([metakeys,regexp,replacement,condkey,condregexp])
for fullline in replace.splitlines():
line=fullline
try:
(metakeys,regexp,replacement,condkey,condregexp)=(None,None,None,None,None)
if "&&" in line:
(line,conditional) = line.split("&&")
(condkey,condregexp) = conditional.split("=>")
if "=>" in line:
parts = line.split("=>")
if len(parts) > 2:
metakeys = map( lambda x: x.strip(), parts[0].split(",") )
(regexp,replacement)=parts[1:]
else:
(regexp,replacement)=parts
if regexp:
regexp = re_compile(regexp,line)
if condregexp:
condregexp = re_compile(condregexp,line)
# A way to explicitly include spaces in the
# replacement string. The .ini parser eats any
# trailing spaces.
replacement=replacement.replace(SPACE_REPLACE,' ')
retval.append([metakeys,regexp,replacement,condkey,condregexp])
except Exception as e:
logger.error("Problem with Replacement Line:%s"%fullline)
raise exceptions.PersonalIniFailed(e,'replace_metadata unpacking failed',fullline)
# raise
return retval
class Story(Configurable):
@@ -793,6 +798,33 @@ class Story(Configurable):
if retlist is None:
retlist = []
# reorder ships so b/a and c/b/a become a/b and a/b/c. Only on '/',
# use replace_metadata to change separator first if needed.
# ships=>[ ]*(/|&amp;|&)[ ]*=>/
if listname == 'ships' and self.getConfig('sort_ships') and doreplacements and retlist:
# retlist = [ '/'.join(sorted(x.split('/'))) for x in retlist ]
## empty default of /=>/
sort_ships_splits = self.getConfig('sort_ships_splits',"/=>/")
for line in sort_ships_splits.splitlines():
if line:
## logger.debug("sort_ships_splits:%s"%line)
## logger.debug(retlist)
(splitre,splitmerge) = line.split("=>")
splitmerge = splitmerge.replace(SPACE_REPLACE,' ')
newretlist = []
for x in retlist:
curlist = []
for y in re.split(splitre,x):
## for SPLIT_META(\,)
if doreplacements:
y = self.doReplacements(y,'ships_CHARS',return_list=True)
curlist.extend(y)
newretlist.append( splitmerge.join(sorted(curlist)) )
retlist = newretlist
## logger.debug(retlist)
if retlist:
if doreplacements:
newretlist = []
@@ -812,12 +844,6 @@ class Story(Configurable):
)) > 1:
retlist.append(self.getConfig('add_genre_when_multi_category'))
# reorder ships so b/a and c/b/a become a/b and a/b/c. Only on '/',
# use replace_metadata to change separator first if needed.
# ships=>[ ]*(/|&amp;|&)[ ]*=>/
if listname == 'ships' and self.getConfig('sort_ships') and retlist:
retlist = [ '/'.join(sorted(x.split('/'))) for x in retlist ]
if retlist:
if listname in ('author','authorUrl','authorId') or self.getConfig('keep_in_order_'+listname):
# need to retain order for author & authorUrl so the
@@ -1070,7 +1096,7 @@ class Story(Configurable):
return retlist
def __str__(self):
return "Metadata: " +str(self.metadata)
return "Metadata: " +unicode(self.metadata)
def commaGroups(s):
groups = []
+4 -4
View File
@@ -1,4 +1,4 @@
#-*-coding:utf-8-*-
#-*-coding:utf-8-*-
# Code taken from http://python.su/forum/viewtopic.php?pid=66946
import unicodedata
def is_syllable(letter):
@@ -10,11 +10,11 @@ def is_consonant(letter):
return not is_syllable(letter)
def romanize(letter):
try:
str(letter)
unicode(letter)
except UnicodeEncodeError:
pass
else:
return str(letter)
return unicode(letter)
unid = unicodedata.name(letter)
exceptions = {"NUMERO SIGN": "No", "LEFT-POINTING DOUBLE ANGLE QUOTATION MARK": "\"", "RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK": "\"", "DASH": "-"}
for name_contains in exceptions:
@@ -54,4 +54,4 @@ def translit(text):
#bolgh.: Ah, chudna b'lgharska ziem'o, polyushkvay ts'ftyashchi zhita.
#sierb.: Nieuriednie notshnie damie dosadjivalie su Dzhieku K.
if __name__=="__main__":
main()
main()
+2 -2
View File
@@ -578,7 +578,7 @@ div { margin: 0pt; padding: 0pt; }
if title :
navPoint = newTag(tocncxdom,"navPoint",
attrs={'id':id,
'playOrder':str(index)})
'playOrder':unicode(index)})
tocnavMap.appendChild(navPoint)
navLabel = newTag(tocncxdom,"navLabel")
navPoint.appendChild(navLabel)
@@ -653,7 +653,7 @@ div { margin: 0pt; padding: 0pt; }
for index, chap in enumerate(self.story.getChapters()): # (url,title,html)
if chap.html:
logger.debug('Writing chapter text for: %s' % chap.title)
#logger.debug('Writing chapter text for: %s' % chap.title)
vals={'url':removeEntities(chap.url),
'chapter':removeEntities(chap.title),
'origchapter':removeEntities(chap.origtitle),
+1 -1
View File
@@ -23,7 +23,7 @@ setup(
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
version="2.2.14",
version="2.2.15",
description='A tool for downloading fanfiction to eBook formats',
long_description=long_description,
+1 -1
View File
@@ -1,6 +1,6 @@
# ffd-retief-hrd fanficfare
application: fanficfare
version: 2-2-14
version: 2-2-15
runtime: python27
api_version: 1
threadsafe: true
+1 -21
View File
@@ -27,26 +27,6 @@
much easier. </p>
</div>
<h3>Changes:</h3>
<ul>
<li>Remove site thequidditchpitch.org, domain is parked.</li>
<li>Remove site devianthearts.com, server not found for a week.</li>
<li>Change site www.nickandgreg.net to www.nickngreg.nl while still accepting old URLs.</li>
<li>Change site ninelives.dark-solace.org to new domain ninelivesarchive.com.</li>
<li>Change site questionablequesting.com to forum.questionablequesting.com only. Supporting both caused problems.</li>
<li>Fixes for hpfandom.net changes.</li>
<li>Fix for bloodshedverse.com.</li>
<li>Fixes for quotev.com (thanks, cryzed)</li>
<li>Fixes for base_xenforoforum adapters.</li>
<li>Add continue_on_chapter_error feature for base_xenforoforum adapters.</li>
<li>Add user/pass login for NSFW forums on forum.questionablequesting.com.</li>
<li>Add capitalize_forumtags feature to base_xenforoforum and include forumtags by default.</li>
<li>Account for base href in base_xenforoforum so emoticon images work.</li>
<li>Correct cover_exclusion_regexp for base_xenforoforum.</li>
<li>Change default HTML output to HTML5 header.</li>
<li>Update html tag header at beginning of each epub file. Prompted by failure of old header on latest tolino ereader.</li>
<li>Fix replace_hr feature.</li>
</ul>
<p>
Questions? Check out our
<a href="https://github.com/JimmXinu/FanFicFare/wiki/FAQs">FAQs</a>.
@@ -55,7 +35,7 @@
If you have any problems with this application, please
report them in
the <a href="http://groups.google.com/group/fanfic-downloader">FanFicFare Google Group</a>. The
<a href="http://2-2-13.fanficfare.appspot.com">previous version
<a href="http://2-2-14.fanficfare.appspot.com">previous version
</a> is also available for you to use if necessary.
</p>
<div id='error'>
+6 -6
View File
@@ -153,7 +153,7 @@ class EditConfigServer(UserConfigServer):
self.redirect("/?error=configsaved")
except Exception, e:
logging.info("Saved Config Failed:%s"%e)
self.redirect("/?error=custom&errtext=%s"%urlEscape(str(e)))
self.redirect("/?error=custom&errtext=%s"%urlEscape(unicode(e)))
else: # not update, assume display for edit
if uconfig is not None and uconfig.config:
config = uconfig.config
@@ -309,7 +309,7 @@ class RecentFilesServer(webapp2.RequestHandler):
for fic in fics:
if fic.completed and fic.format == 'epub':
fic.escaped_url = urlEscape(self.request.host_url+"/file/"+fic.name+"."+fic.format+"?id="+str(fic.key())+"&fake=file."+fic.format)
fic.escaped_url = urlEscape(self.request.host_url+"/file/"+fic.name+"."+fic.format+"?id="+unicode(fic.key())+"&fake=file."+fic.format)
template_values = dict(fics = fics, nickname = user.nickname())
path = os.path.join(os.path.dirname(__file__), 'recent.html')
@@ -391,7 +391,7 @@ class FanfictionDownloader(UserConfigServer):
try:
configuration = self.getUserConfig(user,url,format)
except Exception, e:
self.redirect("/?error=custom&errtext=%s"%urlEscape("There's an error in your User Configuration: "+str(e)))
self.redirect("/?error=custom&errtext=%s"%urlEscape("There's an error in your User Configuration: "+unicode(e)))
return
adapter = adapters.getAdapter(configuration,url)
@@ -425,7 +425,7 @@ class FanfictionDownloader(UserConfigServer):
taskqueue.add(url='/fdowntask',
queue_name="download",
params={'id':str(download.key()),
params={'id':unicode(download.key()),
'format':format,
'url':download.url,
'login':login,
@@ -433,7 +433,7 @@ class FanfictionDownloader(UserConfigServer):
'user':user.email(),
'is_adult':is_adult})
logging.info("enqueued download key: " + str(download.key()))
logging.info("enqueued download key: " + unicode(download.key()))
except (exceptions.FailedToLogin,exceptions.AdultCheckRequired), e:
download.failure = unicode(e)
@@ -467,7 +467,7 @@ class FanfictionDownloader(UserConfigServer):
download.failure = unicode(e)
download.put()
self.redirect('/status?id='+str(download.key()))
self.redirect('/status?id='+unicode(download.key()))
return