commit f0d9c07af3f341e8e2f61b94981c0872cdfcfc37 Author: Florian Bach Date: Sun Sep 19 16:20:56 2021 +0200 Initial commit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..74c9eb3 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,23 @@ +name: Build binaries + +on: + push: + branches: [ master ] + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Compile + run: | + DOCKER_BUILDKIT=1 docker build -o final . + cp calibre-plugin/* final/stretch/ + + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: linux + path: | + final/stretch/ + \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7abc428 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +# 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 new file mode 100644 index 0000000..58f100a --- /dev/null +++ b/README @@ -0,0 +1,8 @@ +- Linux x86_64 only + +- Debian Stretch or newer (or comparable) + +- You need these packages: +- qtbase5-dev + + diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py new file mode 100644 index 0000000..5566b66 --- /dev/null +++ b/calibre-plugin/__init__.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Calibre plugin for ACSM files. + + +from calibre.customize import FileTypePlugin # type: ignore +__version__ = '0.0.1' + +PLUGIN_NAME = "DeACSM" +PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")]) +PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE]) + +import os +import traceback + +from calibre.utils.config import config_dir # type: ignore +from calibre.constants import iswindows, isosx # type: ignore + + + +class DeACSM(FileTypePlugin): + name = PLUGIN_NAME + description = "Takes an Adobe ACSM file and converts that into a useable EPUB file" + supported_platforms = ['linux'] + author = "Leseratte10" + version = PLUGIN_VERSION_TUPLE + minimum_calibre_version = (5, 0, 0) + file_types = set(['acsm']) + on_import = True + on_preprocess = True + priority = 2000 + + def initialize(self): + """ + Dynamic modules can't be imported/loaded from a zipfile. + So this routine will extract the appropriate + library for the target OS and copy it to the 'alfcrypto' subdirectory of + calibre's configuration directory. That 'alfcrypto' directory is then + inserted into the syspath (as the very first entry) in the run function + so the CDLL stuff will work in the alfcrypto.py script. + The extraction only happens once per version of the plugin + Also perform upgrade of preferences once per version + """ + try: + self.pluginsdir = os.path.join(config_dir,"plugins") + if not os.path.exists(self.pluginsdir): + os.mkdir(self.pluginsdir) + self.maindir = os.path.join(self.pluginsdir,"DeACSM") + if not os.path.exists(self.maindir): + os.mkdir(self.maindir) + + # 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") + return + else: + names = ["acsmdownloader", "adept_activate", "libgourou.so", "libzip.so.4"] + + lib_dict = self.load_resources(names) + print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION)) + + for entry, data in lib_dict.items(): + file_path = os.path.join(self.alfdir, entry) + try: + os.remove(file_path) + except: + pass + + try: + open(file_path,'wb').write(data) + except: + print("{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION)) + traceback.print_exc() + pass + + # mark that this version has been initialized + os.mkdir(self.verdir) + except Exception as e: + traceback.print_exc() + raise + + def is_customizable(self): + return True + + def config_widget(self): + import calibre_plugins.deacsm.config as config # type: ignore + return config.ConfigWidget(self.plugin_path) + + def save_settings(self, config_widget): + config_widget.save_settings() + + def run(self, path_to_ebook: str): + # This code gets called by Calibre with a path to the new book file. + # We need to check if it's an ACSM file + + print("{0} v{1}: Trying to parse file {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook))) + + 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 new file mode 100644 index 0000000..bf5caa7 --- /dev/null +++ b/calibre-plugin/config.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# pyright: reportUndefinedVariable=false + + +from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit, + QGroupBox, QPushButton, QListWidget, QListWidgetItem, + QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl) + +from PyQt5 import Qt as QtGui +from zipfile import ZipFile + +# calibre modules and constants. +from calibre.gui2 import (question_dialog, info_dialog) # 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 + + +class ConfigWidget(QWidget): + def __init__(self, plugin_path): + QWidget.__init__(self) + + self.plugin_path = plugin_path + + # get the prefs + self.deacsmprefs = prefs.DeACSM_Prefs() + + # make a local copy + self.tempdeacsmprefs = {} + self.tempdeacsmprefs['path_to_account_data'] = self.deacsmprefs['path_to_account_data'] + + + # Start Qt Gui dialog layout + layout = QVBoxLayout(self) + self.setLayout(layout) + + + ua_group_box = QGroupBox(_('Path to account:'), self) + layout.addWidget(ua_group_box) + ua_group_box_layout = QVBoxLayout() + ua_group_box.setLayout(ua_group_box_layout) + + self.txtboxUA = QtGui.QLineEdit(self) + self.txtboxUA.setToolTip(_("Enter folder path to account data")) + self.txtboxUA.setText(self.tempdeacsmprefs['path_to_account_data']) + ua_group_box_layout.addWidget(self.txtboxUA) + + + self.resize(self.sizeHint()) + + + def save_settings(self): + self.deacsmprefs.set('path_to_account_data', self.txtboxUA.text()) + self.deacsmprefs.writeprefs() + + def load_resource(self, name): + with ZipFile(self.plugin_path, 'r') as zf: + if name in zf.namelist(): + return zf.read(name).decode('utf-8') + return "" + diff --git a/calibre-plugin/plugin-import-name-deacsm.txt b/calibre-plugin/plugin-import-name-deacsm.txt new file mode 100644 index 0000000..e69de29 diff --git a/calibre-plugin/prefs.py b/calibre-plugin/prefs.py new file mode 100644 index 0000000..1df84db --- /dev/null +++ b/calibre-plugin/prefs.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + + + +# Standard Python modules. +import os +import traceback + +from calibre.utils.config import JSONConfig, config_dir # type: ignore +from calibre_plugins.deacsm.__init__ import PLUGIN_NAME # type: ignore +from calibre.constants import isosx, islinux # type: ignore + + +class DeACSM_Prefs(): + def __init__(self): + JSON_PATH = os.path.join("plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json') + self.deacsmprefs = JSONConfig(JSON_PATH) + + self.deacsmprefs.defaults['configured'] = False + + self.pluginsdir = os.path.join(config_dir,"plugins") + if not os.path.exists(self.pluginsdir): + os.mkdir(self.pluginsdir) + self.maindir = os.path.join(self.pluginsdir,"DeACSM") + if not os.path.exists(self.maindir): + os.mkdir(self.maindir) + self.accountdir = os.path.join(self.maindir,"account") + if not os.path.exists(self.accountdir): + os.mkdir(self.accountdir) + + # Default to the builtin UA + self.deacsmprefs.defaults['path_to_account_data'] = self.accountdir + + + def __getitem__(self,kind = None): + if kind is not None: + return self.deacsmprefs[kind] + return self.deacsmprefs + + def set(self, kind, value): + self.deacsmprefs[kind] = value + + def writeprefs(self,value = True): + self.deacsmprefs['configured'] = value + + def addnamedvaluetoprefs(self, prefkind, keyname, keyvalue): + try: + if keyvalue not in self.deacsmprefs[prefkind].values(): + # ensure that the keyname is unique + # by adding a number (starting with 2) to the name if it is not + namecount = 1 + newname = keyname + while newname in self.deacsmprefs[prefkind]: + namecount += 1 + newname = "{0:s}_{1:d}".format(keyname,namecount) + # add to the preferences + self.deacsmprefs[prefkind][newname] = keyvalue + return (True, newname) + except: + traceback.print_exc() + pass + return (False, keyname) + + def addvaluetoprefs(self, prefkind, prefsvalue): + # ensure the keyvalue isn't already in the preferences + try: + if prefsvalue not in self.deacsmprefs[prefkind]: + self.deacsmprefs[prefkind].append(prefsvalue) + return True + except: + traceback.print_exc() + return False +