update at 2026-03-21 18:44:12
This commit is contained in:
@@ -4,12 +4,15 @@ set -eu
|
||||
ROOT_DIR="$(CDPATH= cd -- "$(dirname "$0")/../.." && pwd)"
|
||||
CALENDAR_DIR="$ROOT_DIR/calendar"
|
||||
DIST_DIR="$CALENDAR_DIR/dist"
|
||||
KINDLE_BACKGROUNDS_DIR="$CALENDAR_DIR/kindle-backgrounds"
|
||||
PORT=${PORT:-4173}
|
||||
SWIFT_SCRIPT="$CALENDAR_DIR/scripts/export-kindle-background.swift"
|
||||
THEMES_SOURCE="$CALENDAR_DIR/config/themes.json"
|
||||
|
||||
THEME_FILTER=""
|
||||
ORIENTATION_FILTER=""
|
||||
LOCATION_LAT=""
|
||||
LOCATION_LON=""
|
||||
|
||||
print_usage() {
|
||||
cat <<'EOF'
|
||||
@@ -19,12 +22,15 @@ print_usage() {
|
||||
选项:
|
||||
--theme <theme-id> 只导出指定主题
|
||||
--orientation <value> 只导出指定方向;必须和 --theme 一起使用
|
||||
--location-lat <value> 导图时显式覆盖天气定位纬度
|
||||
--location-lon <value> 导图时显式覆盖天气定位经度
|
||||
-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
|
||||
sh scripts/export-theme-backgrounds.sh --location-lat 30.274084 --location-lon 120.15507
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -38,6 +44,14 @@ while [ "$#" -gt 0 ]; do
|
||||
shift
|
||||
ORIENTATION_FILTER=${1:?"missing orientation"}
|
||||
;;
|
||||
--location-lat)
|
||||
shift
|
||||
LOCATION_LAT=${1:?"missing location latitude"}
|
||||
;;
|
||||
--location-lon)
|
||||
shift
|
||||
LOCATION_LON=${1:?"missing location longitude"}
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
@@ -57,6 +71,11 @@ if [ -n "$ORIENTATION_FILTER" ] && [ -z "$THEME_FILTER" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if { [ -n "$LOCATION_LAT" ] && [ -z "$LOCATION_LON" ]; } || { [ -z "$LOCATION_LAT" ] && [ -n "$LOCATION_LON" ]; }; then
|
||||
echo "--location-lat 和 --location-lon 必须同时提供。" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
selection_output=$(
|
||||
node --input-type=module -e "
|
||||
import fs from 'node:fs';
|
||||
@@ -143,6 +162,7 @@ fi
|
||||
|
||||
cd "$CALENDAR_DIR"
|
||||
npm run build >/dev/null
|
||||
mkdir -p "$KINDLE_BACKGROUNDS_DIR"
|
||||
|
||||
python3 -m http.server "$PORT" -d "$DIST_DIR" >/tmp/kindle-calendar-http.log 2>&1 &
|
||||
SERVER_PID=$!
|
||||
@@ -153,8 +173,15 @@ 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"
|
||||
flat_background_png="$KINDLE_BACKGROUNDS_DIR/${theme_id}-${orientation}.png"
|
||||
url="http://127.0.0.1:$PORT/?mode=background&theme=$theme_id&orientation=$orientation"
|
||||
if [ -n "$LOCATION_LAT" ] && [ -n "$LOCATION_LON" ]; then
|
||||
url="${url}&location-lat=${LOCATION_LAT}&location-lon=${LOCATION_LON}"
|
||||
fi
|
||||
/usr/bin/swift "$SWIFT_SCRIPT" "$url" "$out_png" "$out_region" >/dev/null
|
||||
# Web 侧额外维护一份扁平命名的背景图目录,方便 nginx 单独暴露给 Kindle 拉图。
|
||||
# 主题 JSON 会把 background.url 指向这里,例如 /kindle-backgrounds/simple-portrait.png。
|
||||
cp "$out_png" "$flat_background_png"
|
||||
|
||||
# 根目录的 kindlebg.png / clock-region.json 只给默认主题兜底使用。
|
||||
# 定向导出其它主题时不覆盖它,避免把默认主题的运行时入口意外改掉。
|
||||
|
||||
@@ -9,12 +9,14 @@ const clockRegionPath = path.join(distDir, 'clock-region.json');
|
||||
const themesSourcePath = path.resolve(currentDir, '../config/themes.json');
|
||||
const themesDistPath = path.join(distDir, 'themes.json');
|
||||
const themesDir = path.join(distDir, 'themes');
|
||||
const kindleBackgroundsDir = path.resolve(currentDir, '../kindle-backgrounds');
|
||||
const dashboardBaseUrl = 'https://shell.biboer.cn:20001';
|
||||
|
||||
const themesSource = JSON.parse(fs.readFileSync(themesSourcePath, 'utf8'));
|
||||
const generatedAt = new Date().toISOString();
|
||||
const defaultVariant = themesSource.themes.find((theme) => theme.id === themesSource.defaultThemeId)?.variants?.[themesSource.defaultOrientation];
|
||||
const defaultDeviceClock = defaultVariant ? toDeviceClock(defaultVariant, themesSource.defaultOrientation) : null;
|
||||
const defaultTheme = themesSource.themes.find((theme) => theme.id === themesSource.defaultThemeId);
|
||||
const defaultVariant = defaultTheme?.variants?.[themesSource.defaultOrientation];
|
||||
const defaultDeviceClock = defaultVariant ? buildRuntimeClock(defaultTheme.id, themesSource.defaultOrientation, defaultVariant) : null;
|
||||
const defaultClockRegion = defaultVariant
|
||||
? {
|
||||
x: defaultDeviceClock.x,
|
||||
@@ -95,30 +97,74 @@ function toDeviceClock(variant, orientation) {
|
||||
};
|
||||
}
|
||||
|
||||
function resolveVariantClock(themeId, orientation, variant) {
|
||||
const regionPath = path.join(distDir, 'themes', themeId, orientation, 'kindlebg.clock-region.json');
|
||||
const exportedRegion = fs.existsSync(regionPath)
|
||||
? JSON.parse(fs.readFileSync(regionPath, 'utf8'))
|
||||
: null;
|
||||
|
||||
return {
|
||||
...variant,
|
||||
clock: {
|
||||
...variant.clock,
|
||||
...(exportedRegion
|
||||
? {
|
||||
x: exportedRegion.x,
|
||||
y: exportedRegion.y,
|
||||
width: exportedRegion.width,
|
||||
height: exportedRegion.height,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildRuntimeClock(themeId, orientation, variant) {
|
||||
const resolvedVariant = resolveVariantClock(themeId, orientation, variant);
|
||||
const hasExportedRegion =
|
||||
resolvedVariant.clock.x !== variant.clock.x ||
|
||||
resolvedVariant.clock.y !== variant.clock.y ||
|
||||
resolvedVariant.clock.width !== variant.clock.width ||
|
||||
resolvedVariant.clock.height !== variant.clock.height;
|
||||
|
||||
if (orientation === 'landscape' && hasExportedRegion) {
|
||||
return {
|
||||
...resolvedVariant.clock,
|
||||
rotationDegrees: 90,
|
||||
};
|
||||
}
|
||||
|
||||
return toDeviceClock(resolvedVariant, orientation);
|
||||
}
|
||||
|
||||
function buildThemeConfig(theme) {
|
||||
return {
|
||||
id: theme.id,
|
||||
label: theme.label,
|
||||
updatedAt: generatedAt,
|
||||
variants: Object.fromEntries(
|
||||
Object.entries(theme.variants).map(([orientation, variant]) => [
|
||||
orientation,
|
||||
{
|
||||
devicePlacement: variant.devicePlacement,
|
||||
background: {
|
||||
path: variant.backgroundPath,
|
||||
url: `${dashboardBaseUrl}/${variant.backgroundPath}`,
|
||||
refreshIntervalMinutes: 120,
|
||||
Object.entries(theme.variants).map(([orientation, variant]) => {
|
||||
return [
|
||||
orientation,
|
||||
{
|
||||
devicePlacement: variant.devicePlacement,
|
||||
background: {
|
||||
// Kindle 端统一走扁平目录,避免设备侧自己拼主题子目录规则。
|
||||
path: `kindle-backgrounds/${theme.id}-${orientation}.png`,
|
||||
url: `${dashboardBaseUrl}/kindle-backgrounds/${theme.id}-${orientation}.png`,
|
||||
refreshIntervalMinutes: 120,
|
||||
},
|
||||
clock: buildRuntimeClock(theme.id, orientation, variant),
|
||||
},
|
||||
clock: toDeviceClock(variant, orientation),
|
||||
},
|
||||
]),
|
||||
];
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
fs.mkdirSync(distDir, { recursive: true });
|
||||
fs.mkdirSync(themesDir, { recursive: true });
|
||||
fs.mkdirSync(kindleBackgroundsDir, { recursive: true });
|
||||
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
||||
fs.writeFileSync(themesDistPath, `${JSON.stringify(themesIndex, null, 2)}\n`, 'utf8');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user