diff --git a/src/titles/titleRenderer.ts b/src/titles/titleRenderer.ts index fdbebe9..44d058d 100644 --- a/src/titles/titleRenderer.ts +++ b/src/titles/titleRenderer.ts @@ -1,12 +1,12 @@ import { VideoID, getVideoID } from "../../maze-utils/src/video"; -import Config, { TitleFormatting } from "../config/config"; +import Config from "../config/config"; import { getVideoTitleIncludingUnsubmitted } from "../dataFetching"; import { logError } from "../utils/logger"; import { MobileFix, addNodeToListenFor, getOrCreateTitleButtonContainer } from "../utils/titleBar"; import { BrandingLocation, ShowCustomBrandingInfo, extractVideoIDFromElement, getActualShowCustomBranding, toggleShowCustom } from "../videoBranding/videoBranding"; -import { cleanEmojis, formatTitle } from "./titleFormatter"; +import { formatTitle } from "./titleFormatter"; import { setCurrentVideoTitle } from "./pageTitleHandler"; -import { getTitleFormatting, shouldCleanEmojis, shouldDefaultToCustom, shouldReplaceTitles, shouldReplaceTitlesFastCheck, shouldUseCrowdsourcedTitles } from "../config/channelOverrides"; +import { shouldDefaultToCustom, shouldReplaceTitles, shouldReplaceTitlesFastCheck, shouldUseCrowdsourcedTitles } from "../config/channelOverrides"; import { countTitleReplacement } from "../config/stats"; import { onMobile } from "../../maze-utils/src/pageInfo"; import { isFirefoxOrSafari, waitFor } from "../../maze-utils/src"; @@ -28,7 +28,7 @@ export async function replaceTitle(element: HTMLElement, videoID: VideoID, showC showOriginalTitle(element, brandingLocation); return false; } - + if (brandingLocation === BrandingLocation.Watch) { const currentWatchPageType = document.URL.includes("watch") ? WatchPageType.Video : WatchPageType.Miniplayer; @@ -72,25 +72,20 @@ export async function replaceTitle(element: HTMLElement, videoID: VideoID, showC if (!await isOnCorrectVideo(element, brandingLocation, videoID)) return false; const title = titleData?.title; - const originalTitle = originalTitleElement?.textContent?.trim?.() ?? ""; - if (title && await shouldUseCrowdsourcedTitles(videoID) - // If there are just formatting changes, and the user doesn't want those, don't replace - && (await getTitleFormatting(videoID) !== TitleFormatting.Disable || originalTitle.toLowerCase() !== title.toLowerCase()) - && (await getTitleFormatting(videoID) !== TitleFormatting.Disable - || await shouldCleanEmojis(videoID) || cleanEmojis(originalTitle.toLowerCase()) !== cleanEmojis(title.toLowerCase()))) { + if (title && await shouldUseCrowdsourcedTitles(videoID)) { const formattedTitle = await formatTitle(title, true, videoID); if (!await isOnCorrectVideo(element, brandingLocation, videoID)) return false; - if (originalTitleElement?.textContent + if (originalTitleElement?.textContent && originalTitleElement.textContent.trim() === formattedTitle) { showOriginalTitle(element, brandingLocation); return false; } - + if (onMobile()) { hideOriginalTitle(element, brandingLocation); } - + setCustomTitle(formattedTitle, element, brandingLocation); countTitleReplacement(videoID); } else { @@ -163,7 +158,7 @@ function hideOriginalTitle(element: HTMLElement, brandingLocation: BrandingLocat function showOriginalTitle(element: HTMLElement, brandingLocation: BrandingLocation) { const originalTitleElement = getOriginalTitleElement(element, brandingLocation); const titleElement = getOrCreateTitleElement(element, brandingLocation, originalTitleElement); - + titleElement.style.display = "none"; if (originalTitleElement.classList.contains("ta-title-container")) { // Compatibility with Tube Archivist @@ -276,7 +271,7 @@ function getTitleSelector(brandingLocation: BrandingLocation): string[] { switch (brandingLocation) { case BrandingLocation.Watch: return [ - "yt-formatted-string", + "yt-formatted-string", ".ytp-title-link.yt-uix-sessionlink", ".yt-core-attributed-string" ]; @@ -320,15 +315,15 @@ function getTitleSelector(brandingLocation: BrandingLocation): string[] { } export function getOrCreateTitleElement(element: HTMLElement, brandingLocation: BrandingLocation, originalTitleElement?: HTMLElement): HTMLElement { - return element.querySelector(".cbCustomTitle") as HTMLElement ?? + return element.querySelector(".cbCustomTitle") as HTMLElement ?? createTitleElement(element, originalTitleElement ?? getOriginalTitleElement(element, brandingLocation), brandingLocation); } function createTitleElement(element: HTMLElement, originalTitleElement: HTMLElement, brandingLocation: BrandingLocation): HTMLElement { - const titleElement = brandingLocation !== BrandingLocation.Watch + const titleElement = brandingLocation !== BrandingLocation.Watch || originalTitleElement.classList.contains("miniplayer-title") || originalTitleElement.classList.contains("ytp-title-link") - ? originalTitleElement.cloneNode() as HTMLElement + ? originalTitleElement.cloneNode() as HTMLElement : document.createElement("span"); if (brandingLocation === BrandingLocation.ChannelTrailer && originalTitleElement.classList.contains("yt-formatted-string")) { @@ -441,7 +436,7 @@ export async function hideAndUpdateShowOriginalButton(element: HTMLElement, bran buttonElement.title = chrome.i18n.getMessage("ShowModified"); } - const isDefault = showCustomBranding.knownValue === null + const isDefault = showCustomBranding.knownValue === null || showCustomBranding.knownValue === showCustomBranding.originalValue; if (isDefault && brandingLocation !== BrandingLocation.Watch @@ -457,7 +452,7 @@ export async function hideAndUpdateShowOriginalButton(element: HTMLElement, bran } export async function findShowOriginalButton(originalTitleElement: HTMLElement, brandingLocation: BrandingLocation): Promise { - const referenceNode = brandingLocation === BrandingLocation.Watch + const referenceNode = brandingLocation === BrandingLocation.Watch ? (await getOrCreateTitleButtonContainer()) : originalTitleElement.parentElement; return referenceNode?.querySelector?.(".cbShowOriginal") as HTMLElement; } @@ -465,7 +460,7 @@ export async function findShowOriginalButton(originalTitleElement: HTMLElement, export async function findOrCreateShowOriginalButton(element: HTMLElement, brandingLocation: BrandingLocation, videoID: VideoID): Promise { const originalTitleElement = getOriginalTitleElement(element, brandingLocation); - const buttonElement = await findShowOriginalButton(originalTitleElement, brandingLocation) + const buttonElement = await findShowOriginalButton(originalTitleElement, brandingLocation) ?? await createShowOriginalButton(originalTitleElement, brandingLocation, videoID); buttonElement.setAttribute("videoID", videoID); @@ -494,7 +489,7 @@ async function createShowOriginalButton(originalTitleElement: HTMLElement, } buttonElement.classList.add("cbButton"); - if (brandingLocation === BrandingLocation.Watch + if (brandingLocation === BrandingLocation.Watch || Config.config!.alwaysShowShowOriginalButton) { buttonElement.classList.add("cbDontHide"); } @@ -577,7 +572,7 @@ async function createShowOriginalButton(originalTitleElement: HTMLElement, if (brandingLocation === BrandingLocation.Watch) { const referenceNode = await getOrCreateTitleButtonContainer(); - + // Verify again it doesn't already exist const existingButton = referenceNode?.querySelector?.(".cbShowOriginal"); if (existingButton) { diff --git a/src/videoBranding/videoBranding.ts b/src/videoBranding/videoBranding.ts index 4df3b84..b9cc4f2 100644 --- a/src/videoBranding/videoBranding.ts +++ b/src/videoBranding/videoBranding.ts @@ -10,7 +10,6 @@ import Config, { ThumbnailCacheOption } from "../config/config"; import { logError } from "../utils/logger"; import { getVideoTitleIncludingUnsubmitted } from "../dataFetching"; import { handleOnboarding } from "./onboarding"; -import { cleanEmojis, cleanResultingTitle } from "../titles/titleFormatter"; import { shouldDefaultToCustom, shouldDefaultToCustomFastCheck, shouldUseCrowdsourcedTitles } from "../config/channelOverrides"; import { onMobile } from "../../maze-utils/src/pageInfo"; import { addMaxTitleLinesCssToPage } from "../utils/cssInjector"; @@ -89,7 +88,7 @@ export async function replaceCurrentVideoBranding(): Promise<[boolean, boolean]> const showCustomBranding = videoBrandingInstance.showCustomBranding; // Replace each title and return true only if all true - promises[0] = Promise.all(titles.map((title) => + promises[0] = Promise.all(titles.map((title) => replaceTitle(title, videoID, showCustomBranding, brandingLocation))) .then((results) => results.every((result) => result)); @@ -197,8 +196,8 @@ export async function replaceVideoCardBranding(element: HTMLElement, brandingLoc knownValue: false, originalValue: false } : showCustomBranding); - const titlePromise = !isPlaylistOrClipTitleStatus - ? replaceTitle(element, videoID, showCustomBranding, brandingLocation) + const titlePromise = !isPlaylistOrClipTitleStatus + ? replaceTitle(element, videoID, showCustomBranding, brandingLocation) : Promise.resolve(false); if (isPlaylistOrClipTitleStatus) { @@ -283,7 +282,7 @@ async function extractVideoID(link: HTMLAnchorElement) { await waitForImageSrc(image); href = image.getAttribute("src"); } - + if (href) { videoID = href.match(/\/vi\/(\S{11})/)?.[1] as VideoID; } @@ -296,7 +295,7 @@ async function extractVideoID(link: HTMLAnchorElement) { export async function extractVideoIDFromElement(element: HTMLElement, brandingLocation: BrandingLocation): Promise { const link = getLinkElement(element, brandingLocation); - if (link) { + if (link) { return await extractVideoID(link); } else { return null; @@ -304,7 +303,7 @@ export async function extractVideoIDFromElement(element: HTMLElement, brandingLo } function isPlaylistOrClipTitle(element: HTMLElement, link: HTMLAnchorElement) { - return (link.href?.match(/list=/)?.[0] !== undefined + return (link.href?.match(/list=/)?.[0] !== undefined && link.href?.match(/index=/)?.[0] === undefined) || link.href?.match(/\/clip\//)?.[0] !== undefined; } @@ -318,15 +317,13 @@ export async function handleShowOriginalButton(element: HTMLElement, videoID: Vi const result = await Promise.race(promises); if (result || (await Promise.all(promises)).some((r) => r)) { const title = await getVideoTitleIncludingUnsubmitted(videoID, brandingLocation); - const originalTitle = getOriginalTitleElement(element, brandingLocation)?.textContent; - const customTitle = title && !title.original - && (!originalTitle || (cleanResultingTitle(cleanEmojis(title.title))).toLowerCase() !== (cleanResultingTitle(cleanEmojis(originalTitle))).toLowerCase()) + const customTitle = title && !title.original && await shouldUseCrowdsourcedTitles(videoID); if (!customTitle && !Config.config!.showIconForFormattedTitles && !await promises[1]) { return; } - + const button = await findOrCreateShowOriginalButton(element, brandingLocation, videoID); const image = button.querySelector("img") as HTMLImageElement; if (image) { @@ -383,7 +380,7 @@ export async function setShowCustom(videoID: VideoID, value: boolean): Promise { - if (videoBrandingInstances[videoID] + if (videoBrandingInstances[videoID] && [null, videoBrandingInstances[videoID].showCustomBranding.originalValue] .includes(videoBrandingInstances[videoID].showCustomBranding.knownValue)) { @@ -471,7 +468,7 @@ export function setupOptionChangeListener(): void { } } - if (changes.titleMaxLines + if (changes.titleMaxLines && changes.titleMaxLines.newValue !== changes.titleMaxLines.oldValue) { addMaxTitleLinesCssToPage(); } @@ -510,7 +507,7 @@ function waitForImageSrc(image: HTMLImageElement): Promise { } export function getActualShowCustomBranding(showCustomBranding: ShowCustomBrandingInfo): Promise { - return showCustomBranding.knownValue === null + return showCustomBranding.knownValue === null ? showCustomBranding.actualValue : Promise.resolve(showCustomBranding.knownValue); }