Compare commits

..

No commits in common. "488fcabd69ca6f9e306a3ca30ccef600209115b1" and "14edc3974b351c4a921ff717bc302fe04e66024f" have entirely different histories.

7 changed files with 278 additions and 383 deletions

View file

@ -1,28 +0,0 @@
{
"paddingVertical": "45px",
"paddingHorizontal": "45px",
"backgroundImage": null,
"backgroundImageSelection": null,
"backgroundMode": "color",
"backgroundColor": "rgba(31,129,109,1)",
"dropShadow": true,
"dropShadowOffsetY": "12px",
"dropShadowBlurRadius": "22px",
"theme": "nord",
"windowTheme": "none",
"language": "text",
"fontFamily": "Hack",
"fontSize": "14px",
"lineHeight": "150%",
"windowControls": true,
"widthAdjustment": true,
"lineNumbers": false,
"firstLineNumber": 1,
"exportSize": "4x",
"watermark": false,
"squaredImage": false,
"hiddenCharacters": false,
"name": "",
"width": 680,
"highlights": null
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

View file

@ -16,21 +16,6 @@
"url": "https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git"
}
},
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"gourou-src": {
"flake": false,
"locked": {
@ -83,7 +68,6 @@
"root": {
"inputs": {
"base64-src": "base64-src",
"flake-utils": "flake-utils",
"gourou-src": "gourou-src",
"nixpkgs": "nixpkgs",
"pugixml-src": "pugixml-src",

102
flake.nix
View file

@ -1,7 +1,6 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
gourou-src = {
url = "git://soutade.fr/libgourou.git";
flake = false;
@ -20,25 +19,20 @@
};
};
outputs = flakes:
flakes.flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ]
(system:
let
version = "1.3.1";
self = flakes.self.packages.${system};
nixpkgs = flakes.nixpkgs.legacyPackages.${system}.pkgsStatic;
nixpkgs-dyn = flakes.nixpkgs.legacyPackages.${system};
nixpkgs-fmt = flakes.nixpkgs-fmt.defaultPackage.${system};
outputs = flakes: let
version = "1.3.0";
self = flakes.self.packages.x86_64-linux;
nixpkgs = flakes.nixpkgs.legacyPackages.x86_64-linux.pkgsStatic;
nixpkgs-dyn = flakes.nixpkgs.legacyPackages.x86_64-linux;
gourou-src = flakes.gourou-src;
updfparser-src = flakes.updfparser-src;
base64-src = flakes.base64-src;
pugixml-src = flakes.pugixml-src;
cxx = "${nixpkgs.stdenv.cc}/bin/${nixpkgs.stdenv.cc.targetPrefix}c++";
ar = "${nixpkgs.stdenv.cc.bintools.bintools_bin}/bin/${nixpkgs.stdenv.cc.targetPrefix}ar";
cxx = "${nixpkgs.stdenv.cc}/bin/x86_64-unknown-linux-musl-g++";
ar = "${nixpkgs.stdenv.cc.bintools.bintools_bin}/bin/x86_64-unknown-linux-musl-ar";
obj-flags = "-O2 -static";
in
rec {
packages.libzip-static = nixpkgs.libzip.overrideAttrs (prev: {
in rec {
packages.x86_64-linux.libzip-static = nixpkgs.libzip.overrideAttrs (prev: {
cmakeFlags = (prev.cmakeFlags or []) ++ [
"-DBUILD_SHARED_LIBS=OFF"
"-DBUILD_EXAMPLES=OFF"
@ -48,48 +42,39 @@
];
outputs = ["out"];
});
packages.base64 = derivation {
packages.x86_64-linux.base64 = derivation {
name = "updfparser";
inherit system;
system = "x86_64-linux";
builder = "${nixpkgs.bash}/bin/bash";
PATH = "${nixpkgs.coreutils}/bin";
args = [
"-c"
''
args = ["-c" ''
mkdir -p $out/include/base64
cp ${base64-src}/Base64.h $out/include/base64/Base64.h
''
];
''];
};
packages.updfparser = derivation {
packages.x86_64-linux.updfparser = derivation {
name = "updfparser";
inherit system;
system = "x86_64-linux";
builder = "${nixpkgs.bash}/bin/bash";
PATH = "${nixpkgs.coreutils}/bin";
args = [
"-c"
''
args = [ "-c" ''
${cxx} \
-c ${updfparser-src}/src/*.cpp \
-I ${updfparser-src}/include \
${obj-flags}
mkdir -p $out/lib
${ar} crs $out/lib/libupdfparser.a *.o
''
];
'' ];
};
packages.gourou = derivation {
packages.x86_64-linux.gourou = derivation {
name = "gourou";
inherit system;
system = "x86_64-linux";
builder = "${nixpkgs.bash}/bin/bash";
PATH = "${nixpkgs.coreutils}/bin";
args = [
"-c"
''
shopt -s extglob
args = [ "-c" ''
${cxx} \
-c \
${gourou-src}/src/!(pugixml).cpp \
${gourou-src}/src/*.cpp \
${pugixml-src}/src/pugixml.cpp \
-I ${self.base64}/include \
-I ${gourou-src}/include \
@ -99,17 +84,14 @@
mkdir -p $out/lib $out/debug
${ar} crs $out/lib/libgourou.a *.o
cp *.o $out/debug
''
];
'' ];
};
packages.utils-common = derivation {
packages.x86_64-linux.utils-common = derivation {
name = "utils-common";
inherit system;
system = "x86_64-linux";
builder = "${nixpkgs.bash}/bin/bash";
PATH = "${nixpkgs.coreutils}/bin";
args = [
"-c"
''
args = [ "-c" ''
${cxx} \
-c ${gourou-src}/utils/drmprocessorclientimpl.cpp \
${gourou-src}/utils/utils_common.cpp \
@ -123,23 +105,19 @@
${obj-flags}
mkdir -p $out/lib
${ar} crs $out/lib/libutils-common.a *.o
''
];
'' ];
};
packages.knock = derivation {
packages.x86_64-linux.knock = derivation {
name = "knock";
inherit system;
system = "x86_64-linux";
builder = "${nixpkgs.bash}/bin/bash";
PATH = "${nixpkgs.coreutils}/bin";
args = [
"-c"
''
args = [ "-c" ''
mkdir -p $out/bin
${cxx} \
-o $out/bin/knock \
${./src/knock.cpp} \
-D KNOCK_VERSION='"${version}"' \
--std=c++17 \
-Wl,--as-needed -static \
${self.utils-common}/lib/libutils-common.a \
${self.gourou}/lib/libgourou.a \
@ -164,19 +142,16 @@
-I ${nixpkgs.curl.dev}/include \
-I ${nixpkgs.zlib.dev}/include \
-I ${self.libzip-static}/include
''
];
'' ];
};
packages.default = self.knock;
packages.tests = nixpkgs-dyn.stdenv.mkDerivation {
packages.x86_64-linux.default = self.knock;
packages.x86_64-linux.tests = nixpkgs-dyn.stdenv.mkDerivation {
name = "tests";
src = ./tests;
buildInputs = [
(nixpkgs-dyn.python3.withPackages (p: [
buildInputs = [ (nixpkgs-dyn.python3.withPackages(p: [
p.beautifulsoup4
p.requests
]))
];
])) ];
patchPhase = ''
substituteInPlace tests.py --replace "./result/bin/knock" "${self.knock}/bin/knock"
'';
@ -186,12 +161,5 @@
chmod +x $out/bin/tests
'';
};
packages.formatter = nixpkgs.writeShellScriptBin "formatter" ''
set -x
${nixpkgs-dyn.clang-tools}/bin/clang-format -i --verbose ./src/*.cpp
${nixpkgs-dyn.nixpkgs-fmt}/bin/nixpkgs-fmt .
${nixpkgs-dyn.black}/bin/black ./tests
'';
}
);
};
}

View file

@ -2,32 +2,13 @@
Convert ACSM files to PDF/EPUBs with one command on Linux ([and MacOS very soon](https://github.com/BentonEdmondson/knock/issues/58)).
![Demonstration of CLI usage](./assets/demo.png)
*This software does not utilize Adobe Digital Editions nor Wine. It is completely free and open-source software written natively for Linux.*
## Installation
1. Run `uname -ms` and, based on the output, download the latest corresponding [release](https://github.com/BentonEdmondson/knock/releases).
1. `cd` into the directory that knock is in (e.g. `cd ~/Downloads`).
1. Run `mv knock-version-arch-os knock` to rename the binary to `knock`.
1. Run `chmod +x knock` to make it executable.
1. Run `./knock ./path/to/book.acsm` to perform the conversion.
1. Run `mv knock ~/.local/bin` to allow it to be run from anywhere (might not work on some distributions).
### Nix and NixOS (with [flakes](https://nixos.wiki/wiki/Flakes) enabled)
If you are on a system with [Nix](https://github.com/NixOS/nix), you can use the following.
```
nix profile install github:BentonEdmondson/knock
```
If you are on [NixOS](https://github.com/NixOS/nixpkgs), you can add the flake to your system config.
## Dependencies
There are no userspace runtime dependencies.
* Download the latest [release](https://github.com/BentonEdmondson/knock/releases). Make sure it is the correct version for your architecture (run `uname -m` to check).
* Rename the binary and make it executable.
* Run `knock /path/to/book.acsm` to perform the conversion.
## Verified Book Sources
@ -35,6 +16,23 @@ Knock should work on any ACSM file, but it has been specifically verified to wor
Before buying your ebook, check if it is available for free on [Project Gutenberg](https://gutenberg.org/).
## The Name
The name comes from the [D&D 5e spell](https://roll20.net/compendium/dnd5e/Knock#content) for freeing locked items:
> ### Knock
> *2nd level transmutation*\
> **Casting Time**: 1 action\
> **Range**: 60 feet\
> **Components**: V\
> **Duration**: Instantaneous\
> **Classes**: Bard, Sorcerer, Wizard\
> Choose an object that you can see within range. The object can be a door, a box, a chest, a set of manacles, a padlock, or another object that contains a mundane or magical means that prevents access. A target that is held shut by a mundane lock or that is stuck or barred becomes unlocked, unstuck, or unbarred. If the object has multiple locks, only one of them is unlocked. If you choose a target that is held shut with arcane lock, that spell is suppressed for 10 minutes, during which time the target can be opened and shut normally. When you cast the spell, a loud knock, audible from as far away as 300 feet, emanates from the target object.
## Dependencies
There are no userspace runtime dependencies.
## Contributing
Install [Nix](https://github.com/NixOS/nix) if you don't have it. Enable [flakes](https://nixos.wiki/wiki/Flakes) if you haven't.
@ -59,25 +57,6 @@ nix run .#tests -- ./tests/workspace
Test books can be found [here](https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html).
### Formatting
```
nix run .#formatter
```
## The Name
The name comes from the [D&D 5e spell](https://roll20.net/compendium/dnd5e/Knock#content) for freeing locked items:
> ### Knock
> *2nd level transmutation*\
> **Casting Time**: 1 action\
> **Range**: 60 feet\
> **Components**: V\
> **Duration**: Instantaneous\
> **Classes**: Bard, Sorcerer, Wizard\
> Choose an object that you can see within range. The object can be a door, a box, a chest, a set of manacles, a padlock, or another object that contains a mundane or magical means that prevents access. A target that is held shut by a mundane lock or that is stuck or barred becomes unlocked, unstuck, or unbarred. If the object has multiple locks, only one of them is unlocked. If you choose a target that is held shut with arcane lock, that spell is suppressed for 10 minutes, during which time the target can be opened and shut normally. When you cast the spell, a loud knock, audible from as far away as 300 feet, emanates from the target object.
## License
This software is licensed under GPLv3. The linked libraries have various licenses.

View file

@ -1,7 +1,7 @@
#include "drmprocessorclientimpl.h"
#include "libgourou.h"
#include "libgourou_common.h"
#include <filesystem>
#include "drmprocessorclientimpl.h"
#include "libgourou_common.h"
#include "libgourou.h"
#ifndef KNOCK_VERSION
#error KNOCK_VERSION must be defined
@ -14,11 +14,11 @@ void verify_presence(std::string file);
int main(int argc, char** argv) try {
if (argc == 1) {
std::cout << "info: knock version " << KNOCK_VERSION
<< ", libgourou version " << LIBGOUROU_VERSION << "\n"
<< "usage: " << argv[0] << " [ACSM]\n"
<< "result: converts file ACSM to a plain EPUB/PDF if present, "
"otherwise prints this"
std::cout
<< "info: knock version " << KNOCK_VERSION << ", libgourou version "
<< LIBGOUROU_VERSION << "\n"
<< "usage: " << argv[0] << " [ACSM]" << "\n"
<< "result: converts file ACSM to a plain EPUB/PDF if present, otherwise prints this"
<< std::endl;
return EXIT_SUCCESS;
}
@ -29,8 +29,7 @@ int main(int argc, char **argv) try {
const std::string acsm_file = argv[1];
verify_presence(acsm_file);
const std::string acsm_stem =
acsm_file.substr(0, acsm_file.find_last_of("."));
const std::string acsm_stem = acsm_file.substr(0, acsm_file.find_last_of("."));
const std::string drm_file = acsm_stem + ".drm";
const std::string out_file = acsm_stem + ".out";
verify_absence(drm_file);
@ -41,7 +40,8 @@ int main(int argc, char **argv) try {
gourou::DRMProcessor* processor = gourou::DRMProcessor::createDRMProcessor(
&client,
false, // don't "always generate a new device" (default)
knock_data);
knock_data
);
std::cout << "anonymously signing in..." << std::endl;
processor->signIn("anonymous", "");
@ -74,15 +74,14 @@ int main(int argc, char **argv) try {
}
if (std::filesystem::exists(ext_file)) {
std::cerr << "warning: failed to update file extension; " + ext_file +
" already exists"
std::cerr
<< "warning: failed to update file extension; " + ext_file + " already exists"
<< std::endl;
ext_file = out_file;
} else {
std::filesystem::rename(out_file, ext_file);
}
std::filesystem::remove(acsm_file);
std::cout << file_type + " file generated at " + ext_file << std::endl;
return 0;
@ -109,8 +108,7 @@ std::string get_data_dir() {
void verify_absence(std::string file) {
if (std::filesystem::exists(file)) {
throw std::runtime_error("file " + file +
" must be moved out of the way or deleted");
throw std::runtime_error("file " + file + " must be moved out of the way or deleted");
}
}

View file

@ -14,10 +14,9 @@ if not knock.exists():
if len(sys.argv) != 2:
print(
"error: missing required argument: directory in which to perform the tests",
file=sys.stderr,
file=sys.stderr
)
sys.exit()
workspace = Path(sys.argv[1])
print("Testing " + str(knock))
@ -27,13 +26,18 @@ if result.returncode != 0:
sys.exit()
print("---")
html = requests.get(
"https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html"
).text
soup = BeautifulSoup(html, "html.parser")
workspace = Path(sys.argv[1])
if workspace.exists():
shutil.rmtree(workspace)
workspace.mkdir()
html = requests \
.get("https://www.adobe.com/solutions/ebook/digital-editions/sample-ebook-library.html") \
.text
soup = BeautifulSoup(html, 'html.parser')
links = []
for a_tag in soup.find_all("a"):
for a_tag in soup.find_all('a'):
if a_tag.string != "Download eBook":
continue
if not urlparse(a_tag.get("href")).path.endswith(".acsm"):
@ -41,19 +45,9 @@ for a_tag in soup.find_all("a"):
links.append(a_tag.get("href"))
if len(links) >= 10:
break
for time in ["first", "second"]:
if workspace.exists():
shutil.rmtree(workspace)
workspace.mkdir()
for i, link in enumerate(links):
i = str(i)
print("Testing URL #" + i + " for the " + time + " time:\n" + link)
print("Testing URL #" + i + ":\n" + link)
file = workspace.joinpath(i + ".acsm")
r = requests.get(link)