import type { Express } from "express"; import { createServer, type Server } from "http"; import { storage } from "./storage"; import { z } from "zod"; import { BunnyService } from "./bunny"; export async function registerRoutes(app: Express): Promise { // Get videos with pagination and filtering app.get("/api/videos", async (req, res) => { try { const limit = parseInt(req.query.limit as string) || 20; const offset = parseInt(req.query.offset as string) || 0; const search = req.query.search as string; const category = req.query.category as string; const videos = await storage.getVideos(limit, offset, search, category); const total = await storage.getVideoCount(search, category); res.json({ videos, total, hasMore: offset + limit < total }); } catch (error) { res.status(500).json({ message: "Failed to fetch videos" }); } }); // Get single video by ID app.get("/api/videos/:id", async (req, res) => { try { const video = await storage.getVideo(req.params.id); if (!video) { return res.status(404).json({ message: "Video not found" }); } res.json(video); } catch (error) { res.status(500).json({ message: "Failed to fetch video" }); } }); // Update video views app.post("/api/videos/:id/view", async (req, res) => { try { await storage.updateVideoViews(req.params.id); res.json({ success: true }); } catch (error) { res.status(500).json({ message: "Failed to update views" }); } }); // Get video categories app.get("/api/categories", async (req, res) => { try { const videos = await storage.getVideos(1000); const categories = Array.from(new Set(videos.map(v => v.category).filter(Boolean))); res.json(categories); } catch (error) { console.error("Error fetching categories:", error); res.status(500).json({ message: "Failed to fetch categories" }); } }); // Serve video page with meta tags for social sharing app.get("/video/:id", async (req, res) => { try { const video = await storage.getVideo(req.params.id); if (!video) { return res.redirect("/"); } const shareUrl = `${req.protocol}://${req.get('host')}/video/${video.id}`; const html = ` ${video.title} - VideoStream
`; res.send(html); } catch (error) { console.error("Error serving video page:", error); res.redirect("/"); } }); // Generate real thumbnails from Bunny.net videos using FFmpeg app.get("/thumbnail/:videoId", async (req, res) => { try { const { videoId } = req.params; const { exec } = require('child_process'); const fs = require('fs'); const path = require('path'); // Get video URL from Bunny.net const video = await storage.getVideo(videoId); if (!video) { return res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&h=225`); } // Create thumbnails directory if it doesn't exist const thumbnailDir = path.join(process.cwd(), 'thumbnails'); if (!fs.existsSync(thumbnailDir)) { fs.mkdirSync(thumbnailDir, { recursive: true }); } const thumbnailPath = path.join(thumbnailDir, `${videoId}.jpg`); // Check if thumbnail already exists if (fs.existsSync(thumbnailPath)) { res.setHeader('Content-Type', 'image/jpeg'); res.setHeader('Cache-Control', 'public, max-age=86400'); return res.sendFile(thumbnailPath); } // Generate video URL for FFmpeg (use direct CDN URL without iframe) const directVideoUrl = `https://vz-7982dfc4-cc8.b-cdn.net/${videoId}/playlist.m3u8`; // Generate thumbnail using FFmpeg const ffmpegCommand = `ffmpeg -i "${directVideoUrl}" -ss 00:00:05 -vframes 1 -vf "scale=400:225" "${thumbnailPath}" -y`; exec(ffmpegCommand, (error: any, stdout: any, stderr: any) => { if (error) { console.error(`FFmpeg error: ${error.message}`); // Fallback to placeholder return res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&h=225`); } if (fs.existsSync(thumbnailPath)) { res.setHeader('Content-Type', 'image/jpeg'); res.setHeader('Cache-Control', 'public, max-age=86400'); res.sendFile(thumbnailPath); } else { // Fallback if thumbnail generation failed res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&h=225`); } }); } catch (error) { console.error("Error generating thumbnail:", error); res.redirect(`https://images.unsplash.com/photo-1536240478700-b869070f9279?ixlib=rb-4.0.3&auto=format&fit=crop&w=400&h=225`); } }); const httpServer = createServer(app); return httpServer; }