calibre 模板语言¶
calibre模板语言是calibre特定的语言,用于在calibre中执行任务,例如指定文件路径、格式化值和计算用户指定列的值。示例:
在将文件从calibre书库保存到磁盘或电子书阅读器时,指定文件夹结构和文件名。
定义规则以将图标和颜色添加到calibre图书列表中。
定义包含来自其他列的数据的“虚拟列”。
高级书库搜索。
高级元数据搜索和替换。
该语言是围绕“模板”的概念构建的,它指定要使用的图书元数据、元数据的计算以及如何格式化。
基本模板¶
基本模板由一个或多个“模板表达式”组成。 “模板表达式”由大括号 (“{}”) 中的文本和名称组成,并由正在处理的书籍中的相应元数据替换。 例如,calibre 中用于将书籍保存到设备的默认模板有 4 个“模板表达式”:
{author_sort}/{title}/{title} - {authors}
对于 "Isaac Asimov"的作品 "The Foundation" ,该模板将变为:
Asimov, Isaac/The Foundation/The Foundation - Isaac Asimov
斜杠并非``模板表达式``,因为它们不在 ``{}``之间。此类文本将保留在原处。例如,如果模板为:
{author_sort} Some Important Text {title}/{title} - {authors}
那么对于“The Foundation”,模板会生成:
Asimov, Isaac Some Important Text The Foundation/The Foundation - Isaac Asimov
“模板表达式”可以通过使用列的“查找名称”来访问 calibre 中可用的所有元数据,包括自定义列(您自己创建的列)。 要查找“列”(有时称为“字段”)的查找名称,请将鼠标悬停在 calibre 图书列表中的列标题上。 自定义列的查找名称始终以“#”开头。 对于系列类型列,有一个名为“#lookup name_index”的附加字段,它是该系列中该书的系列索引。 例如,如果您有一个名为“#myseries”的自定义系列列,那么还将有一个名为“#myseries_index”的列。 标准系列列的索引名为“series_index”。
除了基于标准列的字段之外,您还可以使用:
{formats}- calibre 书库中书籍可用的格式列表
{identifiers:select(isbn)}- 书籍的 ISBN
如果某本书的字段元数据未定义,则模板中的该字段将被替换为空字符串(“'''”)。例如,请考虑以下模板:
{author_sort}/{series}/{title} {series_index}
如果Asimov的书《Second Foundation》属于《Foundation》丛书,那么模板将生成:
Asimov, Isaac/Foundation/Second Foundation 3
如果尚未输入该书的丛书,则模板将生成:
Asimov, Isaac/Second Foundation
模板处理器会自动去除多余的斜杠以及开头和结尾的空格。
高级格式¶
除了元数据替换之外,模板还可以有条件地包含附加文本,并控制替换数据的格式。
有条件地包含文本
有时,您希望仅当字段不为空时文本才出现在输出中。 常见的情况是“series”和“series_index”,其中您不需要任何内容或用连字符分隔两个值。 calibre 使用特殊的“模板表达式”语法来处理这种情况。
例如,使用上面的 Foundation 示例,假设您希望模板生成“Foundation - 3 - Second Foundation”。此模板将生成以下输出:
{series} - {series_index} - {title}
但是,如果一本书没有系列,模板将生成“- - 标题”,这可能不是您想要的。 一般来说,人们希望结果是没有无关连字符的标题。 您可以使用以下模板语法来完成此操作:
{field:|prefix_text|suffix_text}
这个“模板表达式”表示,如果“field”的值为“XXXX”,那么结果将为“prefix_textXXXXXsuffix_text”。 如果“field”为空(没有值),那么结果将是空字符串(什么也没有),因为前缀和后缀被忽略。 前缀和后缀可以包含空格。
不要在“前缀”(prefix)或“后缀”(suffix)中使用子模板(`{ ... }`)或函数(见下文)。
使用这种语法,我们可以通过以下模板来解决上述提到的“无丛书名”问题:
{series}{series_index:| - | - }{title}
仅当该书具有系列索引时才会包含连字符,而该索引仅当它具有系列时才包含。 再次继续 Foundation 示例,模板将生成“Foundation - 1 - Second Foundation”。
注意:
如果使用前缀或后缀,则必须在“查找名称”后面包含冒号。
你必须要么完全不使用
|字符,要么同时使用两个。像 ``{field:| - }``这样只使用一个 | 是不被允许的。可以不为前缀或后缀提供任何文本,例如
{series:|| - }``是允许的。模板 ``{title:||}与{title:||}是等效的。
格式化
假设你希望将 ``series_index``格式化为三位数,并在前面补零。这段代码就能实现这个效果:
{series_index:0>3s}- 三位数,前面补零
若要在后面补零,请使用:
{series_index:0<3s}- - 三位数,后面补零
如果您使用带有小数值的系列索引,例如 1.1,您可能希望小数点对齐。 例如,您可能希望索引 1 和 2.5 显示为 01.00 和 02.50,以便它们在进行词法排序的设备上正确排序。 为此,请使用:
{series_index:0>5.2f}- 总共五个字符:前面补零的两位整数,一个小数点,和小数点后两位数字。
如果你只想保留数据的前两个字母,请使用:
{author_sort:.2}- 仅显示作者排序名称(author sort name)的前两个字母
大部分 calibre 模板语言格式都来自 Python。 有关这些高级格式化操作的语法的更多详细信息,请参阅“Python 文档 <https://docs.python.org/3/library/string.html#formatstrings>”_。
使用模板来定义自定义栏目¶
模板可用于显示 calibre 元数据中没有的信息,或以不同于 calibre 正常格式的方式显示元数据。 例如,您可能想要显示“ISBN”,这是 calibre 不显示的字段。 您可以通过创建类型为“从其他列构建的列”(以下称为“复合列”)的自定义列来完成此操作,并提供一个模板来生成显示的文本。 该列将显示评估模板的结果。 例如,要显示 ISBN,请创建列并在模板框中输入“{identifiers:select(isbn)}”。 要显示包含两个系列自定义列的值(以逗号分隔)的列,请使用``{#series1:||,}{#series2}``。
组合列可以使用任何模板选项,包括格式化功能。
注意:您无法编辑复合列中显示的数据。 相反,您可以编辑源列。 如果您编辑复合列,例如通过双击它,calibre 将打开模板进行编辑,而不是基础数据。
模板与元数据处理规则¶
插件板用于在发送到设备和保存到磁盘操作期间更改写入书籍的元数据。 插件板允许您指定一个模板来提供要写入书籍元数据的数据。 您可以使用插件板修改以下字段:作者、author_sort、语言、出版商、标签、书名、title_sort。 此功能可以帮助想要在设备上使用图书中的不同元数据的人们解决排序或显示问题。
创建插接板时,您可以指定插接板要使用的格式和设备。 提供了一个特殊的设备“save_to_disk”,在保存格式时使用(而不是将它们发送到设备)。 选择格式和设备后,您可以选择要更改的元数据字段,并提供模板来提供新值。 这些模板“连接”到其目标字段,因此称为“插件板”。 您当然可以在这些模板中使用复合列。
处理规则功能非常灵活,可以使用“单函数模式”、“模板程序模式”、“通用程序模式”或“Python 模板模式”进行编写。
当插件板可能适用时(内容服务器、保存到磁盘或发送到设备),calibre 会搜索定义的插件板,为给定的格式和设备选择正确的插件板。 例如,要为发送到 ANDROID 设备的 EPUB 图书找到合适的插件板,calibre 使用以下搜索顺序搜索插件板:
一条针对格式和设备进行了精确匹配的处理规则,例如:格式为``EPUB``且设备为``ANDROID``。
一条针对格式进行了精确匹配、但设备选择为特殊的
any device``(任何设备)的处理规则,例如:格式为``EPUB``且设备为 ``any device一条格式选择为特殊的 ``any format ``(任何格式)、但针对设备进行了精确匹配的处理规则,例如:格式为 ``any format `` 且设备为 ``ANDROID ``。
标签和作者字段有特殊处理,因为这两个字段都可以容纳多个项目。 一本书可以有多个标签和多个作者。 当您指定要更改这两个字段之一时,将检查模板的结果以查看是否存在多个项目。 对于标签,只要 calibre 找到逗号,结果就会被分割。 例如,如果模板生成值“Thriller, Horror”,则结果将是两个标签“Thriller”和“Horror”。 无法在标签中间放置逗号。
作者也会发生同样的情况,但使用不同的字符进行剪切,即“&”(与号)而不是逗号。 例如,如果模板生成值“Blogs, Joe&Posts, Susan”,则该书最终将有两个作者:“Blogs, Joe”和“Posts, Susan”。 如果模板生成值“博客,乔;帖子,苏珊”,那么这本书将有一位作者的名字相当奇怪。
当将书籍保存到磁盘或写入设备时,插件板会影响写入书中的元数据。 插件板不会影响“保存到磁盘”和“发送到设备”用来创建文件名的元数据。 相反,文件名是使用在适当的首选项窗口中输入的模板构建的。
在模板中使用函数 - 单函数模式¶
假设您想要以大写形式显示某个字段的值,而该字段通常为标题大写形式。 您可以使用“模板函数”来完成此操作。 例如,要以大写形式显示标题,请使用“uppercase”函数,如“{title:uppercase()}”。 要以标题大小写显示它,请使用``{title:titlecase()}``。
函数进入模板的格式部分,在 : 之后和第一个 | 之前,或者如果没有使用前缀/后缀,则在结束的 } 之前。 如果您同时具有格式和函数引用,则该函数位于第二个 : 后面。 函数返回模板中指定的列的值,经过适当修改。
使用函数的语法形式为以下之一:
{lookup_name:function(arguments)}
{lookup_name:format:function(arguments)}
{lookup_name:function(arguments)|prefix|suffix}
{lookup_name:format:function(arguments)|prefix|suffix}
函数名称后面必须始终跟有左括号和右括号。 有些函数需要额外的值(参数),这些值位于括号内。 参数之间用逗号分隔。 文字逗号(逗号作为文本,而不是参数分隔符)前面必须有反斜杠 (\) 。 最后一个(或唯一的)参数不能包含文本右括号。
函数会在格式规范(format specifications)以及前缀/后缀(prefix/suffix)之前执行评估。有关同时使用格式和函数的示例,请参阅下文。
重要:如果您有编程经验,请注意“单函数模式”中的语法不是您所期望的。 字符串不加引号,并且空格很重要。 所有参数都被视为常量; 没有表达式。
请勿将子模板({ ... })作为函数参数使用。 取而代之,请使用 模板程序模式 或 通用程序模式。
关于在“单函数模式”下调用函数的注意事项:
在“单函数模式”下使用函数时,第一个参数
value会自动被模板中指定的字段内容所替换。例如,当处理模板`` {title:capitalize()}`` 时,title字段的内容会作为value参数传递给 capitalize 函数。在函数文档中,标记 ``[something]*``表示 something可重复零次或多次。标记 ``[something]+``表示 ``something``重复一次或多次(必须至少存在一次)。
某些函数会使用正则表达式。在模板语言中,正则表达式的匹配是不区分大小写的。
函数的详细说明可参阅 :ref:`template_functions_reference`函数。该文档会说明各函数所需的参数及其功能。例如,以下是 :ref:`ff_ifempty`函数的说明。
ifempty(value, text_if_empty)——如果``value``不为空则返回该``value``,否则返回``text_if_empty``。
您可以看到该函数需要两个参数:value 和 text_if_empty。但是,由于我们使用的是“单函数模式”,因此可以省略 value 参数,仅传递 text_if_empty。例如,使用如下模板:
{tags:ifempty(No tags on this book)}
显示书籍的标签(如果有的话)。如果书籍没有标签,则显示 No tags on this book。
由于下列函数的第一个参数均为 value,因此它们都可以在“单函数模式”下使用:
capitalize
(value)——返回首字母大写、其余字母小写的``value``。ceiling
(value)—— 返回大于或等于value的最小整数。cmp
(value, y, lt, eq, gt)-- 将value和y转换为数字后进行比较。contains
(value, pattern, text_if_match, text_if_not_match)—— 检查值是否与正则表达式patterndate_arithmetic
(value, calc_spec, fmt)-- 使用calc_spec根据value计算新的日期。encode_for_url
(value, use_plus)-- 返回已编码的value,用于 URL,并由use_plus指定。该值首先进行 URL 编码。接下来,如果use_plus为0,则空格将被'+'``(加号)替换。如果为 ``1,则空格将被%20替换。floor
(value)—— 返回小于或等于value的最大整数。format_date
(value, format_string)-- 使用format_string格式化 ``value``(必须是日期字符串),并返回字符串。format_duration
(value, template, [largest_unit])-- 将以秒为单位的数字 value 格式化为包含周、天、小时、分钟和秒的人类可读字符串。如果值为浮点数,则四舍五入为最接近的整数。format_number
(value, template)—— 将value解释为数字,并使用 Python 格式化模板(例如{0:5.2f}、{0:,d}或${0:5,.2f})格式化该数字。fractional_part
(value)—— 返回值的小数点后的部分。human_readable
(value)—— 期望value是一个数字,并返回一个以 KB、MB、GB 等为单位表示该数字的字符串。ifempty
(value, text_if_empty)——如果``value``不为空则返回该``value``,否则返回``text_if_empty``。language_strings
(value, localize)-- 返回传入value的语言代码对应的语言名称(请参阅此处了解名称和代码)。list_contains
(value, separation, [ pattern, found_val, ]* not_found_val)-- 将value解释为由separator分隔的项列表,并将pattern与列表中的每个项进行匹配。list_count
(value, separator)-- 将值解析为由separator分隔的项目列表,并返回列表中的项目数量。list_count_matching
(value, pattern, separation)——将value解释为由separator分隔的项目列表,返回列表中与正则表达式pattern匹配的项目数。list_item
(value, index, separation)—— 将 value 视为由 separator 分隔的项目列表,返回第 index 个项目。list_sort
(value, direction, separation)-- 返回使用不区分大小写的字典排序方法排序的value。lookup
(value, [ pattern, key, ]* else_key)—— 模式会按顺序依次与 value 进行匹配lowercase
(value)——以小写形式返回``value``。mod
(value, y)—— 返回value / y余数的floor。rating_to_stars
(value, use_half_stars)-- 将value以星号 (★) 字符串的形式返回。re
(value, pattern, replacement)—— 对 value 应用正则表达式后返回结果。re_group
(value, pattern [, template_for_group]*)-- 将正则表达式 pattern 应用于 value,并将每个匹配的部分替换为对应模板生成的值,最终返回处理后的字符串。在 模板程序模式 中,与template和``eval`` 函数一样,使用[[表示{,使用]]表示}。以下示例查找包含多个单词的丛书,并将第一个单词大写:
program: re_group(field('series'), "(\S* )(.*)", "{$:uppercase()}", "{$}")'}
round
(value)—— 返回最接近value的整数。select
(value, key)—— 将value解释为以逗号分隔的项目列表,其中每个项目都具有id:id_value的形式(calibreidentifier格式)。shorten
(value, left_chars, middle_text, right_chars)-- 返回valuestr_in_list
(value, separation, [ string, found_val, ]+ not_found_val)——将value解释为由separator分隔的项目列表,然后将string与列表中的每个值进行比较。subitems
(value, start_index, end_index)-- 此函数用于拆分类似标签的层级显示项目列表,例如类型。sublist
(value, start_index, end_index, separation)-- 将value解释为由separator分隔的项列表,并返回一个由start_index到end_index的项组成的新列表。substr
(value, start, end)—— 返回valueswap_around_articles
(value, separation)-- 返回将冠词移至末尾并用分号分隔的value。swap_around_comma
(value)—— 如果value的格式为``B, A`` ,则返回A B。switch
(value, [patternN, valueN,]+ else_value)—— 对于每个patternN, valueN对,检查value是否与正则表达式patternNtest
(value, text_if_not_empty, text_if_empty)——如果值不为空,则返回``text_if_not_empty``,否则返回``text_if_empty``。titlecase
(value)——返回将 value 转换为标题大小写的结果。transliterate
(value)—— 返回一个由近似于value中的单词的发音组成的拉丁字母字符串。uppercase
(value)——以大写形式返回``value``。
在同一个模板中同时使用函数和格式化
假设您有一个整数自定义列“#myint”,您希望显示前导零,如“003”。 实现此目的的一种方法是使用“0>3s”的格式。 但是,默认情况下,如果数字(整数或浮点数)等于零,则该值将显示为空字符串,因此零值将生成空字符串,而不是“000”。 如果您想查看“000”值,则可以使用格式字符串和“ifempty”函数将空值更改回零。 模板将是:
{#myint:0>3s:ifempty(0)}
请注意,您也可以同时使用前缀和后缀。如果您希望数字显示为 [003] 或 [000],请使用如下模板:
{#myint:0>3s:ifempty(0)|[|]}
通用程序模式¶
“通用程序模式 (GPM)”会将“模板表达式”替换为一段使用“模板语言”编写的程序。该语言的语法由以下文法定义:
program ::= 'program:' expression_list
expression_list ::= top_expression [ ';' top_expression ]*
top_expression ::= or_expression
or_expression ::= and_expression [ '||' and_expression ]*
and_expression ::= not_expression [ '&&' not_expression ]*
not_expression ::= [ '!' not_expression ]* | concatenate_expr
concatenate_expr::= compare_expr [ '&' compare_expr ]*
compare_expr ::= add_sub_expr [ compare_op add_sub_expr ]
compare_op ::= '==' | '!=' | '>=' | '>' | '<=' | '<' |
'in' | 'inlist' | 'inlist_field' |
'==#' | '!=#' | '>=#' | '>#' | '<=#' | '<#'
add_sub_expr ::= times_div_expr [ add_sub_op times_div_expr ]*
add_sub_op ::= '+' | '-'
times_div_expr ::= unary_op_expr [ times_div_op unary_op_expr ]*
times_div_op ::= '*' | '/'
unary_op_expr ::= [ add_sub_op unary_op_expr ]* | expression
expression ::= identifier | constant | function | assignment | field_reference |
if_expr | for_expr | break_expr | continue_expr | return_stmt
'(' expression_list ')' | function_def
field_reference ::= '$' [ '$' ] [ '#' ] identifier
identifier ::= id_start [ id_rest ]*
id_start ::= letter | underscore
id_rest ::= id_start | digit
constant ::= " string " | ' string ' | number
function ::= identifier '(' expression_list [ ',' expression_list ]* ')'
function_def ::= 'def' identifier '(' top_expression [ ',' top_expression ]* ')' ':'
expression_list 'fed'
assignment ::= identifier '=' top_expression
if_expr ::= 'if' condition 'then' expression_list
[ elif_expr ] [ 'else' expression_list ] 'fi'
condition ::= top_expression
elif_expr ::= 'elif' condition 'then' expression_list elif_expr | ''
for_expr ::= for_list | for_range
for_list ::= 'for' identifier 'in' list_expr
[ 'separator' separator_expr ] ':' expression_list 'rof'
for_range ::= 'for' identifier 'in' range_expr ':' expression_list 'rof'
range_expr ::= 'range' '(' [ start_expr ',' ] stop_expr
[ ',' step_expr [ ',' limit_expr ] ] ')'
with_expr ::= 'with' top_expression ':' expression_list 'htiw'
list_expr ::= top_expression
break_expr ::= 'break'
continue_expr ::= 'continue'
return_stmt ::= 'return' top_expression
separator_expr ::= top_expression
start_expr ::= top_expression
stop_expr ::= top_expression
step_expr ::= top_expression
limit_expr ::= top_expression
注意:
top_expression(顶层表达式)始终具有一个值。expression_list(表达式列表)的值是列表中最后一个 top_expression 的值。例如,表达式列表 1;2;'foobar';3 的值是 3。
在逻辑语境中,任何非空值(non-empty value)均被视为 True(真)。
在逻辑语境中,空值(empty value)被视为 False(假)。
字符串和数字可以互换使用。例如,10 和 '10' 是完全相同的。
注释是以 # 字符开头的行,其前面可以包含空格或制表符(tabs)。
运算符优先级
运算符优先级(求值顺序)从最高(最先求值)到最低(最后求值)依次为:
函数调用、常量、括号表达式、语句表达式、赋值表达式、字段引用。
一元正号 (+) 和一元负号 (-)。这些运算符按从右至左的顺序求值。
这些运算符以及所有其他算术运算符,在表达式结果的小数部分为零时,都会返回整数。例如,如果表达式的结果为 3.0,它将被转换为 3。
乘法 (*) 和除法 (/)。这些运算符具有结合性,且按从左至右的顺序求值。如果您想改变求值顺序,请使用括号。
加法 (+) 和减法 (-)。这些运算符具有结合性,且按从左至右的顺序求值。
数值与字符串比较。如果比较成功,这些运算符返回 '1',否则返回空字符串 ('')。比较运算符不具备结合性:例如 a < b < c 是语法错误。
字符串拼接 (&)。& 运算符返回由左侧表达式和右侧表达式连接而成的字符串。例如:'aaa' & 'bbb' 返回 'aaabbb'。该运算符具有结合性,且按从左至右的顺序求值。
一元逻辑非 (!)。如果表达式为“假”(求值为空字符串),则此运算符返回 '1';否则返回 ''(空字符串)。
逻辑与 (&&)。如果左侧和右侧表达式均为“真”(True),则此运算符返回 '1';如果其中任何一个为“假”(False),则返回空字符串 ''。该运算符具有结合性,按从左至右的顺序求值,并支持`短路计算 (short-circuiting) <https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html>`_。
逻辑或 (
||)。 如果左侧表达式或右侧表达式为 True,则此运算符返回'1';如果均为 False,则返回''。 它是关联的,从左到右求值,并进行“短路”<https://chortle.ccsu.edu/java5/Notes/chap40/ch40_2.html>`_。 它是一个“包含或”,如果左侧和右侧表达式都为 True,则返回“'1””。
字段引用
field_reference(字段引用)的求值结果是 $ 或 $$ 之后所接“查找名称”(lookup name)对应的元数据字段值。使用 $ 等同于使用 field 函数;使用 $$ 则等同于使用 raw_field 函数。示例如下:
* $authors ==> field('authors')
* $#genre ==> field('#genre')
* $$pubdate ==> raw_field('pubdate')
* $$#my_int ==> raw_field('#my_int')
If 表达式
If 表达式首先评估``condition``。 如果“condition”为 True(非空值),则评估“then”子句中的“expression_list”。 如果为 False,则评估“elif”或“else”子句中的“expression_list”。 elif 和 else 部分是可选的。 单词“if”、“then”、“elif”、“else”和“fi”被保留; 您不能将它们用作标识符名称。 您可以在任何有意义的地方放置换行符和空格。 “condition”是“top_expression”,而不是“expression_list”; 不允许使用分号。 “表达式列表”是以分号分隔的“top_表达式”序列。 if 表达式返回已计算的 expression_list 中最后一个 top_expression 的结果,如果没有计算表达式列表,则返回空字符串。
示例:
* program: if field('series') then 'yes' else 'no' fi
* program:
if field('series') then
a = 'yes';
b = 'no'
else
a = 'no';
b = 'yes'
fi;
strcat(a, '-', b)
嵌套 if 示例:
program:
if field('series') then
if check_yes_no(field('#mybool'), '', '', '1') then
'yes'
else
'no'
fi
else
'no series'
fi
如上所述,if 语句会产生一个值。这意味着以下所有写法都是等效的:
* program: if field('series') then 'foo' else 'bar' fi
* program: if field('series') then a = 'foo' else a = 'bar' fi; a
* program: a = if field('series') then 'foo' else 'bar' fi; a
例如,如果书籍有关联的丛书,此程序将返回 series(丛书)列的值,否则返回 title(书名)列的值:
program: field(if field('series') then 'series' else 'title' fi)
For表达式
for 表达式会迭代一个值列表,并逐个处理这些值。其中 list_expression(列表表达式)必须求值为元数据字段的“查找名称”(如 tags 或 #genre),或者求值为一个值列表。:ref:ff_range 函数可用于生成数字列表。如果求值结果是一个有效的“查找名称”,系统将获取该字段的值,并使用该字段类型指定的默认分隔符。如果结果不是有效的查找名称,则将其视为普通的值列表。除非提供了可选关键字 separator(分隔符),否则默认列表以逗号分隔;若提供了该关键字,则列表值必须以 separator_expr 的求值结果进行分隔。注意,如果列表是由 range() 生成的,则不能使用自定义分隔符。列表中的每个值都会被分配给指定的变量,然后执行 expression_list(表达式列表)。你可以使用 break 跳出循环,或使用 continue 跳回循环开头进行下一次迭代。
示例:此模板会移除“类型 (#genre)”字段中每个值的第一个层级名称,并用处理后的新名称构建一个列表:
program:
new_tags = '';
for i in '#genre':
j = re(i, '^.*?\.(.*)$', '\1');
new_tags = list_union(new_tags, j, ',')
rof;
new_tags
如果原始类型是“History.Military, Science Fiction.Alternate History, ReadMe”,则模板将返回“Military, Alternate History, ReadMe”。 您可以在 calibre 的“批量编辑元数据 -> 搜索和替换”中使用此模板,并将“搜索”设置为“模板”,以剥离层次结构的第一级并将结果值分配给类型。
注意:在这种情况下,模板中的最后一行“new_tags”并不是绝对必要的,因为“for”返回表达式列表中最后一个 top_expression 的值。 赋值的值是其表达式的值,因此“for”语句的值就是分配给“new_tags”的值。
with表达式
The with expression:
将当前书籍切换为由 top_expression 求值生成的 Calibre 书籍 ID(整数)所对应的书籍。
运行
expression_list.随后将当前书籍重置回其原本的状态。
with 表达式返回所执行的 expression_list 中最后一个 top_expression 的计算结果;如果没有执行任何表达式列表,则返回空字符串。
示例:此模板会返回一个列表,其中包含当前在GUI中所选中的每本书的书名:
program:
res = '';
ids = selected_books();
for id in ids:
with id:
res = (if res then res & ', ' fi) & $title
htiw
rof;
res
Return 语句
返回 expression(表达式)的值。如果在函数内部执行,它会将该值返回给调用者。如果在最外层上下文(即模板本身)中执行,它会将模板的最终值设定为该表达式的值并退出模板。
函数定义
如果你在模板中有重复的代码,可以将其放入一个局部函数中。def 关键字标志着定义的开始,其后依次是函数名、参数列表以及函数体内的代码。函数定义以 fed 关键字结束。
参数是立场性的。 调用函数时,提供的参数会从左到右与定义的参数进行匹配,并将参数的值分配给参数。 提供的参数多于定义的参数是错误的。 参数可以有默认值,例如“a = 25”。 如果没有为该参数提供参数,则使用默认值,否则该参数将设置为空字符串。
return 语句可以在局部函数中使用。
函数必须先定义才能使用。
示例:此模板根据总天数计算出大概的年、月、日时长。其中 to_plural() 函数用于格式化计算出的数值(处理复数形式)。请注意,此示例还使用了字符串连接符 &:
program:
days = 2112;
years = floor(days/360);
months = floor(mod(days, 360)/30);
days = days - ((years*360) + (months * 30));
def to_plural(v, str):
if v == 0 then return '' fi;
return v & ' ' & (if v == 1 then str else str & 's' fi) & ' '
fed;
to_plural(years, 'year') & to_plural(months, 'month') & to_plural(days,'day')
关系运算符
如果比较结果为真(true),关系运算符将返回 '1';否则返回空字符串('')。
关系运算符有两种形式:字符串比较和数值比较。
字符串比较使用词典顺序进行不区分大小写的比较。支持的字符串比较运算符包括 ==、!=、<、<=、>、>=、in、inlist 以及 inlist_field。对于 in、inlist 和 inlist_field 运算符,左侧表达式的结果被视为正则表达式模式;如果左侧的正则表达式与右侧表达式的值匹配,则结果为真。这些正则表达式同样不区分大小写。
如果左侧的正则表达式匹配右侧列表中的任何一项(列表项以逗号分隔),则 inlist 运算符为真。如果左侧的正则表达式匹配由右侧表达式命名的字段(列)中的任何一项,则 inlist_field 运算符为真,匹配时使用该字段定义的专用分隔符。注意:inlist_field 要求右侧表达式的计算结果为一个“字段名”,而 inlist 要求右侧表达式的结果为一个“包含逗号分隔列表的字符串”。由于这种差异,inlist_field 的执行速度明显快于 inlist,因为它不需要进行字符串转换或列表构建。
数值比较运算符包括 ==#、!=#、<#、<=#、># 和 >=#。左侧和右侧的表达式必须计算为数值,但有两个例外:字符串值 "None"(未定义字段)和空字符串都会被视为数值 0。
例如:
program: field('series') == 'foo' 如果书籍的丛书名为 foo,则返回 '1',否则返回空字符串('')。
program: 'f.o' in field('series') 如果书籍的丛书名匹配正则表达式 f.o(例如:foo、Off Onyx 等),则返回 '1',否则返回空字符串('')。
program: 'science' inlist $#genre 如果从书籍的“类型(genre)”列中获取的任何一个值匹配正则表达式 science,则返回 '1',否则返回空字符串('')。例如,它能匹配 Science、History of Science、Science Fiction等。
program: '^science$' inlist $#genre 如果书籍的“类型(genre)”列中有任何一项与正则表达式 ^science$ 完全匹配(例如 Science),则返回 '1',否则返回空字符串('')。这种情况下,History of Science 和 Science Fiction 将不会匹配。
program: 'asimov' inlist $authors 如果作者列表中的任何一位作者匹配正则表达式 asimov(例如 Asimov, Isaac 或 Isaac Asimov),则返回 '1',否则返回空字符串('')。
program: 'asimov' inlist_field 'authors' 如果作者列中的任何一位作者匹配正则表达式 asimov(例如 Asimov, Isaac 或 Isaac Asimov),则返回 '1',否则返回空字符串('')。
program: 'asimov$' inlist_field 'authors' 如果作者列中的任何一位作者匹配正则表达式 asimov$(例如 Isaac Asimov),则返回 '1',否则返回空字符串('')。由于正则表达式中使用了 $ 锚点,它不会匹配 Asimov, Isaac。
program: if field('series') != 'foo' then 'bar' else 'mumble' fi 如果书籍的丛书名不是 foo,则返回 'bar';否则返回 'mumble'。
program: if field('series') == 'foo' || field('series') == '1632' then 'yes' else 'no' fi 如果丛书名是 foo 或 1632,则返回 'yes',否则返回 'no'。
program: if '^(foo|1632)$' in field('series') then 'yes' else 'no' fi 如果丛书名是 foo 或 1632,则返回 'yes',否则返回 'no'。
program: if 11 > 2 then 'yes' else 'no' fi 返回 'no',因为 > 运算符执行的是词典顺序(字符串)比较。
program: if 11 ># 2 then 'yes' else 'no' fi 返回 'yes',因为 ># 运算符执行的是数值比较。
通用程序模式中的函数
关于模板语言内置的函数列表,请参阅:ref:template_functions_reference 。
注意:
与 :ref:`Single Function Mode<single_mode>不同,在通用程序模式下,你必须指定第一个参数 value。
所有参数均为 expression_lists(表达式列表,参见上文的语法说明)。
模板表达式中的复杂程序 —— 模板程序模式¶
“模板程序模式”(“TPM”) 是“通用程序模式 <general_mode>”和“单功能模式 <single_mode>”的混合。 “TPM”与单函数模式的不同之处在于它允许编写引用其他元数据字段的模板表达式、使用嵌套函数、修改变量和进行算术。 它与“通用程序模式”的不同之处在于,模板包含在“{”和“}”字符之间,并且不以单词“program:”开头。 模板的程序部分是通用程序模式表达式列表。
示例:假设您希望模板显示一本书的系列,如果有,否则显示自定义字段 #genre 的值。 您无法在“单函数模式 <single_mode>”中执行此操作,因为您无法引用模板表达式中的另一个元数据字段。 在“TPM”中,您可以,如以下表达式所示:
{series:'ifempty($, $#genre)'}
该示例展示了以下几点:
如果表达式以 :' 开头并以 '} 结尾,则使用 TPM(模板程序模式)。除此之外的任何内容都将被视为处于 :ref:`Single Function Mode<single_mode>`下
如果模板包含前缀和后缀,则表达式以 '| 结尾,其中 | 是前缀的分隔符。示例如下:
{series:'ifempty($, $#genre)'|prefix | suffix}
调用函数时必须提供其所有参数。例如,标准内置函数必须被赋予初始参数 value。
变量 $ 可作为 value 参数使用,它代表模板中所指定字段的值(在本例中为 series 丛书字段)。
空格将被忽略,并可以用于表达式中的任何位置。
常量字符串需封闭在成对的引号中,使用单引号 ' 或双引号 " 均可。
在 TPM(模板程序模式)中,在字符串字面量中使用 { 和 } 字符可能会导致错误或意外结果,因为它们会干扰模板解析器。解析器会尝试将它们视为模板表达式的分隔边界,而不是普通字符。在某些(但非所有)情况下,你可以用 [[ 替换 {,用 ]] 替换 }。建议:如果你的程序包含 { 和 } 字符,则应使用通用程序模式。
Python 模板模式¶
Python 模板模式 (PTM) 允许你使用原生 Python 语言和 `calibre API <https://manual.calibre-ebook.com/develop.html#api-documentation-for-various-parts-of-calibre>`_来编写模板。其中数据库 API(database API)最为常用;更深入的讨论超出了本手册的范围。PTM 模板运行速度更快,且能执行更复杂的逻辑操作,但前提是你必须了解如何使用 calibre API 编写 Python 代码。
PTM 模板以以下内容开头:
python:
def evaluate(book, context):
# book is a calibre metadata object
# context is an instance of calibre.utils.formatter.PythonTemplateContext,
# which currently contains the following attributes:
# db: a calibre legacy database object.
# globals: the template global variable dictionary.
# arguments: is a list of arguments if the template is called by a GPM template, otherwise None.
# funcs: used to call Built-in/User functions and Stored GPM/Python templates.
# Example: context.funcs.list_re_group()
# your Python code goes here
return 'a string'
你可以通过右键点击调出的上下文菜单(右键菜单)将上述文本添加到模板中。其中的注释并不重要,可以删除。但你必须使用 Python 缩进规范。
context 对象支持使用 str(context) 来返回其包含内容的字符串,同时也支持使用 context.attributes 来返回该上下文中所有属性名称的列表。
context.funcs 属性允许调用内置和用户模板函数以及存储的 GPM/Python 模板,以便您可以直接在代码中执行它们。 使用函数的名称来检索函数。 如果名称与 Python 关键字冲突,请在名称末尾添加下划线。 例子:
context.funcs.list_re_group()
context.funcs.assert_()
以下是 PTM 模板的示例,该模板生成一个系列的所有作者的列表。 该列表存储在“从其他列构建的列,行为类似于标签”中。 它显示在“书籍详细信息”中,并选中“在单独的行上”(在“首选项->外观->书籍详细信息”中)。 该选项要求列表以逗号分隔。 为了满足该要求,模板将作者姓名中的逗号转换为分号,然后构建以逗号分隔的作者列表。 然后对作者进行排序,这就是模板使用author_sort的原因。
python:
def evaluate(book, context):
if book.series is None:
return ''
db = context.db.new_api
ans = set()
# Get the list of books in the series
ids = db.search(f'series:"={book.series}"', '')
if ids:
# Get all the author_sort values for the books in the series
author_sorts = (v for v in db.all_field_for('author_sort', ids).values())
# Add the names to the result set, removing duplicates
for aus in author_sorts:
ans.update(v.strip() for v in aus.split('&'))
# Make a sorted comma-separated string from the result set
return ', '.join(v.replace(',', ';') for v in sorted(ans))
“:guilabel:图书详情”面板中的输出效果如下:
模板与 URL¶
你可以使用模板来构建 URL。这里描述了两种情况:
自定义列:“:guilabel:图书详情”搜索 URL
calibre URL协议
自定义列:图书详情搜索 URL
当你创建自定义列时,可以使用模板提供一个用于“:guilabel:图书详情”面板的 URL。例如,如果你有一个名为“译者”(Translators)的自定义列,你可以定义一个 URL,点击后跳转到某个译者相关的网站。图书详情搜索 URL 适用于“文本”(Text)、“枚举”(Enumerated)、“丛书”(Series)以及“从其他列构建的列”(Column built from other column)等列类型。
当在“:guilabel:图书详情”面板中点击一个带有“搜索模板”的项目时,该模板将被执行。系统会为其提供常规的书籍元数据,此外还会额外提供三个字段:
item_value:被点击项目的具体内容。
item_value_quoted:被点击项目的 URL 编码值。特殊字符将被转义以确保在 URL 中有效,空格则会被替换为 '+'(加号)。
item_value_no_plus:被点击项目的 URL 编码值。特殊字符将被转义以确保在 URL 中有效,空格将被替换为 %20,而不是加号。
构建 URL 有多种方式。以下以维基百科(Wikipedia)为例。
最简单的是基础模板:
https://en.wikipedia.org/w/index.php?search={item_value_encoded}
在某些情况下,你可能希望进行更多的处理。根据处理过程的复杂程度,有四种模板函数可供使用。
make_url
(path, [query_name, query_value]+)-- 此函数是构建查询 URL 的最简单方法。它使用path、要查询的网站和页面,以及用于构建查询的query_name和query_value对。 通常,query_value必须进行 URL 编码。使用此函数时,它始终会进行编码,并且空格始终会替换为'+'符号。make_url_extended
(...)—— 此函数与 make_url() 类似,但 它能让你更好地控制 URL 的组成部分。URL 的组成部分包括:scheme:://authority/path?query string。
更多详情,请参阅维基百科上的 统一资源定位符。
该函数有两种用法:
make_url_extended(scheme, authority, path, [query_name, query_value]+)
和
make_url_extended(scheme, authority, path, query_string)
query_string
([query_name, query_value, how_to_encode]+)-- 返回由query_name, query_value, how_to_encode三元组构成的 URL 查询字符串。 查询字符串由一系列项组成,其中每个项的形式类似于query_name=query_value,其中query_value按照要求进行 URL 编码。查询项之间以 ``&'``(与号)分隔。encode_for_url
(value, use_plus)-- 返回已编码的value,用于 URL,并由use_plus指定。该值首先进行 URL 编码。接下来,如果use_plus为0,则空格将被'+'``(加号)替换。如果为 ``1,则空格将被%20替换。
例如,假设你有一个名为‘译者’(#translators)的自定义列,其中的姓名格式为‘姓, 名’。在创建 URL 时,你可能需要将姓名转换为‘名 姓’。你可以使用 :ref:ff_make_url 函数来实现这一点:
program: make_url('https://en.wikipedia.org/w/index.php', 'search', swap_around_comma($item_value))
假设译者的姓名是 Boy-Żeleński, Tadeusz,那么上述模板将生成如下链接:
https://en.wikipedia.org/w/index.php?search=Tadeusz+Boy-%C5%BBele%C5%84ski
请注意,现在名字(First name)排在了前面,空格被替换成了加号(+),并且姓氏中的非英语字符已进行了 URL 编码。
根据后续处理的复杂程度, make_url_extended, query_string, 以及 encode_for_url 这些函数可能也会非常有用。
calibre URL协议
Calibre 支持多种不同的 URL 用于导航你的 Calibre 书库。本节将展示如何使用模板来构建其中的部分 URL。有关可用 URL 的详细信息,请参阅 Calibre:// URL方案 。
切换到特定的书库。此 URL 的语法如下:
calibre://switch-library/Library_Name
Library_Name 必须替换为你希望打开的 Calibre 书库的名称。书库名称显示在窗口的标题栏中。它是一个简单的名称,而不是书库的文件路径。你必须按照标题栏中显示的名称书写,包括字母的大小写。字符 _(下划线)代表当前书库。如果名称包含任何空格或特殊字符,则必须使用 :ref:ff_to_hex 函数进行十六进制编码,如下例所示:
program: strcat('calibre://switch-library/_hex_-', to_hex(current_library_name()))
该模板将生成 URL:
calibre://switch-library/_hex_-4c6962726172792e746573745f736d616c6c
你可以将 current_library_name() 函数替换为书库的实际名称,例如:
program: strcat('calibre://switch-library/_hex_-', to_hex('Library.test_small'))
显示书籍的链接。这些链接用于在 Calibre 书库中选中某本书。此 URL 的语法如下:
calibre://show-book/Library_Name/book_id
book id 是 Calibre 为书籍分配的数字 ID,在模板中可以通过 $id 获取。与前文相同,书库名称可能需要进行十六进制编码。示例如下:
program: strcat('calibre://show-book/_hex_-', to_hex(current_library_name()), '/', $id)它生成如下的 URL:
calibre://show-book/_hex_-4c6962726172792e746573745f736d616c6c/1353
搜索书籍。这些链接用于在指定的 Calibre 书库中搜索书籍。此 URL 的语法如下:
calibre://search/Library_Name?q=query calibre://search/Library_Name?eq=hex_encoded_query
其中 query 是任何有效的 Calibre 搜索表达式。如果查询内容包含空格或特殊字符(通常所有查询都包含),则必须进行十六进制编码。例如,搜索以‘AA’开头的层级标签,其 Calibre 搜索表达式为 tags:"=.AA"。以下模板为该表达式构建了一个搜索 URL:
program: strcat('calibre://search/_hex_-', to_hex(current_library_name()), '?eq=', to_hex('tags:"=.AA"'))
生成的 URL 如下:
calibre://search/_hex_-4c6962726172792e746573745f736d616c6c?eq=746167733a223d2e414122
以下是使用:ref:
ff_make_url_extended函数代替 strcat 构建相同 URL 的示例:program: make_url_extended('calibre', '', 'search/_hex_-' & to_hex(current_library_name()), 'eq', to_hex('tags:"=.AA"'))
在某个书库中打开书籍详情窗口。此 URL 的语法如下:
calibre://book-details/Library_Name/book_id
示例模板如下:
program: strcat('calibre://book-details/_hex_-', to_hex(current_library_name()), '/', $id)生成如下的 URL:
calibre://book-details/_hex_-4c6962726172792e746573745f736d616c6c/1353
打开与作者、丛书等相关联的备注。此 URL 的语法如下:
calibre://book-details/Library_Name/Field_Name/id_Item_Id calibre://book-details/Library_Name/Field_Name/hex_Hex_Encoded_Item_Name
Field_Name 是该字段的查找名称(Lookup name)。如果该字段是自定义列,请将 # 字符替换为下划线(_)。Item_Id 是该字段值在内部的数字 ID。由于目前没有返回 Item_Id 的模板函数,因此模板通常使用第二种形式,即 Hex_Encoded_Item_Name(经十六进制编码的项目名称)。以下是一个示例模板,用于打开 #authtest 字段中名为 Boy-Żeleński, Tadeusz 这一作者的备注:
program: strcat('calibre://show-note/_hex_-', to_hex(current_library_name()), '/_authtest/hex_', to_hex('Boy-Żeleński, Tadeusz'))
生成如下的 URL:
calibre://show-note/_hex_-4c6962726172792e746573745f736d616c6c/_authtest/hex_426f792dc5bb656c65c584736b692c205461646575737a
保存的模板¶
“通用程序模式 <general_mode>”和“Python 模板模式 <python_mode>”都支持保存模板并从另一个模板调用这些模板,就像调用存储的函数一样。 您可以使用“首选项->高级->模板功能”保存模板。 该对话框中提供了更多信息。 您可以像调用函数一样调用模板,并根据需要传递位置参数。 参数可以是任何表达式。 调用模板的示例,假设存储的模板名为“foo”:
foo() —— 调用该模板函数且不传递任何参数。
foo(a, b) —— 调用该模板函数并传递变量 a 和 b 的值。
foo(if field('series') then field('series_index') else 0 fi) —— 如果书籍有“丛书,则传递其“丛书编号”(series_index);否则传递值 0。
在 GPM 中,您可以使用“arguments”函数检索在调用存储模板时传递的参数。 它声明并初始化局部变量,即有效的参数。 变量是位置变量; 他们在同一位置获取调用中给出的参数值。 如果调用中未提供相应的参数,则“arguments”会为该变量分配提供的默认值。 如果没有默认值,则该变量将设置为空字符串。 例如,以下“arguments”函数声明 2 个变量“key”、“alternate”:
arguments(key, alternate='series')
示例(同样假设存档模板名称为 foo):
foo('#myseries') —— 参数 key 被赋值为 'myseries',而参数 alternate 则被赋予默认值 'series'。
foo('series', '#genre') —— 变量 key 被赋值为 'series',变量 alternate 被赋值为 '#genre'。
foo() —— 变量 key 被赋值为空字符串,而变量 alternate 则被赋予值 'series'。
在PTM中,参数在“arguments”参数中传递,该参数是字符串列表。没有任何方法可以指定默认值。您必须检查“参数”列表的长度,以确保参数的数量符合您的期望。
测试存储模板的一种简单方法是使用“模板测试器”对话框。 为了便于访问,请在“首选项->高级->键盘快捷键->模板测试器”中为其提供键盘快捷键。 为“存储的模板”对话框提供快捷方式将有助于在测试器和编辑存储的模板的源代码之间更快地切换。
为模板提供额外信息¶
开发人员可以选择将附加信息传递给模板处理器,例如应用程序特定的书籍元数据或有关处理器被要求执行什么的信息。模板可以访问此信息并在评估期间使用它。
开发者:如何传递额外信息
附加信息是一个包含“变量名:变量值”对的 Python 字典,其中值必须是字符串。 模板可以访问字典,创建名为“variable_name”的模板局部变量,其中包含值“variable_value”。 用户无法更改名称,因此最好使用不会与其他模板局部变量冲突的名称,例如在名称前添加下划线。
该字典通过命名参数 global_vars=your_dict 传递给模板处理器(即 formatter)。完整的方法签名如下:
def safe_format(self, fmt, kwargs, error_value, book,
column_name=None, template_cache=None,
strip_results=True, template_functions=None,
global_vars={})
模板编写者:如何访问额外信息
您可以在模板中使用以下模板函数来访问额外信息(即 globals 字典):
globals(id[=expression] [, id[=expression]]*)
其中“id”是任何合法的变量名称。 该函数检查开发者提供的附加信息中是否包含该名称。 如果是,则该函数将提供的值分配给具有该名称的模板局部变量。 如果附加信息中没有该名称,并且提供了“表达式”,则计算“表达式”并将结果分配给局部变量。 如果既没有提供值也没有提供表达式,函数会将空字符串 ('') 分配给局部变量。
模板可以使用以下模板函数在 globals 字典中设置值:
set_globals(id[=expression] [, id[=expression]]*)
此函数用于设置“全局范围”词典,key:value配对``id:value`` ,其中“value”是模板局部变量“id”的值。如果该局部变量不存在,则将“value”设置为“表达式”的求值结果。
关于不同模式之间差异的注释¶
三种程序模式:Single Function Mode (SFM), Template Program Mode<template_mode> ` (`TPM), 以及 General Program Mode<general_mode> ` (`GPM),的工作方式各不相同。SFM 旨在保持‘简单’,因此它隐藏了许多编程语言的细节。
差异:
在单函数模式(SFM)中,列的值总是作为‘不可见’的第一个参数,传递给模板中所包含的函数。
SFM 不支持变量与字符串之间的区别;所有值都被视为字符串。
以下 SFM 模板会返回丛书名称,或者返回字符串“no series”:
{series:ifempty(no series)}
在 TPM(模板程序模式)中,等效的模板为:
{series:'ifempty($, 'no series')'}
在 GPM(通用程序模式)中,等效的模板为:
program: ifempty(field('series'), 'no series')
ifempty 的第一个参数是 series 字段的值。第二个参数是字符串 no series。在单函数模式(SFM)中,第一个参数(即字段的值)是自动传递的(即不可见参数)。
一些模板函数(例如 booksize() 和 current_library_name())不接受任何参数。由于存在‘不可见参数’,您无法在单函数模式(SFM)中使用这些函数。
嵌套函数,指其中一个函数调用另一个函数来计算参数,不能在SFM中使用。例如,此模板旨在返回升级后的系列值的前5个字符,在SFM中不起作用:
{series:uppercase(substr(0,5))}
TPM 和 GPM 支持嵌套函数。上述模板在 TPM(模板程序模式)中应为:
{series:'uppercase(substr($, 0,5))'}
在 GPM(通用程序模式)中,它应为:
program: uppercase(substr(field('series'), 0,5))
如上面“模板程序模式 1”部分所述,在“TPM”字符串文字中使用“{”和“}”字符可能会导致错误或意外结果,因为它们会混淆模板处理器。 它尝试将它们视为模板边界,而不是字符。 在某些情况下,但不是所有情况下,您可以将
{替换为[[,将}替换为 ]] 。 一般来说,如果您的程序包含“{”和“}”字符,那么您应该使用“通用程序模式”。
用户自定义 Python 模板函数¶
您可以向模板处理器添加自定义的 Python 函数。此类函数可用于三种模板编程模式中的任何一种。通过进入 :guilabel:首选项 -> 高级 -> 模板函数 即可添加这些函数。该对话框中会显示具体的说明。请注意,您也可以出于类似目的使用 Python 模板。由于调用用户自定义函数的执行速度比调用 Python 模板更快,因此根据函数或模板逻辑的复杂程度,用户自定义函数可能会更加高效。
在不同语境下使用模板的特别说明¶
在GUI中 (Columns made from other columns 和 Template searches):
GPM(通用程序模式)模板的工作方式与之前相同。
Python 模板拥有访问 calibre 数据库的完整权限。
在图标规则中:
图标规则模板不包含书籍数据,因此基于字段的函数,如 format_date_field, list_count_field, 和 check_yes_no 将无法工作。
在内容服务器中:
模板可以访问新的 API,但无法访问旧的 API(LibraryDatabase)。
由于上述原因,以下格式化函数在 GPM 模板(复合列、图标规则等)中无法保证正常运行;如果您使用内容服务器,应当避免使用这些函数:
关于保存/发送模板的特别说明¶
当模板用于 :guilabe Save to disk 或 Send to device 时,系统会进行特殊处理。字段的值会被‘清洗’,即文件系统中的特殊字符(包括斜杠)都会被替换为下划线。这意味着字段内的文本不能直接用于创建文件夹。然而,前缀或后缀字符串中的斜杠不会被更改,因此这些字符串中的斜杠会导致文件夹的创建。利用这一特性,您可以创建深度可变的文件夹结构。
例如,假设我们想要构建 series/series_index - title 这样的文件夹结构,但有一个前提条件:如果‘丛书’(series)不存在,则书名应直接放在根目录(最顶层文件夹)下。实现这一点的模板为:
{series:||/}{series_index:|| - }{title}
斜杠和连字符仅在‘丛书’(series)不为空时才会出现。
查找功能让我们可以进行更高级的处理。 例如,假设一本书有一个系列,那么我们需要文件夹结构“系列/系列索引 - title.fmt”。 如果这本书没有系列,那么我们需要文件夹结构“genre/author_sort/title.fmt”。 如果这本书没有类型,那么我们想使用“未知”。 我们想要两条完全不同的路径,具体取决于系列的值。
为了实现这一目标,我们需要:
创建一个复合列(其搜索代号设为 #aa),内容为 {series}/{series_index} - {title}。如果‘丛书’(series)不为空,该模板将生成 series/series_index - title。
创建一个包含“{#genre:ifempty(Unknown)}/{Author_sort}/{title}'的复合字段(为其查找名称#bb)。此模板生成“genre/Author_sort/title”,其中空的genre被“Unknown”替换。
将保存模板设置为“{series:lookup(.,#aa,#bb)}`”。如果系列不为空,则此模板选择复合字段“#aa”,如果系列为空,请选择复合字段。因此,根据“系列”是否为空,我们有两个完全不同的保存路径。
技巧与建议¶
使用‘模板测试器’来测试模板。您可以将测试器添加到书籍的右键上下文菜单中,或者为其设置键盘快捷键。
模板可以通过引用基于所需模板构建的‘复合列’来调用其他模板。或者,您也可以使用‘存档模板’。
在元数据处理规则中,您可以使用特殊模板 {} 将某个字段设置为空(或等同于空的值)。该模板的结果始终为一个空字符串。
上述用于即使数值为零也显示数字的技巧,同样适用于标准字段 series_index(丛书编号)。
