First version with Anthology feature in Plugin.

This commit is contained in:
Jim Miller 2013-02-15 17:50:12 -06:00
parent cd8f5f2769
commit 2db927665a
7 changed files with 660 additions and 258 deletions

View file

@ -911,7 +911,7 @@ titleLabels = {
'ships':'Relationships',
'datePublished':'Published',
'dateUpdated':'Updated',
'dateCreated':'Packaged',
'dateCreated':'Created',
'rating':'Rating',
'warnings':'Warnings',
'numChapters':'Chapters',
@ -922,7 +922,7 @@ titleLabels = {
'extratags':'Extra Tags',
'title':'Title',
'storyUrl':'Story URL',
'description':'Summary',
'description':'Description',
'author':'Author',
'authorUrl':'Author URL',
'formatname':'File Format',

View file

@ -30,13 +30,13 @@ from calibre_plugins.fanfictiondownloader_plugin.common_utils \
import (ReadOnlyTableWidgetItem, ReadOnlyTextIconWidgetItem, SizePersistedDialog,
ImageTitleLayout, get_icon)
SKIP='Skip'
ADDNEW='Add New Book'
UPDATE='Update EPUB if New Chapters'
UPDATEALWAYS='Update EPUB Always'
OVERWRITE='Overwrite if Newer'
OVERWRITEALWAYS='Overwrite Always'
CALIBREONLY='Update Calibre Metadata Only'
SKIP=u'Skip'
ADDNEW=u'Add New Book'
UPDATE=u'Update EPUB if New Chapters'
UPDATEALWAYS=u'Update EPUB Always'
OVERWRITE=u'Overwrite if Newer'
OVERWRITEALWAYS=u'Overwrite Always'
CALIBREONLY=u'Update Calibre Metadata Only'
collision_order=[SKIP,
ADDNEW,
UPDATE,
@ -44,6 +44,10 @@ collision_order=[SKIP,
OVERWRITE,
OVERWRITEALWAYS,
CALIBREONLY,]
anthology_collision_order=[UPDATE,
UPDATEALWAYS,
OVERWRITEALWAYS]
# This is a more than slightly kludgey way to get
# EditWithComplete to *not* alpha-order the reasons, but leave
@ -84,10 +88,23 @@ class DroppableQTextEdit(QTextEdit):
class AddNewDialog(SizePersistedDialog):
def __init__(self, gui, prefs, icon, url_list_text):
def __init__(self, gui, prefs, icon, url_list_text, merge=False, newmerge=False):
SizePersistedDialog.__init__(self, gui, 'FanFictionDownLoader plugin:add new dialog')
self.gui = gui
self.merge = merge
self.newmerge = newmerge
if merge:
labeltext = 'Story URL(s) for anthology, one per line:'
tooltiptext = 'URLs for stories to include in the anthology, one per line.\nWill take URLs from clipboard, but only valid URLs.'
collisiontext = 'If Story Already Exists in Anthology?'
collisiontooltip = "What to do if there's already an existing story with the same URL in the anthology."
else:
labeltext = 'Story URL(s), one per line:'
tooltiptext = 'URLs for stories, one per line.\nWill take URLs from clipboard, but only valid URLs.\nAdd [1,5] after the URL to limit the download to chapters 1-5.'
collisiontext = 'If Story Already Exists?'
collisiontooltip = "What to do if there's already an existing story with the same URL or title and author."
if prefs['adddialogstaysontop']:
QDialog.setWindowFlags ( self, Qt.Dialog|Qt.WindowStaysOnTopHint )
@ -98,55 +115,58 @@ class AddNewDialog(SizePersistedDialog):
self.setWindowTitle('FanFictionDownLoader')
self.setWindowIcon(icon)
self.l.addWidget(QLabel('Story URL(s), one per line:'))
self.l.addWidget(QLabel(labeltext))
self.url = DroppableQTextEdit(self)
self.url.setToolTip('URLs for stories, one per line.\nWill take URLs from clipboard, but only valid URLs.\nAdd [1,5] after the URL to limit the download to chapters 1-5.')
self.url.setToolTip(tooltiptext)
self.url.setLineWrapMode(QTextEdit.NoWrap)
self.url.setText(url_list_text)
self.l.addWidget(self.url)
horz = QHBoxLayout()
label = QLabel('Output &Format:')
horz.addWidget(label)
self.fileform = QComboBox(self)
self.fileform.addItem('epub')
self.fileform.addItem('mobi')
self.fileform.addItem('html')
self.fileform.addItem('txt')
self.fileform.setCurrentIndex(self.fileform.findText(prefs['fileform']))
self.fileform.setToolTip('Choose output format to create. May set default from plugin configuration.')
self.fileform.activated.connect(self.set_collisions)
label.setBuddy(self.fileform)
horz.addWidget(self.fileform)
self.l.addLayout(horz)
horz = QHBoxLayout()
label = QLabel('If Story Already Exists?')
horz.addWidget(label)
self.collision = QComboBox(self)
self.collision.setToolTip("What to do if there's already an existing story with the same URL or title and author.")
# add collision options
self.set_collisions()
i = self.collision.findText(prefs['collision'])
if i > -1:
self.collision.setCurrentIndex(i)
label.setBuddy(self.collision)
horz.addWidget(self.collision)
self.l.addLayout(horz)
if not merge:
horz = QHBoxLayout()
label = QLabel('Output &Format:')
horz.addWidget(label)
self.fileform = QComboBox(self)
self.fileform.addItem('epub')
self.fileform.addItem('mobi')
self.fileform.addItem('html')
self.fileform.addItem('txt')
self.fileform.setCurrentIndex(self.fileform.findText(prefs['fileform']))
self.fileform.setToolTip('Choose output format to create. May set default from plugin configuration.')
self.fileform.activated.connect(self.set_collisions)
label.setBuddy(self.fileform)
horz.addWidget(self.fileform)
self.l.addLayout(horz)
horz = QHBoxLayout()
self.updatemeta = QCheckBox('Update Calibre &Metadata?',self)
self.updatemeta.setToolTip("Update metadata for existing stories in Calibre from web site?\n(Columns set to 'New Only' in the column tabs will only be set for new books.)")
self.updatemeta.setChecked(prefs['updatemeta'])
horz.addWidget(self.updatemeta)
self.updateepubcover = QCheckBox('Update EPUB Cover?',self)
self.updateepubcover.setToolTip('Update book cover image from site or defaults (if found) <i>inside</i> the EPUB when EPUB is updated.')
self.updateepubcover.setChecked(prefs['updateepubcover'])
horz.addWidget(self.updateepubcover)
self.l.addLayout(horz)
if not newmerge:
horz = QHBoxLayout()
label = QLabel(collisiontext)
horz.addWidget(label)
self.collision = QComboBox(self)
self.collision.setToolTip(collisiontooltip)
# add collision options
self.set_collisions()
i = self.collision.findText(prefs['collision'])
if i > -1:
self.collision.setCurrentIndex(i)
label.setBuddy(self.collision)
horz.addWidget(self.collision)
self.l.addLayout(horz)
horz = QHBoxLayout()
self.updatemeta = QCheckBox('Update Calibre &Metadata?',self)
self.updatemeta.setToolTip("Update metadata for existing stories in Calibre from web site?\n(Columns set to 'New Only' in the column tabs will only be set for new books.)")
self.updatemeta.setChecked(prefs['updatemeta'])
horz.addWidget(self.updatemeta)
if not merge: # hide if anthology merge.
self.updateepubcover = QCheckBox('Update EPUB Cover?',self)
self.updateepubcover.setToolTip('Update book cover image from site or defaults (if found) <i>inside</i> the EPUB when EPUB is updated.')
self.updateepubcover.setChecked(prefs['updateepubcover'])
horz.addWidget(self.updateepubcover)
self.l.addLayout(horz)
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
button_box.accepted.connect(self.accept)
@ -163,20 +183,39 @@ class AddNewDialog(SizePersistedDialog):
def set_collisions(self):
prev=self.collision.currentText()
self.collision.clear()
for o in collision_order:
if self.fileform.currentText() == 'epub' or o not in [UPDATE,UPDATEALWAYS]:
if self.merge:
order = anthology_collision_order
else:
order = collision_order
for o in order:
if self.merge or self.fileform.currentText() == 'epub' or o not in [UPDATE,UPDATEALWAYS]:
self.collision.addItem(o)
i = self.collision.findText(prev)
if i > -1:
self.collision.setCurrentIndex(i)
def get_ffdl_options(self):
return {
'fileform': unicode(self.fileform.currentText()),
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
'updateepubcover': self.updateepubcover.isChecked(),
}
if self.merge:
if self.newmerge:
updatemeta=True
collision=ADDNEW
else:
updatemeta=self.updatemeta.isChecked()
collision=unicode(self.collision.currentText())
return {
'fileform': 'epub',
'collision': collision,
'updatemeta': updatemeta,
'updateepubcover': True,
}
else:
return {
'fileform': unicode(self.fileform.currentText()),
'collision': unicode(self.collision.currentText()),
'updatemeta': self.updatemeta.isChecked(),
'updateepubcover': self.updateepubcover.isChecked(),
}
def get_urlstext(self):
return unicode(self.url.toPlainText())
@ -193,10 +232,11 @@ class CollectURLDialog(SizePersistedDialog):
'''
Collect single url for get urls.
'''
def __init__(self, gui, title, url_text):
def __init__(self, gui, title, url_text, epubmerge_plugin=None):
SizePersistedDialog.__init__(self, gui, 'FanFictionDownLoader plugin:get story urls')
self.gui = gui
self.status=False
self.anthology=False
self.setMinimumWidth(300)
@ -204,28 +244,40 @@ class CollectURLDialog(SizePersistedDialog):
self.setLayout(self.l)
self.setWindowTitle(title)
self.l.addWidget(QLabel(title),0,0,1,2)
self.l.addWidget(QLabel(title),0,0,1,3)
self.l.addWidget(QLabel("URL:"),1,0)
self.url = QLineEdit(self)
self.url.setText(url_text)
self.l.addWidget(self.url,1,1)
self.l.addWidget(self.url,1,1,1,2)
self.ok_button = QPushButton('OK', self)
self.ok_button.clicked.connect(self.ok)
self.l.addWidget(self.ok_button,2,0)
self.indiv_button = QPushButton('For Individual Books', self)
self.indiv_button.setToolTip('Get URLs and go to dialog for individual story downloads.')
self.indiv_button.clicked.connect(self.indiv)
self.l.addWidget(self.indiv_button,2,0)
self.merge_button = QPushButton('For Anthology Book', self)
self.merge_button.setToolTip('Get URLs and go to dialog for Anthology download.\nRequires EpubMerge 1.3.0+ plugin.')
self.merge_button.clicked.connect(self.merge)
self.l.addWidget(self.merge_button,2,1)
self.merge_button.setEnabled(epubmerge_plugin!=None)
self.cancel_button = QPushButton('Cancel', self)
self.cancel_button.clicked.connect(self.cancel)
self.l.addWidget(self.cancel_button,2,1)
self.l.addWidget(self.cancel_button,2,2)
# restore saved size.
self.resize_dialog()
def ok(self):
def indiv(self):
self.status=True
self.accept()
def merge(self):
self.status=True
self.anthology=True
self.accept()
def cancel(self):
self.status=False
self.reject()

View file

@ -161,6 +161,22 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
self.update_action = self.create_menu_item_ex(self.menu, '&Update Existing FanFiction Book(s)', image='plusplus.png',
triggered=self.update_dialog)
if self.get_epubmerge_plugin():
self.menu.addSeparator()
self.get_list_url_action = self.create_menu_item_ex(self.menu, 'Get Story URLs to Download from Web Page', image='view.png',
unique_name='Get Story URLs from Web Page',
triggered=self.get_urls_from_page_menu)
self.makeanth_action = self.create_menu_item_ex(self.menu, '&Make Anthology Manually from URL(s)', image='plusplus.png',
unique_name='Make FanFiction Anthology Manually from URL(s)',
shortcut_name='Make FanFiction Anthology Manually from URL(s)',
triggered=partial(self.add_dialog,merge=True) )
self.updateanth_action = self.create_menu_item_ex(self.menu, '&Update Anthology', image='plusplus.png',
unique_name='Update FanFiction Anthology',
shortcut_name='Update FanFiction Anthology',
triggered=self.update_anthology)
if 'Reading List' in self.gui.iactions and (prefs['addtolists'] or prefs['addtoreadlists']) :
self.menu.addSeparator()
addmenutxt, rmmenutxt = None, None
@ -188,8 +204,9 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
self.get_list_action = self.create_menu_item_ex(self.menu, 'Get URLs from Selected Books', image='bookmarks.png',
triggered=self.list_story_urls)
self.get_list_url_action = self.create_menu_item_ex(self.menu, 'Get Story URLs from Web Page', image='view.png',
triggered=self.get_urls_from_page)
if not self.get_epubmerge_plugin():
self.get_list_url_action = self.create_menu_item_ex(self.menu, 'Get Story URLs from Web Page', image='view.png',
triggered=self.get_urls_from_page_menu)
self.reject_list_action = self.create_menu_item_ex(self.menu, 'Reject Selected Books', image='rotate-right.png',
triggered=self.reject_list_urls)
@ -254,6 +271,10 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
else:
self.add_dialog()
def get_epubmerge_plugin(self):
if 'EpubMerge' in self.gui.iactions and self.gui.iactions['EpubMerge'].interface_action_base_plugin.version >= (1,3,0):
return self.gui.iactions['EpubMerge']
def update_lists(self,add=True):
if prefs['addtolists'] or prefs['addtoreadlists']:
if not self.is_library_view():
@ -266,7 +287,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
self.update_reading_lists(self.gui.library_view.get_selected_ids(),add)
def get_urls_from_page(self):
def get_urls_from_page_menu(self):
if prefs['urlsfromclip']:
try:
@ -274,27 +295,30 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
except:
urltxt = ""
d = CollectURLDialog(self.gui,"Get Story URLs from Web Page",urltxt)
d = CollectURLDialog(self.gui,"Get Story URLs from Web Page",urltxt,self.get_epubmerge_plugin())
d.exec_()
if not d.status:
return
url = u"%s"%d.url.text()
print("get_urls_from_page URL:%s"%url)
if 'archiveofourown.org' in url:
configuration = get_ffdl_config(url)
else:
configuration = None
url_list = get_urls_from_page(url,configuration)
url_list = self.get_urls_from_page(url)
if url_list:
self.add_dialog("\n".join(url_list))
self.add_dialog("\n".join(url_list),merge=d.anthology,anthology_url=url)
else:
info_dialog(self.gui, _('List of Story URLs'),
_('No Valid Story URLs found on given page.'),
show=True,
show_copy_button=False)
def get_urls_from_page(self,url):
print("get_urls_from_page URL:%s"%url)
if 'archiveofourown.org' in url:
configuration = get_ffdl_config(url)
else:
configuration = None
return get_urls_from_page(url,configuration)
def list_story_urls(self):
'''Get list of URLs from existing books.'''
if self.gui.current_view().selectionModel().selectedRows() == 0 :
@ -357,7 +381,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
if len(book_list) == 0 :
self.gui.status_bar.show_message(_('No Selected Books have URLs to Reject'), 3000)
return
# Progbar because fetching urls from device epubs can be slow.
LoopProgressDialog(self.gui,
book_list,
partial(self.reject_list_urls_loop, db=self.gui.current_db),
@ -367,7 +392,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
status_prefix="URL retrieved")
def reject_list_urls_loop(self,book,db=None):
self.get_list_story_urls_loop(book,db) # common with.
self.get_list_story_urls_loop(book,db) # common with get_list_story_urls_loop
if book['calibre_id']:
# want title/author, too, for rejects.
self.populate_book_from_calibre_id(book,db)
@ -414,7 +439,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
if confirm(message,'fanfictiondownloader_reject_non_fanfiction', self.gui):
self.gui.iactions['Remove Books'].delete_books()
def add_dialog(self,url_list_text=None):
def add_dialog(self,url_list_text=None,merge=False,anthology_url=None):
#print("add_dialog()")
@ -430,6 +455,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
prefs,
self.qaction.icon(),
url_list_text,
merge=merge,
newmerge=merge # if here, it's a new anthology.
)
d.exec_()
if d.result() != d.Accepted:
@ -442,10 +469,118 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
options = d.get_ffdl_options()
options['version'] = self.version
options['anthology_url']=anthology_url
print(self.version)
self.prep_downloads( options, add_books )
self.prep_downloads( options, add_books, merge=merge )
def update_anthology(self):
if not self.get_epubmerge_plugin():
self.gui.status_bar.show_message(_('Cannot Make Anthologys without EpubMerge 1.3.0+'), 3000)
return
if not self.is_library_view():
self.gui.status_bar.show_message(_('Cannot Update Books from Device View'), 3000)
return
if len(self.gui.library_view.get_selected_ids()) != 1:
self.gui.status_bar.show_message(_('Can only update 1 anthology at a time'), 3000)
return
#print("update_existing()")
db = self.gui.current_db
book_id = self.gui.library_view.get_selected_ids()[0]
mergebook = self.make_book_id_only(book_id)
self.populate_book_from_calibre_id(mergebook, db)
if not db.has_format(book_id,'EPUB',index_is_id=True):
self.gui.status_bar.show_message(_('Can only Update Epub Anthologies'), 3000)
return
tdir = PersistentTemporaryDirectory(prefix='ffdl_anthology_')
print("tdir:\n%s"%tdir)
bookepubio = StringIO(db.format(book_id,'EPUB',index_is_id=True))
filenames = self.get_epubmerge_plugin().do_unmerge(bookepubio,tdir)
urlmapfile = {}
url_list = []
for f in filenames:
url = get_dcsource(f)
if url:
urlmapfile[url]=f
url_list.append(url)
if not filenames or len(filenames) != len (url_list):
info_dialog(self.gui, _("Cannot Update Anthology"),
_("<p>Cannot Update Anthology</p><p>Book isn't an FFDL Anthology or contains book(s) without valid FFDL URLs."),
show=True,
show_copy_button=False)
remove_dir(tdir)
return
# get list from identifiers:url/uri if present, but only if
# it's *not* a valid story URL.
mergeurl = self.get_story_url(db,book_id)
if mergeurl and not self.is_good_downloader_url(mergeurl):
url_list = self.get_urls_from_page(mergeurl)
url_list_text = "\n".join(url_list)
#print("urlmapfile:%s"%urlmapfile)
# self.gui is the main calibre GUI. It acts as the gateway to access
# all the elements of the calibre user interface, it should also be the
# parent of the dialog
# AddNewDialog just collects URLs, format and presents buttons.
d = AddNewDialog(self.gui,
prefs,
self.qaction.icon(),
url_list_text,
merge=True,
newmerge=False
)
d.exec_()
if d.result() != d.Accepted:
return
url_list = split_text_to_urls(d.get_urlstext())
update_books = self.convert_urls_to_books(url_list)
for j, book in enumerate(update_books):
url = book['url']
book['mergeorder'] = j
if url in urlmapfile:
#print("found epub for %s"%url)
book['epub_for_update']=urlmapfile[url]
del urlmapfile[url]
#else:
#print("didn't found epub for %s"%url)
if urlmapfile:
text = '''
<p>There are %d stories in the current anthology that are <b>not</b> going kept if you go ahead.</p>
<p>Story URLs that will be removed:</p>
<ul>
<li>%s</li>
</ul>
<p>Update anyway?</p>
'''%(len(urlmapfile),"</li><li>".join(urlmapfile.keys()))
if not question_dialog(self.gui, 'Stories Removed',
text, show_copy_button=False):
print("Canceling anthology update due to removed stories.")
return
options = d.get_ffdl_options()
options['version'] = self.version
options['tdir'] = tdir
#options['collision'] = UPDATEALWAYS
print(self.version)
options['mergebook'] = mergebook
self.prep_downloads( options, update_books, merge=True )
def update_dialog(self):
if not self.is_library_view():
self.gui.status_bar.show_message(_('Cannot Update Books from Device View'), 3000)
@ -508,21 +643,22 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
# No need to do anything with perfs here, but we could.
prefs
def prep_downloads(self, options, books):
def prep_downloads(self, options, books, merge=False):
'''Fetch metadata for stories from servers, launch BG job when done.'''
#print("prep_downloads:%s"%books)
# create and pass temp dir.
tdir = PersistentTemporaryDirectory(prefix='fanfictiondownloader_')
options['tdir']=tdir
if 'tdir' not in options: # if merging an anthology, there's alread a tdir.
# create and pass temp dir.
tdir = PersistentTemporaryDirectory(prefix='fanfictiondownloader_')
options['tdir']=tdir
if 0 < len(filter(lambda x : x['good'], books)):
self.gui.status_bar.show_message(_('Started fetching metadata for %s stories.'%len(books)), 3000)
LoopProgressDialog(self.gui,
books,
partial(self.prep_download_loop, options = options),
partial(self.start_download_job, options = options))
partial(self.prep_download_loop, options = options, merge=merge),
partial(self.start_download_job, options = options, merge=merge))
else:
self.gui.status_bar.show_message(_('No valid story URLs entered.'), 3000)
# LoopProgressDialog calls prep_download_loop for each 'good' story,
@ -534,7 +670,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
options={'fileform':'epub',
'collision':ADDNEW,
'updatemeta':True,
'updateepubcover':True}):
'updateepubcover':True},
merge=False):
'''
Update passed in book dict with metadata from website and
necessary data. To be called from LoopProgressDialog
@ -544,26 +681,27 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
url = book['url']
print("url:%s"%url)
rejnote = rejecturllist.check(url)
if rejnote:
if question_dialog(self.gui, 'Reject URL?',
'<p>Reject URL?</p>'+
'<p>%s is on the Reject URL list:<br />"%s"</p>'%(url,rejnote)+
"<p>Click 'No' to download anyway.</p>",
show_copy_button=False):
book['comment'] = "Story on Reject URLs list (%s)."%rejnote
book['good']=False
book['icon']='rotate-right.png'
book['status'] = 'Rejected'
return
else:
if question_dialog(self.gui, 'Remove Reject URL?',
"<p>Remove URL from Reject List?</p>"+
if not merge: # skip reject list when merging.
rejnote = rejecturllist.check(url)
if rejnote:
if question_dialog(self.gui, 'Reject URL?',
'<p>Reject URL?</p>'+
'<p>%s is on the Reject URL list:<br />"%s"</p>'%(url,rejnote)+
"<p>Click 'Yes' to remove it from the list and download,<br /> 'No' to download, but leave it on the Reject list.</p>",
"<p>Click 'No' to download anyway.</p>",
show_copy_button=False):
rejecturllist.remove(url)
book['comment'] = "Story on Reject URLs list (%s)."%rejnote
book['good']=False
book['icon']='rotate-right.png'
book['status'] = 'Rejected'
return
else:
if question_dialog(self.gui, 'Remove Reject URL?',
"<p>Remove URL from Reject List?</p>"+
'<p>%s is on the Reject URL list:<br />"%s"</p>'%(url,rejnote)+
"<p>Click 'Yes' to remove it from the list and download,<br /> 'No' to download, but leave it on the Reject list.</p>",
show_copy_button=False):
rejecturllist.remove(url)
# The current database shown in the GUI
# db is an instance of the class LibraryDatabase2 from database.py
# This class has many, many methods that allow you to do a lot of
@ -635,120 +773,120 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
book['status'] = 'Add'
if story.getMetadataRaw('datePublished'):
book['pubdate'] = story.getMetadataRaw('datePublished').replace(tzinfo=local_tz)
book['timestamp'] = None # filled below if not skipped.
if collision in (CALIBREONLY):
book['icon'] = 'metadata.png'
book['status'] = 'Meta'
book_id = None
if book['calibre_id'] != None:
# updating an existing book. Update mode applies.
print("update existing id:%s"%book['calibre_id'])
book_id = book['calibre_id']
# No handling needed: OVERWRITEALWAYS,CALIBREONLY
# only care about collisions when not ADDNEW
elif collision != ADDNEW:
# 'new' book from URL. collision handling applies.
print("from URL(%s)"%url)
# try to find by identifier url or uri first.
searchstr = 'identifiers:"~ur(i|l):=%s"'%url.replace(":","|")
identicalbooks = db.search_getting_ids(searchstr, None)
if len(identicalbooks) < 1:
# find dups
authlist = story.getList("author", removeallentities=True)
if len(authlist) > 100 and calibre_version < (0, 8, 61):
## should be fixed from 0.8.61 on. In the
## meantime, if it matches the title *and* first
## 100 authors, I'm prepared to assume it's a
## match.
print("reduce author list to 100 only when calibre < 0.8.61")
authlist = authlist[:100]
mi = MetaInformation(story.getMetadata("title", removeallentities=True),
authlist)
identicalbooks = db.find_identical_books(mi)
if len(identicalbooks) > 0:
print("existing found by title/author(s)")
else:
print("existing found by identifier URL")
if collision == SKIP and identicalbooks:
raise NotGoingToDownload("Skipping duplicate story.","list_remove.png")
if len(identicalbooks) > 1:
raise NotGoingToDownload("More than one identical book by Identifer URL or title/author(s)--can't tell which book to update/overwrite.","minusminus.png")
## changed: add new book when CALIBREONLY if none found.
if collision == CALIBREONLY and not identicalbooks:
collision = ADDNEW
options['collision'] = ADDNEW
if len(identicalbooks)>0:
book_id = identicalbooks.pop()
book['calibre_id'] = book_id
book['icon'] = 'edit-redo.png'
book['status'] = 'Update'
if book_id != None and collision != ADDNEW:
if collision in (CALIBREONLY):
book['comment'] = 'Metadata collected.'
# don't need temp file created below.
return
## newer/chaptercount checks are the same for both:
# Update epub, but only if more chapters.
if collision in (UPDATE,UPDATEALWAYS): # collision == UPDATE
# 'book' can exist without epub. If there's no existing epub,
# let it go and it will download it.
if db.has_format(book_id,fileform,index_is_id=True):
(epuburl,chaptercount) = \
get_dcsource_chaptercount(StringIO(db.format(book_id,'EPUB',
index_is_id=True)))
urlchaptercount = int(story.getMetadata('numChapters'))
if chaptercount == urlchaptercount:
if collision == UPDATE:
raise NotGoingToDownload("Already contains %d chapters."%chaptercount,'edit-undo.png')
else:
# UPDATEALWAYS
skip_date_update = True
elif chaptercount > urlchaptercount:
raise NotGoingToDownload("Existing epub contains %d chapters, web site only has %d. Use Overwrite to force update." % (chaptercount,urlchaptercount),'dialog_error.png')
if collision == OVERWRITE and \
db.has_format(book_id,formmapping[fileform],index_is_id=True):
# check make sure incoming is newer.
lastupdated=story.getMetadataRaw('dateUpdated').date()
fileupdated=datetime.fromtimestamp(os.stat(db.format_abspath(book_id, formmapping[fileform], index_is_id=True))[8]).date()
if fileupdated > lastupdated:
raise NotGoingToDownload("Not Overwriting, web site is not newer.",'edit-undo.png')
# For update, provide a tmp file copy of the existing epub so
# it can't change underneath us.
if collision in (UPDATE,UPDATEALWAYS) and \
db.has_format(book['calibre_id'],'EPUB',index_is_id=True):
tmp = PersistentTemporaryFile(prefix='old-%s-'%book['calibre_id'],
suffix='.epub',
dir=options['tdir'])
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 collision != CALIBREONLY and not skip_date_update:
# I'm half convinced this should be dateUpdated instead, but
# this behavior matches how epubs come out when imported
# dateCreated == packaged--epub/etc created.
if story.getMetadataRaw('dateUpdated'):
book['updatedate'] = story.getMetadataRaw('dateUpdated').replace(tzinfo=local_tz)
if story.getMetadataRaw('dateCreated'):
book['timestamp'] = story.getMetadataRaw('dateCreated').replace(tzinfo=local_tz)
else:
book['timestamp'] = None # need *something* there for calibre.
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:
book['calibre_series'] = (mi.series,mi.series_index)
print("calibre_series:%s [%s]"%book['calibre_series'])
if not merge:# skip all the collision code when d/ling for merging.
if collision in (CALIBREONLY):
book['icon'] = 'metadata.png'
book['status'] = 'Meta'
book_id = None
if book['calibre_id'] != None:
# updating an existing book. Update mode applies.
print("update existing id:%s"%book['calibre_id'])
book_id = book['calibre_id']
# No handling needed: OVERWRITEALWAYS,CALIBREONLY
# only care about collisions when not ADDNEW
elif collision != ADDNEW:
# 'new' book from URL. collision handling applies.
print("from URL(%s)"%url)
# try to find by identifier url or uri first.
searchstr = 'identifiers:"~ur(i|l):=%s"'%url.replace(":","|")
identicalbooks = db.search_getting_ids(searchstr, None)
if len(identicalbooks) < 1:
# find dups
authlist = story.getList("author", removeallentities=True)
if len(authlist) > 100 and calibre_version < (0, 8, 61):
## should be fixed from 0.8.61 on. In the
## meantime, if it matches the title *and* first
## 100 authors, I'm prepared to assume it's a
## match.
print("reduce author list to 100 only when calibre < 0.8.61")
authlist = authlist[:100]
mi = MetaInformation(story.getMetadata("title", removeallentities=True),
authlist)
identicalbooks = db.find_identical_books(mi)
if len(identicalbooks) > 0:
print("existing found by title/author(s)")
else:
print("existing found by identifier URL")
if collision == SKIP and identicalbooks:
raise NotGoingToDownload("Skipping duplicate story.","list_remove.png")
if len(identicalbooks) > 1:
raise NotGoingToDownload("More than one identical book by Identifer URL or title/author(s)--can't tell which book to update/overwrite.","minusminus.png")
## changed: add new book when CALIBREONLY if none found.
if collision == CALIBREONLY and not identicalbooks:
collision = ADDNEW
options['collision'] = ADDNEW
if len(identicalbooks)>0:
book_id = identicalbooks.pop()
book['calibre_id'] = book_id
book['icon'] = 'edit-redo.png'
book['status'] = 'Update'
if book_id != None and collision != ADDNEW:
if collision in (CALIBREONLY):
book['comment'] = 'Metadata collected.'
# don't need temp file created below.
return
## newer/chaptercount checks are the same for both:
# Update epub, but only if more chapters.
if collision in (UPDATE,UPDATEALWAYS): # collision == UPDATE
# 'book' can exist without epub. If there's no existing epub,
# let it go and it will download it.
if db.has_format(book_id,fileform,index_is_id=True):
(epuburl,chaptercount) = \
get_dcsource_chaptercount(StringIO(db.format(book_id,'EPUB',
index_is_id=True)))
urlchaptercount = int(story.getMetadata('numChapters'))
if chaptercount == urlchaptercount:
if collision == UPDATE:
raise NotGoingToDownload("Already contains %d chapters."%chaptercount,'edit-undo.png')
else:
# UPDATEALWAYS
skip_date_update = True
elif chaptercount > urlchaptercount:
raise NotGoingToDownload("Existing epub contains %d chapters, web site only has %d. Use Overwrite to force update." % (chaptercount,urlchaptercount),'dialog_error.png')
if collision == OVERWRITE and \
db.has_format(book_id,formmapping[fileform],index_is_id=True):
# check make sure incoming is newer.
lastupdated=story.getMetadataRaw('dateUpdated').date()
fileupdated=datetime.fromtimestamp(os.stat(db.format_abspath(book_id, formmapping[fileform], index_is_id=True))[8]).date()
if fileupdated > lastupdated:
raise NotGoingToDownload("Not Overwriting, web site is not newer.",'edit-undo.png')
# For update, provide a tmp file copy of the existing epub so
# it can't change underneath us.
if collision in (UPDATE,UPDATEALWAYS) and \
db.has_format(book['calibre_id'],'EPUB',index_is_id=True):
tmp = PersistentTemporaryFile(prefix='old-%s-'%book['calibre_id'],
suffix='.epub',
dir=options['tdir'])
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:
book['calibre_series'] = (mi.series,mi.series_index)
print("calibre_series:%s [%s]"%book['calibre_series'])
if book['good']: # there shouldn't be any !'good' books at this point.
# if still 'good', make a temp file to write the output to.
# For HTML format users, make the filename inside the zip something reasonable.
@ -767,7 +905,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
options={'fileform':'epub',
'collision':ADDNEW,
'updatemeta':True,
'updateepubcover':True}):
'updateepubcover':True},
merge=False):
'''
Called by LoopProgressDialog to start story downloads BG processing.
adapter_list is a list of tuples of (url,adapter)
@ -775,7 +914,8 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
#print("start_download_job:book_list:%s"%book_list)
## No need to BG process when CALIBREONLY! Fake it.
if options['collision'] in (CALIBREONLY):
#print("options:%s"%options)
if options['collision'] == CALIBREONLY:
class NotJob(object):
def __init__(self,result):
self.failed=False
@ -809,7 +949,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
(book_list, options, cpus)]
desc = 'Download FanFiction Book'
job = self.gui.job_manager.run_job(
self.Dispatcher(partial(self.download_list_completed,options=options)),
self.Dispatcher(partial(self.download_list_completed,options=options,merge=merge)),
func, args=args,
description=desc)
@ -878,7 +1018,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
cp_plugin = self.gui.iactions['Count Pages']
cp_plugin.count_statistics(all_ids,prefs['countpagesstats'])
def download_list_completed(self, job, options={}):
def download_list_completed(self, job, options={},merge=False):
if job.failed:
self.gui.job_exception(job, dialog_title='Failed to Download Stories')
return
@ -892,34 +1032,121 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
#print("book_list:%s"%book_list)
payload = (good_list, bad_list, options)
msg = '''
if merge:
if len(good_list) < 1:
info_dialog(self.gui, _('No Good Stories for Anthology'),
_('No good stories/updates where downloaded, Anthology creation/update aborted.'),
show=True,
show_copy_button=False)
return
msg = '<p>FFDL found <b>%s</b> good and <b>%s</b> bad updates.</p>'%(len(good_list),len(bad_list))
if len(bad_list) > 0:
msg = msg + '''<p>Are you sure you want to continue with creating/updating this Anthology?</p>
<p>Any updates that failed will <b>not</b> be included in the Anthology.</p>
<p>However, if there's an older version, it will still be included.</p>
<p>See log for details.</p>
'''
msg = msg + '<p>Proceed with updating this anthology and your library?</p>'
htmllog='<html><body><table border="1"><tr><th>Status</th><th>Title</th><th>Author</th><th>Comment</th><th>URL</th></tr>'
for book in sorted(good_list+bad_list,key=lambda x : x['mergeorder']):
if 'status' in book:
status = book['status']
else:
if book in good_list:
status = 'Good'
else:
status = 'Bad'
htmllog = htmllog + '<tr><td>' + '</td><td>'.join([escapehtml(status),escapehtml(book['title']),escapehtml(", ".join(book['author'])),escapehtml(book['comment']),book['url']]) + '</td></tr>'
htmllog = htmllog + '</table></body></html>'
for book in bad_list:
if 'epub_for_update' in book:
book['good']=True
book['outfile'] = book['epub_for_update']
good_list.append(book)
do_update_func = self.do_download_merge_update
else:
msg = '''
<p>FFDL found <b>%s</b> good and <b>%s</b> bad updates.</p>
<p>See log for details.</p>
<p>Proceed with updating your library?</p>
'''%(len(good_list),len(bad_list))
htmllog='<html><body><table border="1"><tr><th>Status</th><th>Title</th><th>Author</th><th>Comment</th><th>URL</th></tr>'
for book in good_list:
if 'status' in book:
status = book['status']
else:
status = 'Good'
htmllog = htmllog + '<tr><td>' + '</td><td>'.join([escapehtml(status),escapehtml(book['title']),escapehtml(", ".join(book['author'])),escapehtml(book['comment']),book['url']]) + '</td></tr>'
for book in bad_list:
if 'status' in book:
status = book['status']
else:
status = 'Bad'
htmllog = htmllog + '<tr><td>' + '</td><td>'.join([escapehtml(status),escapehtml(book['title']),escapehtml(", ".join(book['author'])),escapehtml(book['comment']),book['url']]) + '</td></tr>'
htmllog = htmllog + '</table></body></html>'
self.gui.proceed_question(partial(self.do_download_list_update),
htmllog='<html><body><table border="1"><tr><th>Status</th><th>Title</th><th>Author</th><th>Comment</th><th>URL</th></tr>'
for book in good_list:
if 'status' in book:
status = book['status']
else:
status = 'Good'
htmllog = htmllog + '<tr><td>' + '</td><td>'.join([escapehtml(status),escapehtml(book['title']),escapehtml(", ".join(book['author'])),escapehtml(book['comment']),book['url']]) + '</td></tr>'
for book in bad_list:
if 'status' in book:
status = book['status']
else:
status = 'Bad'
htmllog = htmllog + '<tr><td>' + '</td><td>'.join([escapehtml(status),escapehtml(book['title']),escapehtml(", ".join(book['author'])),escapehtml(book['comment']),book['url']]) + '</td></tr>'
htmllog = htmllog + '</table></body></html>'
do_update_func = self.do_download_list_update
self.gui.proceed_question(do_update_func,
payload, htmllog,
'FFDL log', 'FFDL download complete', msg,
show_copy_button=False)
def do_download_merge_update(self, payload):
(good_list,bad_list,options) = payload
total_good = len(good_list)
print("merge titles:\n%s"%"\n".join([ "%s %s"%(x['title'],x['mergeorder']) for x in good_list ]))
good_list = sorted(good_list,key=lambda x : x['mergeorder'])
bad_list = sorted(bad_list,key=lambda x : x['mergeorder'])
self.gui.status_bar.show_message(_('Merging %s books.'%total_good))
existingbook = None
if 'mergebook' in options:
existingbook = options['mergebook']
mergebook = self.merge_meta_books(existingbook,good_list)
if 'mergebook' in options:
mergebook['calibre_id'] = options['mergebook']['calibre_id']
#mergeid = options['mergebook']['calibre_id']
if 'anthology_url' in options:
mergebook['url'] = options['anthology_url']
print("mergebook:\n%s"%mergebook)
if mergebook['good']: # there shouldn't be any !'good' books at this point.
# if still 'good', make a temp file to write the output to.
# For HTML format users, make the filename inside the zip something reasonable.
# For crazy long titles/authors, limit it to 200chars.
# For weird/OS-unsafe characters, use file safe only.
tmp = PersistentTemporaryFile(suffix='.'+options['fileform'],
dir=options['tdir'])
print("title:"+mergebook['title'])
print("outfile:"+tmp.name)
mergebook['outfile'] = tmp.name
self.get_epubmerge_plugin().do_merge(tmp.name,
[ x['outfile'] for x in good_list ],
titleopt=mergebook['title'],
keepmetadatafiles=True)
options['collision']=OVERWRITEALWAYS
self.update_books_loop(mergebook,self.gui.current_db,options)
self.update_books_finish([mergebook], options=options, showlist=False)
def do_download_list_update(self, payload):
(good_list,bad_list,options) = payload
@ -1081,7 +1308,6 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
configuration = None
if prefs['allow_custcol_from_ini']:
configuration = get_ffdl_config(book['url'],options['fileform'])
# meta => custcol[,a|n|r]
# cliches=>\#acolumn,r
for line in configuration.getConfig('custom_columns_settings').splitlines():
@ -1122,8 +1348,7 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
vallist = [book['all_metadata'][meta]]
db.set_custom(book_id, ", ".join(vallist), label=label, commit=False)
db.commit()
if 'Generate Cover' in self.gui.iactions and (book['added'] or not prefs['gcnewonly']):
@ -1258,12 +1483,14 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
def convert_urls_to_books(self, urls):
books = []
uniqueurls = set()
for url in urls:
for i, url in enumerate(urls):
book = self.convert_url_to_book(url)
if book['url'] in uniqueurls:
book['good'] = False
book['comment'] = "Same story already included."
uniqueurls.add(book['url'])
book['mergeorder']=i # BG d/l jobs don't come back in order.
# Didn't matter until anthologies.
books.append(book)
return books
@ -1379,6 +1606,105 @@ class FanFictionDownLoaderPlugin(InterfaceAction):
def is_good_downloader_url(self,url):
return adapters.getNormalStoryURL(url)
def merge_meta_books(self,existingbook,book_list):
book = self.make_book()
book['author'] = []
book['tags'] = []
book['url'] = ''
book['all_metadata'] = {}
book['comment'] = ''
book['added'] = True
book['good'] = True
book['calibre_id'] = None
book['series'] = None
serieslist=[]
# copy list top level
for b in book_list:
if b['series']:
serieslist.append(b['series'][:b['series'].index(" [")])
#print("book series:%s"%serieslist[-1])
if b['publisher']:
if 'publisher' not in book:
book['publisher']=b['publisher']
elif book['publisher']!=b['publisher']:
book['publisher']=None # if any are different, don't use.
# copy authors & tags.
for k in ('author','tags'):
for v in b[k]:
if v not in book[k]:
book[k].append(v)
# fill from first of each if not already present:
for k in ('pubdate', 'timestamp', 'updatedate'):
if k not in b: # not in this book? Skip it.
continue
if k not in book: # first is good enough for publisher.
book[k]=b[k]
# Do these even on first to get the all_metadata settings.
# pubdate should be earliest date.
if k == 'pubdate' and book[k] >= b[k]:
book[k]=b[k]
book['all_metadata']['datePublished'] = b['all_metadata']['datePublished']
# timestamp should be latest date.
if k == 'timestamp' and book[k] <= b[k]:
book[k]=b[k]
book['all_metadata']['dateCreated'] = b['all_metadata']['dateCreated']
# updated should be latest date.
if k == 'updatedate' and book[k] <= b[k]:
book[k]=b[k]
book['all_metadata']['dateUpdated'] = b['all_metadata']['dateUpdated']
# copy list all_metadata
for (k,v) in b['all_metadata'].iteritems():
#print("merge_meta_books v:%s k:%s"%(v,k))
if k in ('numChapters','numWords'):
if k not in book['all_metadata']:
book['all_metadata'][k] = b['all_metadata'][k]
else:
# lot of work for a simple add.
book['all_metadata'][k] = unicode(int(book['all_metadata'][k].replace(',',''))+int(b['all_metadata'][k].replace(',','')))
elif k in ('dateUpdated','datePublished','dateCreated',
'series','status','title'):
pass # handled above, below or skip these for now, not going to do anything with them.
elif k not in book['all_metadata'] or not book['all_metadata'][k]:
book['all_metadata'][k]=v
elif v:
if k == 'description':
book['all_metadata'][k]=book['all_metadata'][k]+"\n"+v
else:
book['all_metadata'][k]=book['all_metadata'][k]+", "+v
if existingbook:
book['title'] = deftitle = existingbook['title']
book['comments'] = existingbook['comments']
else:
book['title'] = deftitle = book_list[0]['title']+" Anthology"
book['comments'] = book['all_metadata']['description']
# "Anthology containing:\n" + \
# "\n".join([ "%s by %s"%(b['title'],', '.join(b['author'])) for b in book_list ])
# if all same series, use series for name. But only if all and not previous named
if len(serieslist) == len(book_list):
book['title'] = serieslist[0]
for sr in serieslist:
if book['title'] != sr:
book['title'] = deftitle;
break
book['all_metadata']['title'] = book['title'] # because custom columns are set from all_metadata
book['all_metadata']['author'] = ", ".join(book['author'])
book['author_sort']=book['author']
for v in ['Completed','In-Progress']:
if v in book['tags']:
book['tags'].remove(v)
book['tags'].append('Anthology')
return book
def split_text_to_urls(urls):
# remove dups while preserving order.
dups=set()

View file

@ -27,7 +27,12 @@ make_firstimage_cover:true
def get_ffdl_config(url,fileform="EPUB",personalini=None):
if not personalini:
personalini = get_ffdl_personalini()
configuration = Configuration(adapters.getConfigSectionFor(url),fileform)
site='unknown'
try:
site = adapters.getConfigSectionFor(url)
except Exception as e:
print("Failed trying to get ini config for url(%s): %s, using section [%s] instead"%(url,e,site))
configuration = Configuration(site,fileform)
configuration.readfp(StringIO(get_resources("plugin-defaults.ini")))
configuration.readfp(StringIO(personalini))

View file

@ -171,6 +171,17 @@ def do_download_for_worker(book,options):
adapter.calibrebookmark,
adapter.logfile) = get_update_data(book['epub_for_update'])
# dup handling from ffdl_plugin needed for anthology updates.
if options['collision'] == UPDATE:
if chaptercount == urlchaptercount:
book['comment']="Already contains %d chapters. Reuse as is."%chaptercount
book['outfile'] = book['epub_for_update'] # for anthology merge ops.
return book
# dup handling from ffdl_plugin needed for anthology updates.
if chaptercount > urlchaptercount:
raise NotGoingToDownload("Existing epub contains %d chapters, web site only has %d. Use Overwrite to force update." % (chaptercount,urlchaptercount),'dialog_error.png')
print("Do update - epub(%d) vs url(%d)" % (chaptercount, urlchaptercount))
print("write to %s"%outfile)

View file

@ -115,6 +115,10 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
self.story.addToList('authorUrl','http://author/url')
self.story.addToList('authorUrl','http://author/url-2')
self.story.addToList('category','Power Rangers')
self.story.addToList('category','SG-1')
self.story.addToList('genre','Porn')
self.story.addToList('genre','Drama')
else:
self.story.setMetadata('authorId','98765')
self.story.setMetadata('authorUrl','http://author/url')
@ -162,9 +166,9 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
('Chapter 3, Over Cinnabar',self.url+"&chapter=4"),
('Chapter 4',self.url+"&chapter=5"),
('Chapter 5',self.url+"&chapter=6"),
#('Chapter 6',self.url+"&chapter=7"),
#('Chapter 7',self.url+"&chapter=8"),
#('Chapter 8',self.url+"&chapter=9"),
('Chapter 6',self.url+"&chapter=7"),
('Chapter 7',self.url+"&chapter=8"),
('Chapter 8',self.url+"&chapter=9"),
#('Chapter 9',self.url+"&chapter=0"),
#('Chapter 0',self.url+"&chapter=a"),
#('Chapter a',self.url+"&chapter=b"),
@ -187,9 +191,6 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
def getChapterText(self, url):
logger.debug('Getting chapter text from: %s' % url)
if self.story.getMetadata('storyId') == '667':
raise exceptions.FailedToDownload("Error downloading Chapter: %s!" % url)
if self.story.getMetadata('storyId').startswith('670') or \
self.story.getMetadata('storyId').startswith('672'):
time.sleep(1.0)
@ -202,7 +203,7 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
<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>
<p>http://test1.com?sid=667 - raises FailedToDownload on chapter 1</p>
<p>http://test1.com?sid=667 - raises FailedToDownload on chapters 2+</p>
<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>
@ -211,7 +212,10 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
<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=0 - Succeeds, generates some text specifically for testing hyphenation problems with Nook STR/STRwG</p><p>Odd sid's will be In-Progress, evens complete. sid&lt;10 will be assigned one of four languages and included in a series.</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>
<p>http://test1.com?sid=0 - Succeeds, generates some text specifically for testing hyphenation problems with Nook STR/STRwG</p>
<p>Odd sid's will be In-Progress, evens complete. sid&lt;10 will be assigned one of four languages and included in a series.</p>
</div>
'''
elif self.story.getMetadata('storyId') == '0':
@ -226,9 +230,13 @@ Some more longer description. "I suck at summaries!" "Better than it sounds!"
<br />
'''
else:
if self.story.getMetadata('storyId') == '667':
raise exceptions.FailedToDownload("Error downloading Chapter: %s!" % url)
text=u'''
<div>
<h3>Chapter title from site</h3>
<p>Timestamp:'''+datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")+'''</p>
<p>Lorem '''+self.crazystring+u''' <i>italics</i>, <b>bold</b>, <u>underline</u> consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
br breaks<br><br>
Puella Magi Madoka Magica/魔法少女まどかマギカ

View file

@ -27,7 +27,7 @@ if __name__=="__main__":
exclude=['*.pyc','*~','*.xcf','*[0-9].png']
# from top dir. 'w' for overwrite
createZipFile(filename,"w",
['plugin-defaults.ini','plugin-example.ini','epubmerge.py','fanficdownloader'],
['plugin-defaults.ini','plugin-example.ini','fanficdownloader'],
exclude=exclude)
#from calibre-plugin dir. 'a' for append
os.chdir('calibre-plugin')