From 2904e187c4f4fe0ed73a1cd2427104b4b307cef9 Mon Sep 17 00:00:00 2001 From: Florian Bach Date: Mon, 5 Sep 2022 18:34:40 +0200 Subject: [PATCH] Add GUI, rename to "ACSM Input" --- .github/ISSUE_TEMPLATE/bug_report.yml | 8 +- calibre-plugin/__init__.py | 86 +++++++++++-- calibre-plugin/acsm_logo_2.png | Bin 0 -> 12886 bytes calibre-plugin/config.py | 74 +++++++---- .../exportPluginAuthToWindowsADE.py | 2 +- calibre-plugin/getEncryptionKeyLinux.py | 2 +- calibre-plugin/getEncryptionKeyWindows.py | 2 +- calibre-plugin/gui_main.py | 116 ++++++++++++++++++ calibre-plugin/gui_main_wrapper.py | 36 ++++++ calibre-plugin/libadobeAccount.py | 2 +- calibre-plugin/libadobeFulfill.py | 12 +- calibre-plugin/prefs.py | 26 ++-- 12 files changed, 308 insertions(+), 58 deletions(-) create mode 100644 calibre-plugin/acsm_logo_2.png create mode 100644 calibre-plugin/gui_main.py create mode 100644 calibre-plugin/gui_main_wrapper.py diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9a27807..e27eea5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,16 +35,16 @@ body: id: calibre-version attributes: label: Which version of Calibre are you running? - description: "Example: 6.2.1" - placeholder: "6.2.1" + description: "Example: 6.4" + placeholder: "6.4" validations: required: true - type: input id: plugin-version attributes: label: Which version of the DeACSM plugin are you running? - description: "Example: v0.0.16" - placeholder: "v0.0.16" + description: "Example: v0.0.17" + placeholder: "v0.0.17" validations: required: true - type: textarea diff --git a/calibre-plugin/__init__.py b/calibre-plugin/__init__.py index 4d49e70..4eb4b3a 100644 --- a/calibre-plugin/__init__.py +++ b/calibre-plugin/__init__.py @@ -43,10 +43,11 @@ # update python-oscrypto to unofficial fork to fix OpenSSL 3 support. # In Progress: # Fix bug that would sometimes return the wrong book (or none at all) if you had -# multiple active loans from the same distributor. +# multiple active loans from the same distributor, add experimental GUI button, +# rename plugin from "DeACSM" to "ACSM Input" -PLUGIN_NAME = "DeACSM" +PLUGIN_NAME = "ACSM Input" PLUGIN_VERSION_TUPLE = (0, 0, 16) from calibre.customize import FileTypePlugin # type: ignore @@ -60,10 +61,11 @@ 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@@ -class DeACSM(FileTypePlugin): +class ACSMInput(FileTypePlugin): name = PLUGIN_NAME description = "ACSM Input Plugin - Takes an Adobe ACSM file and converts that into a useable EPUB or PDF file. Python reimplementation of libgourou by Grégory Soutadé" supported_platforms = ['linux', 'osx', 'windows'] @@ -75,7 +77,35 @@ class DeACSM(FileTypePlugin): on_preprocess = True priority = 2000 + def init_embedded_plugins(self): + """ + A Calibre plugin can normally only contain one Plugin class. + In our case, this would be the DeACSM 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 + + def init_plg(plg_type): + for plugin in _initialized_plugins: + if isinstance(plugin, plg_type): + return plugin + + plg_type.version = self.version + plg_type.minimum_calibre_version = self.minimum_calibre_version + plugin = plg_type(self.plugin_path) + _initialized_plugins.append(plugin) + plugin.initialize() + + return plugin + + init_plg(DeACSMGUIExtension) + + + def initialize(self): + """ On initialization, make sure we have all the libraries (oscrypto and its dependency asn1crypto) that the plugin needs. Unfortunately the Adobe encryption is kinda weird @@ -98,7 +128,49 @@ class DeACSM(FileTypePlugin): 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") + + # 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 + + return + + + # 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 + + if not os.path.exists(self.maindir): os.mkdir(self.maindir) @@ -222,7 +294,7 @@ class DeACSM(FileTypePlugin): import calibre_plugins.deacsm.prefs as prefs # type: ignore - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() update_account_path(deacsmprefs["path_to_account_data"]) except Exception as e: @@ -242,7 +314,7 @@ class DeACSM(FileTypePlugin): def ADE_sanity_check(self): import calibre_plugins.deacsm.prefs as prefs # type: ignore - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() from libadobe import get_activation_xml_path @@ -403,7 +475,7 @@ class DeACSM(FileTypePlugin): print("{0} v{1}: Try to fulfill ...".format(PLUGIN_NAME, PLUGIN_VERSION)) import calibre_plugins.deacsm.prefs as prefs # type: ignore - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() success, replyData = fulfill(path_to_ebook, deacsmprefs["notify_fulfillment"]) diff --git a/calibre-plugin/acsm_logo_2.png b/calibre-plugin/acsm_logo_2.png new file mode 100644 index 0000000000000000000000000000000000000000..f26ced3774e1d2b26f52b6d76ece3d1d42970b9e GIT binary patch literal 12886 zcmbWdWl$YW&@g(=!JVK9?(Q0bTX1)W;O=e**WeJ`-QDF7+&vK7-GV!p=dJpxz8~+8 zdv9&+>`qTlPtUeg_w+_6%1fXi;voV602C=nF=YS%`0)w^z{7q#be&4g0D#YqUaFcd z%0})a4o>!FmNup&E}jmiB&HsgAA=st6B$}=z)Rg2lU^MACGMK1UKH= zR;D^m_FpxotT#Dz?RVY0%N_Luh&(d-Zl6zzSJ(gTaZRs`zj+~iYj_h7L>h7R*K_~J zrKq>aT$b{pHSdRSv1-SczuIlwrnCFFvZ^<1VTp(dbD`nTo1L?lKyuiE_H)t85AnY1 zl?a9TZS|NbWR>wkv^NL)rs`^<;4X)Dqh;mxGI#98tm)gU)vcwPDY+#<<>8RfU!3{9QXYy z^X@G~c7>s;b=Fkgeg7*{{jSFh=7xJkY#yC;*AQL(S3K5vr>DdYmf;CxJmd4ZMepfl zg-{et#h7#?B%5XfQ5Q$zZtR#WsC$GO4S1f!16zP<|7su$lOZre7yVb}#Jc&xv~jNx z%7|}xw=s}A(`JS*BS$?R)_SNTZBFak znngv;RP4>0HG2h$@QFk&56YI{^aVTeFt2p2ocB2i)@Q_Dk#{@_YE70Nz2T>@r=C}= zb=NJ=^Kr)vRMV(tEeqZJd!w6pnB^#I^7nmZM&l43A}G2~!_4KI-o(H#i`VMi-_oJC z-s_e!{R`X0cxsEUZ+2z{OzkDD$SKXWA~CS1@YkO?S*L9w6kTzRju&2|@w;@7aaQly zN%w5xo2ea9OR?r}qnp*%aX$aED38=o)A{K;+*%dOX(WFRZGCi>3{*wSo9)=D*`Hc- zcQ)Oa*PSW4}}#F#}}fYV3J}?444@wU3~GPPO_Ld zBU#i97lfN~kEx`X+zPbNbNKqzc$E9^uqFef?$RA$oT>Cu8$5OGFz%Zz^6AUsyTi2A zhxDO}&XvFM54;=>7F-tm(b%~eG>`(5mO%o9840M(E-5>6Pm5afi-FI(7{$cxWEeNWLF4j0AhKSfCvC#FH#`Ozq z%Ow^{5B-Dup{0jzFYcgYVHI@xqMiKbl??K|c=cm%+wJ1*9aE^7rVAd2iP|9s1(6~J zW5;L06y~pehWnfdvS4UA=;C!WkxC{j7~ajq{}|5)_1B zZ)zvyM-Ex%_*|H*aDFY|mZsy?H@BaGDb%}|#IR=E@PCA7`;YtV>Xn=@?1qt2x>~rG z#3$I0l8S^mr#U?8o7Iq@37x-h&8iL)6;fgegUSC1I~WQo7?y+Mj`+I0)#gX7B_|~V z!+n!2Me`N$K|hs4jf0fYuW%$gWWE%N_!UuL?#zqy%9rCjU{)5wJWQL@kT&hP`Y1Qz z_+44OIY1Rpw)h}+%q|b%;*3UK3n?C2>igW+!Pyr`lvkUudHuKh&I;gk$UHSgt1Kfi7FRb7-0)!b;QBW^v#j2OeaTB(7m}ejEF)}iSO%>tC&(KuW zybE8^?`92Yp4&hZzqtK6MQ}>2`8K87)>R$3Ev~WHo3iI!baujod56!ooki0x7@w3X zeX7;g=L=vQd8`Bwzb&&8h~31@H#oghIf+}k7WmjNURMN5qA!2q5!Tj4qk{b6KDb>VgvxSG*ggw

L4X=I8%+W_LQ7cVqN~Ny=#1>8w?I< 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

pfApi6pbREk({dd2bLh zMiemA66UN|fWK)!Dfs%#s~=oIZM5_0VUH=+VkidpWBf-GeRJNMMzQf>*Mq&{ zn8rG%SkTdk3a~d@25bY*&tstc$(|)_pYF1%w-wYo^@HpcysbYWmr>T6IU9WSB+CbX z(s*FW_o9_950K>F-^QlCjFl%TFnxYHl+gnG07%F`p}l6U9}~w5)<6w@{LawvY|1{l z5!(h7I2r6KmdMv0w_rEE@b_ip^WH0dN?6mPh_2V!rp0)QR`NT@ezg2Z^r4wIiTH(y>dHq-E< zT`#Lz`moJY4!(!Ky{PcBI{`;U69f1jjD$8`&rZg4YzL#lTAn)4f^M(v zzbQZaoym@B!m%$$11bdp#D25>?^&d-DB3taF7mZHunq$^OSt@6fF?5?+!q@O)0o#` zuJ^pAYuHcViyn17?%Yi2wnEJA)_(t-9a2;*uj~FzPjq6QMgJck(LYF$%lcmZ?xE+F z=PlT5ZuIeA10mHFA!lH)wNE&XjLLHK`Sng{!%6-_87>Kfz$Nr}`v&i$9Q_1NC;6C> z(}vkQ>#bh_LX?l$#3k%9!6E0)dTX#mHm*LieK|QK$0n%4Z<{}&c=!ShvLG0u z<8S<3FvYq(XBcM6Fq7D+h+q+#o?jjRt-rHfD^gb&qJ7l>PPRGkS#9LlyQ)}om}Bm2 zsam&9S-RRzL>gZX@{`dyy9hxOFftqKx?nJus{KC4K6Ai;a5uy>2XlXhBAQWqeMl^4 zRlB)!y_t`|(y&%}yOF@f4(Hs=v%gPncAL)$v>VApL>_wG1tlJ1RUv57$J}Rhdu{x& z32*p^1KsH7ZV%4bPtB_?YUKXi(}N=L<~;X$Fa%YTBf!qYP~V*9Er{{9^;>gtu)=kW z88=s*&l}?Zc!od8bWwz7N5%21P^)cNY? zF{Foe*#Xy*O4$@-A)T@4Rn}}<m%SOb@m1YeXGE5kd8?H|L1$e ziSumJop*3}X;WOeJMa(`S>rPPRGNz}RsMn#=Pp28+Dxe{m&m)9`t=t`0f3Tf8Fz&7 zZeRzNlpbiw3a$OL1Y`i-DVNLzAWc51Lm`1(<&l58HILx^M*xPJ6NC?T=Sdb+oA?%2 zEeu;25CpjgJ3>ymE_qT zMb9Y#sc4-f+(NWe`|q%6FIyeZKOy{dCeoi2?m-idtdBdzvX&Y`=tGNELOU?Qf{%!) z8|PQ*3_<5YZURhcm@VSMQGFCcpp~rXDRu{<0KKcY;BBeo@)yq%WIH%JM%se9e+@ei zLf4RFh;g+nbC%c^XF>Vwz4hYd-@mR`J_Qea46*fU4xG6cK%!Wk69csJ?%9RogK02n z(J}JnNL#mo7O4z*%Dxh3^4f1%T7hFtC|Y?&wjX7bSlG2Bk;y%uSD$q@Gnk@2d@2;K zSyo~Skgz%|z9!z1>A2!*rMf=JZPsn``=(!ct4A0-PMzB(mto7eS`lK=8srR;`pr`4 zWRSxR&S3DXaGLuMSYz3N&S+usx_-rBW2&OF-_d&pKM}qW-_kf^fWud1%6IcLQ-!}+ z4gmXEbisUPk~Mm>OIRj%5*uXGQ3~Rkh4bP6>zVn-IXaow3vuys5VPo22HD4%KR`-c LUaVTgF!28Y^fov} literal 0 HcmV?d00001 diff --git a/calibre-plugin/config.py b/calibre-plugin/config.py index f3bb3c6..3680546 100644 --- a/calibre-plugin/config.py +++ b/calibre-plugin/config.py @@ -46,7 +46,7 @@ class ConfigWidget(QWidget): self.plugin_path = plugin_path # get the prefs - self.deacsmprefs = prefs.DeACSM_Prefs() + self.deacsmprefs = prefs.ACSMInput_Prefs() # make a local copy self.tempdeacsmprefs = {} @@ -58,7 +58,6 @@ class ConfigWidget(QWidget): self.tempdeacsmprefs['list_of_rented_books'] = self.deacsmprefs['list_of_rented_books'] - # Start Qt Gui dialog layout layout = QVBoxLayout(self) self.setLayout(layout) @@ -150,7 +149,13 @@ class ConfigWidget(QWidget): self.button_rented_books.setEnabled(activated) ua_group_box_layout.addWidget(self.button_rented_books) - if (len(self.deacsmprefs["list_of_rented_books"]) == 0): + + # First remove all overdue books from the loan list, + # to determine if we should enable the button. + self.delete_overdue_books_from_loan_list() + + if (len(self.tempdeacsmprefs["list_of_rented_books"]) == 0): + self.button_rented_books.setText(_("No loaned books available")) self.button_rented_books.setEnabled(False) @@ -1275,17 +1280,47 @@ class ConfigWidget(QWidget): def show_rented_books(self): - d = RentedBooksDialog(self, self.deacsmprefs["list_of_rented_books"]) + self.delete_overdue_books_from_loan_list() + + d = RentedBooksDialog(self) d.exec_() + def delete_overdue_books_from_loan_list(self): + overdue_books = [] + + for book in self.deacsmprefs["list_of_rented_books"]: + try: + book_time_stamp = book["validUntil"] + timestamp = datetime.datetime.strptime(book_time_stamp, "%Y-%m-%dT%H:%M:%SZ") + currenttime = datetime.datetime.utcnow() + except: + # Invalid book timestano + continue + + if (timestamp <= currenttime): + # Book is overdue, no need to return. Delete from list. + overdue_books.append(book) + continue + + templist = self.deacsmprefs["list_of_rented_books"] + + for book in overdue_books: + templist.remove(book) + + self.deacsmprefs.set("list_of_rented_books", templist) + self.deacsmprefs.writeprefs() + + class RentedBooksDialog(QDialog): - def __init__(self, parent, booklist): + def __init__(self, parent): QDialog.__init__(self,parent) self.parent = parent self.setWindowTitle("DeACSM: Manage loaned Books") + self.deacsmprefs = prefs.ACSMInput_Prefs() + # Start Qt Gui dialog layout layout = QVBoxLayout(self) self.setLayout(layout) @@ -1353,37 +1388,22 @@ class RentedBooksDialog(QDialog): def populate_list(self): self.listy.clear() - overdue_books = [] - - for book in self.parent.deacsmprefs["list_of_rented_books"]: + for book in self.deacsmprefs["list_of_rented_books"]: try: book_time_stamp = book["validUntil"] timestamp = datetime.datetime.strptime(book_time_stamp, "%Y-%m-%dT%H:%M:%SZ") currenttime = datetime.datetime.utcnow() except: - # Invalid book timestano + # Invalid book timestamp continue - - if (timestamp <= currenttime): - # Book is overdue, no need to return. Delete from list. - overdue_books.append(book) - continue - else: - info = "(" + self.td_format(timestamp - currenttime) - info += " remaining)" - + info = "(" + self.td_format(timestamp - currenttime) + " remaining)" item = QListWidgetItem(book["book_name"] + " " + info) item.setData(QtCore.Qt.UserRole, book["loanID"]) self.listy.addItem(item) - for book in overdue_books: - self.parent.deacsmprefs["list_of_rented_books"].remove(book) - - self.parent.deacsmprefs.writeprefs() - def return_book(self): if not self.listy.currentItem(): @@ -1400,7 +1420,7 @@ class RentedBooksDialog(QDialog): traceback.print_exc() Ret_book = None - for book in self.parent.deacsmprefs["list_of_rented_books"]: + for book in self.deacsmprefs["list_of_rented_books"]: if book["loanID"] == userdata: Ret_book = book break @@ -1430,14 +1450,16 @@ class RentedBooksDialog(QDialog): success = False done = False + templist = self.deacsmprefs["list_of_rented_books"] while not done: done = True - for book in self.parent.deacsmprefs["list_of_rented_books"]: + for book in templist: if book["loanID"] == userdata: done = False - self.parent.deacsmprefs["list_of_rented_books"].remove(book) + templist.remove(book) success = True break + self.deacsmprefs.set("list_of_rented_books", templist) self.populate_list() diff --git a/calibre-plugin/exportPluginAuthToWindowsADE.py b/calibre-plugin/exportPluginAuthToWindowsADE.py index 4415df2..d2a2c54 100644 --- a/calibre-plugin/exportPluginAuthToWindowsADE.py +++ b/calibre-plugin/exportPluginAuthToWindowsADE.py @@ -101,7 +101,7 @@ def GetMasterKey(): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass diff --git a/calibre-plugin/getEncryptionKeyLinux.py b/calibre-plugin/getEncryptionKeyLinux.py index ee46351..c659486 100644 --- a/calibre-plugin/getEncryptionKeyLinux.py +++ b/calibre-plugin/getEncryptionKeyLinux.py @@ -11,7 +11,7 @@ def GetMasterKey(wineprefix): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass diff --git a/calibre-plugin/getEncryptionKeyWindows.py b/calibre-plugin/getEncryptionKeyWindows.py index 5ee41e6..74874ec 100644 --- a/calibre-plugin/getEncryptionKeyWindows.py +++ b/calibre-plugin/getEncryptionKeyWindows.py @@ -115,7 +115,7 @@ def GetMasterKey(): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass diff --git a/calibre-plugin/gui_main.py b/calibre-plugin/gui_main.py new file mode 100644 index 0000000..e6cd9b3 --- /dev/null +++ b/calibre-plugin/gui_main.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# GUI for the ACSM plugin. +# +# "create_menu_action_unique" taken from the Quality Check plugin: +# GPLv3, Copyright 2011, Grant Drake + + +from calibre.gui2.actions import InterfaceAction +from calibre.gui2.actions import menu_action_unique_name +from PyQt5.QtGui import QMenu, QToolButton + + +#@@CALIBRE_COMPAT_CODE@@ + + +def create_menu_action_unique(ia, parent_menu, menu_text, image=None, tooltip=None, + shortcut=None, triggered=None, is_checked=None, shortcut_name=None, + unique_name=None, favourites_menu_unique_name=None): + ''' + Create a menu action with the specified criteria and action, using the new + InterfaceAction.create_menu_action() function which ensures that regardless of + whether a shortcut is specified it will appear in Preferences->Keyboard + ''' + orig_shortcut = shortcut + kb = ia.gui.keyboard + if unique_name is None: + unique_name = menu_text + if not shortcut == False: + full_unique_name = menu_action_unique_name(ia, unique_name) + if full_unique_name in kb.shortcuts: + shortcut = False + else: + if shortcut is not None and not shortcut == False: + if len(shortcut) == 0: + shortcut = None + else: + shortcut = _(shortcut) + + if shortcut_name is None: + shortcut_name = menu_text.replace('&','') + + ac = ia.create_menu_action(parent_menu, unique_name, menu_text, icon=None, shortcut=shortcut, + description=tooltip, triggered=triggered, shortcut_name=shortcut_name) + if shortcut == False and not orig_shortcut == False: + if ac.calibre_shortcut_unique_name in ia.gui.keyboard.shortcuts: + kb.replace_action(ac.calibre_shortcut_unique_name, ac) + #if image: + #ac.setIcon(get_icons(image, "ACSM Input")) + + + return ac + +class ActualDeACSMGUIExtension(InterfaceAction): + name = "ACSM Input Plugin GUI Extension" + + popup_type = QToolButton.ToolButtonPopupMode.InstantPopup + action_type = 'global' + action_spec = ("ACSM Input", None, "ACSM Input Plugin by Leseratte10", None) + # Text, icon, tooltip, keyboard shortcut + + def genesis(self): + print("Genesis!") + self.menu = QMenu(self.gui) + + self.rebuild_menus() + + self.qaction.setMenu(self.menu) + icon = get_icons('acsm_logo_2.png', "ACSM Input Plugin") + self.qaction.setIcon(icon) + #self.qaction.triggered.connect(self.trigger_config_dialog) + + def rebuild_menus(self): + m = self.menu + m.clear() + + create_menu_action_unique(self, m, "ACSM Input configuration", None, shortcut=None, shortcut_name="Open ACSM Input plugin settings dialog", triggered=self.trigger_config_dialog) + create_menu_action_unique(self, m, "Show loaned books", None, shortcut=None, shortcut_name="ACSM: Open list of loaned books", triggered=self.trigger_loan_dialog) + + + def trigger_loan_dialog(self): + import calibre_plugins.deacsm.prefs as prefs + from calibre.gui2 import info_dialog + deacsmprefs = prefs.ACSMInput_Prefs() + + if (len(deacsmprefs["list_of_rented_books"]) == 0): + return info_dialog(None, "No loaned books", "You currently have no loaned books.", show=True, show_copy_button=False) + + from calibre_plugins.deacsm.config import RentedBooksDialog # type: ignore + d = RentedBooksDialog(self.gui) + d.exec_() + + + + + def trigger_config_dialog(self): + from calibre.customize.ui import _initialized_plugins + from calibre_plugins.deacsm.__init__ import PLUGIN_NAME + from calibre.gui2 import error_dialog + + plg = None + for plugin in _initialized_plugins: + if plugin.name == PLUGIN_NAME: + plg = plugin + 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 += "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) + + plg.do_user_config(self.gui) + + diff --git a/calibre-plugin/gui_main_wrapper.py b/calibre-plugin/gui_main_wrapper.py new file mode 100644 index 0000000..0ebf149 --- /dev/null +++ b/calibre-plugin/gui_main_wrapper.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# GUI for the ACSM plugin. +# + +from calibre.customize import InterfaceActionBase # type: ignore +from calibre.customize import PluginInstallationType + + + +#@@CALIBRE_COMPAT_CODE@@ + +class DeACSMGUIExtension(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." + supported_platforms = ['linux', 'osx', 'windows'] + author = "Leseratte10" + minimum_calibre_version = (4, 0, 0) + + 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.gui_main:ActualDeACSMGUIExtension" + + def is_customizable(self): + return False + + diff --git a/calibre-plugin/libadobeAccount.py b/calibre-plugin/libadobeAccount.py index 2549a25..e294582 100644 --- a/calibre-plugin/libadobeAccount.py +++ b/calibre-plugin/libadobeAccount.py @@ -787,7 +787,7 @@ def activateDevice(useVersionIndex = 0, proxyData = None): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass diff --git a/calibre-plugin/libadobeFulfill.py b/calibre-plugin/libadobeFulfill.py index 93555bd..4610b4a 100644 --- a/calibre-plugin/libadobeFulfill.py +++ b/calibre-plugin/libadobeFulfill.py @@ -321,7 +321,7 @@ def fulfill(acsm_file, do_notify = False): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass @@ -549,7 +549,7 @@ def updateLoanReturnData(fulfillmentResultToken, forceTestBehaviour=False): try: import calibre_plugins.deacsm.prefs as prefs # type: ignore - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() except: print("Exception while reading config file") return False @@ -563,7 +563,7 @@ def updateLoanReturnData(fulfillmentResultToken, forceTestBehaviour=False): done = False deacsmprefs["list_of_rented_books"].remove(book) break - + # Add all necessary information for a book return to the JSON array. # The config widget can then read this and present a list of not-yet-returned @@ -583,7 +583,7 @@ def tryReturnBook(bookData): verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass @@ -620,7 +620,7 @@ def tryReturnBook(bookData): etree.SubElement(full_text_xml, etree.QName(NSMAP["adept"], "signature")).text = signature - print("Would notify server %s:" % (operatorURL + "/LoanReturn")) + print("Notifying loan return server %s" % (operatorURL + "/LoanReturn")) doc_send = "\n" + etree.tostring(full_text_xml, encoding="utf-8", pretty_print=True, xml_declaration=False).decode("utf-8") if verbose_logging: print(doc_send) @@ -650,7 +650,7 @@ def performFulfillmentNotification(fulfillmentResultToken, forceOptional = False verbose_logging = False try: import calibre_plugins.deacsm.prefs as prefs - deacsmprefs = prefs.DeACSM_Prefs() + deacsmprefs = prefs.ACSMInput_Prefs() verbose_logging = deacsmprefs["detailed_logging"] except: pass diff --git a/calibre-plugin/prefs.py b/calibre-plugin/prefs.py index 8fe4815..c5746fd 100644 --- a/calibre-plugin/prefs.py +++ b/calibre-plugin/prefs.py @@ -8,12 +8,20 @@ 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 iswindows # type: ignore -class DeACSM_Prefs(): +class ACSMInput_Prefs(): def __init__(self): - JSON_PATH = os.path.join("plugins", PLUGIN_NAME.strip().lower().replace(' ', '_') + '.json') + + JSON_PATH_OLD = os.path.join("plugins", "deacsm.json") + 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) + self.deacsmprefs = JSONConfig(JSON_PATH) self.deacsmprefs.defaults['configured'] = False @@ -30,19 +38,15 @@ class DeACSM_Prefs(): 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.maindir = os.path.join(self.pluginsdir,"ACSMInput") self.accountdir = os.path.join(self.maindir,"account") if not os.path.exists(self.accountdir): - os.mkdir(self.accountdir) + raise Exception("Why does the account folder not exist?") - # Default to the builtin UA + # Default to the builtin account path self.deacsmprefs.defaults['path_to_account_data'] = self.accountdir - + def __getitem__(self,kind = None): if kind is not None: return self.deacsmprefs[kind]