diff --git a/README.md b/README.md index 23fcbb0..cf01937 100644 --- a/README.md +++ b/README.md @@ -49,4 +49,5 @@ Though, generally it's recommended to use the Calibre plugin instead of these st - Support to copy an authorization from the plugin to an ADE install - Support for Adobe's "auth" download method instead of the "simple" method. +- Support to authorize an eReader that's connected over USB - ... \ No newline at end of file diff --git a/bundle_calibre_plugin.sh b/bundle_calibre_plugin.sh index daad259..7c0f055 100755 --- a/bundle_calibre_plugin.sh +++ b/bundle_calibre_plugin.sh @@ -9,13 +9,18 @@ pushd calibre-plugin pushd keyextract -# Compile: +# Compile C programs: make popd +# Set module ID. This needs to be changed if any of the module ZIPs change. echo -n "2021-12-15-01" > module_id.txt +# Copy LICENSE so it'll be included in the ZIP. +cp ../LICENSE LICENSE + +# Create ZIP file from calibre-plugin folder. zip -r ../calibre-plugin.zip * popd diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 9b8fd47..9d10f78 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -13,6 +13,7 @@ import time, datetime from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QGroupBox, QPushButton, QListWidget, QListWidgetItem, QInputDialog, + QFileDialog, QLineEdit, QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) from PyQt5.QtWidgets import QShortcut @@ -105,11 +106,18 @@ class ConfigWidget(QWidget): if mail is None: # Current auth is anon auth. Offer to link to account. - self.button_convert_anon_to_account = QtGui.QPushButton(self) self.button_convert_anon_to_account.setText(_("Connect anonymous auth to ADE account")) self.button_convert_anon_to_account.clicked.connect(self.convert_anon_to_account) - ua_group_box_layout.addWidget(self.button_convert_anon_to_account) + ua_group_box_layout.addWidget(self.button_convert_anon_to_account) + + #if mail is not None: + # We do have an email. Offer to manage devices / eReaders + # Button commented out as this isn't fully implemented yet. + #self.button_manage_ext_device = QtGui.QPushButton(self) + #self.button_manage_ext_device.setText(_("Manage connected eReaders")) + #self.button_manage_ext_device.clicked.connect(self.manage_ext_device) + #ua_group_box_layout.addWidget(self.button_manage_ext_device) self.button_switch_ade_version = QtGui.QPushButton(self) self.button_switch_ade_version.setText(_("Change ADE version")) @@ -229,6 +237,107 @@ class ConfigWidget(QWidget): warning_dialog(None, "Warning", msg, show=True, show_copy_button=False) + def manage_ext_device(self): + # Unfortunately, I didn't find a nice cross-platform API to query for USB mass storage devices. + # So just open up a folder picker dialog and have the user select the eReader's root folder. + + info_string, activated, mail = self.get_account_info() + + if not activated: + return + + if mail is None: + return + + info_dialog(None, "Manage eReader", "Please select the eBook reader you want to manage", show=True, show_copy_button=False) + + dialog = QFileDialog() + dialog.setFileMode(QFileDialog.Directory) + opts = dialog.options() + opts |= QFileDialog.ShowDirsOnly + dialog.setOptions(opts) + + if dialog.exec_(): + x = dialog.selectedFiles()[0] + if not os.path.isdir(x): + # This is not supposed to happen. + error_dialog(None, "Manage eReader", "Device not found", show=True, show_copy_button=False) + return + + idx = 0 + while True: + idx = idx + 1 + if idx > 15: + # Failsafe, max. 15 folder levels. + break + adobe_path = os.path.join(x, ".adobe-digital-editions") + print("Checking " + adobe_path) + if os.path.isdir(adobe_path): + print("Found Adobe path at " + adobe_path) + break + + x_old = x + x = os.path.dirname(x) + + if x_old == x: + # We're at the drive root and still didn't find an activation + error_dialog(None, "Manage eReader", "Didn't find an ADE-compatible eReader in that location. (No Folder)", show=True, show_copy_button=False) + return + + if not os.path.isfile(os.path.join(adobe_path, "device.xml")): + error_dialog(None, "Manage eReader", "Didn't find an ADE-compatible eReader in that location. (No File)", show=True, show_copy_button=False) + return + + dev_xml_path = os.path.join(adobe_path, "device.xml") + act_xml_path = os.path.join(adobe_path, "activation.xml") + + dev_xml_tree = etree.parse(dev_xml_path) + adNS = lambda tag: '{%s}%s' % ('http://ns.adobe.com/adept', tag) + try: + devClass = dev_xml_tree.find("./%s" % (adNS("deviceClass"))).text + devName = dev_xml_tree.find("./%s" % (adNS("deviceName"))).text + except: + error_dialog(None, "Manage eReader", "Reader data is invalid.", show=True, show_copy_button=False) + return + + try: + devSerial = None + devSerial = dev_xml_tree.find("./%s" % (adNS("deviceSerial"))).text + except: + pass + + + print("Found Reader with Class " + devClass + " and Name " + devName) + + if os.path.isfile(act_xml_path): + print("Already activated.") + msg = "The given device (Type \""+devClass+"\", Name \""+devName+"\") is already connected to an AdobeID.\n" + msg += "Currently, this plugin does not support un-authorizing an eReader." + + error_dialog(None, "Manage eReader", msg, show=True, show_copy_button=False) + return + + + msg = "Found an unactivated eReader:\n\n" + msg += "Path: " + os.path.dirname(adobe_path) + "\n" + msg += "Type: " + devClass + "\n" + msg += "Name: " + devName + "\n" + if devSerial is not None: + msg += "Serial: " + devSerial + "\n" + msg += "\nDo you want to authorize this device with your AdobeID?" + + ok = question_dialog(None, "Manage eReader", msg) + if not ok: + return + + + # Okay, if we end up here, the user wants to authorize his eReader to his current AdobeID. + # Rest still needs to be implemented. + # + + error_dialog(None, "Manage eReader", "Not yet implemented.", show=True, show_copy_button=False) + + def delete_ade_auth(self):