Improve social media sharing with better video linking
Refactor server logic to support finding videos by short or long IDs and update meta tags for social sharing previews. 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/gueMD9C
This commit is contained in:
parent
1e50f5ba67
commit
abf99afeb0
@ -1,6 +1,6 @@
|
||||
import express, { type Request, Response, NextFunction } from "express";
|
||||
import compression from "compression";
|
||||
import { registerRoutes } from "./routes";
|
||||
import { registerRoutes, findVideoByAnyId } from "./routes";
|
||||
import { videoSyncService } from "./videoSync";
|
||||
import { setupVite, serveStatic, log } from "./vite";
|
||||
import { storage } from "./storage";
|
||||
@ -91,21 +91,16 @@ app.use((req, res, next) => {
|
||||
|
||||
if (isSocialBot) {
|
||||
try {
|
||||
// Support both short and long video IDs for social media
|
||||
let video;
|
||||
|
||||
// If it's a short ID (8 chars), find video by short ID
|
||||
if (videoId.length === 8) {
|
||||
const allVideosResponse = await storage.getVideos({ limit: 200, offset: 0 });
|
||||
video = allVideosResponse.videos.find((v: any) => v.id.replace(/-/g, '').substring(0, 8) === videoId);
|
||||
} else {
|
||||
// Try as full ID
|
||||
video = await storage.getVideo(videoId);
|
||||
}
|
||||
console.log(`🤖 Social bot detected: ${userAgent}, looking for video: ${videoId}`);
|
||||
// Use the same findVideoByAnyId function as in routes
|
||||
const video = await findVideoByAnyId(videoId);
|
||||
|
||||
if (!video) {
|
||||
console.log(`❌ Video not found for social bot: ${videoId}`);
|
||||
return next(); // Če video ne obstaja, preusmerimo na običajno SPA
|
||||
}
|
||||
|
||||
console.log(`✅ Video found for social bot: ${video.title}`);
|
||||
|
||||
// Preberemo osnovni HTML template
|
||||
const clientTemplate = path.resolve(import.meta.dirname, "..", "client", "index.html");
|
||||
@ -116,8 +111,10 @@ app.use((req, res, next) => {
|
||||
|
||||
// Zamenimo meta oznake z video specifičnimi
|
||||
const baseUrl = req.protocol + '://' + req.get('host');
|
||||
const videoUrl = `${baseUrl}/video/${video.id}`;
|
||||
const thumbnailUrl = `${baseUrl}/api/video-thumbnail/${video.id}`;
|
||||
// Use short ID for sharing URLs
|
||||
const shortId = video.id.replace(/-/g, '').substring(0, 8);
|
||||
const videoUrl = `${baseUrl}/video/${shortId}`;
|
||||
const thumbnailUrl = video.thumbnailUrl || `${baseUrl}/api/video-thumbnail/${video.id}`;
|
||||
|
||||
// Zamenjamo osnovne meta oznake
|
||||
template = template.replace(
|
||||
@ -141,15 +138,17 @@ app.use((req, res, next) => {
|
||||
`<meta property="og:description" content="${escapeHtml(video.description || `Watch ${video.title} on go4.video`)}"`
|
||||
);
|
||||
|
||||
// Replace og:image - handle both with and without id attribute
|
||||
template = template.replace(
|
||||
/<meta property="og:image" content="[^"]*"/,
|
||||
`<meta property="og:image" content="${thumbnailUrl}"`
|
||||
/<meta property="og:image"[^>]*>/,
|
||||
`<meta property="og:image" id="og-image" content="${thumbnailUrl}"`
|
||||
);
|
||||
|
||||
// Zamenjamo tudi Twitter image
|
||||
// Replace Twitter image - handle both with and without id attribute
|
||||
template = template.replace(
|
||||
/<meta name="twitter:image" content="[^"]*"/,
|
||||
`<meta name="twitter:image" content="${thumbnailUrl}"`
|
||||
/<meta name="twitter:image"[^>]*>/,
|
||||
`<meta name="twitter:image" id="twitter-image" content="${thumbnailUrl}"`
|
||||
);
|
||||
|
||||
template = template.replace(
|
||||
@ -162,13 +161,22 @@ app.use((req, res, next) => {
|
||||
`<meta name="twitter:description" content="${escapeHtml(video.description || `Watch ${video.title} on go4.video`)}"`
|
||||
);
|
||||
|
||||
// Also update URL and secure image tags that have id attributes
|
||||
template = template.replace(
|
||||
/<meta property="og:url"[^>]*>/,
|
||||
`<meta property="og:url" id="og-url" content="${videoUrl}">`
|
||||
);
|
||||
|
||||
template = template.replace(
|
||||
/<meta property="og:image:secure_url"[^>]*>/,
|
||||
`<meta property="og:image:secure_url" id="og-image-secure" content="${thumbnailUrl}">`
|
||||
);
|
||||
|
||||
// Dodamo dodatne OG oznake za video z detajlnimi informacijami
|
||||
const duration = Math.floor(video.duration / 60) + ':' + (video.duration % 60).toString().padStart(2, '0');
|
||||
const additionalMeta = `
|
||||
<meta property="og:url" content="${videoUrl}">
|
||||
<meta property="og:type" content="video.other">
|
||||
<meta property="og:video:duration" content="${video.duration}">
|
||||
<meta property="og:site_name" content="go4.video">
|
||||
<meta property="video:duration" content="${video.duration}">
|
||||
<meta property="video:release_date" content="${video.createdAt?.toISOString()}">
|
||||
|
||||
|
||||
@ -20,6 +20,27 @@ import { setupAuth, isAuthenticated, isAdmin } from "./replitAuth";
|
||||
import { ObjectStorageService, ObjectNotFoundError } from "./objectStorage";
|
||||
import { generateVideoDescription, generateBulkDescriptions } from "./aiService";
|
||||
|
||||
// Find video by short or long ID - moved to top level for export
|
||||
export async function findVideoByAnyId(id: string) {
|
||||
try {
|
||||
// If it's already a full UUID, use it directly
|
||||
if (id.length === 36 && id.includes('-')) {
|
||||
return await storage.getVideo(id);
|
||||
}
|
||||
|
||||
// If it's an 8-character short ID, find by short ID
|
||||
if (id.length === 8) {
|
||||
const allVideos = await storage.getVideos(200, 0);
|
||||
return allVideos.find(v => v.id.replace(/-/g, '').substring(0, 8) === id);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
console.error(`Error finding video by ID ${id}:`, error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// Extend express session
|
||||
declare module "express-session" {
|
||||
interface SessionData {
|
||||
@ -358,34 +379,6 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
return longId.replace(/-/g, '').substring(0, 8);
|
||||
}
|
||||
|
||||
// Find video by short or long ID
|
||||
async function findVideoByAnyId(id: string) {
|
||||
try {
|
||||
// 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) {
|
||||
// HybridStorage calls BunnyStorage.getVideos() which returns Video[]
|
||||
const allVideosArray = await storage.getVideos(200, 0);
|
||||
|
||||
// This should be Video[] directly from BunnyStorage
|
||||
if (Array.isArray(allVideosArray)) {
|
||||
const video = allVideosArray.find(v => generateShortId(v.id) === id);
|
||||
return video || null;
|
||||
}
|
||||
return video || null;
|
||||
}
|
||||
|
||||
// Try as full ID anyway
|
||||
return await storage.getVideo(id);
|
||||
} catch (error) {
|
||||
console.error('Error in findVideoByAnyId:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get single video by ID (supports both short and long IDs)
|
||||
app.get("/api/videos/:id", async (req, res) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user