Skip to content

Commit

Permalink
Prepare v0.0.14, add code to auto-delete ACSM
Browse files Browse the repository at this point in the history
  • Loading branch information
Leseratte10 committed Dec 11, 2021
1 parent 481e6c8 commit 1ac47e8
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 14 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
- ...
37 changes: 33 additions & 4 deletions calibre-plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down Expand Up @@ -202,7 +206,14 @@ def download(self, replyData: str):
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 <src> 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)
Expand Down Expand Up @@ -325,6 +336,12 @@ def run(self, path_to_ebook: str):
# 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
Expand Down Expand Up @@ -376,6 +393,7 @@ def run(self, path_to_ebook: str):
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)

Expand All @@ -393,10 +411,21 @@ def run(self, path_to_ebook: str):


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

Expand Down
21 changes: 20 additions & 1 deletion calibre-plugin/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self, plugin_path):

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']

Expand Down Expand Up @@ -134,6 +135,12 @@ def __init__(self, plugin_path):
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)
Expand Down Expand Up @@ -185,12 +192,23 @@ def toggle_logging(self):
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):
Expand Down Expand Up @@ -830,6 +848,7 @@ def export_key(self):
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):
Expand Down
3 changes: 0 additions & 3 deletions calibre-plugin/libadobeAccount.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down
7 changes: 2 additions & 5 deletions calibre-plugin/libadobeFulfill.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions calibre-plugin/prefs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(self):

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'] = []

Expand Down

0 comments on commit 1ac47e8

Please sign in to comment.