Video once-per-day dedup + featured gold border & spacing

- Video watch-to-earn: a member can no longer re-earn from the same video the
  same day. New video_seen table (dual-mode); serveVideo excludes today's
  already-watched videos; videowatch double-checks before crediting.
- Featured card: equal top/bottom margin (no longer touches the stat cards) and
  a gold border on the card + the rotating link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-07 08:13:09 -05:00
parent bc95965814
commit 19b7770a4d
5 changed files with 17 additions and 6 deletions
+3
View File
@@ -934,9 +934,12 @@ const server = http.createServer(async (req, res) => {
if (age < t.secs * 1000 - 600) return json(res, 400, { error: 'Watch the full video first.' });
if (age > t.secs * 1000 + 10 * 60 * 1000) { videoTokens.delete(s.email); return json(res, 400, { error: 'That watch went stale. Load a fresh video.' }); }
videoTokens.delete(s.email); // single use
if (await ads.hasWatchedVideoToday(s.email, t.id)) // once-per-day-per-video: no double earning
return json(res, 200, { ok: true, credited: 0, status: await ads.videoStatus(s.email), already: true });
const tier = await ads.chargeVideoView(t.id); // charge advertiser; null if it ran dry
if (!tier) return json(res, 200, { ok: true, credited: 0, status: await ads.videoStatus(s.email), gone: true });
await ads.addEarned(s.email, tier.reward);
await ads.markVideoSeen(s.email, t.id);
const status = await ads.recordVideoWatch(s.email);
return json(res, 200, { ok: true, credited: tier.reward, status });
}