update at 2026-03-17 10:37:27
This commit is contained in:
175
calendar/scripts/export-theme-backgrounds.sh
Normal file
175
calendar/scripts/export-theme-backgrounds.sh
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
|
||||
CALENDAR_DIR="$ROOT_DIR/calendar"
|
||||
DIST_DIR="$CALENDAR_DIR/dist"
|
||||
PORT=${PORT:-4173}
|
||||
SWIFT_SCRIPT="$CALENDAR_DIR/scripts/export-kindle-background.swift"
|
||||
THEMES_SOURCE="$CALENDAR_DIR/config/themes.json"
|
||||
|
||||
THEME_FILTER=""
|
||||
ORIENTATION_FILTER=""
|
||||
|
||||
print_usage() {
|
||||
cat <<'EOF'
|
||||
用法:
|
||||
sh scripts/export-theme-backgrounds.sh [选项]
|
||||
|
||||
选项:
|
||||
--theme <theme-id> 只导出指定主题
|
||||
--orientation <value> 只导出指定方向;必须和 --theme 一起使用
|
||||
-h, --help 查看帮助
|
||||
|
||||
示例:
|
||||
sh scripts/export-theme-backgrounds.sh
|
||||
sh scripts/export-theme-backgrounds.sh --theme simple
|
||||
sh scripts/export-theme-backgrounds.sh --theme simple --orientation portrait
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--theme)
|
||||
shift
|
||||
THEME_FILTER=${1:?"missing theme id"}
|
||||
;;
|
||||
--orientation)
|
||||
shift
|
||||
ORIENTATION_FILTER=${1:?"missing orientation"}
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "未知参数: $1" >&2
|
||||
echo >&2
|
||||
print_usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -n "$ORIENTATION_FILTER" ] && [ -z "$THEME_FILTER" ]; then
|
||||
echo "--orientation 必须和 --theme 一起使用。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
selection_output=$(
|
||||
node --input-type=module -e "
|
||||
import fs from 'node:fs';
|
||||
|
||||
const data = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
|
||||
const requestedTheme = process.argv[2];
|
||||
const requestedOrientation = process.argv[3];
|
||||
const themes = data.themes ?? [];
|
||||
const themeMap = new Map(themes.map((theme) => [theme.id, theme]));
|
||||
|
||||
if (requestedTheme && !themeMap.has(requestedTheme)) {
|
||||
console.error(\`未知主题: \${requestedTheme}\`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (requestedOrientation && !requestedTheme) {
|
||||
console.error('--orientation 必须和 --theme 一起使用。');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const filteredThemes = requestedTheme ? themes.filter((theme) => theme.id === requestedTheme) : themes;
|
||||
const items = [];
|
||||
|
||||
for (const theme of filteredThemes) {
|
||||
const orientations = Object.keys(theme.variants ?? {});
|
||||
if (requestedOrientation) {
|
||||
if (!orientations.includes(requestedOrientation)) {
|
||||
console.error(
|
||||
\`主题 \${theme.id} 不支持方向 \${requestedOrientation},可用方向: \${orientations.join(', ')}\`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const filteredOrientations = requestedOrientation ? [requestedOrientation] : orientations;
|
||||
for (const orientation of filteredOrientations) {
|
||||
const variant = theme.variants[orientation];
|
||||
items.push([theme.id, orientation, variant.backgroundPath].join('\t'));
|
||||
}
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
console.error('没有可导出的主题背景。');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(\`DEFAULT_THEME_ID=\${data.defaultThemeId}\`);
|
||||
console.log(\`DEFAULT_ORIENTATION=\${data.defaultOrientation}\`);
|
||||
for (const item of items) {
|
||||
console.log(\`ITEM=\${item}\`);
|
||||
}
|
||||
" "$THEMES_SOURCE" "$THEME_FILTER" "$ORIENTATION_FILTER"
|
||||
)
|
||||
|
||||
DEFAULT_THEME_ID=""
|
||||
DEFAULT_ORIENTATION=""
|
||||
EXPORT_ITEMS=""
|
||||
|
||||
while IFS= read -r line; do
|
||||
case "$line" in
|
||||
DEFAULT_THEME_ID=*)
|
||||
DEFAULT_THEME_ID=${line#DEFAULT_THEME_ID=}
|
||||
;;
|
||||
DEFAULT_ORIENTATION=*)
|
||||
DEFAULT_ORIENTATION=${line#DEFAULT_ORIENTATION=}
|
||||
;;
|
||||
ITEM=*)
|
||||
if [ -n "$EXPORT_ITEMS" ]; then
|
||||
EXPORT_ITEMS="${EXPORT_ITEMS}
|
||||
${line#ITEM=}"
|
||||
else
|
||||
EXPORT_ITEMS=${line#ITEM=}
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done <<EOF
|
||||
$selection_output
|
||||
EOF
|
||||
|
||||
if [ -z "$DEFAULT_THEME_ID" ] || [ -z "$DEFAULT_ORIENTATION" ] || [ -z "$EXPORT_ITEMS" ]; then
|
||||
echo "无法解析导出目标。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$CALENDAR_DIR"
|
||||
npm run build >/dev/null
|
||||
|
||||
python3 -m http.server "$PORT" -d "$DIST_DIR" >/tmp/kindle-calendar-http.log 2>&1 &
|
||||
SERVER_PID=$!
|
||||
trap 'kill "$SERVER_PID" 2>/dev/null || true' EXIT INT TERM
|
||||
|
||||
sleep 1
|
||||
|
||||
printf '%s\n' "$EXPORT_ITEMS" | while IFS="$(printf '\t')" read -r theme_id orientation background_path; do
|
||||
out_png="$DIST_DIR/$background_path"
|
||||
out_region="${out_png%.png}.clock-region.json"
|
||||
url="http://127.0.0.1:$PORT/?mode=background&theme=$theme_id&orientation=$orientation"
|
||||
/usr/bin/swift "$SWIFT_SCRIPT" "$url" "$out_png" "$out_region" >/dev/null
|
||||
|
||||
# 根目录的 kindlebg.png / clock-region.json 只给默认主题兜底使用。
|
||||
# 定向导出其它主题时不覆盖它,避免把默认主题的运行时入口意外改掉。
|
||||
if [ "$theme_id" = "$DEFAULT_THEME_ID" ] && [ "$orientation" = "$DEFAULT_ORIENTATION" ]; then
|
||||
cp "$out_png" "$DIST_DIR/kindlebg.png"
|
||||
cp "$out_region" "$DIST_DIR/clock-region.json"
|
||||
fi
|
||||
|
||||
printf 'Exported %s %s -> %s\n' "$theme_id" "$orientation" "$out_png"
|
||||
done
|
||||
|
||||
node "$CALENDAR_DIR/scripts/generate-dashboard-manifest.mjs" >/dev/null
|
||||
|
||||
if [ -f "$DIST_DIR/clock-region.json" ]; then
|
||||
printf 'Default region saved to %s\n' "$DIST_DIR/clock-region.json"
|
||||
else
|
||||
printf 'Skipped default region update (default theme not exported this run)\n'
|
||||
fi
|
||||
Reference in New Issue
Block a user