From 258383ce366abf99d86f798bb16e944a2db4dc4a Mon Sep 17 00:00:00 2001
From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com>
Date: Wed, 3 Sep 2025 11:03:27 +0000
Subject: [PATCH] Shorten video URLs for improved sharing and user experience
Refactors video routing and client-side components to generate and utilize shortened, 8-character IDs derived from the full UUIDs. This change enhances the usability of shared video links by removing dashes and truncating the UUID. The backend now supports fetching videos by either the short or full ID, ensuring backward compatibility. Additionally, ad retrieval and view count updates correctly use the full video ID to maintain data integrity with Bunny.net and the database.
Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 2cd2c0bc-434c-4bc9-ad3f-b99d3897a0d1
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/2cd2c0bc-434c-4bc9-ad3f-b99d3897a0d1/HCAS0JG
---
client/src/components/netflix-grid.tsx | 6 ++--
client/src/components/video-card.tsx | 6 ++--
server/routes.ts | 44 ++++++++++++++++++++++----
3 files changed, 45 insertions(+), 11 deletions(-)
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" });