From 4f5f22a63d97a968edef0dae8888dcad5e89e207 Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Mon, 20 Sep 2021 11:24:11 +0200 Subject: [PATCH] Make plugin compile itself --- .github/workflows/main.yml | 7 +-- Dockerfile | 67 ----------------------- README | 14 ++--- bundle_calibre_plugin.sh | 13 +++++ calibre-plugin/__init__.py | 49 +++++++++++++---- calibre-plugin/config.py | 108 ++++++++++++++++++++++++++++++++++++- package_sources.sh | 42 +++++++++++++++ 7 files changed, 213 insertions(+), 87 deletions(-) delete mode 100644 Dockerfile create mode 100755 bundle_calibre_plugin.sh create mode 100755 package_sources.sh diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 74c9eb3..552b87f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,15 +9,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Compile run: | - DOCKER_BUILDKIT=1 docker build -o final . - cp calibre-plugin/* final/stretch/ + ./bundle_calibre_plugin.sh - name: Upload uses: actions/upload-artifact@v2 with: name: linux path: | - final/stretch/ + calibre-plugin.zip + libgourou_bundle_raw.tar.xz \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 7abc428..0000000 --- a/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -# Clear cache: -# docker builder prune - -# Build: -# DOCKER_BUILDKIT=1 docker build -o output . -# for Windows, use -# { "features": { "buildkit": true } } -# instead of the environment variable - -# Build a container -#FROM debian:bullseye as main_bullseye -#ENV DEBIAN_FRONTEND="noninteractive" TZ="Europe/London" -#RUN apt-get update -y && apt-get install -y \ -# git && apt-get install -y --no-install-recommends make g++ pkg-config qtbase5-dev libssl-dev libzip-dev -# -#FROM debian:buster as main_buster -#ENV DEBIAN_FRONTEND="noninteractive" TZ="Europe/London" -#RUN apt-get update -y && apt-get install -y \ -# git && apt-get install -y --no-install-recommends make g++ pkg-config qtbase5-dev libssl-dev libzip-dev - -FROM debian:stretch as main_stretch -ENV DEBIAN_FRONTEND="noninteractive" TZ="Europe/London" -RUN apt-get update -y && apt-get install -y \ - git && apt-get install -y --no-install-recommends make g++ pkg-config qtbase5-dev libssl-dev libzip-dev - - - - -#FROM main_bullseye as compile_bullseye -#RUN git clone git://soutade.fr/libgourou.git && \ -# cd libgourou && \ -# make BUILD_SHARED=1 BUILD_UTILS=1 -# mkdir final && \ -# cp utils/acsmdownloader final/ && \ -# cp utils/adept_activate final/ && \ -# cp libgourou.so final/ && \ -# cp /usr/lib/x86_64-linux-gnu/libzip.so.4 final/ && \ -# true -# -#FROM main_buster as compile_buster -#RUN git clone git://soutade.fr/libgourou.git && \ -# cd libgourou && \ -# make BUILD_SHARED=1 BUILD_UTILS=1 -# mkdir final && \ -# cp utils/acsmdownloader final/ && \ -# cp utils/adept_activate final/ && \ -# cp libgourou.so final/ && \ -# cp /usr/lib/x86_64-linux-gnu/libzip.so.4 final/ && \ -# true - -FROM main_stretch as compile_stretch -RUN git clone git://soutade.fr/libgourou.git && \ - cd libgourou && \ - make BUILD_SHARED=1 BUILD_UTILS=1 && \ - mkdir final && \ - cp utils/acsmdownloader final/ && \ - cp utils/adept_activate final/ && \ - cp libgourou.so final/ && \ - cp /usr/lib/x86_64-linux-gnu/libzip.so.4 final/ && \ - true - - -FROM scratch AS export-stage -#COPY --from=compile_bullseye /libgourou/final/ /bullseye/ -#COPY --from=compile_buster /libgourou/final/ /buster/ -COPY --from=compile_stretch /libgourou/final/ /stretch/ - diff --git a/README b/README index 58f100a..fb21057 100644 --- a/README +++ b/README @@ -1,8 +1,10 @@ -- Linux x86_64 only - -- Debian Stretch or newer (or comparable) - -- You need these packages: -- qtbase5-dev +- Needed packages to compile (Debian Bullseye or Ubuntu 20.04): make, g++, libssl-dev, pkg-config, qtbase5-dev, libzip-dev +- Needed packages to run: Either just keep the above installed, or if you want to cleanup the dev stuff, you'll need: +- libqt5core5a, libqt5network5, libzip5 (Ubuntu) / libzip4 (Debian) + +- Run "package_sources.sh" to checkout source code bundle. That bundle (tar.xz) should be included in the plugin. +- Run "bundle_calibre_plugin.sh" to create the Calibre Plugin using the source code. + +- In folder "libgourou", run "scripts/setup.sh" then run "make BUILD_SHARED=1 BUILD_UTILS=1" \ No newline at end of file diff --git a/bundle_calibre_plugin.sh b/bundle_calibre_plugin.sh new file mode 100755 index 0000000..deecbce --- /dev/null +++ b/bundle_calibre_plugin.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +[ ! -f libgourou_bundle_release.tar.xz ] && ./package_sources.sh + +cp libgourou_bundle_release.tar.xz calibre-plugin/ + +pushd calibre-plugin + +zip -r ../calibre-plugin.zip * + +popd + +rm calibre-plugin/libgourou_bundle_release.tar.xz \ No newline at end of file diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index 5566b66..a94f553 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -13,6 +13,7 @@ PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE]) import os import traceback +import subprocess from calibre.utils.config import config_dir # type: ignore from calibre.constants import iswindows, isosx # type: ignore @@ -53,20 +54,20 @@ class DeACSM(FileTypePlugin): # only continue if we've never run this version of the plugin before self.verdir = os.path.join(self.maindir,PLUGIN_VERSION) if not os.path.exists(self.verdir): - if iswindows: - print("Windows not supported yet") - return - elif isosx: - print("Mac not supported yet") + if iswindows or isosx: + print("Windows and MacOS not supported!") return else: - names = ["acsmdownloader", "adept_activate", "libgourou.so", "libzip.so.4"] + names = ["libgourou_bundle_release.tar.xz"] + + # mark that this version has been initialized + os.mkdir(self.verdir) lib_dict = self.load_resources(names) - print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) + print("{0} v{1}: Copying needed library files from plugin zip".format(PLUGIN_NAME, PLUGIN_VERSION)) for entry, data in lib_dict.items(): - file_path = os.path.join(self.alfdir, entry) + file_path = os.path.join(self.verdir, entry) try: os.remove(file_path) except: @@ -79,8 +80,6 @@ class DeACSM(FileTypePlugin): traceback.print_exc() pass - # mark that this version has been initialized - os.mkdir(self.verdir) except Exception as e: traceback.print_exc() raise @@ -101,6 +100,36 @@ class DeACSM(FileTypePlugin): print("{0} v{1}: Trying to parse file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))) + ext = os.path.splitext(path_to_ebook)[1].lower() + + if (ext != ".acsm"): + print("{0} v{1}: That's not an ACSM, returning (is {2} instead)... ".format(PLUGIN_NAME, PLUGIN_VERSION, ext)) + return path_to_ebook + + import calibre_plugins.deacsm.prefs as prefs # type: ignore + deacsmprefs = prefs.DeACSM_Prefs() + + print("{0} v{1}: Try to execute {2} ".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.join(self.verdir, "acsmdownloader"))) + + outputname = self.temporary_file(".epub").name + + my_env = os.environ.copy() + my_env["LD_LIBRARY_PATH"] = ".:" + my_env["LD_LIBRARY_PATH"] + + os.chmod(os.path.join(self.verdir, "acsmdownloader"), 0o775) + + ret = subprocess.run([os.path.join(self.verdir, "acsmdownloader"), "-d", os.path.join(deacsmprefs["path_to_account_data"], "device.xml"), + "-a", os.path.join(deacsmprefs["path_to_account_data"], "activation.xml"), + "-k", os.path.join(deacsmprefs["path_to_account_data"], "devicesalt"), + "-o", outputname, + "-v", "-v", + "-f", path_to_ebook ], capture_output=True, shell=False, cwd=self.verdir, env=my_env) + + print(ret) + + return outputname + + print("{0} v{1}: Failed, return original ...".format(PLUGIN_NAME, PLUGIN_VERSION)) return path_to_ebook diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index bf5caa7..722c10d 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -3,6 +3,7 @@ # pyright: reportUndefinedVariable=false +import os, glob, shutil, tarfile, subprocess, time from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, QGroupBox, QPushButton, QListWidget, QListWidgetItem, @@ -12,10 +13,12 @@ from PyQt5 import Qt as QtGui from zipfile import ZipFile # calibre modules and constants. -from calibre.gui2 import (question_dialog, info_dialog) # type: ignore +from calibre.gui2 import (question_dialog, error_dialog, info_dialog, choose_save_file) # type: ignore # modules from this plugin's zipfile. from calibre_plugins.deacsm.__init__ import PLUGIN_NAME, PLUGIN_VERSION # type: ignore import calibre_plugins.deacsm.prefs as prefs # type: ignore +from calibre.utils.config import config_dir # type: ignore + class ConfigWidget(QWidget): @@ -36,6 +39,12 @@ class ConfigWidget(QWidget): layout = QVBoxLayout(self) self.setLayout(layout) + self.button_compile = QtGui.QPushButton(self) + self.button_compile.setToolTip(_("Click to compile")) + self.button_compile.setText(_("Compile")) + self.button_compile.clicked.connect(self.compile) + layout.addWidget(self.button_compile) + ua_group_box = QGroupBox(_('Path to account:'), self) layout.addWidget(ua_group_box) @@ -47,9 +56,106 @@ class ConfigWidget(QWidget): self.txtboxUA.setText(self.tempdeacsmprefs['path_to_account_data']) ua_group_box_layout.addWidget(self.txtboxUA) + self.button_export_key = QtGui.QPushButton(self) + self.button_export_key.setText(_("Export account key")) + self.button_export_key.clicked.connect(self.export_key) + ua_group_box_layout.addWidget(self.button_export_key) + self.resize(self.sizeHint()) + def export_key(self): + pluginsdir = os.path.join(config_dir,"plugins") + maindir = os.path.join(pluginsdir,"DeACSM") + verdir = os.path.join(maindir,PLUGIN_VERSION) + + filters = [("DER Files", ["der"])] + + + filename = choose_save_file(self, "Export ADE keys", _("Export ADE keys"), filters, all_files=False) + + print("would export to " + filename) + + my_env = os.environ.copy() + my_env["LD_LIBRARY_PATH"] = ".:" + my_env["LD_LIBRARY_PATH"] + + + old_files = glob.glob(os.path.join(verdir, "*.der")) + for file in old_files: + try: + os.remove(file) + except: + pass + + try: + os.chmod(os.path.join(verdir, "acsmdownloader"), 0o775) + except FileNotFoundError: + return error_dialog(None, "Tool not found", "Helper tool not found. Press \"Compile\" then try again.", show=True, show_copy_button=False) + + ret = None + + import calibre_plugins.deacsm.prefs as prefs # type: ignore + deacsmprefs = prefs.DeACSM_Prefs() + + try: + ret = subprocess.run([os.path.join(verdir, "acsmdownloader"), "-d", os.path.join(deacsmprefs["path_to_account_data"], "device.xml"), + "-a", os.path.join(deacsmprefs["path_to_account_data"], "activation.xml"), + "-k", os.path.join(deacsmprefs["path_to_account_data"], "devicesalt"), + "-e" + ], capture_output=True, shell=False, cwd=verdir, env=my_env) + + except: + return error_dialog(None, "Export failed", "Export failed.", det_msg=str(ret), show=True, show_copy_button=True) + + try: + new_key = glob.glob(os.path.join(verdir, "*.der"))[0] + shutil.move(new_key, filename) + info_dialog(None, "Done", "Key successfully exported", show=True, show_copy_button=False) + except IndexError: + return error_dialog(None, "Export failed", "Export failed.", det_msg=str(ret), show=True, show_copy_button=True) + + + print(ret) + + + + def compile(self): + + # Get path to source code: + pluginsdir = os.path.join(config_dir,"plugins") + maindir = os.path.join(pluginsdir,"DeACSM") + verdir = os.path.join(maindir,PLUGIN_VERSION) + + # Delete old version + try: + shutil.rmtree(os.path.join(verdir, "libgourou")) + except: + pass + + # extract source + with tarfile.open(os.path.join(verdir, "libgourou_bundle_release.tar.xz")) as f: + f.extractall(verdir) + + # Run script, compile 1st: + os.chmod(os.path.join(verdir, "libgourou", "scripts", "setup.sh"), 0o775) + + + ret1 = subprocess.run([ os.path.join(verdir, "libgourou", "scripts", "setup.sh") ], capture_output=True, shell=True, cwd=os.path.join(verdir, "libgourou")) + print(ret1) + + ret2 = subprocess.run([ "make", "BUILD_SHARED=1", "BUILD_UTILS=1" ], capture_output=True, shell=True, cwd=os.path.join(verdir, "libgourou")) + print(ret2) + + try: + shutil.copy(os.path.join(verdir, "libgourou", "AAlibgourou.so"), verdir) + shutil.copy(os.path.join(verdir, "libgourou", "utils", "acsmdownloader"), verdir) + shutil.copy(os.path.join(verdir, "libgourou", "utils", "adept_activate"), verdir) + info_dialog(None, "Done", "Compiling successful", show=True, show_copy_button=False) + except: + print("Can't copy ...") + error_dialog(None, "Compiling failed", "Compiling failed. Did you install all dependencies?", det_msg=str(ret1) + "\n" + str(ret2), show=True, show_copy_button=True) + + def save_settings(self): self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text()) diff --git a/package_sources.sh b/package_sources.sh new file mode 100755 index 0000000..4be7d40 --- /dev/null +++ b/package_sources.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +rm -rf libgourou + +git clone git://soutade.fr/libgourou.git +pushd libgourou + +# Pugixml +git clone https://github.com/zeux/pugixml.git lib/pugixml +pushd lib/pugixml +git checkout latest +popd + +# Base64 +git clone https://gist.github.com/f0fd86b6c73063283afe550bc5d77594.git lib/base64 + +# uPDFParser +git clone git://soutade.fr/updfparser.git lib/updfparser + + + +popd +rm -f libgourou_bundle_raw.tar.xz 2>/dev/null +XZ_OPT=-9 tar -Jcvf libgourou_bundle_raw.tar.xz libgourou +pushd libgourou + +# Delete unnecessary stuff from release archive so the file stays small. +rm -rf ./.git/ +rm -rf ./lib/*/.git/ +rm -rf ./lib/pugixml/docs/ + +# Now patch the setup file: + +echo "#!/bin/bash" > scripts/setup.sh +echo "pushd lib/updfparser" >> scripts/setup.sh +echo "make BUILD_STATIC=1 BUILD_SHARED=0" >> scripts/setup.sh +echo "popd" >> scripts/setup.sh + +popd + +rm -f libgourou_bundle_release.tar.xz 2>/dev/null +XZ_OPT=-9 tar -Jcvf libgourou_bundle_release.tar.xz libgourou \ No newline at end of file