diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx index 4d9a1844..5f21aa7b 100644 --- a/src/components/Image/index.tsx +++ b/src/components/Image/index.tsx @@ -1,6 +1,6 @@ import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' -import { isRenderableMediaUrl, isSafeMediaUrl } from '@/lib/url' +import { isRenderableMediaUrl, isSafeMediaUrl, resolvePrimalBlossomPlayableUrl } from '@/lib/url' import { TImetaInfo } from '@/types' import { blurHashPlaceholderForMediaUrl } from '@/lib/media-placeholder-blurhash' import { decode } from 'blurhash' @@ -102,7 +102,7 @@ export default function Image({ const [isLoading, setIsLoading] = useState(urlOk && !effectiveHoldUntilClick) const [displaySkeleton, setDisplaySkeleton] = useState(urlOk) const [hasError, setHasError] = useState(!urlOk) - const [imageUrl, setImageUrl] = useState(url) + const [imageUrl, setImageUrl] = useState(() => resolvePrimalBlossomPlayableUrl(url ?? '')) const [fallbackIndex, setFallbackIndex] = useState(0) const loadWatchRef = useRef(null) // Kept in sync in the reset effect; load-timeout runs only while tap-to-load is actually active. @@ -149,7 +149,7 @@ export default function Image({ } useEffect(() => { - setImageUrl(url) + setImageUrl(resolvePrimalBlossomPlayableUrl(url ?? '')) loadSettledRef.current = false wasInitiallyHeldRef.current = effectiveHoldUntilClick const shouldHold = effectiveHoldUntilClick @@ -220,7 +220,7 @@ export default function Image({ const next = fallback[fallbackIndex] setFallbackIndex((prev) => prev + 1) loadSettledRef.current = false - setImageUrl(next) + setImageUrl(resolvePrimalBlossomPlayableUrl(next)) return } setIsLoading(false) diff --git a/src/components/MediaPlayer/index.tsx b/src/components/MediaPlayer/index.tsx index d0827133..d16b2478 100644 --- a/src/components/MediaPlayer/index.tsx +++ b/src/components/MediaPlayer/index.tsx @@ -1,4 +1,4 @@ -import { isHlsPlaylistUrl, isImage, isZapStreamWatchPageUrl } from '@/lib/url' +import { isHlsPlaylistUrl, isImage, isZapStreamWatchPageUrl, resolvePrimalBlossomPlayableUrl } from '@/lib/url' import { cn } from '@/lib/utils' import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' @@ -63,15 +63,17 @@ export default function MediaPlayer({ const [embedPainted, setEmbedPainted] = useState(false) const readyOnceRef = useRef(false) + const playableSrc = useMemo(() => resolvePrimalBlossomPlayableUrl(src), [src]) + // imeta `thumb` / `image` are sometimes the same .mp4 as `url` — cannot use that, and it // would hide the blurhash placeholder in LazyMediaTapPlaceholder. const imagePoster = useMemo(() => { const p = poster?.trim() if (!p) return undefined - return isImage(p) ? p : undefined + return isImage(p) ? resolvePrimalBlossomPlayableUrl(p) : undefined }, [poster]) - const urlEmbedSurfaceHint = useMemo(() => embedMediaSurfaceHintFromUrl(src), [src]) + const urlEmbedSurfaceHint = useMemo(() => embedMediaSurfaceHintFromUrl(playableSrc), [playableSrc]) /** Probe result wins when set (e.g. audio-only mp4); URL hint avoids a blank frame before useEffect runs. */ const effectiveMediaType = mediaType ?? urlEmbedSurfaceHint @@ -86,7 +88,7 @@ export default function MediaPlayer({ setEmbedPainted(false) setMediaType(null) setProbeFailed(false) - }, [src]) + }, [playableSrc]) useEffect(() => { if (!showEmbed) { @@ -96,7 +98,7 @@ export default function MediaPlayer({ } readyOnceRef.current = false setEmbedPainted(false) - if (!src) { + if (!playableSrc) { setProbeFailed(true) return } @@ -108,17 +110,17 @@ export default function MediaPlayer({ try { // Firefox/Chrome do not expose HLS via