Quote:
Originally Posted by capink
If you don't want to modify the values it is better to steer away from SFE. Two solutions spring to mind:
[snip]
|
I ended up using chain variables and a formula to show output, as you suggested.
Quote:
A solution is not necessarily a new action, it might be a matter of a new template function. While we are at it, is there for a way for a function to know which template mode it is running in, to modify the output depending on the mode. e.g. from_selection() could return a python list instead of a string if running in a python mode.
|
Code:
utils.formatter_functions.function_object_type(template)
This accepts template text, a stored template, or a template function.
Quote:
Edit: As for errors, you can save them in a global variable, and display them using a Formula Action at the end of the chain. This works for any kind of solution you choose, whether a Formula Action, a Chains Variables Action or even a "Run Python Code" Action.
|
I ended up building an "example" action chain that I can use as the starting point. It contains two actions: a chain variables action that is run over the selected books, and a formulas action that displays any output.
The chain variables action puts its output into a list stored in globals. It also sets the chain variable "has_output" if there is anything in the list. The formulas action has a condition ensuring the action is run only if "has_output" is set to the non-empty string. The export of the prototype action chain is attached.
Here is the prototype chain variables action:
Code:
python:
def evaluate(book, context):
# Run the desired template. It must produce a string
output = run_template(book, context)
# if the template produced some output, store it in globals to be printed later
all_output = context.globals.get('output', [])
if output:
all_output.append(f'{book.title} ({book.id}): {output}')
context.globals['output'] = all_output
return 'yes' if all_output else ''
def run_template(book, context):
# you can directly enter a python template here, or alternatively call
# a stored template (GPM or python) or formatter function using
# context.funcs.name.
# Example: context.funcs.template('{series:|| [}{series_index:||]}')
# runs the SFM template to return the series and series index
return context.funcs.template('{series:|| [}{series_index:||]}')
Here is the formula action. It formats the list of "errors" from the chain vars action into a single string.
Code:
python:
def evaluate(book, context):
return '\n'.join(context.globals.get('output', ''))
The condition check is equally simple:
Code:
program:
return globals(has_output) != ''
Here is an example that uses @ownedbycats request, to copy the cover into the data directory for all selected books. At the end it shows a dialog listing the books and whether or not a cover was copied.
Code:
python:
def evaluate(book, context):
# Run the desired template. It must produce a string
output = run_template(book, context)
# if the template produced some output, store it in globals to be printed later
all_output = context.globals.get('output', [])
if output:
all_output.append(f'{book.title} ({book.id}): {output}')
context.globals['output'] = all_output
return 'yes' if all_output else ''
def run_template(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 'cover copied'
return 'cover not copied'