Simplify Threads captions and capture five minutes after sunrise

This commit is contained in:
2026-11-02 04:14:11 +09:00
parent 6ce32d33fe
commit 39374cea05
8 changed files with 37 additions and 22 deletions
+1
View File
@@ -50,6 +50,7 @@ export function renderCaption(config: Captions, capture: {
const date = new Date(capture.capturedAt);
const values: Record<string, string> = {
message: config.messages[condition].join('\n'), date: kstFields(date).date,
dateDots: kstFields(date).date.replaceAll('-', '.'), hostname: new URL(capture.url).hostname,
time: kstClock(date).slice(0, 5), sunrise: kstClock(new Date(capture.sunrise)).slice(0, 5), url: capture.url,
};
const text = (capture.test ? config.testPrefix : '') + config.template.join('\n').replace(/\{(\w+)\}/g, (_, key: string) => {
+5 -1
View File
@@ -7,7 +7,11 @@ import { THREADS } from '../../src/constants/threads.ts';
export function dailySchedule(now: Date) {
const sunrise = calculateSun(now, observer).sunrise;
if (!sunrise || !Number.isFinite(+sunrise)) throw new Error('No sunrise for this date');
return { date: kstFields(now).date, sunrise, prepareAt: new Date(+sunrise - THREADS.prepareMs) };
return { date: kstFields(now).date, sunrise, captureAt: sunriseCaptureTime(sunrise), prepareAt: new Date(+sunrise - THREADS.prepareMs) };
}
export function sunriseCaptureTime(sunrise: Date): Date {
return new Date(+sunrise + THREADS.captureDelayMs);
}
export async function threadsRequest(token: string, path: string, params: Record<string, string>, method = 'GET', request: typeof fetch = fetch) {
+8 -6
View File
@@ -5,7 +5,7 @@ import { setTimeout as delay } from 'node:timers/promises';
import { chromium } from 'playwright';
import { THREADS } from '../../src/constants/threads.ts';
import { kstClock, kstFields } from '../../src/ui/time.ts';
import { dailySchedule, publishCarousel, threadsRequest } from './publisher.ts';
import { dailySchedule, publishCarousel, threadsRequest, sunriseCaptureTime } from './publisher.ts';
import { loadThreadsConfig } from './config.ts';
import { cleanupPublished } from './cleanup.ts';
import { loadCaptions, renderCaption } from './captions.ts';
@@ -79,8 +79,8 @@ async function main() {
const immediate = postNow || (dryRun && args.includes('--now'));
const capturePath = join(stateDir, `${runId}.capture.json`);
const savedCapture = dryRun ? null : await readJson(capturePath);
if (!savedCapture && !immediate && Date.now() > +schedule.sunrise + THREADS.maxLatenessMs) {
console.log(`${schedule.date}: sunrise has passed; skipping late capture`);
if (!savedCapture && !immediate && Date.now() > +schedule.captureAt + THREADS.maxLatenessMs) {
console.log(`${schedule.date}: sunrise +5 minute capture window has passed; skipping late capture`);
return;
}
const captured = savedCapture ?? await captureSunrise(schedule, immediate);
@@ -142,14 +142,16 @@ async function captureSunrise(schedule: ReturnType<typeof dailySchedule>, immedi
sunrise = new Date(await page.evaluate(() => window.sunriseThreads!.sunrise()));
if (!Number.isFinite(+sunrise) || kstFields(sunrise).date !== schedule.date) throw new Error('Unexpected page sunrise date');
console.log(`Page sunrise: ${sunrise.toISOString()} (${kstClock(sunrise)} KST)`);
const captureAt = sunriseCaptureTime(sunrise);
console.log(`Capture target: ${captureAt.toISOString()} (${kstClock(captureAt)} KST)`);
if (!immediate) {
if (Date.now() > +sunrise + THREADS.maxLatenessMs) throw new Error('Page loaded after capture deadline');
await waitUntil(sunrise);
if (Date.now() > +captureAt + THREADS.maxLatenessMs) throw new Error('Page loaded after capture deadline');
await waitUntil(captureAt);
}
const capture = await page.evaluate(() => window.sunriseThreads!.capture());
capturedAt = capture.capturedAt;
weather = capture.weather ?? null;
if (!immediate && Math.abs(Date.parse(capturedAt) - +sunrise) > THREADS.maxLatenessMs) throw new Error('Capture missed sunrise deadline');
if (!immediate && Math.abs(Date.parse(capturedAt) - +captureAt) > THREADS.maxLatenessMs) throw new Error('Capture missed sunrise +5 minute deadline');
if (capture.images?.length !== THREADS.angles.length) throw new Error('Deploy the two-angle capture frontend first');
for (const [index, shot] of capture.images.entries()) {
if (shot.name !== THREADS.angles[index].name) throw new Error('Unexpected capture angle order');