diff options
author | Apprentice Harper <[email protected]> | 2020-09-26 21:22:47 +0100 |
---|---|---|
committer | Apprentice Harper <[email protected]> | 2020-09-26 21:22:47 +0100 |
commit | afa4ac571664368dc27512768a67656ea7dd645a (patch) | |
tree | 57ca18d568ce426ce3bfa27b45dff60efa803604 /Obok_plugin | |
parent | 4868a7460e39910ad22d0949d26b3250656dea46 (diff) |
Starting on Version 7.0 using the work done by others. Completely untested. I will be testing things, but I thought I'd get this base version up for others to give pull requests.
THIS IS ON THE MASTER BRANCH. The Master branch will be Python 3.0 from now on. While Python 2.7 support will not be deliberately broken, all efforts should now focus on Python 3.0 compatibility.
I can see a lot of work has been done. There's more to do. I've bumped the version number of everything I came across to the next major number for Python 3.0 compatibility indication.
Thanks everyone. I hope to update here at least once a week until we have a stable 7.0 release for calibre 5.0
Diffstat (limited to 'Obok_plugin')
-rw-r--r-- | Obok_plugin/action.py | 37 | ||||
-rw-r--r-- | Obok_plugin/common_utils.py | 4 | ||||
-rw-r--r-- | Obok_plugin/config.py | 8 | ||||
-rw-r--r-- | Obok_plugin/obok/obok.py | 55 | ||||
-rw-r--r-- | Obok_plugin/utilities.py | 15 |
5 files changed, 63 insertions, 56 deletions
diff --git a/Obok_plugin/action.py b/Obok_plugin/action.py index a0b63a6..802a833 100644 --- a/Obok_plugin/action.py +++ b/Obok_plugin/action.py @@ -6,13 +6,14 @@ __license__ = 'GPL v3' __docformat__ = 'restructuredtext en' +import codecs import os, traceback, zipfile try: from PyQt5.Qt import QToolButton, QUrl except ImportError: from PyQt4.Qt import QToolButton, QUrl - + from calibre.gui2 import open_url, question_dialog from calibre.gui2.actions import InterfaceAction from calibre.utils.config import config_dir @@ -24,7 +25,7 @@ from calibre.ebooks.metadata.meta import get_metadata from calibre_plugins.obok_dedrm.dialogs import (SelectionDialog, DecryptAddProgressDialog, AddEpubFormatsProgressDialog, ResultsSummaryDialog) from calibre_plugins.obok_dedrm.config import plugin_prefs as cfg -from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME, +from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION, HELPFILE_NAME) from calibre_plugins.obok_dedrm.utilities import ( get_icon, set_plugin_icon_resources, showErrorDlg, format_plural, @@ -53,7 +54,7 @@ class InterfacePluginAction(InterfaceAction): def genesis(self): icon_resources = self.load_resources(PLUGIN_ICONS) set_plugin_icon_resources(PLUGIN_NAME, icon_resources) - + self.qaction.setIcon(get_icon(PLUGIN_ICONS[0])) self.qaction.triggered.connect(self.launchObok) self.gui.keyboard.finalize() @@ -106,10 +107,10 @@ class InterfacePluginAction(InterfaceAction): # Get a list of Kobo titles books = self.build_book_list() if len(books) < 1: - msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed\configured\synchronized?') + msg = _('<p>No books found in Kobo Library\nAre you sure it\'s installed/configured/synchronized?') showErrorDlg(msg, None) return - + # Check to see if a key can be retrieved using the legacy obok method. legacy_key = legacy_obok().get_legacy_cookie_id if legacy_key is not None: @@ -154,7 +155,7 @@ class InterfacePluginAction(InterfaceAction): # Close Kobo Library object self.library.close() - # If we have decrypted books to work with, feed the list of decrypted books details + # If we have decrypted books to work with, feed the list of decrypted books details # and the callback function (self.add_new_books) to the ProgressDialog dispatcher. if len(self.books_to_add): d = DecryptAddProgressDialog(self.gui, self.books_to_add, self.add_new_books, self.db, 'calibre', @@ -212,7 +213,7 @@ class InterfacePluginAction(InterfaceAction): def get_decrypted_kobo_books(self, book): ''' This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to decrypt Kobo books - + :param book: A KoboBook object that is to be decrypted. ''' print (_('{0} - Decrypting {1}').format(PLUGIN_NAME + ' v' + PLUGIN_VERSION, book.title)) @@ -233,7 +234,7 @@ class InterfacePluginAction(InterfaceAction): ''' This method is a call-back function used by DecryptAddProgressDialog in dialogs.py to add books to calibre (It's set up to handle multiple books, but will only be fed books one at a time by DecryptAddProgressDialog) - + :param books_to_add: List of calibre bookmaps (created in get_decrypted_kobo_books) ''' added = self.db.add_books(books_to_add, add_duplicates=False, run_hooks=False) @@ -253,7 +254,7 @@ class InterfacePluginAction(InterfaceAction): def add_epub_format(self, book_id, mi, path): ''' This method is a call-back function used by AddEpubFormatsProgressDialog in dialogs.py - + :param book_id: calibre ID of the book to add the encrypted epub to. :param mi: calibre metadata object :param path: path to the decrypted epub (temp file) @@ -281,7 +282,7 @@ class InterfacePluginAction(InterfaceAction): self.formats_to_add.append((home_id, mi, tmp_file)) else: self.no_home_for_book.append(mi) - # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book + # If we found homes for decrypted epubs in existing calibre entries, feed the list of decrypted book # details and the callback function (self.add_epub_format) to the ProgressDialog dispatcher. if self.formats_to_add: d = AddEpubFormatsProgressDialog(self.gui, self.formats_to_add, self.add_epub_format) @@ -306,10 +307,10 @@ class InterfacePluginAction(InterfaceAction): sd = ResultsSummaryDialog(self.gui, caption, msg, log) sd.exec_() return - + def ask_about_inserting_epubs(self): ''' - Build question dialog with details about kobo books + Build question dialog with details about kobo books that couldn't be added to calibre as new books. ''' ''' Terisa: Improve the message @@ -327,13 +328,13 @@ class InterfacePluginAction(InterfaceAction): msg = _('<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />').format(self.duplicate_book_list[0][0].title, self.duplicate_book_list[0][2]) msg += _('Would you like to try and add the EPUB format to an available calibre duplicate?<br /><br />') msg += _('NOTE: no pre-existing EPUB will be overwritten.') - + return question_dialog(self.gui, caption, msg, det_msg) def find_a_home(self, ids): ''' Find the ID of the first EPUB-Free duplicate available - + :param ids: List of calibre IDs that might serve as a home. ''' for id in ids: @@ -373,7 +374,7 @@ class InterfacePluginAction(InterfaceAction): zin = zipfile.ZipFile(book.filename, 'r') #print ('Kobo library filename: {0}'.format(book.filename)) for userkey in self.userkeys: - print (_('Trying key: '), userkey.encode('hex_codec')) + print (_('Trying key: '), codecs.encode(userkey, 'hex')) check = True try: fileout = PersistentTemporaryFile('.epub', dir=self.tdir) @@ -455,7 +456,7 @@ class InterfacePluginAction(InterfaceAction): if cancelled_count > 0: log += _('<p><b>Book imports cancelled by user:</b> {}</p>\n').format(cancelled_count) return (msg, log) - log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds)) + log += _('<p><b>New EPUB formats inserted in existing calibre books:</b> {0}</p>\n').format(len(self.successful_format_adds)) if self.successful_format_adds: log += '<ul>\n' for id, mi in self.successful_format_adds: @@ -474,7 +475,7 @@ class InterfacePluginAction(InterfaceAction): log += _('<p><b>Format imports cancelled by user:</b> {}</p>\n').format(cancelled_count) return (msg, log) else: - + # Single book ... don't get fancy. if self.ids_of_new_books: title = self.ids_of_new_books[0][1].title @@ -494,4 +495,4 @@ class InterfacePluginAction(InterfaceAction): reason = _('of unknown reasons. Gosh I\'m embarrassed!') msg = _('<p>{0} not added because {1}').format(title, reason) return (msg, log) - + diff --git a/Obok_plugin/common_utils.py b/Obok_plugin/common_utils.py index 0f2164a..babad1c 100644 --- a/Obok_plugin/common_utils.py +++ b/Obok_plugin/common_utils.py @@ -427,7 +427,7 @@ class KeyValueComboBox(QComboBox): def selected_key(self): for key, value in self.values.iteritems(): - if value == unicode(self.currentText()).strip(): + if value == self.currentText().strip(): return key @@ -450,7 +450,7 @@ class KeyComboBox(QComboBox): def selected_key(self): for key, value in self.values.iteritems(): - if key == unicode(self.currentText()).strip(): + if key == self.currentText().strip(): return key diff --git a/Obok_plugin/config.py b/Obok_plugin/config.py index 8244b91..522b86a 100644 --- a/Obok_plugin/config.py +++ b/Obok_plugin/config.py @@ -75,7 +75,7 @@ class ConfigWidget(QWidget): def save_settings(self): - plugin_prefs['finding_homes_for_formats'] = unicode(self.find_homes.currentText()) + plugin_prefs['finding_homes_for_formats'] = self.find_homes.currentText() plugin_prefs['kobo_serials'] = self.tmpserials plugin_prefs['kobo_directory'] = self.kobodirectory @@ -165,7 +165,7 @@ class ManageKeysDialog(QDialog): def delete_key(self): if not self.listy.currentItem(): return - keyname = unicode(self.listy.currentItem().text()) + keyname = 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) @@ -202,11 +202,11 @@ class AddSerialDialog(QDialog): @property def key_name(self): - return unicode(self.key_ledit.text()).strip() + return self.key_ledit.text().strip() @property def key_value(self): - return unicode(self.key_ledit.text()).strip() + return self.key_ledit.text().strip() def accept(self): if len(self.key_name) == 0 or self.key_name.isspace(): diff --git a/Obok_plugin/obok/obok.py b/Obok_plugin/obok/obok.py index 21ef14a..80bc058 100644 --- a/Obok_plugin/obok/obok.py +++ b/Obok_plugin/obok/obok.py @@ -1,6 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +# Version 4.0.0 September 2020 +# Python 3.0 +# # Version 3.2.5 December 2016 # Improve detection of good text decryption. # @@ -152,8 +155,8 @@ """Manage all Kobo books, either encrypted or DRM-free.""" from __future__ import print_function -__version__ = '3.2.4' -__about__ = u"Obok v{0}\nCopyright © 2012-2016 Physisticated et al.".format(__version__) +__version__ = '4.0.0' +__about__ = u"Obok v{0}\nCopyright © 2012-2020 Physisticated et al.".format(__version__) import sys import os @@ -231,7 +234,7 @@ def _load_crypto_libcrypto(): raise ENCRYPTIONError(_('Failed to initialize AES key')) def decrypt(self, data): - clear = '' + clear = b'' for i in range(0, len(data), 16): out = create_string_buffer(16) rv = AES_ecb_encrypt(data[i:i+16], out, self._key, 0) @@ -276,7 +279,7 @@ class SafeUnbuffered: if self.encoding == None: self.encoding = "utf-8" def write(self, data): - if isinstance(data,unicode): + if isinstance(data,bytes): data = data.encode(self.encoding,"replace") self.stream.write(data) self.stream.flush() @@ -381,7 +384,7 @@ class KoboLibrary(object): print(self.newdb.name) olddb = open(kobodb, 'rb') self.newdb.write(olddb.read(18)) - self.newdb.write('\x01\x01') + self.newdb.write(b'\x01\x01') olddb.read(2) self.newdb.write(olddb.read()) olddb.close() @@ -488,14 +491,14 @@ class KoboLibrary(object): pass row = cursor.fetchone() return userids - + def __getuserkeys (self, macaddr): userids = self.__getuserids() userkeys = [] for hash in KOBO_HASH_KEYS: - deviceid = hashlib.sha256(hash + macaddr).hexdigest() + deviceid = hashlib.sha256((hash + macaddr).encode('ascii')).hexdigest() for userid in userids: - userkey = hashlib.sha256(deviceid + userid).hexdigest() + userkey = hashlib.sha256((deviceid + userid).encode('ascii')).hexdigest() userkeys.append(binascii.a2b_hex(userkey[32:])) return userkeys @@ -556,7 +559,7 @@ class KoboBook(object): # Convert relative URIs href = item.attrib['href'] if not c.match(href): - href = string.join((basedir, href), '') + href = ''.join((basedir, href)) # Update books we've found from the DB. if href in self._encryptedfiles: @@ -606,57 +609,57 @@ class KoboFile(object): stride = 1 print(u"Checking text:{0}:".format(contents[:10])) # check for byte order mark - if contents[:3]=="\xef\xbb\xbf": + if contents[:3]==b"\xef\xbb\xbf": # seems to be utf-8 with BOM print(u"Could be utf-8 with BOM") textoffset = 3 - elif contents[:2]=="\xfe\xff": + elif contents[:2]==b"\xfe\xff": # seems to be utf-16BE print(u"Could be utf-16BE") textoffset = 3 stride = 2 - elif contents[:2]=="\xff\xfe": + elif contents[:2]==b"\xff\xfe": # seems to be utf-16LE print(u"Could be utf-16LE") textoffset = 2 stride = 2 else: print(u"Perhaps utf-8 without BOM") - + # now check that the first few characters are in the ASCII range - for i in xrange(textoffset,textoffset+5*stride,stride): - if ord(contents[i])<32 or ord(contents[i])>127: + for i in range(textoffset,textoffset+5*stride,stride): + if contents[i]<32 or contents[i]>127: # Non-ascii, so decryption probably failed - print(u"Bad character at {0}, value {1}".format(i,ord(contents[i]))) + print(u"Bad character at {0}, value {1}".format(i,contents[i])) raise ValueError print(u"Seems to be good text") return True - if contents[:5]=="<?xml" or contents[:8]=="\xef\xbb\xbf<?xml": + if contents[:5]==b"<?xml" or contents[:8]==b"\xef\xbb\xbf<?xml": # utf-8 return True - elif contents[:14]=="\xfe\xff\x00<\x00?\x00x\x00m\x00l": + elif contents[:14]==b"\xfe\xff\x00<\x00?\x00x\x00m\x00l": # utf-16BE return True - elif contents[:14]=="\xff\xfe<\x00?\x00x\x00m\x00l\x00": + elif contents[:14]==b"\xff\xfe<\x00?\x00x\x00m\x00l\x00": # utf-16LE return True - elif contents[:9]=="<!DOCTYPE" or contents[:12]=="\xef\xbb\xbf<!DOCTYPE": + elif contents[:9]==b"<!DOCTYPE" or contents[:12]==b"\xef\xbb\xbf<!DOCTYPE": # utf-8 of weird <!DOCTYPE start return True - elif contents[:22]=="\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E": + elif contents[:22]==b"\xfe\xff\x00<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E": # utf-16BE of weird <!DOCTYPE start return True - elif contents[:22]=="\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00": + elif contents[:22]==b"\xff\xfe<\x00!\x00D\x00O\x00C\x00T\x00Y\x00P\x00E\x00": # utf-16LE of weird <!DOCTYPE start return True else: print(u"Bad XML: {0}".format(contents[:8])) raise ValueError elif self.mimetype == 'image/jpeg': - if contents[:3] == '\xff\xd8\xff': + if contents[:3] == b'\xff\xd8\xff': return True else: - print(u"Bad JPEG: {0}".format(contents[:3].encode('hex'))) + print(u"Bad JPEG: {0}".format(contents[:3].hex())) raise ValueError() return False @@ -690,7 +693,7 @@ def decrypt_book(book, lib): return 0 result = 1 for userkey in lib.userkeys: - print(u"Trying key: {0}".format(userkey.encode('hex_codec'))) + print(u"Trying key: {0}".format(userkey.hex())) try: zout = zipfile.ZipFile(outname, "w", zipfile.ZIP_DEFLATED) for filename in zin.namelist(): @@ -735,7 +738,7 @@ def cli_main(): print(u"{0}: {1}".format(i + 1, book.title)) print(u"Or 'all'") - choice = raw_input(u"Convert book number... ") + choice = input(u"Convert book number... ") if choice == u'all': books = list(lib.books) else: diff --git a/Obok_plugin/utilities.py b/Obok_plugin/utilities.py index 62e305c..f81bd1c 100644 --- a/Obok_plugin/utilities.py +++ b/Obok_plugin/utilities.py @@ -7,21 +7,24 @@ __docformat__ = 'restructuredtext en' import os, struct, time -from StringIO import StringIO +try: + from StringIO import StringIO +except ImportError: + from io import StringIO from traceback import print_exc try: from PyQt5.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem) except ImportError: from PyQt4.Qt import (Qt, QDialog, QPixmap, QIcon, QLabel, QHBoxLayout, QFont, QTableWidgetItem) - + from calibre.utils.config import config_dir from calibre.constants import iswindows, DEBUG from calibre import prints from calibre.gui2 import (error_dialog, gprefs) from calibre.gui2.actions import menu_action_unique_name -from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, +from calibre_plugins.obok_dedrm.__init__ import (PLUGIN_NAME, PLUGIN_SAFE_NAME, PLUGIN_VERSION, PLUGIN_DESCRIPTION) plugin_ID = None @@ -39,7 +42,7 @@ else: def convert_qvariant(x): vt = x.type() if vt == x.String: - return unicode(x.toString()) + return x.toString() if vt == x.List: return [convert_qvariant(i) for i in x.toList()] return x.toPyObject() @@ -62,7 +65,7 @@ except NameError: def format_plural(number, possessive=False): ''' Cosmetic ditty to provide the proper string formatting variable to handle singular/plural situations - + :param: number: variable that represents the count/len of something ''' if not possessive: @@ -141,7 +144,7 @@ def showErrorDlg(errmsg, parent, trcbk=False): ''' if trcbk: error= '' - f=StringIO() + f=StringIO() print_exc(file=f) error_mess = f.getvalue().splitlines() for line in error_mess: |