diff --git a/flake.lock b/flake.lock index c41a39e..52ed2b5 100644 --- a/flake.lock +++ b/flake.lock @@ -7,43 +7,26 @@ ] }, "locked": { - "lastModified": 1633315458, - "narHash": "sha256-CKlsbD3I+CduADnEbPZV+LD4ByQ0RiDy39F3z6DKOQc=", - "type": "git", - "url": "file:///home/benton/git/benpkgs" - }, - "original": { - "type": "git", - "url": "file:///home/benton/git/benpkgs" - } - }, - "inept-epub": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1628616743, - "narHash": "sha256-XNka+o/55SYOtDUXXOTmDvX4Qiqbaz4COq+JZ8MPoe8=", + "lastModified": 1638237664, + "narHash": "sha256-9SkZPl1EZ1ezWZoMdhYOzPm2HK0Ca5hkfphjlPNk/FY=", "owner": "BentonEdmondson", - "repo": "inept-epub", - "rev": "d6ba98a7159f61e7b77dc17c8c6bb62e193c8831", + "repo": "benpkgs", + "rev": "da71a3a488de3ae7ab8daff2c2a9354d2f0464bf", "type": "github" }, "original": { "owner": "BentonEdmondson", - "repo": "inept-epub", + "repo": "benpkgs", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1633080050, - "narHash": "sha256-T9I2WnlUzAIL70dk9V1jqaYk3nypy/cMkWR19S47ZHc=", + "lastModified": 1638198142, + "narHash": "sha256-plU9b8r4St6q4U7VHtG9V7oF8k9fIpfXl/KDaZLuY9k=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "82155ff501c7622cb2336646bb62f7624261f6d7", + "rev": "8a308775674e178495767df90c419425474582a1", "type": "github" }, "original": { @@ -53,11 +36,31 @@ "type": "github" } }, + "rmdrm": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1638240151, + "narHash": "sha256-2FHEb4xaskyLdS5M/eILue74Idplve9g8FRxg9D1Z/4=", + "owner": "BentonEdmondson", + "repo": "rmdrm", + "rev": "c60eaa0c4338be5ee6165f59f2a1816566aba8a0", + "type": "github" + }, + "original": { + "owner": "BentonEdmondson", + "repo": "rmdrm", + "type": "github" + } + }, "root": { "inputs": { "benpkgs": "benpkgs", - "inept-epub": "inept-epub", - "nixpkgs": "nixpkgs" + "nixpkgs": "nixpkgs", + "rmdrm": "rmdrm" } } }, diff --git a/flake.nix b/flake.nix index 32b316f..f499354 100644 --- a/flake.nix +++ b/flake.nix @@ -2,37 +2,37 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - inept-epub.url = "github:BentonEdmondson/inept-epub"; - inept-epub.inputs.nixpkgs.follows = "nixpkgs"; + rmdrm.url = "github:BentonEdmondson/rmdrm"; + rmdrm.inputs.nixpkgs.follows = "nixpkgs"; - benpkgs.url = "git+file:///home/benton/git/benpkgs"; + benpkgs.url = "github:BentonEdmondson/benpkgs"; benpkgs.inputs.nixpkgs.follows = "nixpkgs"; }; - outputs = { self, ... }@flakes: let + outputs = flakes: let nixpkgs = flakes.nixpkgs.legacyPackages.x86_64-linux; libgourou-utils = flakes.libgourou-utils.defaultPackage.x86_64-linux; - inept-epub = flakes.inept-epub.defaultPackage.x86_64-linux; + rmdrm = flakes.rmdrm.defaultPackage.x86_64-linux; benpkgs = flakes.benpkgs.packages.x86_64-linux; in { defaultPackage.x86_64-linux = nixpkgs.python3Packages.buildPythonApplication rec { pname = "knock"; - version = "1.0.0-alpha"; + version = "1.1.0-alpha"; src = ./.; nativeBuildInputs = [ nixpkgs.makeWrapper ]; buildInputs = [ - inept-epub + rmdrm benpkgs.libgourou nixpkgs.ffmpeg ]; propagatedBuildInputs = [ + benpkgs.Audible nixpkgs.python3Packages.python_magic nixpkgs.python3Packages.xdg nixpkgs.python3Packages.click - benpkgs.Audible ]; format = "other"; @@ -42,10 +42,10 @@ cp lib/*.py $out/${nixpkgs.python3.sitePackages} cp src/knock.py $out/bin/knock wrapProgram $out/bin/knock --prefix PATH : ${nixpkgs.lib.makeBinPath buildInputs} - ''; + #''; meta = { - description = "A CLI tool to convert ACSM files to DRM-free EPUB files"; + description = "A CLI tool to convert ACSM files to DRM-free EPUB/PDF files"; homepage = "https://github.com/BentonEdmondson/knock"; license = [ nixpkgs.lib.licenses.gpl3Only ]; maintainers = [{ diff --git a/lib/handle_aax.py b/lib/handle_aax.py index def7126..fe91e5f 100644 --- a/lib/handle_aax.py +++ b/lib/handle_aax.py @@ -48,4 +48,4 @@ def handle_aax(aax_path): '-loglevel', 'error' ]) - click.secho(f'DRM-free M4B file created:\n{aax_path.with_suffix(".m4b")}', fg='green') + click.secho(f'DRM-free M4B file created:\n{m4b_path}', fg='green') diff --git a/lib/handle_acsm.py b/lib/handle_acsm.py index 8beeaea..defc68b 100644 --- a/lib/handle_acsm.py +++ b/lib/handle_acsm.py @@ -1,5 +1,5 @@ from xdg import xdg_config_home -import click, sys, shutil, subprocess +import click, sys, shutil, subprocess, magic from utils import run def handle_acsm(acsm_path): @@ -24,7 +24,7 @@ def handle_acsm(acsm_path): run( [ - 'adept-register', + 'adept_activate', '-u', email, '-O', str(adobe_dir) ], @@ -35,7 +35,7 @@ def handle_acsm(acsm_path): click.echo('Downloading the book from Adobe...') run([ - 'adept-download', + 'acsmdownloader', '-d', str(adobe_dir.joinpath('device.xml')), '-a', str(adobe_dir.joinpath('activation.xml')), '-k', str(adobe_dir.joinpath('devicesalt')), @@ -45,11 +45,11 @@ def handle_acsm(acsm_path): drm_path_type = magic.from_file(str(drm_path), mime=True) if drm_path_type == 'application/epub+zip': - file_type = 'EPUB' + file_type = 'epub' elif drm_path_type == 'application/pdf': - file_type = 'PDF' + file_type = 'pdf' else: - click.echo(f'Error: Received file of media type {drm_path_type}.', err=True) + click.echo(f'Error: Received file of media type {drm_path_type} from Adobe\' servers.', err=True) click.echo('Only the following ACSM conversions are currently supported:', err=True) click.echo(' * ACSM -> EPUB', err=True) click.echo(' * ACSM -> PDF', err=True) @@ -57,20 +57,21 @@ def handle_acsm(acsm_path): click.echo(f' https://github.com/BentonEdmondson/knock/issues/new?title=Support%20{drm_path_type}%20Files&labels=enhancement', err=True) sys.exit(1) - output_file = acsm_path.with_suffix(file_type) - if output_file.exists(): - click.echo(f"Error: {output_file} must be moved out of the way or deleted.", err=True) + output_path = acsm_path.with_suffix('.' + file_type) + if output_path.exists(): + drm_path.unlink() + click.echo(f"Error: {output_path} must be moved out of the way or deleted.", err=True) sys.exit(1) click.echo('Decrypting the file...') run([ - 'inept-' + file_type.lower(), + 'rmdrm-' + file_type, str(adobe_dir.joinpath('activation.xml')), str(drm_path), - str(output_file) + str(output_path) ]) drm_path.unlink() - click.secho(f'DRM-free {file_type} file created:\n{output_file}', fg='green') \ No newline at end of file + click.secho(f'DRM-free {file_type.upper()} file created:\n{output_path}', fg='green') \ No newline at end of file diff --git a/lib/utils.py b/lib/utils.py index 82cae6a..9551d43 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -38,3 +38,8 @@ def close_fake_terminal(exit_code: int, cleanser = lambda: None): cleanser() click.echo(f'Error: Command returned error code {exit_code}.', err=True) sys.exit(1) + +def verify_absence_of(file_path): + if m4b_path.exists(): + click.echo(f"Error: {file_path} must be moved out of the way or deleted.", err=True) + sys.exit(1) \ No newline at end of file diff --git a/readme.md b/readme.md index 1dc0ae1..8358188 100644 --- a/readme.md +++ b/readme.md @@ -1,55 +1,54 @@ # Knock -Convert ACSM files to DRM-free EPUB files using one command. +Perform the following conversions with one command: +* ACSM → EPUB +* ACSM → PDF +* (Soon: AAX → M4B) ![CLI demonstration](demo.png) -This software does not utilize Adobe Digital Editions nor Wine. It is completely free and open-source software written natively for Linux. +*This software does not utilize Adobe Digital Editions nor Wine. It is completely free and open-source software written natively for Linux.* ## Setup and Installation -1. Create a free Adobe account [here](https://account.adobe.com) if you do not already have one. -1. Install the software. - * For NixOS, include this flake in your system `flake.nix`. Then run `knock ~/path/to/my-book.acsm` to use. - ```nix - inputs.knock.url = github:BentonEdmondson/knock - outputs = { self, knock }: { /* knock.defaultPackage.x86_64-linux is the package */ } +* For NixOS users, include this flake in your system `flake.nix`. Then run `knock ~/path/to/my-book.acsm` to use. + ```nix + { + inputs.knock.url = "github:BentonEdmondson/knock"; + outputs = { self, knock }: { /* knock.defaultPackage.x86_64-linux is the package */ }; + } + ``` +* For non-NixOS, use the latest [release](https://github.com/BentonEdmondson/knock/releases). It is large because it includes all dependencies, allowing it to run on any system with an x86_64 Linux kernel. It was built using [`nix bundle`](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-bundle.html). Use it by doing the following: + 1. Download `knock-version-x86_64-linux` and open a terminal + 1. Navigate to the folder within which `knock-version-x86_64-linux` resides (e.g. `cd ~/Downloads`) + 1. Run `mv knock-version-x86_64-linux knock` to rename it to `knock` + 1. Run `chmod +x knock` to make it executable + 1. Run `./knock ~/path/to/my-book.acsm` to convert the ebook + + If you receive an error that says something like `./nix/store/...: not found` or `./nix/store/...: No such file or directory` then you might not have user namespaces enabled. Try running the following to fix it: + + ``` + echo "kernel.unprivileged_userns_clone=1" >> /etc/sysctl.conf + sudo reboot ``` - * For non-NixOS, use the latest [release](https://github.com/BentonEdmondson/knock/releases). It is large because it includes all dependencies, allowing it to run on any system with an x86_64 Linux kernel. It was built using [`nix bundle`](https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-bundle.html). Use it by doing the following: - 1. Download `knock-version-x8664-linux` and open a terminal - 1. Navigate to the folder within which `knock-version-x86_64-linux` resides (e.g. `cd ~/Downloads`) - 1. Run `mv knock-version-x86_64-linux knock` to rename it to `knock` - 1. Run `chmod +x knock` to make it executable - 1. Run `./knock ~/path/to/my-book.acsm` to convert the ebook - If you receive an error that says something like `./nix/store/...: not found` or `./nix/store/...: No such file or directory` then you might not have user namespaces enabled. Try running the following to fix it: + 1. Optionally move the executable to `~/bin` (for your user) or `/usr/local/bin/` (for all users) to allow it to run from anywhere (might not work on some distributions) - ``` - echo "kernel.unprivileged_userns_clone=1" >> /etc/sysctl.conf - sudo reboot - ``` +## Recommended Workflows - 1. Enter in your Adobe email and password when prompted - 1. Optionally move the executable to `~/bin` (for your user) or `/usr/local/bin/` (for all users) to allow it to run from anywhere (might not work on some distributions) +Before buying your ebook/audiobook, check if it is available for free on [Project Gutenberg](https://gutenberg.org/) (ebooks) or [LibriVox](https://librivox.org/) (audiobooks). +If you're looking for an ebook reader or audiobook player, I recommend [Foliate](https://johnfactotum.github.io/foliate/) for the former and [Cozy](https://cozy.sh/) for the latter. ## Verified Book Sources -Knock should work on any ACSM file, but it has been specifically verified to work on ACSM EPUB files from the following: +Knock should work on any ACSM file, but it has been specifically verified to work on ACSM files from the following: * [eBooks.com](https://www.ebooks.com/en-us/) * [Rakuten Kobo](https://www.kobo.com/us/en) * [Google Books](https://books.google.com/) * [Hugendubel.de](https://www.hugendubel.de/de/) (German) -The resulting EPUB file can be read with any EPUB reader. - -## Legality - -[It's Perfectly Legal to Tell People How to Remove DRM](https://gizmodo.com/its-perfectly-legal-to-tell-people-how-to-remove-drm-1670223538) (Gizmodo) - -**Do not use this software for piracy; this software is not intended for piracy.** This software exists to allow readers to *legally* purchase and then legally and *freely* read ebooks. I suspect that the significant prevalence of ebook piracy is caused in large part by the restrictions put on legally purchased ebooks. This software exists to *incentivize* the legal purchase of ebooks by enabling their unrestricted and legal consumption. - ## The Name The name comes from the [D&D 5e spell](https://roll20.net/compendium/dnd5e/Knock#content) for freeing locked items: @@ -65,11 +64,13 @@ The name comes from the [D&D 5e spell](https://roll20.net/compendium/dnd5e/Knock ## Dependencies -* [`libgourou-utils`](https://github.com/BentonEdmondson/libgourou-utils) for using the ACSM file to download the corresponding encrypted EPUB file from Adobe's servers -* [`inept-epub`](https://github.com/BentonEdmondson/inept-epub/) for decrypting the EPUB file +* [`libgourou`](http://indefero.soutade.fr/p/libgourou/) for using the ACSM file to download the corresponding encrypted EPUB/PDF file from Adobe's servers +* [`rmdrm`](https://github.com/BentonEdmondson/rmdrm/) for decrypting the Adobe ADEPT-encrypted EPUB/PDF files +* [`Audible`](https://github.com/mkb79/Audible) for fetching the Audible decryption key used to decrypt AAX files +* [`ffmpeg`](https://www.ffmpeg.org/) for converting AAX files to M4B files using the Audible decryption key These are already included in all releases and in the Nix flake of course. ## License -This software is licensed under GPLv3 because one of its dependencies is licensed under GPLv3. +This software is licensed under GPLv3. diff --git a/src/knock.py b/src/knock.py index 5f989d0..274149a 100755 --- a/src/knock.py +++ b/src/knock.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import subprocess, magic, shutil, click +import subprocess, shutil, click from pathlib import Path from getpass import getpass from xdg import xdg_config_home @@ -8,9 +8,6 @@ from xdg import xdg_config_home from handle_acsm import handle_acsm from handle_aax import handle_aax -__version__ = "1.0.0-alpha" - -@click.version_option() @click.command() @click.argument( "path", @@ -33,14 +30,14 @@ def main(path): if path_type == 'ACSM': click.echo('Received an ACSM (Adobe) file...') handle_acsm(path) - elif path_type == 'AAX': - click.echo('Received an AAX (Audible) file...') - handle_aax(path) + #elif path_type == 'AAX': + # click.echo('Received an AAX (Audible) file...') + # handle_aax(path) else: click.echo(f'Error: Files of type {path_type} are not supported.\n', err=True) click.echo('Only the following file types are currently supported:', err=True) click.echo(' * ACSM (Adobe)\n', err=True) - click.echo(' * AAX (Audible)\n', err=True) + #click.echo(' * AAX (Audible)\n', err=True) click.echo('Please open a feature request at:', err=True) click.echo(f' https://github.com/BentonEdmondson/knock/issues/new?title=Support%20{path_type}%20Files&labels=enhancement', err=True) sys.exit(1)