diff --git a/src/components/Content/index.tsx b/src/components/Content/index.tsx index 5e42154c..55886c48 100644 --- a/src/components/Content/index.tsx +++ b/src/components/Content/index.tsx @@ -39,6 +39,7 @@ import { toNote } from '@/lib/link' import { YOUTUBE_URL_REGEX } from '@/constants' import { isSpotifyOpenUrl } from '@/lib/spotify-url' import { canonicalZapStreamWatchUrl, isZapStreamWatchUrl } from '@/lib/zap-stream-url' +import { shouldDeferLongVideoAutoload } from '@/lib/long-video-load-policy' // Helper function to check if a URL is a YouTube URL function isYouTubeUrl(url: string): boolean { @@ -92,6 +93,9 @@ export default function Content({ () => (iArticleUrl ? cleanUrl(iArticleUrl) || iArticleUrl : ''), [iArticleUrl] ) + const deferLongVideoLoad = shouldDeferLongVideoAutoload(event, { + forceLoadMedia: mustLoadMedia + }) // Use unified media extraction service const extractedMedia = useMediaExtraction(event, _content) @@ -464,6 +468,7 @@ export default function Content({ src={video.url} className="w-full max-w-full" mustLoad={mustLoadMedia} + deferLoadUntilClick={deferLongVideoLoad} poster={video.image || video.thumb} blurHash={video.blurHash} /> @@ -538,6 +543,7 @@ export default function Content({ key={index} src={cleanedUrl} mustLoad={mustLoadMedia} + deferLoadUntilClick={deferLongVideoLoad} poster={tagMediaInfo?.image || tagMediaInfo?.thumb} blurHash={tagMediaInfo?.blurHash} /> @@ -566,6 +572,7 @@ export default function Content({ key={`url-media-${index}`} src={cleanedUrl} mustLoad={mustLoadMedia} + deferLoadUntilClick={deferLongVideoLoad} poster={poster} blurHash={mediaInfo?.blurHash} /> diff --git a/src/components/MediaGridItem/index.tsx b/src/components/MediaGridItem/index.tsx index 81dbbd81..55de2691 100644 --- a/src/components/MediaGridItem/index.tsx +++ b/src/components/MediaGridItem/index.tsx @@ -1,4 +1,5 @@ import { ExtendedKind, isNip71StyleVideoKind } from '@/constants' +import { isLongFormNip71VideoEventKind } from '@/lib/long-video-load-policy' import { getCachedThreadContextEvents } from '@/lib/navigation-related-events' import { toNote } from '@/lib/link' import client from '@/services/client.service' @@ -16,15 +17,18 @@ export default function MediaGridItem({ event }: { event: Event }) { /** Kind 20 is always treated as image unless imeta explicitly says video (rare mis-tag). */ const isPictureKind = event.kind === ExtendedKind.PICTURE + const isLongFormVideo = isLongFormNip71VideoEventKind(event.kind) const isVideo = (!isPictureKind && first?.m?.startsWith('video/')) || (!isPictureKind && isNip71StyleVideoKind(event.kind)) const isAudio = first?.m?.startsWith('audio/') || event.kind === ExtendedKind.VOICE const hasMultiple = media.all.length > 1 - // For videos prefer the poster image; fall back to video URL (browser extracts frame) + // For videos prefer the poster image; long-form feed tiles never prefetch the .mp4 (open note to play). const displayUrl = isVideo - ? (first?.image ?? first?.url) + ? isLongFormVideo + ? (first?.image ?? first?.thumb) + : (first?.image ?? first?.url) : (first?.thumb ?? first?.url) const handleClick = () => { @@ -38,9 +42,9 @@ export default function MediaGridItem({ event }: { event: Event }) { onClick={handleClick} > {displayUrl ? ( - isVideo && !first?.image ? ( + isVideo && !isLongFormVideo && !(first?.image ?? first?.thumb) && first?.url ? (