Skip to content

Instantly share code, notes, and snippets.

@bohwaz
Created June 29, 2023 20:10
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bohwaz/8399cf9561c24667afe7d238c29a58bf to your computer and use it in GitHub Desktop.
Save bohwaz/8399cf9561c24667afe7d238c29a58bf to your computer and use it in GitHub Desktop.
Transform LCP/LCPL files to regular EPUB/PDF/ZIP
#!/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