diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index ee54efb..9642220 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -27,7 +27,7 @@ class FanFictionDownLoaderBase(InterfaceActionBase): description = 'UI plugin to download FanFiction stories from various sites.' supported_platforms = ['windows', 'osx', 'linux'] author = 'Jim Miller' - version = (1, 5, 4) + version = (1, 5, 5) minimum_calibre_version = (0, 8, 30) #: This field defines the GUI plugin class that contains all the code diff --git a/calibre-plugin/dialogs.py b/calibre-plugin/dialogs.py index 92ee25d..c1ac9b1 100644 --- a/calibre-plugin/dialogs.py +++ b/calibre-plugin/dialogs.py @@ -159,25 +159,40 @@ class AddNewDialog(SizePersistedDialog): def get_urlstext(self): return unicode(self.url.toPlainText()) - + + +class FakeLineEdit(): + def __init__(self): + pass + + def text(self): + pass + class UserPassDialog(QDialog): ''' Need to collect User/Pass for some sites. ''' - def __init__(self, gui, site): + def __init__(self, gui, site, exception=None): QDialog.__init__(self, gui) self.gui = gui self.status=False - self.setWindowTitle('User/Password') self.l = QGridLayout() self.setLayout(self.l) - self.l.addWidget(QLabel("%s requires you to login to download this story."%site),0,0,1,2) + if exception.passwdonly: + self.setWindowTitle('Password') + self.l.addWidget(QLabel("Author requires a password for this story(%s)."%exception.url),0,0,1,2) + # user isn't used, but it's easier to still have it for + # post processing. + self.user = FakeLineEdit() + else: + self.setWindowTitle('User/Password') + self.l.addWidget(QLabel("%s requires you to login to download this story."%site),0,0,1,2) - self.l.addWidget(QLabel("User:"),1,0) - self.user = QLineEdit(self) - self.l.addWidget(self.user,1,1) + self.l.addWidget(QLabel("User:"),1,0) + self.user = QLineEdit(self) + self.l.addWidget(self.user,1,1) self.l.addWidget(QLabel("Password:"),2,0) self.passwd = QLineEdit(self) diff --git a/calibre-plugin/ffdl_plugin.py b/calibre-plugin/ffdl_plugin.py index a4fc3e1..6e70c6a 100644 --- a/calibre-plugin/ffdl_plugin.py +++ b/calibre-plugin/ffdl_plugin.py @@ -419,9 +419,9 @@ class FanFictionDownLoaderPlugin(InterfaceAction): for x in range(0,2): try: adapter.getStoryMetadataOnly() - except exceptions.FailedToLogin: + except exceptions.FailedToLogin, f: print("Login Failed, Need Username/Password.") - userpass = UserPassDialog(self.gui,url) + userpass = UserPassDialog(self.gui,url,f) userpass.exec_() # exec_ will make it act modal if userpass.status: adapter.username = userpass.user.text() diff --git a/defaults.ini b/defaults.ini index 4d65760..a801319 100644 --- a/defaults.ini +++ b/defaults.ini @@ -381,6 +381,11 @@ output_filename: ${title}-${siteabbrev}_${authorId}_${storyId}${formatext} ## this should go in your personal.ini, not defaults.ini. #is_adult:true +## fimfiction.net stories can be locked requiring individual +## passwords. If fail_on_password is set, the downloader will fail +## when a password is required rather than prompting every time. +#fail_on_password: false + [www.tthfanfic.org] ## Some sites do not require a login, but do require the user to ## confirm they are adult for adult content. In commandline version, diff --git a/downloader.py b/downloader.py index 1552563..673d75c 100644 --- a/downloader.py +++ b/downloader.py @@ -137,10 +137,13 @@ def main(): for x in range(0,2): try: adapter.getStoryMetadataOnly() - except exceptions.FailedToLogin: - print "Login Failed, Need Username/Password." - sys.stdout.write("Username: ") - adapter.username = sys.stdin.readline().strip() + except exceptions.FailedToLogin, f: + if f.passwdonly: + print "Story requires a password." + else: + print "Login Failed, Need Username/Password." + sys.stdout.write("Username: ") + adapter.username = sys.stdin.readline().strip() adapter.password = getpass.getpass(prompt='Password: ') #print("Login: `%s`, Password: `%s`" % (adapter.username, adapter.password)) except exceptions.AdultCheckRequired: diff --git a/fanficdownloader/adapters/adapter_fimfictionnet.py b/fanficdownloader/adapters/adapter_fimfictionnet.py index 579189f..cc58b51 100644 --- a/fanficdownloader/adapters/adapter_fimfictionnet.py +++ b/fanficdownloader/adapters/adapter_fimfictionnet.py @@ -96,7 +96,10 @@ class FimFictionNetSiteAdapter(BaseSiteAdapter): data = self._postUrl(self.url,params) if "Enter the password the author set for this story to view it." in data: - raise exceptions.FailedToLogin(self.url,"Story requires individual password") + if self.getConfig('fail_on_password'): + raise exceptions.FailedToDownload("%s requires story password and fail_on_password is true."%self.url) + else: + raise exceptions.FailedToLogin(self.url,"Story requires individual password",passwdonly=True) soup = bs.BeautifulSoup(data).find("div", {"class":"content_box post_content_box"}) diff --git a/fanficdownloader/adapters/base_adapter.py b/fanficdownloader/adapters/base_adapter.py index 678a933..3221082 100644 --- a/fanficdownloader/adapters/base_adapter.py +++ b/fanficdownloader/adapters/base_adapter.py @@ -313,7 +313,7 @@ class BaseSiteAdapter(Configurable): if self.getConfig('replace_hr'): # replacing a self-closing tag with a container tag in the # soup is more difficult than it first appears. So cheat. - retval = retval.replace("
","
* * *
") + retval = retval.replace("
","
* * *
") # Don't want body tags in chapter html--writers add them. # This is primarily for epub updates. diff --git a/fanficdownloader/exceptions.py b/fanficdownloader/exceptions.py index cf8e558..8314c26 100644 --- a/fanficdownloader/exceptions.py +++ b/fanficdownloader/exceptions.py @@ -34,12 +34,16 @@ class InvalidStoryURL(Exception): return "Bad Story URL: (%s) for site: (%s) Example: (%s)" % (self.url, self.domain, self.example) class FailedToLogin(Exception): - def __init__(self,url,username): + def __init__(self,url, username, passwdonly=False): self.url=url self.username=username + self.passwdonly=passwdonly def __str__(self): - return "Failed to Login for URL: (%s) with username: (%s)" % (self.url, self.username) + if self.passwdonly: + return "URL Failed, password required: (%s) " % (self.url) + else: + return "Failed to Login for URL: (%s) with username: (%s)" % (self.url, self.username) class AdultCheckRequired(Exception): def __init__(self,url):