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
JavaScript
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 };