]> xmof Git - DeDRM.git/commitdiff
Update standalone
authorcalibre-user <sutemeado2023+calibre@gmail.com>
Sat, 21 Jun 2025 18:22:26 +0000 (03:22 +0900)
committercalibre-user <sutemeado2023+calibre@gmail.com>
Sat, 21 Jun 2025 18:22:26 +0000 (03:22 +0900)
DeDRM_plugin/standalone/__init__.py
DeDRM_plugin/standalone/passhash.py
DeDRM_plugin/standalone/remove_drm.py

index 60b615bffaeb09f49e2b1c527fec47dea27e0ac9..60d236ea66a6f989415bd091a9f8a9959072617b 100644 (file)
@@ -37,6 +37,12 @@ OPT_SHORT_TO_LONG = [
 
 import os, sys
 
+# When executed as a module ("python -m DeDRM_plugin.standalone.__init__"),
+# this file becomes '__main__'. Ensure the package name also maps to this
+# module so that sibling modules can reliably import it.
+if __name__ == "__main__":
+    sys.modules.setdefault('DeDRM_plugin.standalone.__init__', sys.modules[__name__])
+
 
 global _additional_data
 global _additional_params
@@ -48,6 +54,9 @@ _function = None
 global config_file_path
 config_file_path = "dedrm.json"
 
+# Path to a Kindle voucher key file passed via --keyfile
+kfx_skeyfile = None
+
 def print_fname(f, info):
     print("  " + f.ljust(15) + " " + info)
 
@@ -72,13 +81,13 @@ def print_std_usage(name, param_string):
         print("  python3 DeDRM_plugin.zip "+name+" "+param_string, file=sys.stderr)
 
 def print_err_header():
-    from __init__ import PLUGIN_NAME, PLUGIN_VERSION # type: ignore
+    from ..__version import PLUGIN_NAME, PLUGIN_VERSION
 
     print(PLUGIN_NAME + " v" + PLUGIN_VERSION + " - DRM removal plugin by noDRM")
     print()
 
 def print_help():
-    from __version import PLUGIN_NAME, PLUGIN_VERSION
+    from ..__version import PLUGIN_NAME, PLUGIN_VERSION
     print(PLUGIN_NAME + " v" + PLUGIN_VERSION + " - DRM removal plugin by noDRM")
     print("Based on DeDRM Calibre plugin by Apprentice Harper, Apprentice Alf and others.")
     print("See https://github.com/noDRM/DeDRM_tools for more information.")
@@ -98,7 +107,7 @@ def print_help():
     # TODO: All parameters that are global should be listed here.
 
 def print_credits():
-    from __version import PLUGIN_NAME, PLUGIN_VERSION
+    from ..__version import PLUGIN_NAME, PLUGIN_VERSION
     print(PLUGIN_NAME + " v" + PLUGIN_VERSION + " - Calibre DRM removal plugin by noDRM")
     print("Based on DeDRM Calibre plugin by Apprentice Harper, Apprentice Alf and others.")
     print("See https://github.com/noDRM/DeDRM_tools for more information.")
@@ -122,15 +131,21 @@ def handle_single_argument(arg, next):
     global _additional_params
     global config_file_path
     
-    if arg in ["--username", "--password", "--output", "--outputdir"]: 
+    global kfx_skeyfile
+
+    if arg in ["--username", "--password", "--output", "--outputdir", "--keyfile"]:
         used_up = 1
         _additional_params.append(arg)
-        if next is None or len(next) == 0: 
+        if next is None or len(next) == 0:
             print_err_header()
             print("Missing parameter for argument " + arg, file=sys.stderr)
             sys.exit(1)
         else:
-            _additional_params.append(next[0])
+            val = next[0]
+            if arg == "--keyfile":
+                kfx_skeyfile = val
+            else:
+                _additional_params.append(val)
     
     elif arg == "--config":
         if next is None or len(next) == 0: 
@@ -173,14 +188,14 @@ def execute_action(action, filenames, params):
         sys.exit(0)
     
     elif action == "passhash": 
-        from standalone.passhash import perform_action
+        from .passhash import perform_action
         perform_action(params, filenames)
 
     elif action == "remove_drm":
         if not os.path.isfile(os.path.abspath(config_file_path)):
             print("Config file missing ...")
         
-        from standalone.remove_drm import perform_action
+        from .remove_drm import perform_action
         perform_action(params, filenames)
         
     elif action == "config":
index 028828d3bf1fc469406b4291e191a59877f998ef..a0b22dbdcea78b8a3c9f855a362225c47a6d957e 100644 (file)
@@ -23,13 +23,13 @@ change in the future.
 
 import os, sys
 
-from standalone.__init__ import print_opt, print_std_usage
+from .__init__ import print_opt, print_std_usage
 
 iswindows = sys.platform.startswith('win')
 isosx = sys.platform.startswith('darwin')
 
 def print_passhash_help():
-    from __version import PLUGIN_NAME, PLUGIN_VERSION
+    from ..__version import PLUGIN_NAME, PLUGIN_VERSION
     print(PLUGIN_NAME + " v" + PLUGIN_VERSION + " - Calibre DRM removal plugin by noDRM")
     print()
     print("passhash: Manage Adobe PassHashes")
index a67bc6f40eaa650ec465edc81d3184313c574fac..61fd66964b1e12ca4bcee3f37c82de1d6fce48f9 100644 (file)
@@ -26,18 +26,27 @@ import os, sys
 from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
 from contextlib import closing
 
-from standalone.__init__ import print_opt, print_std_usage
+# Ensure absolute imports within the main plugin work when this module is run
+# as part of the standalone package
+package_root = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+if package_root not in sys.path:
+    sys.path.insert(0, package_root)
+
+from .__init__ import print_opt, print_std_usage
 
 iswindows = sys.platform.startswith('win')
 isosx = sys.platform.startswith('darwin')
 
+# Path to a Kindle voucher key file used for KFX books
+kfx_skeyfile = None
+
 def print_removedrm_help():
-    from __init__ import PLUGIN_NAME, PLUGIN_VERSION
+    from ..__version import PLUGIN_NAME, PLUGIN_VERSION
     print(PLUGIN_NAME + " v" + PLUGIN_VERSION + " - Calibre DRM removal plugin by noDRM")
     print()
     print("remove_drm: Remove DRM from one or multiple files")
     print()
-    print_std_usage("remove_drm", "<filename> ... [ -o <filename> ] [ -f ]")
+    print_std_usage("remove_drm", "<filename> ... [ -o <filename> ] [ -f ] [ --keyfile <file> ]")
     
     print()
     print("Options: ")
@@ -45,6 +54,7 @@ def print_removedrm_help():
     print_opt("o", "output", "File name to export the file to")
     print_opt("f", "force", "Overwrite output file if it already exists")
     print_opt(None, "overwrite", "Replace DRMed file with DRM-free file (implies --force)")
+    print_opt(None, "keyfile", "Path to a Kindle voucher key file")
 
 
 def determine_file_type(file):
@@ -113,13 +123,75 @@ def dedrm_single_file(input_file, output_file):
     print("File " + input_file + " to " + output_file)
 
     # Okay, first check the file type and don't rely on the extension. 
-    try: 
+    try:
         ftype = determine_file_type(input_file)
-    except
+    except Exception as e:
         print("Can't determine file type for this file.")
         ftype = None
+        try:
+            with open(input_file, 'rb') as fh:
+                hdr = fh.read(4)
+            if hdr == b'PK\x03\x04':
+                # check if ZIP contains a DRMION file
+                from zipfile import ZipFile
+                with ZipFile(input_file, 'r') as zf:
+                    for name in zf.namelist():
+                        with zf.open(name) as sf:
+                            if sf.read(8) == b'\xeaDRMION\xee':
+                                ftype = "KFX-ZIP"
+                                break
+                if ftype is None:
+                    ftype = "ZIP"
+        except Exception:
+            pass
     
-    if ftype is None: 
+    if ftype is None:
+        return
+
+    if ftype == "KFX-ZIP":
+        from ..kfxdedrm import KFXZipBook
+        from .__init__ import kfx_skeyfile
+        keyfile = kfx_skeyfile
+        temp_keyfile = None
+        # Some key files omit the trailing '.voucher' on the voucher ID.
+        # Add a duplicate entry with the suffix so the ion parser can match.
+        if keyfile and os.path.isfile(keyfile):
+            with open(keyfile, "r", encoding="utf8") as fh:
+                lines = fh.read().splitlines()
+            amended = []
+            changed = False
+            for line in lines:
+                amended.append(line)
+                parts = line.split("$", 1)
+                if len(parts) > 1:
+                    vid, rest = parts[0], parts[1]
+                    if not vid.endswith(".voucher"):
+                        amended.append(f"{vid}.voucher${rest}")
+                        changed = True
+            if changed:
+                import tempfile
+                tf = tempfile.NamedTemporaryFile("w", delete=False)
+                tf.write("\n".join(amended) + "\n")
+                tf.close()
+                temp_keyfile = tf.name
+                keyfile = temp_keyfile
+        try:
+            book = KFXZipBook(input_file, keyfile)
+            book.processBook([''])
+            # Ensure output directory exists
+            outdir = os.path.dirname(output_file)
+            if outdir and not os.path.isdir(outdir):
+                os.makedirs(outdir, exist_ok=True)
+            book.getFile(output_file)
+            print("Saved decrypted KFX book to " + output_file)
+        except Exception as e:
+            print("Failed to remove DRM: " + str(e), file=sys.stderr)
+        finally:
+            if temp_keyfile:
+                try:
+                    os.unlink(temp_keyfile)
+                except Exception:
+                    pass
         return