diff --git a/api/video-meta.ts b/api/video-meta.ts index 682b9ba9..25cb263f 100644 --- a/api/video-meta.ts +++ b/api/video-meta.ts @@ -94,7 +94,7 @@ async function pickCaptions(videoID: string, preferredLangs: string[], manualFir return null } -async function getVimeoMetadata(videoId: string): Promise<{ title: string; description: string }> { +async function getVimeoMetadata(videoId: string): Promise<{ title: string; description: string; thumbnail_url?: string }> { const vimeoUrl = `https://vimeo.com/${videoId}` const oembedUrl = `https://vimeo.com/api/oembed.json?url=${encodeURIComponent(vimeoUrl)}` @@ -107,7 +107,8 @@ async function getVimeoMetadata(videoId: string): Promise<{ title: string; descr return { title: data.title || '', - description: data.description || '' + description: data.description || '', + thumbnail_url: data.thumbnail_url || '' } } @@ -197,11 +198,12 @@ export default async function handler(req: VercelRequest, res: VercelResponse) { return ok(res, response) } else if (videoInfo.source === 'vimeo') { // Vimeo handling - const { title, description } = await getVimeoMetadata(videoInfo.id) + const { title, description, thumbnail_url } = await getVimeoMetadata(videoInfo.id) const response = { title, description, + thumbnail_url, captions: [], // Vimeo doesn't provide captions through oEmbed API transcript: '', // No transcript available lang: 'en', // Default language diff --git a/src/components/VideoView.tsx b/src/components/VideoView.tsx index 4c3d90cf..5f438c8f 100644 --- a/src/components/VideoView.tsx +++ b/src/components/VideoView.tsx @@ -7,6 +7,16 @@ import { IAccount } from 'applesauce-accounts' import { UserSettings } from '../services/settingsService' import { extractYouTubeId, getYouTubeMeta } from '../services/youtubeMetaService' import { buildNativeVideoUrl } from '../utils/videoHelpers' +import { getYouTubeThumbnail } from '../utils/imagePreview' + +// Helper function to get Vimeo thumbnail +const getVimeoThumbnail = (url: string): string | null => { + const vimeoMatch = url.match(/vimeo\.com\/(\d+)/) + if (!vimeoMatch) return null + + const videoId = vimeoMatch[1] + return `https://vumbnail.com/${videoId}.jpg` +} import { createWebsiteReaction, hasMarkedWebsiteAsRead @@ -201,12 +211,18 @@ const VideoView: React.FC = ({ const displayTitle = ytMeta?.title || title const displaySummary = ytMeta?.description || summary const durationText = videoDurationSec !== null ? formatDuration(videoDurationSec) : null + + // Get video thumbnail for cover image + const youtubeThumbnail = getYouTubeThumbnail(videoUrl) + const vimeoThumbnail = getVimeoThumbnail(videoUrl) + const videoThumbnail = youtubeThumbnail || vimeoThumbnail + const displayImage = videoThumbnail || image return ( <>