Skip to content

feat(videos): sync thumbnails to S3#18039

Merged
wackerow merged 6 commits into
devfrom
videos-s3
Apr 28, 2026
Merged

feat(videos): sync thumbnails to S3#18039
wackerow merged 6 commits into
devfrom
videos-s3

Conversation

@myelinated-wackerow
Copy link
Copy Markdown
Collaborator

@myelinated-wackerow myelinated-wackerow commented Apr 24, 2026

Summary

Adds a daily Trigger.dev task that downloads video thumbnails from YouTube (or customThumbnailUrl) to our S3 bucket, and wires the /videos gallery to serve those S3-hosted images instead of hitting img.youtube.com directly.

Motivation

YouTube's thumbnail CDN is inconsistent:

  • sddefault.jpg (best quality) returns 404 for some videos
  • hqdefault.jpg is the documented "guaranteed" fallback but has been flaky in practice (broken thumbnails observed on production /videos recently)
  • Direct YouTube URLs go through Next.js' image optimization proxy, which errors server-side on 404s before client-side onError can fire -- so a transient YouTube 404 becomes a broken image users see

Syncing to S3 solves this with a single reliable origin, and gives us the progressive-quality pick (sd > hq) at sync time instead of render time.

Follows the existing fetchApps / fetchEvents / developer-tools pattern.

Changes

New

  • src/data-layer/fetchers/fetchVideoThumbnails.ts -- daily task. For each video: uploads customThumbnailUrl if present, else tries sddefault.jpg and falls back to hqdefault.jpg. Keyed by slug so each video maps to exactly one S3 object.
  • VIDEO_THUMBNAILS key + registration in src/data-layer/tasks.ts DAILY
  • getVideoThumbnails() getter in src/data-layer/index.ts
  • Cached wrapper in src/lib/data/index.ts

Modified

  • src/data-layer/s3.ts -- uploadToS3 gains an optional customFilename 3rd param so callers with a stable ID (e.g., slug) can override the URL-derived hash key with a human-readable filename. Existing callers unaffected.
  • src/lib/utils/videos.ts -- getVideos() fetches the thumbnail map in parallel with the slug scan; toVideoCardData() uses the S3 URL. getDefaultThumbnailUrl() removed.
  • app/[locale]/videos/[slug]/page-jsonld.tsx -- thumbnailUrl inlined to point at the canonical YouTube URL (JSON-LD is plain string metadata for crawlers, doesn't pass through Next.js Image).
  • next.config.js -- img.youtube.com removed from remotePatterns (no longer rendered via Next.js Image anywhere).

Ops notes

  • First deploy: trigger the task manually once so the map populates before users hit the gallery. The getter in getVideos() has a graceful fallback (.catch(() => null)), so a missing/empty map doesn't break the page -- but cards won't show thumbnails until the sync has run.

Test plan

  • Run fetch-video-thumbnails task in Trigger.dev dev environment
  • Confirm S3 bucket contains videos/thumbnails/<slug>.jpg for each video
  • Spot-check a few S3 URLs render in the browser
  • Load /videos locally and confirm gallery cards use S3 URLs
  • Load a /videos/[slug] page and inspect JSON-LD -- thumbnailUrl should be a YouTube URL
  • Re-run the task; verify no redundant uploads (HEAD check short-circuits)
  • After merge, trigger the task in production; verify /videos renders from S3

Generated by Claude (Opus 4.7)

myelinated-wackerow and others added 4 commits April 24, 2026 11:31
Adds an optional third param to uploadToS3 so callers with a stable
ID (e.g., a slug) can override the URL-derived hash key with a
human-readable filename. Existing callers unaffected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Adds a daily Trigger.dev task that downloads YouTube video thumbnails
to S3, keyed by slug. Tries sddefault.jpg first, falls back to
hqdefault.jpg. customThumbnailUrl takes priority when set.

Wires the thumbnail map into getVideos() with a graceful fallback to
the YouTube URL when the map is missing, so the gallery still renders
before the first sync runs (or if it fails).

Follows the existing fetchApps/fetchEvents pattern for data-layer,
task registration, getters, and cached wrappers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Gallery cards now rely entirely on the S3 thumbnail map; the
pre-S3 fallback to img.youtube.com is removed along with the
getDefaultThumbnailUrl helper and the img.youtube.com entry
in next.config.js remotePatterns.

JSON-LD thumbnailUrl is inlined with the canonical YouTube URL
(customThumbnailUrl || hqdefault.jpg) since structured data is
consumed by crawlers as a plain string and doesn't pass through
Next.js Image optimization.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 24, 2026

Deploy Preview for ethereumorg ready!

Name Link
🔨 Latest commit f459022
🔍 Latest deploy log https://app.netlify.com/projects/ethereumorg/deploys/69f11707f2b8330008894b71
😎 Deploy Preview https://deploy-preview-18039.ethereum.it
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
7 paths audited
Performance: 71 (🟢 up 22 from production)
Accessibility: 93 (no change from production)
Best Practices: 100 (no change from production)
SEO: 98 (🔴 down 1 from production)
PWA: 59 (no change from production)
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions Bot added config ⚙️ Changes to configuration files tooling 🔧 Changes related to tooling of the project labels Apr 24, 2026
Co-Authored-By: wackerow <54227730+wackerow@users.noreply.github.com>
Copy link
Copy Markdown
Member

@pettinarip pettinarip left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@wackerow wackerow merged commit d4b471d into dev Apr 28, 2026
9 checks passed
@wackerow wackerow deleted the videos-s3 branch April 28, 2026 20:46
@wackerow wackerow mentioned this pull request May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config ⚙️ Changes to configuration files tooling 🔧 Changes related to tooling of the project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants