diff --git a/client/src/components/netflix-grid.tsx b/client/src/components/netflix-grid.tsx index ccafcd6..4cd3709 100644 --- a/client/src/components/netflix-grid.tsx +++ b/client/src/components/netflix-grid.tsx @@ -22,8 +22,10 @@ export default function NetflixGrid({ videos, isLoading }: NetflixGridProps) { const [, setLocation] = useLocation(); const handleVideoClick = (video: Video) => { - // Navigate to individual video page instead of modal - setLocation(`/video/${video.id}`); + // Generate short ID for cleaner URLs (first 8 chars without dashes) + const shortId = video.id.replace(/-/g, '').substring(0, 8); + // Navigate to individual video page with short ID + setLocation(`/video/${shortId}`); }; const handleCloseModal = () => { diff --git a/client/src/components/video-card.tsx b/client/src/components/video-card.tsx index 2f78847..7f47fb4 100644 --- a/client/src/components/video-card.tsx +++ b/client/src/components/video-card.tsx @@ -44,6 +44,8 @@ function formatDate(date: Date | string): string { } export default function VideoCard({ video, onClick, className = "", hideOverlay = false }: VideoCardProps) { + // Generate short ID for cleaner URLs (first 8 chars without dashes) + const shortId = video.id.replace(/-/g, '').substring(0, 8); const [isHovered, setIsHovered] = useState(false); const [showPreview, setShowPreview] = useState(false); const [isMuted, setIsMuted] = useState(true); @@ -172,7 +174,7 @@ export default function VideoCard({ video, onClick, className = "", hideOverlay return (
!isMobile && setIsHovered(true)} onMouseLeave={() => !isMobile && setIsHovered(false)} @@ -191,7 +193,7 @@ export default function VideoCard({ video, onClick, className = "", hideOverlay objectPosition: video.faceCenterPosition || 'center center', objectFit: 'cover' }} - data-testid={`img-thumbnail-${video.id}`} + data-testid={`img-thumbnail-${shortId}`} loading="lazy" decoding="async" onError={(e) => { diff --git a/server/routes.ts b/server/routes.ts index 3ffd89c..82333e5 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -352,10 +352,34 @@ export async function registerRoutes(app: Express): Promise { } }); - // Get single video by ID + // Generate short ID from long UUID + function generateShortId(longId: string): string { + // Take first 8 characters and remove dashes for shorter, cleaner URLs + return longId.replace(/-/g, '').substring(0, 8); + } + + // Find video by short or long ID + async function findVideoByAnyId(id: string) { + // If it's already a full UUID, use it directly + if (id.length === 36 && id.includes('-')) { + return await storage.getVideo(id); + } + + // If it's a short ID (8 chars), search for matching video + if (id.length === 8) { + const allVideos = await storage.getVideos({ limit: 200, offset: 0 }); + const video = allVideos.videos.find(v => generateShortId(v.id) === id); + return video; + } + + // Try as full ID anyway + return await storage.getVideo(id); + } + + // Get single video by ID (supports both short and long IDs) app.get("/api/videos/:id", async (req, res) => { try { - const video = await storage.getVideo(req.params.id); + const video = await findVideoByAnyId(req.params.id); if (!video) { return res.status(404).json({ message: "Video not found" }); } @@ -370,8 +394,8 @@ export async function registerRoutes(app: Express): Promise { try { const { id } = req.params; - // Check if video exists first - const video = await storage.getVideo(id); + // Check if video exists first (supports short and long IDs) + const video = await findVideoByAnyId(id); if (!video) { return res.status(404).json({ message: "Video not found" }); } @@ -381,7 +405,7 @@ export async function registerRoutes(app: Express): Promise { try { const { BunnyService } = await import("./bunny"); const bunnyService = new BunnyService(); - ads = await bunnyService.getVideoAds(id); + ads = await bunnyService.getVideoAds(video.id); // Use full ID for API calls console.log(`Retrieved ${ads.length} ad spots for video ${id}`); } catch (error) { console.error(`Failed to get ads from Bunny.net for video ${id}:`, error); @@ -400,10 +424,16 @@ export async function registerRoutes(app: Express): Promise { } }); - // Update video views + // Update video views (supports short and long IDs) app.post("/api/videos/:id/view", async (req, res) => { try { - await storage.updateVideoViews(req.params.id); + const video = await findVideoByAnyId(req.params.id); + if (!video) { + return res.status(404).json({ message: "Video not found" }); + } + + // Use the full video ID for storage operations + await storage.updateVideoViews(video.id); res.json({ success: true }); } catch (error) { res.status(500).json({ message: "Failed to update views" });