diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b8d41fc..78b7d41 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -19,13 +19,15 @@ jobs: - name: Compile run: | ./bundle_calibre_plugin.sh + ./bundle_migration_plugin.sh - name: Upload uses: actions/upload-artifact@v2 with: - name: linux + name: calibre-plugins path: | calibre-plugin.zip + migration-plugin.zip test-ubuntu-2004: runs-on: ubuntu-20.04 @@ -152,4 +154,4 @@ jobs: - name: Run tests (Python 2) run: | - cd tests && PYTHONWARNINGS=ignore python2 ./main.py && cd .. \ No newline at end of file + cd tests && PYTHONWARNINGS=ignore python2 ./main.py && cd .. diff --git a/bundle_migration_plugin.sh b/bundle_migration_plugin.sh new file mode 100755 index 0000000..5982a28 --- /dev/null +++ b/bundle_migration_plugin.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +rm -rf calibre-plugin-tmp || /bin/true + +mkdir calibre-plugin-tmp +cp migration_plugin/* calibre-plugin-tmp/ +cp LICENSE calibre-plugin-tmp/ + +pushd calibre-plugin-tmp + +# Create ZIP file from calibre-plugin folder. +zip -r ../calibre-migration-plugin.zip * + +popd +rm -rf calibre-plugin-tmp + diff --git a/calibre-plugin/__calibre_compat_code.py b/calibre-plugin/__calibre_compat_code.py index 9ec5039..f264bc3 100644 --- a/calibre-plugin/__calibre_compat_code.py +++ b/calibre-plugin/__calibre_compat_code.py @@ -15,7 +15,9 @@ if "calibre" in sys.modules: # Bugfix for Calibre < 5: if sys.version_info[0] == 2: from calibre.utils.config import config_dir - if os.path.join(config_dir, "plugins", "DeACSM.zip") not in sys.path: - sys.path.insert(0, os.path.join(config_dir, "plugins", "DeACSM.zip")) + for filename in ["ACSM Input.zip", "DeACSM.zip"]: + __zip_path = os.path.join(config_dir, "plugins", filename) + if __zip_path not in sys.path and os.path.exists(__zip_path): + sys.path.insert(0, __zip_path) #@@CALIBRE_COMPAT_CODE_END@@ diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index 446fd6f..e79fb87 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -45,10 +45,14 @@ # Fix bug that would sometimes return the wrong book (or none at all) if you had # multiple active loans from the same distributor, add experimental GUI button, # rename plugin from "DeACSM" to "ACSM Input". BETA build, not a normal release!! +# +# v0.1.0: Continue work on renaming from "DeACSM" to "ACSM Input". +# The big version number jump is to make that name change clearer. + PLUGIN_NAME = "ACSM Input" -PLUGIN_VERSION_TUPLE = (0, 0, 17) +PLUGIN_VERSION_TUPLE = (0, 1, 0) from calibre.customize import FileTypePlugin # type: ignore __version__ = PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE]) @@ -61,7 +65,6 @@ from calibre.constants import isosx, iswindows, islinux # type: import os, shutil, traceback, sys, time, io, random import zipfile from lxml import etree -from calibre.gui2 import error_dialog #@@CALIBRE_COMPAT_CODE@@ @@ -80,12 +83,12 @@ class ACSMInput(FileTypePlugin): def init_embedded_plugins(self): """ A Calibre plugin can normally only contain one Plugin class. - In our case, this would be the DeACSM class. + In our case, this would be the file type class. However, we want to load the GUI plugin, too, so we have to trick Calibre into believing that there's actually a 2nd plugin. """ from calibre.customize.ui import _initialized_plugins - from calibre_plugins.deacsm.gui_main_wrapper import DeACSMGUIExtension + from calibre_plugins.deacsm.gui_main_wrapper import ACSMInputGUIExtension def init_plg(plg_type): for plugin in _initialized_plugins: @@ -100,7 +103,7 @@ class ACSMInput(FileTypePlugin): return plugin - init_plg(DeACSMGUIExtension) + init_plg(ACSMInputGUIExtension) @@ -129,51 +132,24 @@ class ACSMInput(FileTypePlugin): if not os.path.exists(self.pluginsdir): os.mkdir(self.pluginsdir) - # Okay, "I" am now the new version. If I'm running under the old name, - # move "me" to the new one. - if os.path.exists(os.path.join(self.pluginsdir, "DeACSM.zip")): - - from calibre.customize.ui import _config - - shutil.copyfile(os.path.join(self.pluginsdir, "DeACSM.zip"), os.path.join(self.pluginsdir, "ACSM Input.zip")) - - # Delete the old plugin. - os.remove(os.path.join(self.pluginsdir, "DeACSM.zip")) - - # Forcibly add the new plugin, circumventing the Calibre code. - ui_plg_config = _config() - plugins = ui_plg_config['plugins'] - plugins["ACSM Input"] = os.path.join(self.pluginsdir, "ACSM Input.zip") - ui_plg_config['plugins'] = plugins - - print("Need another restart due to plugin update ...") - # "Rude" exit, but otherwise it won't work ... - try: - os._exit(42) - except TypeError: - os._exit() - + + # If the old DeACSM plugin still exists, rename it to BAK or something so it doesn't load. + os.rename(os.path.join(self.pluginsdir, "DeACSM.zip"), os.path.join(self.pluginsdir, "DeACSM.BAK")) # Make sure the GUI extension is loaded: self.init_embedded_plugins() - - - self.maindir_old = os.path.join(self.pluginsdir,"DeACSM") self.maindir = os.path.join(self.pluginsdir,"ACSMInput") - if os.path.exists(self.maindir_old) and not os.path.exists(self.maindir): - # Migrate config to new folder - os.rename(self.maindir_old, self.maindir) - if not iswindows: - # Linux and Mac support symlinks, so create one so the old paths - # still work and people can downgrade the plugin again. - # Windows ... doesn't, so downgrading will be tricky. - try: - os.symlink(self.maindir_old, self.maindir) - except: - pass + # Do NOT try to migrate data, that just screws everything up. + # If this is a fresh install of the plugin and there's no old data, + # use the new path. Otherwise, if there's already data at the old location, + # continue to use that. + + if os.path.exists(self.maindir_old): + # We have the old folder, continue to use that + self.maindir = self.maindir_old if not os.path.exists(self.maindir): diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index 3680546..e97010c 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -1317,7 +1317,7 @@ class RentedBooksDialog(QDialog): QDialog.__init__(self,parent) self.parent = parent - self.setWindowTitle("DeACSM: Manage loaned Books") + self.setWindowTitle("ACSM Input: Manage loaned Books") self.deacsmprefs = prefs.ACSMInput_Prefs() diff --git a/calibre-plugin/getEncryptionKeyLinux.py b/calibre-plugin/getEncryptionKeyLinux.py index c659486..400f6a7 100644 --- a/calibre-plugin/getEncryptionKeyLinux.py +++ b/calibre-plugin/getEncryptionKeyLinux.py @@ -51,9 +51,15 @@ def GetMasterKey(wineprefix): try: from calibre.utils.config import config_dir - pluginsdir = os.path.join(config_dir,"plugins") - maindir = os.path.join(pluginsdir,"DeACSM") - moddir = os.path.join(maindir,"modules") + from calibre_plugins.deacsm.__init__ import maindir as plg_maindir + + if plg_maindir is not None: + print("FOUND MOD DIR!") + moddir = os.path.join(plg_maindir,"modules") + else: + pluginsdir = os.path.join(config_dir,"plugins") + maindir = os.path.join(pluginsdir,"ACSMInput") + moddir = os.path.join(maindir,"modules") except: import os moddir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "keyextract") diff --git a/calibre-plugin/gui_main.py b/calibre-plugin/gui_main.py index e6cd9b3..7bfa5ea 100644 --- a/calibre-plugin/gui_main.py +++ b/calibre-plugin/gui_main.py @@ -52,7 +52,7 @@ def create_menu_action_unique(ia, parent_menu, menu_text, image=None, tooltip=No return ac -class ActualDeACSMGUIExtension(InterfaceAction): +class ActualACSMInputGUIExtension(InterfaceAction): name = "ACSM Input Plugin GUI Extension" popup_type = QToolButton.ToolButtonPopupMode.InstantPopup @@ -106,7 +106,7 @@ class ActualDeACSMGUIExtension(InterfaceAction): break if plg is None: - msg = "Tried to open the ACSM Input plugin (DeACSM) settings, but I couldn't find the ACSM Input plugin. " + msg = "Tried to open the ACSM Input plugin settings, but I couldn't find the ACSM Input plugin. " msg += "This is most likely a bug in the plugin. Try restarting Calibre, and if you still get this error, " msg += "please open a bug report. " return error_dialog(None, "Plugin not found", msg, show=True) diff --git a/calibre-plugin/gui_main_wrapper.py b/calibre-plugin/gui_main_wrapper.py index 0ebf149..0a1a89d 100644 --- a/calibre-plugin/gui_main_wrapper.py +++ b/calibre-plugin/gui_main_wrapper.py @@ -11,9 +11,9 @@ from calibre.customize import PluginInstallationType #@@CALIBRE_COMPAT_CODE@@ -class DeACSMGUIExtension(InterfaceActionBase): +class ACSMInputGUIExtension(InterfaceActionBase): name = "ACSM Input Plugin GUI Extension" - description = "GUI code for ACSM Input Plugin (DeACSM). This is automatically installed and updated with the ACSM plugin." + description = "GUI code for ACSM Input Plugin. This is automatically installed and updated with the ACSM plugin." supported_platforms = ['linux', 'osx', 'windows'] author = "Leseratte10" minimum_calibre_version = (4, 0, 0) @@ -28,7 +28,7 @@ class DeACSMGUIExtension(InterfaceActionBase): installation_type = PluginInstallationType.EXTERNAL # Mark this as user-installed so it shows up in the plugin list by default. - actual_plugin = "calibre_plugins.deacsm.gui_main:ActualDeACSMGUIExtension" + actual_plugin = "calibre_plugins.deacsm.gui_main:ActualACSMInputGUIExtension" def is_customizable(self): return False diff --git a/calibre-plugin/libadobe.py b/calibre-plugin/libadobe.py index 0b0cea0..92400cf 100644 --- a/calibre-plugin/libadobe.py +++ b/calibre-plugin/libadobe.py @@ -101,7 +101,7 @@ def are_ade_version_lists_valid(): fail = True if fail: - print("Internal error in DeACSM: Mismatched version list lenghts.") + print("Internal error in ACSM Input: Mismatched version list lenghts.") print("This should never happen, please open a bug report.") return False diff --git a/calibre-plugin/libadobeFulfill.py b/calibre-plugin/libadobeFulfill.py index 4610b4a..b2b0eeb 100644 --- a/calibre-plugin/libadobeFulfill.py +++ b/calibre-plugin/libadobeFulfill.py @@ -572,6 +572,9 @@ def updateLoanReturnData(fulfillmentResultToken, forceTestBehaviour=False): deacsmprefs["list_of_rented_books"].append(new_loan_record) + print("DEBUG, list of books:") + print(deacsmprefs["list_of_rented_books"]) + deacsmprefs.writeprefs() return True diff --git a/calibre-plugin/prefs.py b/calibre-plugin/prefs.py index c5746fd..201a8ed 100644 --- a/calibre-plugin/prefs.py +++ b/calibre-plugin/prefs.py @@ -18,9 +18,8 @@ class ACSMInput_Prefs(): JSON_PATH = os.path.join("plugins", "ACSMInput", "ACSMInput.json") if os.path.exists(JSON_PATH_OLD) and not os.path.exists(JSON_PATH): - os.rename(JSON_PATH_OLD, JSON_PATH) - if not iswindows: - os.symlink(JSON_PATH_OLD, JSON_PATH) + # If the file exists in the old location, use that. + JSON_PATH = JSON_PATH_OLD self.deacsmprefs = JSONConfig(JSON_PATH) @@ -37,14 +36,21 @@ class ACSMInput_Prefs(): - self.pluginsdir = os.path.join(config_dir,"plugins") - self.maindir = os.path.join(self.pluginsdir,"ACSMInput") - self.accountdir = os.path.join(self.maindir,"account") - if not os.path.exists(self.accountdir): - raise Exception("Why does the account folder not exist?") + self.__pluginsdir = os.path.join(config_dir,"plugins") + + success = False + for f in ["ACSMInput", "DeACSM"]: + self.__maindir = os.path.join(self.__pluginsdir, f) + self.__accountdir = os.path.join(self.__maindir,"account") + if os.path.exists(self.__accountdir): + self.deacsmprefs.defaults['path_to_account_data'] = self.__accountdir + success = True + break + - # Default to the builtin account path - self.deacsmprefs.defaults['path_to_account_data'] = self.accountdir + if not success: + raise Exception("Why does the account folder not exist?") + def __getitem__(self,kind = None): diff --git a/migration_plugin/README.md b/migration_plugin/README.md new file mode 100644 index 0000000..457d887 --- /dev/null +++ b/migration_plugin/README.md @@ -0,0 +1,40 @@ +# What is this? + +The original name of the plugin, when I first introduced it in 2021, was "DeACSM" - similar to how the popular DRM removal plugin is called "DeDRM". However, later I realized that this is a terrible name, and I'd rather have the plugin be named "ACSM Input", similar to other file type plugins like "LCPL Input", "KFX Input", "DOC Input" and "KePub Input". + +However, the Calibre plugin updater doesn't support replacing an existing plugin A with a differently-named plugin B. + +This is where this helper plugin comes in. It's a dummy plugin that doesn't have any actual functionality, and will be released under the old name ("DeACSM") with a higher version number (0.0.18 or something like that). + +Then, all this plugin is going to be doing is, without using the Calibre updater, automatically download the renamed plugin ("ACSM Input") from my Github page, installs it into Calibre, the uninstalls itself. This will have no impact on the plugin data, that will still be stored at the same location and will be read from the same location. But it means that the plugin will get auto-renamed to its new name and can still continue to receive plugin updates through the Calibre plugin updater. + +# Process details + +I have asked on MobileRead whether it is possible [to rename a Calibre Plugin](https://www.mobileread.com/forums/showthread.php?t=348941), but I was told that that's not easily possible. Kovid suggested just making a new MobileRead thread for the "new" plugin, mark the old one as "deprecated", and then manually tell users of the old plugin somehow that they need to re-download the new one - but I didn't want to have to do that. + +The "old" plugin's versions go up to 0.0.16 for the latest release, and 0.0.17 for the latest beta. +The migration plugin you can find in this folder will have the same name "DeACSM" as the old plugin, but a higher version number (0.0.20). +Then, I will create a new dummy thread at MobileRead and upload this migration plugin to that thread. + +What this migration plugin does on first start is it downloads the "new" "ACSM Input" plugin from the MobileRead forum thread which is named "ACSM Input" with version number 0.1.0. This circumvents the Calibre Plugin updater which would refuse to update a plugin if that were to change a plugin's name. + +Once that's happened, future updates can go through the plugin updater again. + +So, the chain of versions is going to be the following: + +DeACSM 0.0.16 -> "DeACSM" migration plugin 0.0.20 -> Replaces itself with "ACSM Input 0.1.0" -> more updates in the future to ACSM Input 0.1.1 and so on. + +# MobileRead forum plugin index + +Right now, the thread for my ACSM Input plugin contains the "old" DeACSM 0.0.16 plugin. +Once I release ACSM Input 0.1.0, I will put that version into the MobileRead thread, and I will put the migration build into another dummy thread. + +Then I will have a moderator update the Calibre plugin index with two entries: + +- "DeACSM", pointing to [the dummy thread](https://www.mobileread.com/forums/showthread.php?t=348941) with the migration build. The comment will mention not to install this manually. +- "ACSM Input", pointing to the well-known [ACSM Input plugin thread](https://www.mobileread.com/forums/showthread.php?t=341975). + +This means that users are not going to see any change in behaviour in the forum thread - they're not going to notice the dummy thread, and the well-known thread that's linked everywhere will continue to point to the newest plugin updates. + +After a couple months once everyone has updated to the newest versions, the dummy thread and the old "DeACSM" entry in the plugin index can then be removed. + diff --git a/migration_plugin/__init__.py b/migration_plugin/__init__.py new file mode 100644 index 0000000..bfa5a77 --- /dev/null +++ b/migration_plugin/__init__.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Migration plugin from "DeACSM" to "ACSM Input" +# See README.md for details. + + +# Revision history: +# v0.0.20: First version of the migration plugin, released under the old name. + + +from calibre.customize import InterfaceActionBase # type: ignore +from calibre.customize import PluginInstallationType + + +class DeACSMMigrationPlugin(InterfaceActionBase): + name = "DeACSM" + description = "Extension for the ACSM Input plugin to migrate to a new plugin name" + supported_platforms = ['linux', 'osx', 'windows'] + author = "Leseratte10" + minimum_calibre_version = (4, 0, 0) + version = (0, 0, 20) + + can_be_disabled = False + # This plugin will be auto-loaded from the ACSM Input plugin. It doesn't make sense for the user + # to disable it. If necessary, the menu bar button can be removed through the Calibre settings. + + type = "File type" + # Just so that the GUI extension shows up at the same place as the actual ACSM Input plugin. + + installation_type = PluginInstallationType.EXTERNAL + # Mark this as user-installed so it shows up in the plugin list by default. + + actual_plugin = "calibre_plugins.deacsm.migration:ActualMigrationPlugin" + + def is_customizable(self): + return False + + diff --git a/migration_plugin/migration.py b/migration_plugin/migration.py new file mode 100644 index 0000000..961bb75 --- /dev/null +++ b/migration_plugin/migration.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Migration plugin from "DeACSM" to "ACSM Input" +# See README.md for details. + +import os, sys + +from calibre.gui2.actions import InterfaceAction + + +class ActualMigrationPlugin(InterfaceAction): + name = "DeACSM" + + + + def genesis(self): + print("DeACSM -> ACSM Input migration started ...") + + + + DOWNLOAD_URL = "https://github.com/Leseratte10/acsm-calibre-plugin/releases/download/config/TEST_calibre_plugin_acsminput_new_0_0_30.zip" + + + # Okay, now download the new version and uninstall myself: + from calibre.utils.config import config_dir + self.pluginsdir = os.path.join(config_dir,"plugins") + if not os.path.exists(self.pluginsdir): + os.mkdir(self.pluginsdir) + + new_path = os.path.join(self.pluginsdir, "ACSM Input.zip") + + if os.path.exists(new_path): + # If so, delete ourselves and exit + print("Already done ...") + return + + if sys.version_info[0] == 2: + import urllib + urllib.urlretrieve(DOWNLOAD_URL, new_path) + else: + import urllib.request + urllib.request.urlretrieve(DOWNLOAD_URL, new_path) + + # Check if the download was successful and the new file exists: + if os.path.exists(new_path): + # Delete myself + os.remove(os.path.join(self.pluginsdir, "DeACSM.zip")) + + # Forcibly add the new plugin + from calibre.customize.ui import _config + ui_plg_config = _config() + plugins = ui_plg_config['plugins'] + plugins["ACSM Input"] = new_path + ui_plg_config['plugins'] = plugins + + # Force-kill Calibre and have the user manually restart it: + print("Force-exit, please restart") + try: + os._exit(42) + except TypeError: + os._exit() + + else: + print("Download / Update failed, trying again later ...") + print("Please open a bug report for the ACSM Input plugin") + + + diff --git a/migration_plugin/plugin-import-name-deacsm.txt b/migration_plugin/plugin-import-name-deacsm.txt new file mode 100644 index 0000000..e69de29