diff --git a/README.md b/README.md index 8b6071d..af7792d 100644 --- a/README.md +++ b/README.md @@ -46,4 +46,5 @@ There's a bunch of features that could still be added, but most of them aren't i - Support for anonymous Adobe IDs - Support for un-authorizing a machine - Support to copy an authorization from the plugin to an ADE install -- ... +- Support for Adobe's "auth" download method instead of the "simple" method. +- ... \ No newline at end of file diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index 46913ea..9aac9d1 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -19,15 +19,19 @@ # improve PassHash support, include UUID in key export filename, # fix bug that would block other FileTypePlugins # v0.0.12: Fix Calibre Plugin index / updater -# v0.0.13: Add support for emulating multiple ADE versions (1.7.2, 2.0.1, 3.0.1, 4.0.3, 4.5.11), +# v0.0.13: v0.0.13 was a development / beta version with lots of different published test +# versions. To make support easier there's no "final" v0.0.13 version. Instead, +# all the changes from the various v0.0.13 beta versions are released with v0.0.14. +# v0.0.14: Add support for emulating multiple ADE versions (1.7.2, 2.0.1, 3.0.1, 4.0.3, 4.5.11), # add code to import existing activation from ADE (Windows, MacOS or Linux/Wine), # add code to remove an existing activation from the plugin (Ctrl+Shift+D), # fix race condition when importing multiple ACSMs simultaneously, # fix authorization failing with certain non-ASCII characters in username, -# add detailed logging toggle setting. +# add detailed logging toggle setting, add auto-delete ACSM setting, +# add useful error message for ACSMs with nonstandard download type. PLUGIN_NAME = "DeACSM" -PLUGIN_VERSION_TUPLE = (0, 0, 13) +PLUGIN_VERSION_TUPLE = (0, 0, 14) from calibre.customize import FileTypePlugin # type: ignore __version__ = PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE]) @@ -202,7 +206,14 @@ class DeACSM(FileTypePlugin): NSMAP = { "adept" : "http://ns.adobe.com/adept" } adNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag) - download_url = adobe_fulfill_response.find("./%s/%s/%s" % (adNS("fulfillmentResult"), adNS("resourceItemInfo"), adNS("src"))).text + + try: + download_url = adobe_fulfill_response.find("./%s/%s/%s" % (adNS("fulfillmentResult"), adNS("resourceItemInfo"), adNS("src"))).text + except: + print("{0} v{1}: FulfillmentResult does not contain the tag. This may be an ACSM with download type 'auth'?".format(PLUGIN_NAME, PLUGIN_VERSION)) + print("{0} v{1}: Please open a bug report and attach the ACSM file if you see this message.".format(PLUGIN_NAME, PLUGIN_VERSION)) + return None + license_token_node = adobe_fulfill_response.find("./%s/%s/%s" % (adNS("fulfillmentResult"), adNS("resourceItemInfo"), adNS("licenseToken"))) rights_xml_str = buildRights(license_token_node) @@ -325,6 +336,12 @@ class DeACSM(FileTypePlugin): # Loop through all plugins (the list is already sorted by priority), # then execute all of them that can handle EPUB / PDF. + # if the source file is supposed to be deleted after successful fulfillment, + # this is set to True + # If there's any errors whatsoever during export / plugin execution, + # this will be set back to False to prevent deletion. + delete_src_file = deacsmprefs["delete_acsm_after_fulfill"] + try: from calibre.customize.ui import _initialized_plugins, is_disabled from calibre.customize import FileTypePlugin @@ -376,6 +393,7 @@ class DeACSM(FileTypePlugin): plugin_ret = None plugin_ret = plugin.run(rpl) except: + delete_src_file = False print("{0} v{1}: Running file type plugin failed with traceback:".format(PLUGIN_NAME, PLUGIN_VERSION)) traceback.print_exc(file=oe) @@ -393,10 +411,21 @@ class DeACSM(FileTypePlugin): except: + delete_src_file = False print("{0} v{1}: Error while executing other plugins".format(PLUGIN_NAME, PLUGIN_VERSION)) traceback.print_exc() pass + # If enabled, and if we didn't encounter any errors, delete the source ACSM file. + if delete_src_file: + try: + if os.path.exists(path_to_ebook): + print("{0} v{1}: Deleting existing ACSM file {2} ...".format(PLUGIN_NAME, PLUGIN_VERSION, path_to_ebook)) + os.remove(path_to_ebook) + except: + print("{0} v{1}: Failed to delete source ACSM after fulfillment.".format(PLUGIN_NAME, PLUGIN_VERSION)) + + # Return path - either the original one or the one modified by the other plugins. return rpl diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 4455717..ae46723 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -45,6 +45,7 @@ class ConfigWidget(QWidget): self.tempdeacsmprefs['notify_fulfillment'] = self.deacsmprefs['notify_fulfillment'] self.tempdeacsmprefs['detailed_logging'] = self.deacsmprefs['detailed_logging'] + self.tempdeacsmprefs['delete_acsm_after_fulfill'] = self.deacsmprefs['delete_acsm_after_fulfill'] self.tempdeacsmprefs['list_of_rented_books'] = self.deacsmprefs['list_of_rented_books'] @@ -134,6 +135,12 @@ class ConfigWidget(QWidget): self.chkDetailedLogging.toggled.connect(self.toggle_logging) layout.addWidget(self.chkDetailedLogging) + self.chkDeleteAfterFulfill = QtGui.QCheckBox("Delete ACSM file after successful import") + self.chkDeleteAfterFulfill.setToolTip("Default: False\n\nIf this is enabled, imported ACSM files will be automatically deleted after they've been converted into an EPUB or PDF. \nNote: This is experimental. It is possible that the ACSM will also be deleted if there's errors during import. \nIf you have an important ACSM file that you can't re-download if needed, do not enable this option.") + self.chkDeleteAfterFulfill.setChecked(self.tempdeacsmprefs["delete_acsm_after_fulfill"]) + self.chkDeleteAfterFulfill.toggled.connect(self.toggle_acsm_delete) + layout.addWidget(self.chkDeleteAfterFulfill) + # Key shortcut Ctrl+Shift+D / Cmd+Shift+D to remove authorization, just like in ADE. self.deauthShortcut = QShortcut(QKeySequence("Ctrl+Shift+D"), self) self.deauthShortcut.activated.connect(self.delete_ade_auth) @@ -185,12 +192,23 @@ class ConfigWidget(QWidget): if not self.chkDetailedLogging.isChecked(): return - msg = "You have enabled detailed logging.\n" + msg = "You have enabled verbose logging.\n" msg += "This will cause various data to be included in the logfiles, like encryption keys, account keys and other confidential data.\n" msg += "With this setting enabled, only share log files privately with the developer and don't make them publicly available." info_dialog(None, "Warning", msg, show=True, show_copy_button=False) + def toggle_acsm_delete(self): + if not self.chkDeleteAfterFulfill.isChecked(): + return + + msg = "You have enabled ACSM auto-deletion.\n" + msg += "This means that your source ACSM file will be deleted after import - not just from Calibre, but from the source filesystem, too. " + msg += "As this feature is experimental, it's possible that ACSMs will also sometimes get deleted even when the import failed.\n\n" + msg += "If you're importing an ACSM that you cannot re-download in case of issues, do not enable this option!" + + info_dialog(None, "Warning", msg, show=True, show_copy_button=False) + def delete_ade_auth(self): @@ -830,6 +848,7 @@ class ConfigWidget(QWidget): def save_settings(self): self.deacsmprefs.set('notify_fulfillment', self.chkNotifyFulfillment.isChecked()) self.deacsmprefs.set('detailed_logging', self.chkDetailedLogging.isChecked()) + self.deacsmprefs.set('delete_acsm_after_fulfill', self.chkDeleteAfterFulfill.isChecked()) self.deacsmprefs.writeprefs() def load_resource(self, name): diff --git a/calibre-plugin/libadobeAccount.py b/calibre-plugin/libadobeAccount.py index 33d40d2..f305b7e 100644 --- a/calibre-plugin/libadobeAccount.py +++ b/calibre-plugin/libadobeAccount.py @@ -450,9 +450,6 @@ def activateDevice(useVersionIndex: int = 0): if (result is False): return False, "Building activation request failed: " + activate_req - #print("======================================================") - #print("activate") - #print(activate_req) NSMAP = { "adept" : "http://ns.adobe.com/adept" } etree.register_namespace("adept", NSMAP["adept"]) diff --git a/calibre-plugin/libadobeFulfill.py b/calibre-plugin/libadobeFulfill.py index 57e5302..60b34f2 100644 --- a/calibre-plugin/libadobeFulfill.py +++ b/calibre-plugin/libadobeFulfill.py @@ -632,12 +632,9 @@ def performFulfillmentNotification(fulfillmentResultToken, forceOptional = False device = fulfillmentResultToken.find("./%s/%s/%s/%s" % (adNS("fulfillmentResult"), adNS("resourceItemInfo"), adNS("licenseToken"), adNS("device"))).text except: # B&N Adobe PassHash fulfillment without device ID. - # Not sure what to do in this case. # PassHash books aren't linked to a particular device, so there's no ID to send to Adobe. - # If I leave this out, I get E_ADEPT_MISSING_ELEMENT - # If I use the one from the ADE activation, I get E_LIC_USER_UNKNOWN - # Adobe documentation seems to imply that PassHash books don't support notifications, - # so lets just skip if that's the case. + # If I understand Adobe's documentation correctly, PassHash books do not support notifications + # and are not supposed to contain notify tags, so lets just skip if that's the case. print("Skipping notify due to passHash") continue diff --git a/calibre-plugin/prefs.py b/calibre-plugin/prefs.py index 7b2dce1..8fe4815 100644 --- a/calibre-plugin/prefs.py +++ b/calibre-plugin/prefs.py @@ -20,6 +20,7 @@ class DeACSM_Prefs(): self.deacsmprefs.defaults['notify_fulfillment'] = True self.deacsmprefs.defaults['detailed_logging'] = False + self.deacsmprefs.defaults['delete_acsm_after_fulfill'] = False self.deacsmprefs.defaults['list_of_rented_books'] = []