UNPKG

peertube-plugin-transposer-connector

Version:

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

511 lines (447 loc) 17.4 kB
async function register({ registerHook, peertubeHelpers }) { // Store original tags mapping for search redirection let originalTagsMapping = {}; // Define urlOptions object at module level const urlOptions = {}; // Function to handle UI language change with lots of console logs function handleUILanguage() { console.log("=== STARTING UI LANGUAGE CHANGE PROCESS ==="); // Get the uilanguage parameter from URL const urlParams = new URLSearchParams(window.location.search); const uiLanguage = urlParams.get("uilanguage"); console.log("Checking for uilanguage parameter:", uiLanguage); // If no language parameter, exit early if (!uiLanguage) { console.log( "No UI language parameter found, exiting language change function" ); return; } console.log(`UI Language parameter found: "${uiLanguage}"`); // Language mapping table console.log("Preparing language mapping..."); const languageMapping = { ar: "ar", // Arabic ca: "ca-ES", // Catalan cs: "cs-CZ", // Czech de: "de-DE", // German en: "en-US", // English (US) eo: "eo", // Esperanto es: "es-ES", // Spanish eu: "eu", // Basque fa: "fa-IR", // Persian fi: "fi-FI", // Finnish fr: "fr-FR", // French gd: "gd", // Gaelic gl: "gl-ES", // Galician hu: "hu-HU", // Hungarian it: "it-IT", // Italian ja: "ja-JP", // Japanese kab: "kab", // Kabyle ko: "ko-KR", // Korean nl: "nl-NL", // Dutch oc: "oc", // Occitan pl: "pl-PL", // Polish pt: "pt-PT", // Portuguese "pt-br": "pt-BR", // Brazilian Portuguese ru: "ru-RU", // Russian sq: "sq", // Albanian sv: "sv-SE", // Swedish th: "th-TH", // Thai tr: "tr-TR", // Turkish uk: "uk-UA", // Ukrainian vi: "vi-VN", // Vietnamese zh: "zh-Hans-CN", // Simplified Chinese bg: "bg-BG", // Bulgarian el: "el-GR", // Greek he: "he-IL", // Hebrew ro: "ro-RO", // Romanian }; // Map the language code to PeerTube format const peertubeLanguage = languageMapping[uiLanguage.toLowerCase()] || uiLanguage; console.log( `Mapped standard language code "${uiLanguage}" to PeerTube language code "${peertubeLanguage}"` ); // Save current URL info for redirecting back later console.log("Storing current URL information for later redirect..."); const currentUrl = new URL(window.location.href); const currentFullUrl = currentUrl.toString(); console.log("Current full URL:", currentFullUrl); // Create the cleaned version of the URL (without uilanguage parameter) const newParams = new URLSearchParams(currentUrl.search); console.log( "Current URL parameters:", Object.fromEntries(newParams.entries()) ); newParams.delete("uilanguage"); console.log( "URL parameters after removing uilanguage:", Object.fromEntries(newParams.entries()) ); currentUrl.search = newParams.toString(); const cleanReturnUrl = currentUrl.toString(); console.log( "Clean return URL (without uilanguage parameter):", cleanReturnUrl ); // Store information in sessionStorage for redirecting back console.log("Saving redirect information to sessionStorage..."); sessionStorage.setItem("languageRedirectUrl", cleanReturnUrl); sessionStorage.setItem("changingLanguage", "true"); console.log("Session storage data saved:", { redirectUrl: sessionStorage.getItem("languageRedirectUrl"), changingLanguage: sessionStorage.getItem("changingLanguage"), }); try { // Get the proper base URL from window.location instead of hardcoding const baseUrl = `${window.location.protocol}//${window.location.host}`; console.log("Base URL:", baseUrl); // Construct the correct language change URL const languageUrl = `${baseUrl}/${peertubeLanguage}`; console.log("Constructed language URL:", languageUrl); console.log("About to navigate to language URL..."); window.location.href = languageUrl; console.log( "Navigation initiated. If you see this log, the page hasn't redirected yet." ); } catch (error) { console.error("ERROR in language change process:", error); console.error("Error details:", { message: error.message, stack: error.stack, name: error.name, }); } } // Check if we need to redirect back after language change function checkLanguageRedirect() { console.log("=== CHECKING IF LANGUAGE REDIRECT IS NEEDED ==="); const changingLanguage = sessionStorage.getItem("changingLanguage"); console.log("changingLanguage flag in sessionStorage:", changingLanguage); if (changingLanguage === "true") { console.log("Language change process detected!"); const redirectUrl = sessionStorage.getItem("languageRedirectUrl"); console.log("Redirect URL from sessionStorage:", redirectUrl); if (redirectUrl) { console.log("Valid redirect URL found, preparing to redirect back..."); // Clear the storage items sessionStorage.removeItem("changingLanguage"); sessionStorage.removeItem("languageRedirectUrl"); console.log("Session storage items cleared"); window.location.href = redirectUrl; } else { console.warn( "No redirect URL found in sessionStorage despite changingLanguage flag being set" ); } } else { console.log("No language change in progress, no redirect needed"); } } // Register hooks console.log("Registering PeerTube hooks..."); // Hook for application initialization registerHook({ target: "action:application.init", handler: (params) => { try { console.log("Trigger: action:application.init"); console.log("Params:", params); console.log("'urlOptions':", urlOptions); // Check if we need to redirect back after a language change checkLanguageRedirect(); // Handle UI language change (may redirect) handleUILanguage(); // Get the current URL const urlParams = new URLSearchParams(window.location.search); // Retrieve a specific parameter from the URL const lang = urlParams.get("lang"); if (lang == undefined) return params; console.log("Parameter 'lang':", lang); // Add subtitle urlOption urlOptions.subtitle = lang; console.log("'urlOptions':", urlOptions); return params; } catch (error) { console.error("Error in application init handler:", error); console.error("Error details:", { message: error.message, stack: error.stack, name: error.name, }); return params; } }, }); // Hook for player options registerHook({ target: "filter:internal.video-watch.player.build-options.params", handler: (params) => { try { console.log( "Trigger: filter:internal.video-watch.player.build-options.params" ); console.log("Params:", params); console.log("'urlOptions':", urlOptions); // Get the current URL const urlParams = new URLSearchParams(window.location.search); // Retrieve a specific parameter from the URL const lang = urlParams.get("lang"); if (lang == undefined) return params; console.log("Parameter 'lang':", lang); // Add subtitle urlOption urlOptions.subtitle = lang; console.log("'urlOptions':", urlOptions); return params; } catch (error) { console.error("Error in player options handler:", error); console.error("Error details:", { message: error.message, stack: error.stack, name: error.name, }); return params; } }, }); // Hook for video pages registerHook({ target: "action:video-watch.video.loaded", handler: async ({ video }) => { try { console.log("Video watch page loaded hook triggered"); // Check if we need to redirect back after a language change checkLanguageRedirect(); // Handle UI language change (may redirect) handleUILanguage(); // Get the language parameter from the URL const urlParams = new URLSearchParams(window.location.search); const metadataLanguage = urlParams.get("metadatalanguage"); // If no language parameter is provided, don't modify anything if (!metadataLanguage) { return; } // Store original tags for search redirection if (video && video.tags) { originalTagsMapping = {}; video.tags.forEach((tag) => { originalTagsMapping[tag.toLowerCase()] = tag; }); } // Fetch translated metadata for this video const response = await fetch( `${peertubeHelpers.getBaseRouterRoute()}/language/metadata/video/${ video.id }?languages=${metadataLanguage}`, { headers: peertubeHelpers.getAuthHeader(), } ); if (!response.ok) { console.error(`Failed to fetch metadata: ${response.status}`); return; } const metadata = await response.json(); console.log("Fetched translated metadata:", metadata); // Check if we have translations for this language if ( !metadata || ((!metadata.title || !metadata.title[metadataLanguage]) && (!metadata.description || !metadata.description[metadataLanguage]) && (!metadata.category || !metadata.category[metadataLanguage]) && (!metadata.tags || !metadata.tags[metadataLanguage])) ) { console.log( `No translated metadata found for language: ${metadataLanguage}` ); return; } // Update the DOM with translated content updateVideoMetadata(metadata, metadataLanguage, video.tags); } catch (error) { console.error("Error applying translated metadata:", error); } }, }); // Function to update the video metadata in the DOM function updateVideoMetadata(metadata, language, originalTags) { // Update title if available if (metadata.title && metadata.title[language]) { // Find and update video title elements const titleElements = document.querySelectorAll( ".video-info-name, .video-info-first-row .video-info-name, h1.video-info-name" ); titleElements.forEach((element) => { element.textContent = metadata.title[language]; }); // Update page title if (document.title) { // Keep the site name if it exists after a separator const siteName = document.title.includes(" - ") ? document.title.split(" - ").slice(1).join(" - ") : ""; document.title = siteName ? `${metadata.title[language]} - ${siteName}` : metadata.title[language]; } } // Update description if available if (metadata.description && metadata.description[language]) { const descriptionElements = document.querySelectorAll( ".video-info-description, .video-info-description-html" ); descriptionElements.forEach((element) => { // If it's a HTML description if (element.classList.contains("video-info-description-html")) { element.innerHTML = `<p>${metadata.description[language].replace( /\n/g, "<br>" )}</p>`; } else { element.textContent = metadata.description[language]; } }); } // Update category if available if (metadata.category && metadata.category[language]) { // First, try the Angular selector const angularCategoryElement = document.querySelector( ".attribute-category a.attribute-value" ); if (angularCategoryElement) { angularCategoryElement.textContent = metadata.category[language]; } else { // Fallback to other possible selectors const categoryElements = document.querySelectorAll( ".video-attributes-category, .attribute-category" ); categoryElements.forEach((element) => { const categoryValueElement = element.querySelector("a") || element.querySelector(".attribute-value") || element; if (categoryValueElement) { categoryValueElement.textContent = metadata.category[language]; } }); } } // Update tags if available if (metadata.tags && metadata.tags[language]) { // Try Angular selector const angularTagsContainer = document.querySelector(".attribute-tags"); if (angularTagsContainer) { // Map original tags to their positions for search reference const indexedOriginalTags = {}; if (originalTags && originalTags.length) { originalTags.forEach((tag, index) => { indexedOriginalTags[tag.toLowerCase()] = index; }); } // Remove all existing tag links but keep the label const tagLabel = angularTagsContainer.querySelector(".attribute-label"); angularTagsContainer.innerHTML = ""; if (tagLabel) { angularTagsContainer.appendChild(tagLabel); } // Create new tags const translatedTags = metadata.tags[language] .split(",") .map((tag) => tag.trim()) .filter((tag) => tag); // Create a mapping between translated and original tags based on position // This assumes the order is the same in both arrays const translatedToOriginalMap = {}; translatedTags.forEach((translatedTag, index) => { // Try to get the original tag at the same position if (originalTags && index < originalTags.length) { translatedToOriginalMap[translatedTag.toLowerCase()] = originalTags[index]; } }); translatedTags.forEach((tag, index) => { const tagElement = document.createElement("a"); tagElement.classList.add("attribute-value"); tagElement.textContent = tag; // Use original tag for search if available const originalTag = translatedToOriginalMap[tag.toLowerCase()] || originalTagsMapping[tag.toLowerCase()] || (originalTags && originalTags[index]) || tag; tagElement.href = `/search?tagsOneOf=${encodeURIComponent( originalTag )}`; tagElement.setAttribute("data-translated-tag", tag); tagElement.setAttribute("data-original-tag", originalTag); angularTagsContainer.appendChild(tagElement); }); } else { // Fallback to standard selector const tagsContainers = document.querySelectorAll(".video-info-tags"); tagsContainers.forEach((container) => { // Clear existing tags container.innerHTML = ""; // Create new tags const translatedTags = metadata.tags[language] .split(",") .map((tag) => tag.trim()) .filter((tag) => tag); translatedTags.forEach((tag, index) => { const tagElement = document.createElement("a"); tagElement.classList.add("video-info-tag"); tagElement.textContent = tag; // Use original tag for search if available const originalTag = (originalTags && originalTags[index]) || tag; tagElement.href = `/search?tagsOneOf=${encodeURIComponent( originalTag )}`; container.appendChild(tagElement); }); }); } } // Add a notice about the translation const videoInfoContainer = document.querySelector( ".video-info-description" ); if (videoInfoContainer) { const translationNotice = document.createElement("div"); translationNotice.classList.add("translation-notice"); translationNotice.innerHTML = ` <p class="translation-notice-text"> This content is being displayed in: <strong>${language}</strong> <a href="${window.location.pathname}" class="reset-language">View original</a> </p> `; // Add styles for the notice const noticeStyle = document.createElement("style"); noticeStyle.textContent = ` .translation-notice { margin-top: 15px; padding: 8px 12px; background-color: var(--mainColor); color: var(--mainBackgroundColor); border-radius: 4px; font-size: 14px; } .translation-notice-text { margin: 0; } .reset-language { margin-left: 10px; color: var(--mainBackgroundColor); text-decoration: underline; } `; document.head.appendChild(noticeStyle); videoInfoContainer.parentNode.insertBefore( translationNotice, videoInfoContainer.nextSibling ); } } } export { register };