UNPKG

peertube-plugin-transposer-connector

Version:

Transposer connector is a PeerTube language tool plugin to transcribe and translate with Whisper

551 lines (478 loc) 18.3 kB
const initLanguageMetadata = (router, languageMetadataDAO, peertubeHelpers) => { // Helper function to get video details and extract numeric ID const getVideoNumericId = async (idParam) => { // If the ID is already numeric, return it directly if (!isNaN(idParam) && Number.isInteger(Number(idParam))) { return Number(idParam); } try { // Make a request to the videos API to get the full video object const webserverUrl = peertubeHelpers.config.getWebserverUrl(); const response = await fetch(`${webserverUrl}/api/v1/videos/${idParam}`); if (!response.ok) { throw new Error(`Video API returned status: ${response.status}`); } const data = await response.json(); // Extract the numeric ID from the response if (data && data.id) { return data.id; } throw new Error("Video ID not found in response"); } catch (error) { console.error("Error fetching video details:", error.message); throw new Error("Failed to resolve video ID"); } }; // Retrieve Specific Languages / All Metadata: router.get("/language/metadata/video/:id", async (req, res) => { try { console.log("started /language/metadata/video/:id"); const idParam = req.params.id; console.log("ID parameter:", idParam); // Get the numeric ID let numericVideoId; try { numericVideoId = await getVideoNumericId(idParam); console.log("Resolved to numeric ID:", numericVideoId); } catch (error) { return res.status(404).json({ error: "Video not found" }); } const languages = req.query.languages ? req.query.languages.split(",") : []; console.log("languages:", languages); const attributes = req.query.attribute ? req.query.attribute.split(",") : []; console.log("attributes:", attributes); const completeMetadata = await languageMetadataDAO.findLanguageMetadataByIdAndLanguages( numericVideoId, languages ); if (!completeMetadata) { return res .status(404) .json({ error: "Not Found: Resource not available" }); } const transformedObject = {}; completeMetadata.forEach((item) => { if (languages.length === 0 || languages.includes(item.language)) { if (attributes.length === 0 || attributes.includes(item.attribute)) { if (!transformedObject[item.attribute]) { transformedObject[item.attribute] = {}; } transformedObject[item.attribute][item.language] = item.value; } } }); return res.json(transformedObject); } catch (error) { console.error("Error processing request:", error); return res.status(500).json({ error: "Internal server error" }); } }); // Retrieve All Languages / Specific Metadata: router.get("/language/metadata/video/:id/:attribute", async (req, res) => { try { console.log("started /language/metadata/video/:id/:attribute"); const idParam = req.params.id; const attribute = req.params.attribute; // Get the numeric ID let numericVideoId; try { numericVideoId = await getVideoNumericId(idParam); console.log("Resolved to numeric ID:", numericVideoId); } catch (error) { return res.status(404).json({ error: "Video not found" }); } const specificMetadata = await languageMetadataDAO.findSpecificLanguageMetadata( numericVideoId, attribute ); if (!specificMetadata) { return res .status(404) .json({ error: "Not Found: Resource not available" }); } const transformedObject = {}; specificMetadata.forEach((item) => { transformedObject[item.language] = item.value; }); return res.json(transformedObject); } catch (error) { console.error("Error processing request:", error); return res.status(500).json({ error: "Internal server error" }); } }); router.get("/video/:id/complete", async (req, res) => { try { console.log("started /video/:id/complete"); const idParam = req.params.id; console.log("ID parameter:", idParam); // Get the numeric ID let numericVideoId; try { numericVideoId = await getVideoNumericId(idParam); console.log("Resolved to numeric ID:", numericVideoId); } catch (error) { return res.status(404).json({ error: "Video not found" }); } // Get query parameters for filtering const languages = req.query.languages ? req.query.languages.split(",") : []; console.log("languages filter:", languages); const attributes = req.query.attribute ? req.query.attribute.split(",") : []; console.log("attributes filter:", attributes); // Fetch metadata let metadata = {}; try { const completeMetadata = await languageMetadataDAO.findLanguageMetadataByIdAndLanguages( numericVideoId, languages ); if (completeMetadata && completeMetadata.length > 0) { const transformedMetadata = {}; completeMetadata.forEach((item) => { if (languages.length === 0 || languages.includes(item.language)) { if ( attributes.length === 0 || attributes.includes(item.attribute) ) { if (!transformedMetadata[item.attribute]) { transformedMetadata[item.attribute] = {}; } transformedMetadata[item.attribute][item.language] = item.value; } } }); metadata = transformedMetadata; } } catch (error) { console.error("Error fetching metadata:", error); metadata = {}; } // Fetch captions let captions = []; try { const webserverUrl = peertubeHelpers.config.getWebserverUrl(); const captionsResponse = await fetch( `${webserverUrl}/api/v1/videos/${numericVideoId}/captions` ); if (captionsResponse.ok) { const captionsData = await captionsResponse.json(); captions = captionsData.data || []; // Filter captions by language if specified if (languages.length > 0) { captions = captions.filter((caption) => languages.includes(caption.language.id) ); } } else { console.warn(`Failed to fetch captions: ${captionsResponse.status}`); } } catch (error) { console.error("Error fetching captions:", error); // Continue with empty captions rather than failing completely captions = []; } // Combine the data const combinedData = { videoId: numericVideoId, metadata: metadata, captions: { total: captions.length, data: captions, }, availableLanguages: { metadata: metadata ? Object.keys(metadata).reduce((acc, attr) => { Object.keys(metadata[attr]).forEach((lang) => { if (!acc.includes(lang)) acc.push(lang); }); return acc; }, []) : [], captions: captions.map((caption) => caption.language.id), }, }; return res.json(combinedData); } catch (error) { console.error("Error processing combined video data request:", error); return res.status(500).json({ error: "Internal server error" }); } }); // NEW: Get combined video data with caption content router.get("/video/:id/complete-with-content", async (req, res) => { try { console.log("started /video/:id/complete-with-content"); const idParam = req.params.id; console.log("ID parameter:", idParam); // Get the numeric ID let numericVideoId; try { numericVideoId = await getVideoNumericId(idParam); console.log("Resolved to numeric ID:", numericVideoId); } catch (error) { return res.status(404).json({ error: "Video not found" }); } // Get query parameters for filtering const languages = req.query.languages ? req.query.languages.split(",") : []; console.log("languages filter:", languages); const attributes = req.query.attribute ? req.query.attribute.split(",") : []; console.log("attributes filter:", attributes); // Fetch metadata (same as above) let metadata = {}; try { const completeMetadata = await languageMetadataDAO.findLanguageMetadataByIdAndLanguages( numericVideoId, languages ); if (completeMetadata && completeMetadata.length > 0) { const transformedMetadata = {}; completeMetadata.forEach((item) => { if (languages.length === 0 || languages.includes(item.language)) { if ( attributes.length === 0 || attributes.includes(item.attribute) ) { if (!transformedMetadata[item.attribute]) { transformedMetadata[item.attribute] = {}; } transformedMetadata[item.attribute][item.language] = item.value; } } }); metadata = transformedMetadata; } } catch (error) { console.error("Error fetching metadata:", error); metadata = {}; } // Fetch captions with content let captions = []; try { const webserverUrl = peertubeHelpers.config.getWebserverUrl(); const captionsResponse = await fetch( `${webserverUrl}/api/v1/videos/${numericVideoId}/captions` ); if (captionsResponse.ok) { const captionsData = await captionsResponse.json(); let captionsList = captionsData.data || []; // Filter captions by language if specified if (languages.length > 0) { captionsList = captionsList.filter((caption) => languages.includes(caption.language.id) ); } // Fetch content for each caption for (const caption of captionsList) { try { const captionContentResponse = await fetch( `${webserverUrl}${caption.captionPath}` ); if (captionContentResponse.ok) { caption.content = await captionContentResponse.text(); } else { caption.content = null; caption.contentError = `Failed to fetch content: ${captionContentResponse.status}`; } } catch (error) { caption.content = null; caption.contentError = `Error fetching content: ${error.message}`; } } captions = captionsList; } else { console.warn(`Failed to fetch captions: ${captionsResponse.status}`); } } catch (error) { console.error("Error fetching captions:", error); captions = []; } // Combine the data const combinedData = { videoId: numericVideoId, metadata: metadata, captions: { total: captions.length, data: captions, }, availableLanguages: { metadata: metadata ? Object.keys(metadata).reduce((acc, attr) => { Object.keys(metadata[attr]).forEach((lang) => { if (!acc.includes(lang)) acc.push(lang); }); return acc; }, []) : [], captions: captions.map((caption) => caption.language.id), }, }; return res.json(combinedData); } catch (error) { console.error( "Error processing combined video data with content request:", error ); return res.status(500).json({ error: "Internal server error" }); } }); // Send all language metadata at once to the db router.post("/language/metadata/video/:id/metadata", async (req, res) => { try { console.log("Starting metadata POST endpoint processing"); console.log("res"); console.log(res); let authUser; try { console.log("Attempting to get authenticated user"); authUser = await peertubeHelpers.user.getAuthUser(res); console.log( "Auth user result:", authUser ? `Found: ${authUser.username}` : "No user found" ); if (!authUser) { console.log("Authentication failed: No user found"); return res.status(401).json({ error: "Authentication required" }); } console.log("Authenticated user ID:", authUser.id); console.log("Authenticated user username:", authUser.username); console.log("Authenticated user role:", authUser.role); console.log( "Authenticated user account:", authUser.Account ? authUser.Account.id : "No account info" ); } catch (error) { console.error("Authentication error details:", error); console.error("Error stack:", error.stack); return res.status(401).json({ error: "Authentication failed" }); } const idParam = req.params.id; console.log("Video ID from params:", idParam); const metadata = req.body; console.log("Metadata validation starting"); if (!metadata || typeof metadata !== "object") { console.log("Invalid metadata format:", metadata); return res.status(400).json({ error: "Invalid metadata format" }); } console.log("Metadata validation passed"); let numericVideoId; try { console.log("Resolving video ID to numeric ID"); numericVideoId = await getVideoNumericId(idParam); console.log("Resolved to numeric ID:", numericVideoId); } catch (error) { console.error("Error resolving video ID:", error); return res.status(404).json({ error: "Video not found" }); } let video; try { console.log("Loading video with ID:", numericVideoId); console.log( "Video loader function:", typeof peertubeHelpers.videos.loadByIdOrUUID ); video = await peertubeHelpers.videos.loadByIdOrUUID(numericVideoId); console.log("Video load result:", video ? "Success" : "Not found"); if (!video) { console.log("Video not found with ID:", numericVideoId); return res.status(404).json({ error: "Video not found" }); } console.log("Video object keys:", Object.keys(video)); console.log("Video name:", video.name); console.log( "Video channel:", video.VideoChannel ? video.VideoChannel.name : "No channel info" ); if (video.VideoChannel) { console.log( "Video channel account:", video.VideoChannel.Account ? video.VideoChannel.Account.id : "No account info" ); } } catch (error) { console.error("Error loading video details:", error); console.error("Error stack:", error.stack); return res.status(500).json({ error: "Failed to load video details" }); } console.log("Checking video ownership"); console.log("Video channel account ID:", video.VideoChannel?.Account?.id); console.log("Auth user account ID:", authUser.Account?.id); const isVideoOwner = video.VideoChannel?.Account?.id === authUser.Account?.id; const isAdmin = authUser.role === 0; console.log("Is video owner?", isVideoOwner); console.log("Is admin?", isAdmin); if (!isVideoOwner && !isAdmin) { console.log("Permission denied: User is not video owner or admin"); return res.status(403).json({ error: "You don't have permission to modify this video's metadata", }); } console.log("Permission granted, processing metadata"); console.log("Languages in metadata:", Object.keys(metadata)); Object.keys(metadata).forEach((langCode) => { console.log(`Processing language: ${langCode}`); if (typeof metadata[langCode] !== "object") { console.log( `Invalid format for language '${langCode}'`, metadata[langCode] ); return res.status(400).json({ error: `Invalid format for language '${langCode}'`, }); } console.log( `Fields for language ${langCode}:`, Object.keys(metadata[langCode]) ); Object.keys(metadata[langCode]).forEach((field) => { console.log(`Processing field '${field}' for language '${langCode}'`); console.log(`Original value:`, metadata[langCode][field]); if ( metadata[langCode][field] === null || metadata[langCode][field] === undefined ) { console.log( `Empty value found for '${field}', setting to empty string` ); metadata[langCode][field] = ""; } metadata[langCode][field] = String(metadata[langCode][field]).trim(); console.log(`Final value:`, metadata[langCode][field]); }); }); console.log("Saving metadata to database"); const metadataId = await languageMetadataDAO.addLanguagedMetadata( numericVideoId, metadata ); console.log("Metadata saved successfully with ID:", metadataId); console.log( `User ${authUser.username} updated metadata for video ${numericVideoId} (original ID: ${idParam})` ); return res.json({ metadataId }); } catch (error) { console.error("Error in metadata endpoint:", error); console.error("Error stack:", error.stack); return res.status(500).json({ error: "Internal server error" }); } }); }; module.exports = { initLanguageMetadata, };