article_image

闲来无事,我总喜欢泡在图书馆,倒不是喜欢里面的气氛(一群学生绷着神经备考的酸味令人作呕),而是喜欢书架。书总是看不完的,而实体书架有重要的浏览式查询功能(于良芝语),你可以在图书室里随意走动(所以我不喜欢那些考生,好像裤脚的摩擦声都会导致他们落榜一样),摸索书脊,偶若觉得哪一本有趣,就随手拾起或抽出。

易言之,实体书架提供了逛的功能,大概电子设备很难模仿(Vision Pro 倒是有希望,但我更愿意办一张图书馆的会员卡)。即便勉强效颦,如果还得专程打开一个软件,恐怕也是徒增障碍:工作如此忙碌,谁还有闲情逸致专门装一个模拟书架的软件呢?

偶然间,我在听歌时有了灵感。iTunes(现在的 Apple Music)有个原生的屏幕保护插件,可以在屏保上随机显示专辑,点击后还可以播放,简直就像数字版的唱片店。锁屏时,你暂时从工作中抽身,电脑也进入了佐藤大所谓的“休息时间”,变成了另一种设备,有人把它简单当作大号时钟,有人把它变成电子相框,也有人把它充作赛博水族馆。如果拓展一下思路,在屏保里随机展示藏书封面,岂不就能最大程度接近图书馆书架?

可惜屏保插件也要开发者认证,我不愿意既要自己造轮子还得花一大笔冤枉钱(足够我在好几个一流图书馆办卡了),只好先用原生相册屏保凑合。效果倒也不差,macOS 可以随机显示目标文件夹中的图片,只要我预先备妥书籍封面文件,就可以在休息时看到随机呈现的书籍。屏保内容每次都不一样,我时不时会发现几本过去想看但最终不了了之的大部头,有时还会突然撞上一本踏破铁鞋未能忆起的工具书,甚至看到一些我自己都忘了是什么的古书或小语种著作(我常去古玩市场买旧书,有时也海淘原版书,忘了自己买了什么也在所难免)。

为了尽可能填满屏幕,macOS 似乎会拉伸和裁剪一部分封面图像,确保书籍封面尺寸统一。一般也无伤大雅,毕竟书店畅销书书架上的作品尚且参差不齐(我说的是尺寸,真的没有别的意思,真的……),macOS 的处理反倒比书店书架看上去更顺延,偶尔遇到贴边的标题被裁掉,也不至于完全猜不出书名是什么。至于本就是横版的书籍,则会占用两个位置,我有一些建筑类图书和画册,时不时在屏保上瞥见时,效果也不错。

制作这样的书架式屏保也不难,无非两步,先备好书籍封面照片,然后在系统设置中设好屏保。第一步比较关键,你得想办法弄到藏书封面的图片,我用的管理工具是 Calibre,它独立存储了书籍封面文件,我得以批量提取这些封面图片。下面是一个毫无美感但可用的 Shell 脚本,保存为 .sh 格式后在终端(Terminal)中运行,就能导出 Calibre 书库中封面文件。

#!/bin/bash
SRC_DIR="请填写 Calibre 文件夹路径"
DEST_DIR="$HOME/Downloads"
find "$SRC_DIR" -type f -name "cover.jpg" | while read -r FILE; do
    DEST_FILE=$(printf "%s/cover%04d.jpg" "$DEST_DIR" "$COUNTER")
    cp "$FILE" "$DEST_FILE"
done

显然,这只是权宜之计,因为我隔三差五就要收录新书,经常手动跑脚本并不方便(暂未设计增量机制,每次都要从头跑一遍),而且不分青红皂白导出所有书籍的封面,偶尔也会展示一些我并不想示人的书籍(比如小说这类非常私人的东西,我甚至考虑以后专门建一个虚构类作品的 Calibre 数据库)。好在都有办法慢慢解决,例如,如果你已经习惯在 Calibre 中用标签整理书籍(Calibre 没有文件夹),可以尝试下面的脚本,仅导出匹配指定标签的书籍封面:

#!/usr/local/bin/python3
# -*- coding: UTF-8 -*-

import os
import sqlite3
import subprocess
import shutil
import re

tag_keyword = "关键词"

library_path = subprocess.check_output(
    '/Applications/calibre.app/Contents/MacOS/calibre-debug -c "from calibre.utils.config import prefs; print(prefs.get(\'library_path\'), end=\'\')"',
    shell=True
).decode('utf-8').strip()

metadata_db = os.path.join(library_path, 'metadata.db')
con = sqlite3.connect(metadata_db)
cur = con.cursor()

query = """
SELECT books.id, books.title, books.path
FROM books
WHERE books.id IN (
    SELECT book FROM books_tags_link
    WHERE tag IN (
        SELECT id FROM tags WHERE name LIKE ?
    )
)
"""
cur.execute(query, (f'%{tag_keyword}%',))
results = cur.fetchall()

downloads_path = os.path.join(os.path.expanduser('~'), 'Downloads')
target_dir = os.path.jo

机器阅读:已开放本文前 70% 的正文,剩余内容会员可见。 标准阅读页 · Markdown


author_avatar

法律顾问,前律师, macOS/iOS 自动化业余玩家。有效即合法,无效的优雅有罪。