v0.0.4: Make DeDRM work without modifications

This commit is contained in:
Florian Bach 2021-09-26 12:56:53 +02:00
parent 3a4652a462
commit a930a99c80
4 changed files with 60 additions and 68 deletions

View File

@ -20,5 +20,4 @@ jobs:
name: linux
path: |
calibre-plugin.zip
libgourou_bundle_raw.tar.xz

View File

@ -10,21 +10,17 @@ It is a full Python reimplementation of libgourou by Grégory Soutadé (http://i
3. Click the "Link to ADE account" button
4. Enter your AdobeID and password, then wait a couple seconds for the success message.
5. The settings window should now say "Authorized with ADE ID X on device Y".
6. Click the "Export account activation data" and "Export account encryption key" buttons to export / backup your keys. Do not skip this step. The first file (ZIP) can be used to re-authorize Calibre after a reset / reinstall without using up one of your Adobe authorizations. The second file (DER) can be imported into Alf.
7. Download an EPUB ACSM file from Adobe's test library and see if you can import it into Calibre: https://www.adobe.com/de/solutions/ebook/digital-editions/sample-ebook-library.html
6. Click the "Export account activation data" and "Export account encryption key" buttons to export / backup your keys. Do not skip this step. The first file (ZIP) can be used to re-authorize Calibre after a reset / reinstall without using up one of your Adobe authorizations. The second file (DER) can be imported into DeDRM.
7. If needed (new AdobeID), import the DER file into the DeDRM plugin.
8. Download an EPUB ACSM file from Adobe's test library and see if you can import it into Calibre: https://www.adobe.com/de/solutions/ebook/digital-editions/sample-ebook-library.html
IMPORTANT:
- I would suggest creating a new dummy AdobeID to use for Calibre so just in case Adobe detects this and bans you, you don't lose your main AdobeID.
- Combined with that I suggest importing the DER file into the Alf plugin to make sure that losing your AdobeID doesn't also mean you'll lose access to all your eBooks. See the section "Combining with Alf" below.
- Combined with that I suggest importing the DER file into the DeDRM plugin to make sure that losing your AdobeID doesn't also mean you'll lose access to all your eBooks.
- This plugin doesn't yet work with PDFs. Importing an ACSM file for a PDF book will just result in the ACSM file being imported, it won't be converted into a PDF.
- This software is not approved by Adobe. I am not responsible if Adobe detects that you're using nonstandard software and bans your account. Do not complain to me if Adobe bans your main ADE account - you have been warned.
## Combining with Alf
In order to combine this plugin with Alf, you'll need to go to Alf's settings and import the DER file you've just exported from this plugin.
Also, there's a small code change needed to Alf's plugin to make it work together with this one. Open up the `__init__.py` file in Alf's ZIP file and search for "file_types". You'll find a list of supported file types. Add "acsm" to that list, save the file, put it back into the ZIP file, then re-import the Alf plugin into Calibre. This is needed due to a bug in Calibre - even though this plugin converts the ACSM file into an EPUB on import, Alf still "thinks" it's an ACSM file and doesn't do anything with it. By forcing Alf to accept ACSM files it will work correctly (since it'll then notice that it's actually an EPUB).
## To-Do list for the future?
@ -34,4 +30,5 @@ There's a bunch of features that could still be added, but most of them aren't i
- Support for un-authorizing a machine
- Support to re-import a backed-up authorization after a reinstallation (right now you can only do that manually)
- Support for PDFs
- Support for returning loan books
- ...

View File

@ -8,10 +8,11 @@
# v0.0.1: First version.
# v0.0.2: Allow key extraction without extra binary call (unreleased test version)
# v0.0.3: Standalone Calibre plugin for Linux, Windows, MacOS without the need for libgourou.
# v0.0.4: Manually execute DeDRM (if installed) after converting ACSM to EPUB.
from calibre.customize import FileTypePlugin # type: ignore
__version__ = '0.0.3'
__version__ = '0.0.4'
PLUGIN_NAME = "DeACSM"
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
@ -24,7 +25,6 @@ import os, shutil, traceback, sys
import zipfile
from lxml import etree
class DeACSM(FileTypePlugin):
name = PLUGIN_NAME
description = "Takes an Adobe ACSM file and converts that into a useable EPUB file. Python reimplementation of libgourou by Grégory Soutadé"
@ -101,9 +101,8 @@ class DeACSM(FileTypePlugin):
from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except:
print("error Account")
raise
raise
print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
# Fulfill:
try:
@ -114,11 +113,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill
except:
print("error Fulfill")
raise
raise
print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
import calibre_plugins.deacsm.prefs as prefs # type: ignore
deacsmprefs = prefs.DeACSM_Prefs()
@ -129,9 +125,6 @@ class DeACSM(FileTypePlugin):
raise
def is_customizable(self):
return True
@ -178,9 +171,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill
except:
print("error Fulfill")
raise
raise
print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
adobe_fulfill_response = etree.fromstring(replyData)
@ -264,9 +256,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill
except:
print("error Fulfill")
raise
raise
print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
success, replyData = fulfill(path_to_ebook)
@ -276,6 +267,23 @@ class DeACSM(FileTypePlugin):
print("{0} v{1}: Downloading book ...".format(PLUGIN_NAME, PLUGIN_VERSION))
rpl = self.download(replyData)
if (rpl is not None):
# Got a file
# Because Calibre still thinks this is an ACSM file (not an EPUB)
# it will not run other plugins like Alf / DeDRM.
# So we have to manually check if it's installed,
# and if it is, run it to remove DRM.
try:
from calibre.customize.ui import _initialized_plugins
for plugin in _initialized_plugins:
if (plugin.name == "DeDRM"):
print("{0} v{1}: Executing DeDRM plugin ...".format(PLUGIN_NAME, PLUGIN_VERSION))
return plugin.run(rpl)
except:
print("{0} v{1}: Error while checking for DeDRM plugin.".format(PLUGIN_NAME, PLUGIN_VERSION))
pass
# Looks like DeDRM is not installed, return book with DRM.
return rpl

View File

@ -3,7 +3,7 @@
# pyright: reportUndefinedVariable=false
import os, base64
import os, base64, traceback
from lxml import etree
@ -79,9 +79,9 @@ class ConfigWidget(QWidget):
from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except:
print("error Account")
raise
raise
print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
update_account_path(self.deacsmprefs["path_to_account_data"])
@ -126,7 +126,7 @@ class ConfigWidget(QWidget):
if (filename is None):
return
print("would export to " + filename)
print("{0} v{1}: Exporting activation data to {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
try:
with ZipFile(filename, 'w') as zipfile:
@ -148,9 +148,8 @@ class ConfigWidget(QWidget):
from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except:
print("error Account")
raise
raise
print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
update_account_path(self.deacsmprefs["path_to_account_data"])
@ -195,9 +194,20 @@ class ConfigWidget(QWidget):
def export_key(self):
pluginsdir = os.path.join(config_dir,"plugins")
maindir = os.path.join(pluginsdir,"DeACSM")
verdir = os.path.join(maindir,PLUGIN_VERSION)
try:
from calibre_plugins.deacsm.libadobe import update_account_path
from calibre_plugins.deacsm.libadobeAccount import exportAccountEncryptionKeyDER
except:
try:
from libadobe import update_account_path
from libadobeAccount import exportAccountEncryptionKeyDER
except:
print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
update_account_path(self.deacsmprefs["path_to_account_data"])
filters = [("DER Files", ["der"])]
@ -206,38 +216,16 @@ class ConfigWidget(QWidget):
if (filename is None):
return
print("would export to " + filename)
print("{0} v{1}: Exporting encryption key to {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
import calibre_plugins.deacsm.prefs as prefs # type: ignore
deacsmprefs = prefs.DeACSM_Prefs()
ret = exportAccountEncryptionKeyDER(filename)
if ret:
return info_dialog(None, "Done", "Key successfully exported", show=True, show_copy_button=False)
else:
return error_dialog(None, "Export failed", "Export failed", show=True, show_copy_button=False)
activation_xml_path = os.path.join(self.deacsmprefs["path_to_account_data"], "activation.xml")
container = None
try:
container = etree.parse(activation_xml_path)
except (FileNotFoundError, OSError) as e:
return error_dialog(None, "Export failed", "Export failed - Can't open activation.xml", show=True, show_copy_button=False)
key_binary = None
try:
adeptNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag)
usernameXML = container.find(adeptNS("credentials")).find(adeptNS("privateLicenseKey"))
key_base64 = usernameXML.text
key_binary = base64.decodebytes(key_base64.encode())[26:]
except:
return error_dialog(None, "Export failed", "Export failed - Can't read key from activation.xml", show=True, show_copy_button=False)
try:
output_file = open(filename, "wb")
output_file.write(key_binary)
output_file.close()
except:
return error_dialog(None, "Export failed", "Export failed - Can't write key to file", show=True, show_copy_button=False)
info_dialog(None, "Done", "Key successfully exported", show=True, show_copy_button=False)
def save_settings(self):
#self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text())