feat: preload adjacent media in streams
This commit is contained in:
@@ -18,6 +18,7 @@ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbP
|
|||||||
import { WindowControls } from "@/components/window-controls";
|
import { WindowControls } from "@/components/window-controls";
|
||||||
import { formatDistanceToNow } from "@/lib/time-utils";
|
import { formatDistanceToNow } from "@/lib/time-utils";
|
||||||
import { useStreamPlayback } from "@/hooks/use-stream-playback";
|
import { useStreamPlayback } from "@/hooks/use-stream-playback";
|
||||||
|
import { usePrefetchAdjacentMedia } from "@/hooks/use-prefetch-adjacent-media";
|
||||||
|
|
||||||
function getParticleDisplayName(particle: Particle): string {
|
function getParticleDisplayName(particle: Particle): string {
|
||||||
switch (particle.type) {
|
switch (particle.type) {
|
||||||
@@ -113,6 +114,8 @@ export function StreamView({ path, streamParticle }: StreamViewProps) {
|
|||||||
resume,
|
resume,
|
||||||
} = useStreamPlayback(streamParticle, path);
|
} = useStreamPlayback(streamParticle, path);
|
||||||
|
|
||||||
|
usePrefetchAdjacentMedia(children, currentIndex);
|
||||||
|
|
||||||
const [composeActive, setComposeActive] = useState(false);
|
const [composeActive, setComposeActive] = useState(false);
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
|
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ export function useDownloadUrl(objectId?: string) {
|
|||||||
queryKey: ["download-url", objectId],
|
queryKey: ["download-url", objectId],
|
||||||
queryFn: () => apiClient.getParticleDownloadUrl(objectId!),
|
queryFn: () => apiClient.getParticleDownloadUrl(objectId!),
|
||||||
enabled: !!objectId,
|
enabled: !!objectId,
|
||||||
|
staleTime: 1000 * 60 * 60, // 1 hour — signed URLs valid for 24h
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { preload } from "react-dom";
|
||||||
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
import type { Particle } from "@/api/types";
|
||||||
|
import { apiClient } from "@/api/client";
|
||||||
|
|
||||||
|
// TODO: verify that caching is actually working by slowing down our network to simulate
|
||||||
|
/**
|
||||||
|
* Prefetches download URLs and warms the browser cache for adjacent media particles.
|
||||||
|
*/
|
||||||
|
export function usePrefetchAdjacentMedia(
|
||||||
|
children: Particle[],
|
||||||
|
currentIndex: number,
|
||||||
|
) {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const adjacentIndices = [currentIndex - 1, currentIndex + 1];
|
||||||
|
const mediaParticles = adjacentIndices
|
||||||
|
.filter((i) => i >= 0 && i < children.length)
|
||||||
|
.map((i) => children[i])
|
||||||
|
.filter(
|
||||||
|
(p): p is Extract<Particle, { type: "media" }> => p.type === "media",
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const particle of mediaParticles) {
|
||||||
|
const objectId = particle.properties.object_id;
|
||||||
|
const isAudio = particle.properties.mime_type?.startsWith("audio/");
|
||||||
|
|
||||||
|
queryClient
|
||||||
|
.prefetchQuery({
|
||||||
|
queryKey: ["download-url", objectId],
|
||||||
|
queryFn: () => apiClient.getParticleDownloadUrl(objectId),
|
||||||
|
staleTime: 1000 * 60 * 60,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
const url = queryClient.getQueryData<string>([
|
||||||
|
"download-url",
|
||||||
|
objectId,
|
||||||
|
]);
|
||||||
|
if (url) {
|
||||||
|
preload(url, { as: isAudio ? "audio" : "video" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [children, currentIndex, queryClient]);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user