Created
June 29, 2023 20:10
-
-
Save bohwaz/8399cf9561c24667afe7d238c29a58bf to your computer and use it in GitHub Desktop.
Transform LCP/LCPL files to regular EPUB/PDF/ZIP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/php | |
<?php | |
// Note: this code does not contain any DRM removal, DRM removal is made by https://notabug.org/NewsGuyTor/DeDRM_tools-LCP | |
// | |
// Install steps: | |
// Debian/Ubuntu/Mint: apt install php-cli python3 python3-cryptodome python3-lxml zip unzip | |
// | |
// How to use? | |
// Just run: php lcp_download.php FILE.LCPL PASSWORD | |
// A new FILE_decrypted.epub will be created in the same directory | |
$argv = $_SERVER['argv'] ?? []; | |
if (count($argv) < 3) { | |
printf("Usage: %s EPUB_OR_LCPL_FILE PASSWORD\n", $argv[0]); | |
exit(1); | |
} | |
if (!shell_exec('which python3')) { | |
echo "python3 binary not found"; | |
exit(1); | |
} | |
if (!shell_exec('which unzip')) { | |
echo "unzip binary not found"; | |
exit(1); | |
} | |
$in = $argv[1]; | |
$out = preg_replace('/\.[^\.]+?$/', '', $in); | |
$out .= '_decrypted'; | |
$password = $argv[2]; | |
$fp = fopen($argv[1], 'rb'); | |
$is_zip = fread($fp, 4) == "PK\003\004"; | |
fclose($fp); | |
$license_url = null; | |
// .lcpl file | |
if (!$is_zip && preg_match('/\.lcpl$/i', $in)) { | |
$data = json_decode(file_get_contents($in)); | |
if (!isset($data->links)) { | |
echo "Could not download EPUB: invalid LCPL file?\n"; | |
exit(1); | |
} | |
$url = null; | |
foreach ($data->links as $link) { | |
if ($link->rel == 'publication') { | |
$url = $link->href; | |
} | |
elseif ($link->rel == 'status') { | |
$license_url = $link->href; | |
} | |
} | |
if (!$url) { | |
echo "Could not download EPUB: no publication URL was found?\n"; | |
exit(1); | |
} | |
printf("Downloading ebook from %s...\n", $url); | |
$in = tempnam(sys_get_temp_dir(), 'dedrm'); | |
copy($url, $in); | |
} | |
$has_license = shell_exec(sprintf('unzip -l %s | fgrep license.lcp', escapeshellarg($in))); | |
if (!$has_license) { | |
if (!$license_url) { | |
echo "License file 'license.lcp' is missing?!\n"; | |
exit(1); | |
} | |
if (!$is_zip) { | |
echo "Cannot append license to non-EPUB file\n"; | |
exit(1); | |
} | |
if (!shell_exec('which zip')) { | |
echo "zip binary not found"; | |
exit(1); | |
} | |
printf("Downloading license from %s...\n", $license_url); | |
$tmpdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'dedrm-' . sha1(random_bytes(10)); | |
mkdir($tmpdir); | |
mkdir($tmpdir . '/META-INF'); | |
copy($license_url, $tmpdir . DIRECTORY_SEPARATOR . 'META-INF/license.lcpl'); | |
printf("Appending license to EPUB file...\n"); | |
passthru(sprintf('cd %s && zip -g %s %s && rm -rf %1$s', | |
escapeshellarg($tmpdir), | |
escapeshellarg($in), | |
escapeshellarg('META-INF/license.lcpl') | |
)); | |
} | |
$dedrm_url = 'https://notabug.org/NewsGuyTor/DeDRM_tools-LCP/raw/d08098308c984347f54405959ec99a1a938d68bc/DeDRM_plugin/lcpdedrm.py'; | |
$dedrm = file_get_contents($dedrm_url); | |
// DeDRM patch for use outside of Calibre | |
$patch = <<<EOF | |
20a21 | |
> import sys | |
28c29 | |
< from Crypto.Cipher import AES | |
--- | |
> from Cryptodome.Cipher import AES | |
295c296 | |
< def decryptLCPbook(inpath, passphrases, parent_object): | |
--- | |
> def decryptLCPbook(inpath, outpath, passphrases): | |
445c446 | |
< outputname = parent_object.temporary_file(".pdf").name | |
--- | |
> outputname = outpath + ".pdf" | |
456c457 | |
< outputname = parent_object.temporary_file(".zip").name | |
--- | |
> outputname = outpath + ".zip" | |
470c471 | |
< outputname = parent_object.temporary_file(".epub").name | |
--- | |
> outputname = outpath + ".epub" | |
472c473 | |
< outputname = parent_object.temporary_file(".zip").name | |
--- | |
> outputname = outpath + ".zip" | |
519a521,527 | |
> | |
> def main(argv): | |
> decryptLCPbook(argv[0], argv[1], [argv[2]]) | |
> | |
> if __name__ == "__main__": | |
> main(sys.argv[1:]) | |
> | |
EOF; | |
$tmp_file = tempnam(sys_get_temp_dir(), 'dedrm'); | |
// Save deDRM file in temporary file and patch it | |
file_put_contents($tmp_file, $dedrm); | |
file_put_contents($tmp_file . '.patch', $patch); | |
passthru(sprintf('patch %s < %s', escapeshellarg($tmp_file), escapeshellarg($tmp_file . '.patch')), $r); | |
if($r) { | |
return; | |
} | |
@unlink($tmp_file . '.patch'); | |
passthru(sprintf('python3 %s %s %s %s', escapeshellarg($tmp_file), escapeshellarg($in), escapeshellarg($out), escapeshellarg($password))); | |
@unlink($tmp_file); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment