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 name: linux
path: | path: |
calibre-plugin.zip 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 3. Click the "Link to ADE account" button
4. Enter your AdobeID and password, then wait a couple seconds for the success message. 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". 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. 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. 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 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: 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. - 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 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. - 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? ## 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 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 to re-import a backed-up authorization after a reinstallation (right now you can only do that manually)
- Support for PDFs - Support for PDFs
- Support for returning loan books
- ... - ...

View File

@ -8,10 +8,11 @@
# v0.0.1: First version. # v0.0.1: First version.
# v0.0.2: Allow key extraction without extra binary call (unreleased test 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.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 from calibre.customize import FileTypePlugin # type: ignore
__version__ = '0.0.3' __version__ = '0.0.4'
PLUGIN_NAME = "DeACSM" PLUGIN_NAME = "DeACSM"
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")]) PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
@ -24,7 +25,6 @@ import os, shutil, traceback, sys
import zipfile import zipfile
from lxml import etree from lxml import etree
class DeACSM(FileTypePlugin): class DeACSM(FileTypePlugin):
name = PLUGIN_NAME 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é" 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 libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except: except:
print("error Account") print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
# Fulfill: # Fulfill:
try: try:
@ -114,11 +113,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill from libadobeFulfill import buildRights, fulfill
except: except:
print("error Fulfill") print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
import calibre_plugins.deacsm.prefs as prefs # type: ignore import calibre_plugins.deacsm.prefs as prefs # type: ignore
deacsmprefs = prefs.DeACSM_Prefs() deacsmprefs = prefs.DeACSM_Prefs()
@ -127,9 +123,6 @@ class DeACSM(FileTypePlugin):
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
raise raise
def is_customizable(self): def is_customizable(self):
@ -178,9 +171,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill from libadobeFulfill import buildRights, fulfill
except: except:
print("error Fulfill") print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
adobe_fulfill_response = etree.fromstring(replyData) adobe_fulfill_response = etree.fromstring(replyData)
@ -264,9 +256,8 @@ class DeACSM(FileTypePlugin):
from libadobe import sendHTTPRequest from libadobe import sendHTTPRequest
from libadobeFulfill import buildRights, fulfill from libadobeFulfill import buildRights, fulfill
except: except:
print("error Fulfill") print("{0} v{1}: Error while importing Fulfillment stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
success, replyData = fulfill(path_to_ebook) success, replyData = fulfill(path_to_ebook)
@ -276,6 +267,23 @@ class DeACSM(FileTypePlugin):
print("{0} v{1}: Downloading book ...".format(PLUGIN_NAME, PLUGIN_VERSION)) print("{0} v{1}: Downloading book ...".format(PLUGIN_NAME, PLUGIN_VERSION))
rpl = self.download(replyData) rpl = self.download(replyData)
if (rpl is not None): 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 return rpl

View File

@ -3,7 +3,7 @@
# pyright: reportUndefinedVariable=false # pyright: reportUndefinedVariable=false
import os, base64 import os, base64, traceback
from lxml import etree from lxml import etree
@ -79,9 +79,9 @@ class ConfigWidget(QWidget):
from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except: except:
print("error Account") print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
update_account_path(self.deacsmprefs["path_to_account_data"]) update_account_path(self.deacsmprefs["path_to_account_data"])
@ -126,7 +126,7 @@ class ConfigWidget(QWidget):
if (filename is None): if (filename is None):
return return
print("would export to " + filename) print("{0} v{1}: Exporting activation data to {2}".format(PLUGIN_NAME, PLUGIN_VERSION, filename))
try: try:
with ZipFile(filename, 'w') as zipfile: with ZipFile(filename, 'w') as zipfile:
@ -148,9 +148,8 @@ class ConfigWidget(QWidget):
from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path from libadobe import VAR_HOBBES_VERSION, createDeviceKeyFile, update_account_path
from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice from libadobeAccount import createDeviceFile, createUser, signIn, activateDevice
except: except:
print("error Account") print("{0} v{1}: Error while importing Account stuff".format(PLUGIN_NAME, PLUGIN_VERSION))
raise traceback.print_exc()
raise
update_account_path(self.deacsmprefs["path_to_account_data"]) update_account_path(self.deacsmprefs["path_to_account_data"])
@ -195,9 +194,20 @@ class ConfigWidget(QWidget):
def export_key(self): def export_key(self):
pluginsdir = os.path.join(config_dir,"plugins")
maindir = os.path.join(pluginsdir,"DeACSM") try:
verdir = os.path.join(maindir,PLUGIN_VERSION) 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"])] filters = [("DER Files", ["der"])]
@ -206,38 +216,16 @@ class ConfigWidget(QWidget):
if (filename is None): if (filename is None):
return 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 ret = exportAccountEncryptionKeyDER(filename)
deacsmprefs = prefs.DeACSM_Prefs()
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): def save_settings(self):
#self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text()) #self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text())