Display Storage Files
Display and interact with stored files in your browser application. Since the browser client cannot directly query data (no API key), you’ll need your backend to provide the data.
Backend API for File Data
Section titled “Backend API for File Data”Create an API endpoint to fetch file data:
// pages/api/videos.js (Next.js example)import { createClient } from "zapdos-js";
export default async function handler(req, res) { const client = createClient({ apiKey: process.env.ZAPDOS_API_KEY, });
// Get all videos const videos = await client.videos().fetch();
if (videos.error) { return res.status(500).json(videos); }
// Return simplified data for frontend const videoData = videos.data.map((video) => ({ id: video.id, name: video.metadata.file_name, size: video.metadata.size, contentType: video.metadata.content_type, createdAt: video.created_at, scenes: video.content?.scenes?.length || 0, transcription: !!video.content?.transcription, }));
res.json({ data: videoData });}
Frontend File Display
Section titled “Frontend File Display”Fetch and display files in your React/Vue/vanilla JS app:
// Fetch videos from your backendasync function fetchVideos() { const response = await fetch("/api/videos"); const result = await response.json(); return result.data;}
// Display videosasync function displayVideos() { const videos = await fetchVideos();
const videoList = document.querySelector("#video-list"); videoList.innerHTML = videos .map( (video) => ` <div class="video-item"> <h3>${video.name}</h3> <p>Size: ${formatFileSize(video.size)}</p> <p>Scenes: ${video.scenes}</p> <p>Has transcription: ${video.transcription ? "Yes" : "No"}</p> <button onclick="downloadVideo('${video.id}')">Download</button> </div> ` ) .join("");}
function formatFileSize(bytes) { const sizes = ["Bytes", "KB", "MB", "GB"]; if (bytes === 0) return "0 Bytes"; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];}
Search Interface
Section titled “Search Interface”Create a search interface for your videos:
// Backend search endpointexport default async function handler(req, res) { const client = createClient({ apiKey: process.env.ZAPDOS_API_KEY, });
const { query, limit } = req.body; const results = await client.search(query, { limit });
res.json(results);}
// Frontend search componentasync function searchVideos(query) { const response = await fetch("/api/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, limit: 10 }), });
const results = await response.json();
if (results.data) { displaySearchResults(results.data.items); }}
function displaySearchResults(items) { const resultsDiv = document.querySelector("#search-results"); resultsDiv.innerHTML = items .map( (item) => ` <div class="search-result"> <h4>${item.metadata.object_original_name}</h4> <p>Score: ${(item.score * 100).toFixed(1)}%</p> <p>Time: ${formatTime(item.metadata.start_ms)} - ${formatTime( item.metadata.end_ms )}</p> <button onclick="playClip('${item.metadata.object_id}', ${ item.metadata.start_ms }, ${item.metadata.end_ms})"> Play Clip </button> </div> ` ) .join("");}
function formatTime(milliseconds) { const seconds = Math.floor(milliseconds / 1000); const minutes = Math.floor(seconds / 60); const remainingSeconds = seconds % 60; return `${minutes}:${remainingSeconds.toString().padStart(2, "0")}`;}
React Example
Section titled “React Example”Complete React component for file management:
import { useState, useEffect } from "react";
function VideoManager() { const [videos, setVideos] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState([]); const [loading, setLoading] = useState(false);
useEffect(() => { fetchVideos(); }, []);
const fetchVideos = async () => { const response = await fetch("/api/videos"); const result = await response.json(); setVideos(result.data); };
const handleSearch = async (e) => { e.preventDefault(); setLoading(true);
try { const response = await fetch("/api/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: searchQuery, limit: 10 }), });
const results = await response.json(); setSearchResults(results.data?.items || []); } catch (error) { console.error("Search failed:", error); } finally { setLoading(false); } };
const downloadVideo = async (videoId) => { // Call backend endpoint to get download URL const response = await fetch(`/api/download/${videoId}`); const result = await response.json();
if (result.data?.url) { window.open(result.data.url, "_blank"); } };
return ( <div> <div> <h2>Search Videos</h2> <form onSubmit={handleSearch}> <input type="text" value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} placeholder="Search for moments in videos..." /> <button type="submit" disabled={loading}> {loading ? "Searching..." : "Search"} </button> </form>
{searchResults.length > 0 && ( <div> <h3>Search Results</h3> {searchResults.map((result) => ( <div key={result.id} className="search-result"> <h4>{result.metadata.object_original_name}</h4> <p>Confidence: {(result.score * 100).toFixed(1)}%</p> <p> Time: {Math.floor(result.metadata.start_ms / 1000)}s - {Math.floor(result.metadata.end_ms / 1000)}s </p> </div> ))} </div> )} </div>
<div> <h2>All Videos</h2> {videos.map((video) => ( <div key={video.id} className="video-item"> <h3>{video.name}</h3> <p>Size: {formatFileSize(video.size)}</p> <p>Scenes: {video.scenes}</p> <button onClick={() => downloadVideo(video.id)}>Download</button> </div> ))} </div> </div> );}
function formatFileSize(bytes) { const sizes = ["Bytes", "KB", "MB", "GB"]; if (bytes === 0) return "0 Bytes"; const i = Math.floor(Math.log(bytes) / Math.log(1024)); return Math.round((bytes / Math.pow(1024, i)) * 100) / 100 + " " + sizes[i];}
Best Practices
Section titled “Best Practices”- Proxy API calls - Never expose your API key to browsers
- Implement pagination - For large video libraries
- Cache results - Store frequently accessed data
- Optimize thumbnails - Use scene images for video previews
- Handle errors - Gracefully handle API failures