diff --git a/apps/web/src/components/home-recommendations-section.tsx b/apps/web/src/components/home-recommendations-section.tsx index 40a5697..36a9874 100644 --- a/apps/web/src/components/home-recommendations-section.tsx +++ b/apps/web/src/components/home-recommendations-section.tsx @@ -1,6 +1,7 @@ import { useBlockedFilter } from "../hooks/use-blocked-filter"; import { useHomeRecommendations } from "../hooks/use-home-recommendations"; import { trackRecommendationEvent } from "../lib/recommendation-tracker"; +import { HomeFallbackSection } from "./home-fallback-section"; import { ScrollSentinel } from "./scroll-sentinel"; import { VideoCardSkeleton } from "./video-card-skeleton"; import { VideoGrid } from "./video-grid"; @@ -18,12 +19,21 @@ function SkeletonGrid() { } export function HomeRecommendationsSection() { - const { streams, serviceId, intent, isLoading, hasNextPage, isFetchingNextPage, fetchNextPage } = - useHomeRecommendations(); + const { + streams, + serviceId, + intent, + isLoading, + isError, + hasNextPage, + isFetchingNextPage, + fetchNextPage, + } = useHomeRecommendations(); const { filter } = useBlockedFilter(); const filtered = filter(streams); if (isLoading) return ; + if (isError || filtered.length === 0) return ; return ( <> { @@ -30,20 +28,16 @@ export function VideoPreview({ stream, show }: Props) { } hls = nextHls; }); - } else if (src.type === "application/dash+xml") { - void loadDash(video, src.url).then((player) => { - if (disposed) { - player?.destroy(); - return; - } - dash = player; - }); + } else { + video.src = src.url; } return () => { disposed = true; hls?.destroy(); - dash?.destroy(); + video.pause(); + video.removeAttribute("src"); + video.load(); }; }, [show, stream]); @@ -70,12 +64,7 @@ export function VideoPreview({ stream, show }: Props) { ); } -type PreviewSrc = { url: string; type: "application/x-mpegurl" | "application/dash+xml" } | null; - -function isFirefoxBrowser(): boolean { - if (typeof navigator === "undefined") return false; - return navigator.userAgent.includes("Firefox/"); -} +type PreviewSrc = { url: string; type: "application/x-mpegurl" | "video/mp4" } | null; function resolvePreviewSrc(stream: VideoStream): PreviewSrc { if (typeof window !== "undefined" && window.matchMedia("(hover: none)").matches) { @@ -90,33 +79,14 @@ function resolvePreviewSrc(stream: VideoStream): PreviewSrc { }; } - if (stream.videoOnlyStreams?.length && stream.audioStreams?.length) { - const manifest = buildDashManifest( - stream.videoOnlyStreams, - stream.audioStreams, - stream.duration, - 480, - ); - if (manifest) return { url: manifest, type: "application/dash+xml" }; - } - - if (isFirefoxBrowser()) { - return { - url: proxyDashManifest(`${API_BASE}/streams/manifest?url=${encodeURIComponent(stream.id)}`), - type: "application/dash+xml", - }; - } - - return { - url: proxyDashManifest( - `${API_BASE}/streams/native-manifest?url=${encodeURIComponent(stream.id)}`, - ), - type: "application/dash+xml", - }; + const progressive = [...(stream.videoStreams ?? [])] + .filter((candidate) => candidate.mimeType.includes("video/mp4")) + .sort((left, right) => (right.bitrate ?? 0) - (left.bitrate ?? 0))[0]; + if (!progressive) return null; + return { url: proxyDashManifest(progressive.url), type: "video/mp4" }; } type Hls = { destroy: () => void }; -type DashJs = { destroy: () => void }; async function loadHls(video: HTMLVideoElement, url: string): Promise { if (!supportsNativeHls(video)) return null; @@ -135,16 +105,3 @@ function supportsNativeHls(video: HTMLVideoElement): boolean { const legacyType = video.canPlayType("application/x-mpegURL"); return appleType !== "" || legacyType !== ""; } - -async function loadDash(video: HTMLVideoElement, url: string): Promise { - const dashjs = await import("dashjs"); - const player = dashjs.MediaPlayer().create(); - player.updateSettings({ - streaming: { - buffer: { fastSwitchEnabled: true, bufferTimeAtTopQuality: 10 }, - abr: { initialBitrate: { video: 1000 }, maxBitrate: { video: 2000 } }, - }, - }); - player.initialize(video, url, false); - return player; -} diff --git a/apps/web/src/hooks/use-home-recommendations.ts b/apps/web/src/hooks/use-home-recommendations.ts index 67005ad..8601c48 100644 --- a/apps/web/src/hooks/use-home-recommendations.ts +++ b/apps/web/src/hooks/use-home-recommendations.ts @@ -12,6 +12,7 @@ type Result = { serviceId: number; intent: RecommendationIntent; isLoading: boolean; + isError: boolean; isFetchingNextPage: boolean; hasNextPage: boolean; fetchNextPage: () => void; @@ -43,6 +44,7 @@ export function useHomeRecommendations(): Result { serviceId: settings.defaultService, intent, isLoading: query.isLoading, + isError: query.isError, isFetchingNextPage: query.isFetchingNextPage, hasNextPage: query.hasNextPage, fetchNextPage: query.fetchNextPage,