]> xmof Git - DeDRM.git/commitdiff
add support for linux via device serials and reading from device
authorNorbert Preining <norbert@preining.info>
Mon, 14 Sep 2015 05:12:22 +0000 (14:12 +0900)
committerNorbert Preining <norbert@preining.info>
Mon, 14 Sep 2015 05:12:22 +0000 (14:12 +0900)
Obok_calibre_plugin/obok_plugin/__init__.py
Obok_calibre_plugin/obok_plugin/action.py
Obok_calibre_plugin/obok_plugin/config.py
Obok_calibre_plugin/obok_plugin/obok/obok.py

index 0b4e7d31247fd1deb7ea487aaccf9372b4603ce8..dd643c7ea0fbf475a7efab205c82ba6d1ecdf350 100644 (file)
@@ -29,7 +29,7 @@ class ObokDeDRMAction(InterfaceActionBase):
 
     name                    = PLUGIN_NAME
     description             = PLUGIN_DESCRIPTION
-    supported_platforms     = ['windows', 'osx']
+    supported_platforms     = ['windows', 'osx', 'linux' ]
     author                  = PLUGIN_AUTHORS
     version                 = PLUGIN_VERSION_TUPLE
     minimum_calibre_version = (1, 0, 0)
index d93816e0119d1e9ccede0ba7a2af3422d505e574..e951e8afbf1ea61226ed60f7ddd4e0f734ce3d54 100644 (file)
@@ -79,7 +79,7 @@ class InterfacePluginAction(InterfaceAction):
 
         print ('Running {}'.format(PLUGIN_NAME + ' v' + PLUGIN_VERSION))
         # Get the Kobo Library object (obok v3.01)
-        self.library = KoboLibrary()
+        self.library = KoboLibrary(cfg['kobo_serials'])
 
         # Get a list of Kobo titles
         books = self.build_book_list()
index c4e470e292055413f9cee072d49035125e14e279..8f8fa3d13801c45b086b6a3a70b2186b406dad84 100644 (file)
@@ -3,15 +3,23 @@ from __future__ import (unicode_literals, division, absolute_import,
                         print_function)
 
 try:
-    from PyQt5.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
+    from PyQt5.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
 except ImportError:
-    from PyQt4.Qt import (QWidget, QLabel, QVBoxLayout, QHBoxLayout, QComboBox)
-    
+    from PyQt4.Qt import (Qt, QGroupBox, QListWidget, QLineEdit, QDialogButtonBox, QWidget, QLabel, QDialog, QVBoxLayout, QAbstractItemView, QIcon, QHBoxLayout, QComboBox, QListWidgetItem)
+
+try:
+    from PyQt5 import Qt as QtGui
+except ImportError:
+    from PyQt4 import QtGui
+
+from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url)
 from calibre.utils.config import JSONConfig, config_dir
 
 plugin_prefs = JSONConfig('plugins/obok_dedrm_prefs')
 plugin_prefs.defaults['finding_homes_for_formats'] = 'Ask'
+plugin_prefs.defaults['kobo_serials'] = []
 
+from calibre_plugins.dedrm.__init__ import PLUGIN_NAME, PLUGIN_VERSION
 from calibre_plugins.obok_dedrm.utilities import (debug_print)
 try:
     debug_print("obok::config.py - loading translations")
@@ -26,7 +34,10 @@ class ConfigWidget(QWidget):
         self.plugin_action = plugin_action
         layout = QVBoxLayout(self)
         self.setLayout(layout)
-        
+
+        # copy of preferences
+        self.tmpserials = plugin_prefs['kobo_serials']
+
         combo_label = QLabel(_('When should Obok try to insert EPUBs into existing calibre entries?'), self)
         layout.addWidget(combo_label)
         self.find_homes = QComboBox()
@@ -35,6 +46,178 @@ class ConfigWidget(QWidget):
         self.find_homes.addItems([_('Ask'), _('Always'), _('Never')])
         index = self.find_homes.findText(plugin_prefs['finding_homes_for_formats'])
         self.find_homes.setCurrentIndex(index)
+
+        self.serials_button = QtGui.QPushButton(self)
+        self.serials_button.setToolTip(_(u"Click to manage Kobo serial numbers for Kobo ebooks"))
+        self.serials_button.setText(u"Kobo devices serials")
+        self.serials_button.clicked.connect(self.edit_serials)
+        layout.addWidget(self.serials_button)
+
+
+    def edit_serials(self):
+        d = ManageKeysDialog(self,u"Kobo device serial numbers",self.tmpserials, AddSerialDialog)
+        d.exec_()
+
     
     def save_settings(self):
         plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText())
+        plugin_prefs['kobo_serials'] = self.tmpserials
+
+
+
+
+class ManageKeysDialog(QDialog):
+    def __init__(self, parent, key_type_name, plugin_keys, create_key, keyfile_ext = u""):
+        QDialog.__init__(self,parent)
+        self.parent = parent
+        self.key_type_name = key_type_name
+        self.plugin_keys = plugin_keys
+        self.create_key = create_key
+        self.keyfile_ext = keyfile_ext
+        self.json_file = (keyfile_ext == u"k4i")
+
+        self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
+
+        # Start Qt Gui dialog layout
+        layout = QVBoxLayout(self)
+        self.setLayout(layout)
+
+        keys_group_box = QGroupBox(_(u"{0}s".format(self.key_type_name)), self)
+        layout.addWidget(keys_group_box)
+        keys_group_box_layout = QHBoxLayout()
+        keys_group_box.setLayout(keys_group_box_layout)
+
+        self.listy = QListWidget(self)
+        self.listy.setToolTip(u"{0}s that will be used to decrypt ebooks".format(self.key_type_name))
+        self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
+        self.populate_list()
+        keys_group_box_layout.addWidget(self.listy)
+
+        button_layout = QVBoxLayout()
+        keys_group_box_layout.addLayout(button_layout)
+        self._add_key_button = QtGui.QToolButton(self)
+        self._add_key_button.setIcon(QIcon(I('plus.png')))
+        self._add_key_button.setToolTip(u"Create new {0}".format(self.key_type_name))
+        self._add_key_button.clicked.connect(self.add_key)
+        button_layout.addWidget(self._add_key_button)
+
+        self._delete_key_button = QtGui.QToolButton(self)
+        self._delete_key_button.setToolTip(_(u"Delete highlighted key"))
+        self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
+        self._delete_key_button.clicked.connect(self.delete_key)
+        button_layout.addWidget(self._delete_key_button)
+
+        spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
+        button_layout.addItem(spacerItem)
+
+        layout.addSpacing(5)
+        migrate_layout = QHBoxLayout()
+        layout.addLayout(migrate_layout)
+        migrate_layout.addStretch()
+        self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
+        self.button_box.rejected.connect(self.close)
+        migrate_layout.addWidget(self.button_box)
+
+        self.resize(self.sizeHint())
+
+    def populate_list(self):
+        if type(self.plugin_keys) == dict:
+            for key in self.plugin_keys.keys():
+                self.listy.addItem(QListWidgetItem(key))
+        else:
+            for key in self.plugin_keys:
+                self.listy.addItem(QListWidgetItem(key))
+
+    def add_key(self):
+        d = self.create_key(self)
+        d.exec_()
+
+        if d.result() != d.Accepted:
+            # New key generation cancelled.
+            return
+        new_key_value = d.key_value
+        if new_key_value in self.plugin_keys:
+            info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
+                        u"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
+            return
+
+        self.plugin_keys.append(d.key_value)
+        self.listy.clear()
+        self.populate_list()
+
+    def rename_key(self):
+        if not self.listy.currentItem():
+            errmsg = u"No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
+            r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
+                                    _(errmsg), show=True, show_copy_button=False)
+            return
+
+        d = RenameKeyDialog(self)
+        d.exec_()
+
+        if d.result() != d.Accepted:
+            # rename cancelled or moot.
+            return
+        keyname = unicode(self.listy.currentItem().text())
+        if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
+            return
+        self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
+        del self.plugin_keys[keyname]
+
+        self.listy.clear()
+        self.populate_list()
+
+    def delete_key(self):
+        if not self.listy.currentItem():
+            return
+        keyname = unicode(self.listy.currentItem().text())
+        if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), u"Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
+            return
+        self.plugin_keys.remove(keyname)
+
+        self.listy.clear()
+        self.populate_list()
+
+class AddSerialDialog(QDialog):
+    def __init__(self, parent=None,):
+        QDialog.__init__(self, parent)
+        self.parent = parent
+        self.setWindowTitle(u"{0} {1}: Add New eInk Kobo Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
+        layout = QVBoxLayout(self)
+        self.setLayout(layout)
+
+        data_group_box = QGroupBox(u"", self)
+        layout.addWidget(data_group_box)
+        data_group_box_layout = QVBoxLayout()
+        data_group_box.setLayout(data_group_box_layout)
+
+        key_group = QHBoxLayout()
+        data_group_box_layout.addLayout(key_group)
+        key_group.addWidget(QLabel(u"EInk Kobo Serial Number:", self))
+        self.key_ledit = QLineEdit("", self)
+        self.key_ledit.setToolTip(u"Enter an eInk Kobo serial number. EInk Kobo serial numbers are 13 characters long and usually start with a 'N'. Kobo Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
+        key_group.addWidget(self.key_ledit)
+
+        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        self.button_box.accepted.connect(self.accept)
+        self.button_box.rejected.connect(self.reject)
+        layout.addWidget(self.button_box)
+
+        self.resize(self.sizeHint())
+
+    @property
+    def key_name(self):
+        return unicode(self.key_ledit.text()).strip()
+
+    @property
+    def key_value(self):
+        return unicode(self.key_ledit.text()).strip()
+
+    def accept(self):
+        if len(self.key_name) == 0 or self.key_name.isspace():
+            errmsg = u"Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
+            return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
+        if len(self.key_name) != 13:
+            errmsg = u"EInk Kobo Serial Numbers must be 13 characters long. This is {0:d} characters long.".format(len(self.key_name))
+            return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
+        QDialog.accept(self)
index 18e629bccef21148db2e7ea9c0aa8a339b20d93b..577312e855bc125323b96e55e69bca420fe1bc70 100644 (file)
@@ -244,7 +244,7 @@ class KoboLibrary(object):
     written by the Kobo Desktop Edition application, including the list
     of books, their titles, and the user's encryption key(s)."""
 
-    def __init__ (self):
+    def __init__ (self, serials = []):
         print u"Obok v{0}\nCopyright © 2012-2015 Physisticated et al.".format(__version__)
         if sys.platform.startswith('win'):
             if sys.getwindowsversion().major > 5:
@@ -254,13 +254,20 @@ class KoboLibrary(object):
             self.kobodir = os.path.join(self.kobodir, 'Kobo', 'Kobo Desktop Edition')
         elif sys.platform.startswith('darwin'):
             self.kobodir = os.path.join(os.environ['HOME'], 'Library', 'Application Support', 'Kobo', 'Kobo Desktop Edition')
+        elif sys.platform.startswith('linux'):
+            # TODO TODO TODO needs change - fixed path to mount point
+            self.kobodir = '/media/norbert/KOBOeReader/.kobo'
         self.bookdir = os.path.join(self.kobodir, 'kepub')
-        kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
+        if sys.platform.startswith('linux'):
+            kobodb = os.path.join(self.kobodir, 'KoboReader.sqlite')
+        else:
+            kobodb = os.path.join(self.kobodir, 'Kobo.sqlite')
         self.__sqlite = sqlite3.connect(kobodb)
         self.__cursor = self.__sqlite.cursor()
         self._userkeys = []
         self._books = []
         self._volumeID = []
+        self._serials = serials
 
     def close (self):
         """Closes the database used by the library."""
@@ -319,6 +326,9 @@ class KoboLibrary(object):
             for m in matches:
                 # print "m:",m[0]
                 macaddrs.append(m[0].upper())
+        elif sys.platform.startswith('linux'):
+            macaddrs.extend(self._serials)
+
         return macaddrs
 
     def __getuserids (self):
@@ -486,7 +496,7 @@ def cli_main():
     lib = KoboLibrary()
 
     for i, book in enumerate(lib.books):
-        print ('%d: %s' % (i + 1, book.title))
+        print ('%d: %s' % (i + 1, book.title)).encode('ascii', 'ignore')
 
     num_string = raw_input("Convert book number... ")
     try: