#!/usr/bin/env sh set -eu ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)" THEMES_JSON="$ROOT_DIR/calendar/config/themes.json" SYNC_SCRIPT="$ROOT_DIR/scripts/sync-layered-clock-to-kindle.sh" CAPTURE_SCRIPT="$ROOT_DIR/scripts/capture-kindle-screen.sh" HOST_TARGET="kindle" THEME_ID="" ORIENTATION="" OUTPUT_DIR="$ROOT_DIR/tmp" OUTPUT_BASENAME="" OUTPUT_BASENAME_EXPLICIT=false LIST_ONLY=false DO_SYNC=true DO_SWITCH=true DO_CAPTURE=true DO_DATE=false SYNC_THEME_ID="" SYNC_ORIENTATION="" SELECTED_THEME_ID="" SELECTED_ORIENTATIONS="" RUN_TIMESTAMP="$(date '+%Y%m%d-%H%M%S')" print_usage() { cat <<'EOF' 用法: sh snapshot.sh [选项] 默认流程: 1. 把本地最新主题包同步到 Kindle 2. 切换到指定主题和方向 3. 抓取当前 Kindle 实际屏幕到本地 选项: -t, --theme 指定主题名;默认取 themes.json 的 defaultThemeId -o, --orientation 指定方向;省略时,指定主题会按全部支持方向处理 -k, --kindle Kindle SSH 主机名,默认 kindle -d, --output-dir 本地截图输出目录,默认 ./tmp -b, --basename 截图文件名前缀,默认 --<时间戳> --sync-only 只同步,不切换,不抓图 --switch-only 只切换,不同步,不抓图 --capture-only 只抓图,不同步,不切换 --date 只获取当前 Kindle 系统日期时间 --no-sync 跳过同步 --no-switch 跳过切换 --no-capture 跳过抓图 --list 列出本地现有主题和可用方向 -h, --help 查看帮助 示例: sh snapshot.sh --list sh snapshot.sh -t simple sh snapshot.sh -t simple -o portrait sh snapshot.sh -t simple -o landscape -b simple-check sh snapshot.sh --sync-only sh snapshot.sh --sync-only -t simple sh snapshot.sh --sync-only -t simple -o portrait sh snapshot.sh --switch-only -t simple -o portrait sh snapshot.sh --capture-only -b current-screen sh snapshot.sh --date EOF } list_themes() { python3 - "$THEMES_JSON" <<'PY' import json import pathlib import sys path = pathlib.Path(sys.argv[1]) data = json.loads(path.read_text()) print(f"defaultThemeId: {data.get('defaultThemeId', 'unknown')}") print(f"defaultOrientation: {data.get('defaultOrientation', 'unknown')}") print("themes:") for theme in data.get("themes", []): orientations = ", ".join(theme.get("variants", {}).keys()) or "none" print(f" - {theme['id']}: {orientations}") PY } resolve_selection() { python3 - "$THEMES_JSON" "$THEME_ID" "$ORIENTATION" <<'PY' import json import pathlib import sys path = pathlib.Path(sys.argv[1]) requested_theme = sys.argv[2] requested_orientation = sys.argv[3] data = json.loads(path.read_text()) themes = {theme["id"]: theme for theme in data.get("themes", [])} theme_id = requested_theme or data.get("defaultThemeId", "") if theme_id not in themes: raise SystemExit(f"未知主题: {theme_id}") theme = themes[theme_id] orientations = list(theme.get("variants", {}).keys()) if not orientations: raise SystemExit(f"主题 {theme_id} 没有任何方向配置") print(f"THEME_ID={theme_id}") if requested_theme and not requested_orientation: print(f"ORIENTATIONS={' '.join(orientations)}") else: orientation = requested_orientation if not orientation: default_orientation = data.get("defaultOrientation", "") orientation = default_orientation if default_orientation in orientations else orientations[0] if orientation not in orientations: raise SystemExit( f"主题 {theme_id} 不支持方向 {orientation},可用方向: {', '.join(orientations)}" ) print(f"ORIENTATIONS={orientation}") PY } load_selection() { resolved_output="$(resolve_selection)" SELECTED_THEME_ID="" SELECTED_ORIENTATIONS="" while IFS='=' read -r key value; do case "$key" in THEME_ID) SELECTED_THEME_ID=$value ;; ORIENTATIONS) SELECTED_ORIENTATIONS=$value ;; esac done <&2 exit 1 fi } validate_sync_filter() { if [ -z "$THEME_ID" ]; then SYNC_THEME_ID="" SYNC_ORIENTATION="" return fi python3 - "$THEMES_JSON" "$THEME_ID" "$ORIENTATION" <<'PY' import json import pathlib import sys path = pathlib.Path(sys.argv[1]) requested_theme = sys.argv[2] requested_orientation = sys.argv[3] data = json.loads(path.read_text()) themes = {theme["id"]: theme for theme in data.get("themes", [])} if requested_theme not in themes: raise SystemExit(f"未知主题: {requested_theme}") if requested_orientation: orientations = list(themes[requested_theme].get("variants", {}).keys()) if requested_orientation not in orientations: raise SystemExit( f"主题 {requested_theme} 不支持方向 {requested_orientation},可用方向: {', '.join(orientations)}" ) PY SYNC_THEME_ID=$THEME_ID SYNC_ORIENTATION=$ORIENTATION } log_step() { printf '\n[%s] %s\n' "$1" "$2" } capture_basename_for_orientation() { current_orientation=$1 if [ "$OUTPUT_BASENAME_EXPLICIT" = true ]; then if [ "$DO_SWITCH" = true ] && [ "$SELECTED_ORIENTATIONS" != "$current_orientation" ]; then printf '%s-%s\n' "$OUTPUT_BASENAME" "$current_orientation" else printf '%s\n' "$OUTPUT_BASENAME" fi return fi if [ "$DO_SWITCH" = true ]; then printf '%s-%s-%s\n' "$SELECTED_THEME_ID" "$current_orientation" "$RUN_TIMESTAMP" return fi printf 'current-screen-%s\n' "$RUN_TIMESTAMP" } while [ "$#" -gt 0 ]; do case "$1" in -t|--theme) shift THEME_ID=${1:?"missing theme id"} ;; -o|--orientation) shift ORIENTATION=${1:?"missing orientation"} ;; -k|--kindle) shift HOST_TARGET=${1:?"missing kindle host"} ;; -d|--output-dir) shift OUTPUT_DIR=${1:?"missing output dir"} ;; -b|--basename) shift OUTPUT_BASENAME=${1:?"missing basename"} OUTPUT_BASENAME_EXPLICIT=true ;; --sync-only) DO_SYNC=true DO_SWITCH=false DO_CAPTURE=false ;; --switch-only) DO_SYNC=false DO_SWITCH=true DO_CAPTURE=false ;; --capture-only) DO_SYNC=false DO_SWITCH=false DO_CAPTURE=true ;; --date) DO_SYNC=false DO_SWITCH=false DO_CAPTURE=false DO_DATE=true ;; --no-sync) DO_SYNC=false ;; --no-switch) DO_SWITCH=false ;; --no-capture) DO_CAPTURE=false ;; --list) LIST_ONLY=true ;; -h|--help) print_usage exit 0 ;; *) echo "未知参数: $1" >&2 echo >&2 print_usage >&2 exit 1 ;; esac shift done if [ "$LIST_ONLY" = true ]; then list_themes exit 0 fi if [ "$DO_SYNC" = false ] && [ "$DO_SWITCH" = false ] && [ "$DO_CAPTURE" = false ] && [ "$DO_DATE" = false ]; then echo "没有可执行动作;请至少保留同步、切换、抓图、日期中的一个。" >&2 exit 1 fi if [ "$DO_SYNC" = true ]; then validate_sync_filter fi if [ "$DO_SWITCH" = true ]; then load_selection fi step_total=0 [ "$DO_SYNC" = true ] && step_total=$((step_total + 1)) [ "$DO_DATE" = true ] && step_total=$((step_total + 1)) selection_count=1 if [ "$DO_SWITCH" = true ]; then set -- $SELECTED_ORIENTATIONS selection_count=$# step_total=$((step_total + selection_count)) fi if [ "$DO_CAPTURE" = true ]; then if [ "$DO_SWITCH" = true ]; then step_total=$((step_total + selection_count)) else step_total=$((step_total + 1)) fi fi step_index=0 if [ "$DO_SYNC" = true ]; then step_index=$((step_index + 1)) if [ -n "$SYNC_THEME_ID" ] && [ -n "$SYNC_ORIENTATION" ]; then log_step "${step_index}/${step_total}" "同步主题包到 Kindle ($HOST_TARGET): ${SYNC_THEME_ID} / ${SYNC_ORIENTATION}" sh "$SYNC_SCRIPT" "$HOST_TARGET" --theme "$SYNC_THEME_ID" --orientation "$SYNC_ORIENTATION" elif [ -n "$SYNC_THEME_ID" ]; then log_step "${step_index}/${step_total}" "同步主题包到 Kindle ($HOST_TARGET): ${SYNC_THEME_ID}" sh "$SYNC_SCRIPT" "$HOST_TARGET" --theme "$SYNC_THEME_ID" else log_step "${step_index}/${step_total}" "同步本地最新主题包到 Kindle ($HOST_TARGET)" sh "$SYNC_SCRIPT" "$HOST_TARGET" fi fi if [ "$DO_SWITCH" = true ]; then mkdir -p "$OUTPUT_DIR" for current_orientation in $SELECTED_ORIENTATIONS; do step_index=$((step_index + 1)) log_step "${step_index}/${step_total}" "切换 Kindle 主题到 ${SELECTED_THEME_ID} / ${current_orientation}" ssh "$HOST_TARGET" "/mnt/us/dashboard/switch-theme.sh '$SELECTED_THEME_ID' '$current_orientation'" if [ "$DO_CAPTURE" = true ]; then current_basename="$(capture_basename_for_orientation "$current_orientation")" step_index=$((step_index + 1)) log_step "${step_index}/${step_total}" "抓取当前 Kindle 实际屏幕到本地 (${current_orientation})" sh "$CAPTURE_SCRIPT" "$HOST_TARGET" "$current_basename" "$OUTPUT_DIR" fi done elif [ "$DO_CAPTURE" = true ]; then step_index=$((step_index + 1)) log_step "${step_index}/${step_total}" "抓取当前 Kindle 实际屏幕到本地" mkdir -p "$OUTPUT_DIR" current_basename="$(capture_basename_for_orientation "")" sh "$CAPTURE_SCRIPT" "$HOST_TARGET" "$current_basename" "$OUTPUT_DIR" fi if [ "$DO_DATE" = true ]; then step_index=$((step_index + 1)) log_step "${step_index}/${step_total}" "获取当前 Kindle 系统日期时间" ssh "$HOST_TARGET" "date '+%Y-%m-%d %H:%M:%S %Z'" fi if [ "$DO_SWITCH" = true ]; then printf '\n完成:theme=%s orientations=%s host=%s\n' "$SELECTED_THEME_ID" "$SELECTED_ORIENTATIONS" "$HOST_TARGET" else printf '\n完成:host=%s\n' "$HOST_TARGET" fi if [ "$DO_CAPTURE" = true ]; then printf '截图输出目录:%s\n' "$OUTPUT_DIR" fi