|
|
#1111 | |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
|
Quote:
|
|
|
|
|
|
|
#1112 |
|
Zealot
![]() Posts: 124
Karma: 10
Join Date: Dec 2008
Location: France
Device: None
|
Windows 11 / calibre 6.20 / Action Chains 1.18.7
I have the same problem as Philantrop. I can't add a calibre action. Here is the json file from export. Spoiler:
Thanks |
|
|
|
| Advert | |
|
|
|
|
#1113 | |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
|
Quote:
Anyway, here is modified version of the plugin that has print some debug statements. Please run this modified version in debug mode, and post your debug log. Last edited by capink; 06-14-2023 at 01:43 PM. Reason: remove debug version |
|
|
|
|
|
|
#1114 |
|
Zealot
![]() Posts: 124
Karma: 10
Join Date: Dec 2008
Location: France
Device: None
|
Thank you.
Here is the log file (not sure if it is what you want) and a new export of the action chain, just in case. To be clear, I get the error as soon as I click the icon settings - If I want to modify a calibre action (for example, Extract ISBN in the screenshot) - If I want to add a calibre action (I also tried on a portable version of calibre, and I get the same thing) |
|
|
|
|
|
#1115 |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
|
From what I can see from the log the problem is one of the multiple SortAction is returning an int value of 0 instead of returning a text value (when calling QAction.text() method). I don't why this is happening and why it is not replicated on my system.
Anyway try this new version, see whether it helps with the problem. Last edited by capink; 06-14-2023 at 01:42 PM. Reason: zip removed. New version with modifications releases |
|
|
|
| Advert | |
|
|
|
|
#1116 |
|
Zealot
![]() Posts: 124
Karma: 10
Join Date: Dec 2008
Location: France
Device: None
|
It works.
Thanks a lot ! |
|
|
|
|
|
#1117 | |
|
Addict
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 296
Karma: 32153
Join Date: Dec 2008
Device: Kindles (e-ink)
|
Quote:
|
|
|
|
|
|
|
#1118 |
|
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,679
Karma: 7908443
Join Date: Sep 2020
Device: none
|
Hi, do we have a chain for Prince PDF plugin? I could only set up the first step, that runs the existing Calibre Action and brings up the plugin dialog. Then we would need some script to send alt+P, then maybe wait a second, then send alt + V.
|
|
|
|
|
|
#1119 |
|
Member
![]() Posts: 15
Karma: 10
Join Date: Apr 2013
Device: lots of, frequently changing
|
Persistant vars?
Trying to use an old chain (which has worked AFAIK), I get "EXCEPTION: Formatter: Unknown function set_persistent_vars"?
Problem in front of the screen? |
|
|
|
|
|
#1120 | |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
|
Quote:
I thought no one used them. But if you want to get them back, copy the code below into your module editor: Code:
from calibre.utils.config import JSONConfig
from calibre_plugins.action_chains.templates.functions.base import TemplateFunction
###########################################
# Persistent Vars
###########################################
persistent_storage = JSONConfig('plugins/action_chains/action-chains-persistent-storage')
# initialize root namespace if not present
if not persistent_storage.get('root_namespace'):
persistent_storage['root_namespace'] = {}
class VarStorage(object):
def __init__(self):
self.root_namespace = persistent_storage['root_namespace']
# prefixes are used to avoid vars overwriting namespaces
self.var_type = 'VAR'
self.ns_type = 'NS'
# Naming rules : {{{{
def mangle_name(self, name, typ):
if not self.validate_name(name):
raise ValueError('Invalid {} name: {}'.format(typ, name))
prefix = '_{}_'.format(typ)
name = name if name.startswith(prefix) else prefix + name
return name
def unmangle_name(self, name, typ):
prefix = '_{}_'.format(typ)
if name.startswith(prefix):
name = name.replace(prefix, '')
return name
def is_var(self, name):
prefix = '_{}_'.format(self.var_type)
if name.startswith(prefix):
return True
return False
def is_namespace(self, name):
prefix = '_{}_'.format(self.ns_type)
if name.startswith(prefix):
return True
return False
def validate_namespace_path(self, namespace_path):
for node in namespace_path:
if not self.validate_name(node):
return False
return True
def validate_name(self, name):
if not name.replace('_', '').isalnum():
return False
return True
def mangle_namespace_path(self, namespace_path):
if not self.validate_namespace_path(namespace_path):
raise ValueError('Invalid namespace path: {}'.format(namespace_path))
mangled_path = []
for node in namespace_path:
mangled_node = self.mangle_name(node, self.ns_type)
mangled_path.append(mangled_node)
return mangled_path
# }}}}
def commit(self):
persistent_storage.commit()
def get_namespace(self, namespace_path=[]):
mangled_path = self.mangle_namespace_path(namespace_path)
ns = self.root_namespace
for node in mangled_path:
try:
ns = ns[node]
except KeyError as e:
raise ValueError('Namespace cannot be found: {}'.format(namespace_path))
return ns
def create_namespace_if_not_present(self, namespace_path=[]):
mangled_path = self.mangle_namespace_path(namespace_path)
ns = self.root_namespace
for node in mangled_path:
if not ns.get(node):
ns[node] = {}
ns = ns[node]
def get_var(self, var_name, namespace_path=[]):
try:
namespace = self.get_namespace(namespace_path)
except:
return ''
name = self.mangle_name(var_name, self.var_type)
return namespace.get(name, '')
def set_var(self, var_name, var_value, namespace_path=[]):
if not isinstance(var_value, str):
raise ValueError('Persistent variable values can only of string type')
self.create_namespace_if_not_present(namespace_path)
namespace = self.get_namespace(namespace_path)
name = self.mangle_name(var_name, self.var_type)
namespace[name] = var_value
self.commit()
def list_vars(self, namespace_path=[]):
namespace = self.get_namespace(namespace_path)
all_vars = []
for name in namespace.keys():
if self.is_var(name):
var = self.unmangle_name(name, self.var_type)
all_vars.append(var)
return all_vars
def list_namespaces(self, namespace_path=[]):
if not namespace_path:
namespace = self.root_namespace
else:
namespace = self.get_namespace(namespace_path)
all_ns = []
for name in namespace.keys():
if self.is_namespace(name):
ns = self.unmangle_name(name, self.ns_type)
all_ns.append(ns)
return all_ns
def delete_namespace(self, namespace_path=[]):
if not namespace_path:
self.root_namespace.clear()
else:
if len(namespace_path) == 1:
parent = self.root_namespace
elif len(namespace_path) > 1:
parent_path = namespace_path[:-1]
parent = self.get_namespace(parent_path)
mangled_name = self.mangle_name(namespace_path[-1], self.ns_type)
del parent[mangled_name]
self.commit()
def delete_var(self, var_name, namespace_path=[]):
namespace = self.get_namespace(namespace_path)
mangled_name = self.mangle_name(var_name, self.var_type)
del namespace[mangled_name]
self.commit()
persistent_vars = VarStorage()
###########################################
# Persistent Vars Template Functions
###########################################
class PersistentVars(TemplateFunction):
doc = _('Retrieve value for variable stored in persistent memory using set_persistent_vars()\n'
'Usage: persistent_vars(var_name, [namespace])\n'
'`var_name`: name of the variable\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, the default namespace will be used.\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'persistent_vars'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [1, 2]:
raise ValueError('Incorrect number of arguments')
var_name = args[0]
if len(args) == 2:
namespace_path = args[1].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
try:
return persistent_vars.get_var(var_name, namespace_path)
except:
# if namespace cannot be found
return ''
class SetPersistentVars(TemplateFunction):
doc = _('Set variables to presistent memory\n'
'Usage: set_persistent_vars(var_name, var_value, [namespace])\n'
'`var_name`: name of the variable\n'
'`var_value`: value of the variable\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, the default namespace will be used.\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'set_persistent_vars'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [2, 3]:
raise ValueError('Incorrect number of arguments')
var_name = args[0]
var_value = args[1]
if not persistent_vars.validate_name(var_name):
raise ValueError('Invalid variable name: {}'.format(var_name))
if len(args) == 3:
namespace_path = args[2].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
# We don't want to write vars when the user is typing in the template
if not self.context == 'template_dialog_mode':
persistent_vars.set_var(var_name, var_value, namespace_path)
return ''
class DeletePersistentVars(TemplateFunction):
doc = _('Delete variable from presistent memory\n'
'Usage: set_persistent_vars(var_name, var_value, [namespace])\n'
'`var_name`: name of the variable\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, the default namespace will be used.\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'delete_persistent_vars'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [1, 2]:
raise ValueError('Incorrect number of arguments')
var_name = args[0]
if not persistent_vars.validate_name(var_name):
raise ValueError('Invalid variable name: {}'.format(var_name))
if len(args) == 2:
namespace_path = args[1].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
# We don't want to delete vars when the user is typing in the template
if not self.context == 'template_dialog_mode':
try:
persistent_vars.delete_var(var_name, namespace_path)
except Exception as e:
import traceback
print(traceback.format_exc())
return ''
class ListPersistentVars(TemplateFunction):
doc = _('List variables stored in presistent memory using set_persistent_vars()\n'
'Usage: list_persistent_vars([namespace])\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, the default namespace will be used.\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'list_persistent_vars'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [0, 1]:
raise ValueError('Incorrect number of arguments')
if len(args) == 1:
namespace_path = args[0].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
all_vars = persistent_vars.list_vars(namespace_path)
return ','.join(all_vars)
class ListPersistentNamespaces(TemplateFunction):
doc = _('List namespaces in presistent memory used by set_persistent_vars()\n'
'Usage: list_persistent_namespaces([namespace])\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, all top level namespaces will be listed.\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'list_persistent_namespaces'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [0, 1]:
raise ValueError('Incorrect number of arguments')
if len(args) == 1:
namespace_path = args[0].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
all_ns = persistent_vars.list_namespaces(namespace_path)
return ','.join(all_ns)
class DeletePersistentNamespaces(TemplateFunction):
doc = _('Delete a namespace in presistent memory used by set_persistent_vars()\n'
'Usage: delete_persistent_namespaces([namespace])\n'
'`namespace`: (optional) namespace where the variable is stored. Nested namespaces '
'are possible and are represented by dot notation e.g. namespace1.namspace2 '
'If no namespace is provided, the top level namespace including all namespaces and '
'vars will be deleted\n'
'This function is part of the action chain plugin and will not work elsewhere.')
name = 'delete_persistent_namespaces'
arg_count = -1
def evaluate(self, formatter, kwargs, mi, locals, *args):
if len(args) not in [0, 1]:
raise ValueError('Incorrect number of arguments')
if len(args) == 1:
namespace_path = args[0].split('.')
else:
namespace_path = []
if not persistent_vars.validate_namespace_path(namespace_path):
raise ValueError('Namespaces must contain only letter, numbers and underscores')
# We don't want to delete namespaces when the user is typing in the template
if not self.context == 'template_dialog_mode':
try:
persistent_vars.delete_namespace(namespace_path)
except Exception as e:
import traceback
print(traceback.format_exc())
return ''
|
|
|
|
|
|
|
#1121 |
|
Wizard
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,216
Karma: 1995558
Join Date: Aug 2015
Device: Kindle
|
No. It it is not working through Calibre Actions, the only way is for someone to write a custom action that calls the plugin's code directly.
|
|
|
|
|
|
#1122 |
|
want to learn what I want
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 1,679
Karma: 7908443
Join Date: Sep 2020
Device: none
|
|
|
|
|
|
|
#1123 |
|
Member
![]() Posts: 15
Karma: 10
Join Date: Apr 2013
Device: lots of, frequently changing
|
Thank you, code works, I'll switch then to python templates.
|
|
|
|
|
|
#1124 |
|
Custom User Title
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 11,332
Karma: 79528341
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
|
Question: Is there a method to add a file to the extra data folder similar to SFE on 'formats'?
Context: I have a chain that copies the cover path to clipboard then follows it with the 'add data file' Calibre action where I paste it in. It's a little clunky though. |
|
|
|
|
|
#1125 | |
|
Grand Sorcerer
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() Posts: 12,525
Karma: 8065948
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Code:
python:
def evaluate(book, context):
import os
from shutil import copy
from calibre.db.constants import DATA_DIR_NAME
db = context.db
cache = db.new_api
# Get the normalized library path
library_path = cache.backend.library_path
# Construct the full path to the book folder
path = os.path.join(library_path, cache.field_for('path', book.id).replace('/', os.sep))
# Ensure the data directory exists
data_dir = os.path.join(path, DATA_DIR_NAME)
if not os.path.exists(data_dir):
os.mkdir(data_dir)
cover_file = os.path.join(path, 'cover.jpg')
if os.path.exists(cover_file):
# It does. Copy it to the data directory. The 'copy' method takes a
# directory as a target.
copy(cover_file, data_dir)
return 'copied'
return 'not copied'
|
|
|
|
|
![]() |
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Action Chains Resources | capink | Plugins | 80 | 09-18-2025 04:45 AM |
| [Editor Plugin] Editor Chains | capink | Plugins | 106 | 06-17-2025 05:36 PM |
| [GUI Plugin] Noosfere_util, a companion plugin to noosfere DB | lrpirlet | Plugins | 2 | 08-18-2022 03:15 PM |
| [GUI Plugin] Save Virtual Libraries To Column (GUI) | chaley | Plugins | 14 | 04-04-2021 05:25 AM |