From 56c1fadb9f8071bfb93b262b436f6db65bd2a285 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:23:52 +0000 Subject: [PATCH 1/6] Remove Discord references in src (favor Matrix); drop Discord fields from config; no changes to public/_locales MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/config.ts | 4 ---- src/popup/PopupComponent.tsx | 6 ------ src/submission/SubmissionComponent.tsx | 4 ++-- src/submission/autoWarning.ts | 2 +- 4 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/config.ts b/src/config.ts index e9a8f7cd..5b770ebc 100644 --- a/src/config.ts +++ b/src/config.ts @@ -35,8 +35,6 @@ interface SBConfig { hideDeleteButtonPlayerControls: boolean; hideUploadButtonPlayerControls: boolean; hideSkipButtonPlayerControls: boolean; - hideDiscordLaunches: number; - hideDiscordLink: boolean; invidiousInstances: string[]; supportInvidious: boolean; serverAddress: string; @@ -305,8 +303,6 @@ const syncDefaults = { hideDeleteButtonPlayerControls: false, hideUploadButtonPlayerControls: false, hideSkipButtonPlayerControls: false, - hideDiscordLaunches: 0, - hideDiscordLink: false, invidiousInstances: [], supportInvidious: false, serverAddress: CompileConfig.serverAddress, diff --git a/src/popup/PopupComponent.tsx b/src/popup/PopupComponent.tsx index 03dab6b4..add634b5 100644 --- a/src/popup/PopupComponent.tsx +++ b/src/popup/PopupComponent.tsx @@ -159,12 +159,6 @@ export const PopupComponent = () => { titleFormatting={titleFormatting} /> - - - { window.open(`https://chat.sponsor.ajay.app/#${objectToURI("", { displayName: getChatDisplayName(name), - customDescription: `${chrome.i18n.getMessage("chatboxDescription")}\n\nhttps://discord.gg/SponsorBlock\nhttps://matrix.to/#/#sponsor:ajay.app?via=matrix.org`, + customDescription: `${chrome.i18n.getMessage("chatboxDescription")}\n\nhttps://matrix.to/#/#sponsor:ajay.app?via=matrix.org`, bigDescription: true }, false)}`) }], diff --git a/src/submission/autoWarning.ts b/src/submission/autoWarning.ts index 3880be2b..8538bb56 100644 --- a/src/submission/autoWarning.ts +++ b/src/submission/autoWarning.ts @@ -179,7 +179,7 @@ function showAutoWarningIfRequiredInternal(title: string, element: HTMLElement): window.open(`https://chat.sponsor.ajay.app/#${objectToURI("", { displayName: getChatDisplayName(name), - customDescription: `${chrome.i18n.getMessage("chatboxDescription")}\n\nhttps://discord.gg/SponsorBlock\nhttps://matrix.to/#/#edittogether:matrix.org?via=matrix.org`, + customDescription: `${chrome.i18n.getMessage("chatboxDescription")}\n\nhttps://matrix.to/#/#edittogether:matrix.org?via=matrix.org`, bigDescription: true }, false)}`); } From 071f82f1eeaa3f361da24984a4f6d48fc1de7747 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:39:31 +0000 Subject: [PATCH 2/6] SB 5.14..9a526c5 error handling: vote response union + error formatting; avoid submodule changes by inlining helpers; src-only Discord cleanup preserved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/background.ts | 41 +++++++++--------------- src/components/CategoryPillComponent.tsx | 14 +++++--- src/components/ChapterVoteComponent.tsx | 13 +++++--- src/content.ts | 28 ++++++++++++---- src/popup/SegmentListComponent.tsx | 11 ++++--- src/utils/errorFormat.ts | 15 +++++++++ src/utils/requestLogging.ts | 18 +++++++++++ 7 files changed, 95 insertions(+), 45 deletions(-) create mode 100644 src/utils/errorFormat.ts create mode 100644 src/utils/requestLogging.ts diff --git a/src/background.ts b/src/background.ts index a83944f3..365800e9 100644 --- a/src/background.ts +++ b/src/background.ts @@ -6,6 +6,15 @@ import "content-scripts-register-polyfill"; import { sendRealRequestToCustomServer, setupBackgroundRequestProxy } from "../maze-utils/src/background-request-proxy"; import { setupTabUpdates } from "../maze-utils/src/tab-updates"; import { generateUserID } from "../maze-utils/src/setup"; +function serializeOrStringify(e: unknown): string { + if (e instanceof Error) return e.stack || e.message || String(e); + try { + if (typeof e === "object") return JSON.stringify(e as unknown as Record); + } catch (err) { + return String(err); + } + return String(e); +} import Utils from "./utils"; @@ -217,33 +226,15 @@ async function submitVote(type: number, UUID: string, category: string, videoID: try { const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&videoID=" + videoID + "&userID=" + userID + typeSection); - - if (response.ok) { - return { - successType: 1, - responseText: await response.text() - }; - } else if (response.status == 405) { - //duplicate vote - return { - successType: 0, - statusCode: response.status, - responseText: await response.text() - }; - } else { - //error while connect - return { - successType: -1, - statusCode: response.status, - responseText: await response.text() - }; - } + return { + status: response.status, + ok: response.ok, + responseText: await response.text(), + }; } catch (e) { - console.error(e); + console.error("Error while voting:", e); return { - successType: -1, - statusCode: -1, - responseText: "" + error: serializeOrStringify(e), }; } } diff --git a/src/components/CategoryPillComponent.tsx b/src/components/CategoryPillComponent.tsx index d4c5a47f..1c496948 100644 --- a/src/components/CategoryPillComponent.tsx +++ b/src/components/CategoryPillComponent.tsx @@ -8,8 +8,8 @@ import { downvoteButtonColor, SkipNoticeAction } from "../utils/noticeUtils"; import { VoteResponse } from "../messageTypes"; import { AnimationUtils } from "../../maze-utils/src/animationUtils"; import { Tooltip } from "../render/Tooltip"; -import { getErrorMessage } from "../../maze-utils/src/formating"; - +import { formatJSErrorMessage, getLongErrorMessage } from "../utils/errorFormat"; +import { logRequest } from "../utils/requestLogging"; export interface CategoryPillProps { vote: (type: number, UUID: SegmentUUID, category?: Category) => Promise; showTextByDefault: boolean; @@ -127,15 +127,19 @@ class CategoryPillComponent extends React.Component Promise; @@ -119,12 +120,16 @@ class ChapterVoteComponent extends React.Component console.warn("[SB] Caught error while attempting to log segment skip", e)); } } } @@ -2287,14 +2294,17 @@ async function vote(type: number, UUID: SegmentUUID, category?: Category, skipNo if (response != undefined) { //see if it was a success or failure if (skipNotice != null) { - if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) { + if ("error" in response) { + skipNotice.setNoticeInfoMessage.bind(skipNotice)(formatJSErrorMessage(response.error as unknown as Error)); + skipNotice.resetVoteButtonInfo.bind(skipNotice)(); + } else if (response.ok || response.status === 429) { //success (treat rate limits as a success) skipNotice.afterVote.bind(skipNotice)(utils.getSponsorTimeFromUUID(sponsorTimes, UUID), type, category); - } else if (response.successType == -1) { - if (response.statusCode === 403 && response.responseText.startsWith("Vote rejected due to a tip from a moderator.")) { + } else { + if (response.status === 403 && response.responseText.startsWith("Vote rejected due to a tip from a moderator.")) { openWarningDialog(skipNoticeContentContainer); } else { - skipNotice.setNoticeInfoMessage.bind(skipNotice)(getErrorMessage(response.statusCode, response.responseText)) + skipNotice.setNoticeInfoMessage.bind(skipNotice)(getLongErrorMessage(response.status, response.responseText)); } skipNotice.resetVoteButtonInfo.bind(skipNotice)(); @@ -2334,8 +2344,12 @@ async function voteAsync(type: number, UUID: SegmentUUID, category?: Category): category: category, videoID: getVideoID() }, (response) => { - if (response.successType === 1) { - // Change the sponsor locally + if (!response || "error" in response) { + resolve(response); + return; + } + + if (response.ok || response.status === 429) { const segment = utils.getSponsorTimeFromUUID(sponsorTimes, UUID); if (segment) { if (type === 0) { diff --git a/src/popup/SegmentListComponent.tsx b/src/popup/SegmentListComponent.tsx index aa76763f..e8f30bc6 100644 --- a/src/popup/SegmentListComponent.tsx +++ b/src/popup/SegmentListComponent.tsx @@ -3,7 +3,8 @@ import { ActionType, SegmentUUID, SponsorHideType, SponsorTime, VideoID } from " import Config from "../config"; import { waitFor } from "../../maze-utils/src"; import { shortCategoryName } from "../utils/categoryUtils"; -import { getErrorMessage, getFormattedTime } from "../../maze-utils/src/formating"; +import { getFormattedTime } from "../../maze-utils/src/formating"; +import { formatJSErrorMessage, getLongErrorMessage } from "../utils/errorFormat"; import { AnimationUtils } from "../../maze-utils/src/animationUtils"; import { asyncRequestToServer } from "../utils/requests"; import { Message, MessageResponse, VoteResponse } from "../messageTypes"; @@ -303,11 +304,13 @@ async function vote(props: { if (response != undefined) { // See if it was a success or failure - if (response.successType == 1 || (response.successType == -1 && response.statusCode == 429)) { + if ("error" in response) { + props.setVoteMessage(formatJSErrorMessage(response.error)); + } else if (response.ok || response.status === 429) { // Success (treat rate limits as a success) props.setVoteMessage(chrome.i18n.getMessage("voted")); - } else if (response.successType == -1) { - props.setVoteMessage(getErrorMessage(response.statusCode, response.responseText)); + } else { + props.setVoteMessage(getLongErrorMessage(response.status, response.responseText)); } setTimeout(() => props.setVoteMessage(null), 1500); } diff --git a/src/utils/errorFormat.ts b/src/utils/errorFormat.ts new file mode 100644 index 00000000..9d5fb4d0 --- /dev/null +++ b/src/utils/errorFormat.ts @@ -0,0 +1,15 @@ +export function formatJSErrorMessage(e: unknown): string { + try { + if (e instanceof Error) return e.message || String(e); + if (typeof e === "object") return JSON.stringify(e); + } catch (err) { + return String(err); + } + return String(e); +} + +export function getLongErrorMessage(status: number, responseText?: string): string { + let base = `Error ${status}`; + if (responseText) base += `: ${responseText}`; + return base; +} diff --git a/src/utils/requestLogging.ts b/src/utils/requestLogging.ts new file mode 100644 index 00000000..c072b7c4 --- /dev/null +++ b/src/utils/requestLogging.ts @@ -0,0 +1,18 @@ +export function serializeOrStringify(e: unknown): string { + if (e instanceof Error) return e.stack || e.message || String(e); + try { + if (typeof e === "object") return JSON.stringify(e); + } catch (err) { + return String(err); + } + return String(e); +} + +export function logRequest(response: { status: number; ok: boolean; responseText?: string; headers?: Record | null }, prefix = "SB", action = "request"): void { + try { + // eslint-disable-next-line no-console + console.warn(`[${prefix}] Non-OK ${action}: status=${response.status} ok=${response.ok}`, response.responseText); + } catch (_err) { + void _err; + } +} From 4101c5054e2944c6b50387065324bc2eac4201dc Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 15:39:56 +0000 Subject: [PATCH 3/6] messageTypes: update VoteResponse union for upstream error handling sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/messageTypes.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/messageTypes.ts b/src/messageTypes.ts index f4ed8c39..7a25fc2b 100644 --- a/src/messageTypes.ts +++ b/src/messageTypes.ts @@ -77,7 +77,7 @@ export type Message = BaseMessage & (DefaultMessage | BoolValueMessage | IsInfoF export interface IsInfoFoundMessageResponse { found: boolean; - status: number; + status: number | string | Error; sponsorTimes: SponsorTime[]; time: number; onMobileYouTube: boolean; @@ -120,12 +120,13 @@ export type MessageResponse = | LogResponse | LoopedChapterResponse; -export interface VoteResponse { - successType: number; - statusCode: number; +export type VoteResponse = { + status: number; + ok: boolean; responseText: string; -} - +} | { + error: Error | string; +}; interface ImportSegmentsResponse { importedSegments: SponsorTime[]; } From 90a4c821626664003afeb3179718bc7cc3e95052 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 16:26:55 +0000 Subject: [PATCH 4/6] =?UTF-8?q?DeArrow=202.1.6=E2=80=932.1.8:=20add=20EndA?= =?UTF-8?q?utonav=20observers=20and=20selector=20updates;=20adjust=20relat?= =?UTF-8?q?ed=20selectors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/videoBranding/videoBranding.ts | 9 ++-- src/videoBranding/watchPageBrandingHandler.ts | 49 ++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/videoBranding/videoBranding.ts b/src/videoBranding/videoBranding.ts index e250fbe4..f83544d6 100644 --- a/src/videoBranding/videoBranding.ts +++ b/src/videoBranding/videoBranding.ts @@ -40,6 +40,7 @@ export enum BrandingLocation { Endcards, Autoplay, EndRecommendations, + EndAutonav, EmbedSuggestions, UpNextPreview, Notification, @@ -64,7 +65,7 @@ export interface VideoBrandingInstance { export const brandingBoxSelector = !onMobile() ? "ytd-rich-grid-media, ytd-video-renderer, ytd-movie-renderer, ytd-compact-video-renderer, ytd-compact-radio-renderer, ytd-compact-movie-renderer, ytd-playlist-video-renderer, ytd-playlist-panel-video-renderer, ytd-grid-video-renderer, ytd-grid-movie-renderer, ytd-rich-grid-slim-media, ytd-radio-renderer, ytd-reel-item-renderer, ytd-compact-playlist-renderer, ytd-playlist-renderer, ytd-grid-playlist-renderer, ytd-grid-show-renderer, ytd-structured-description-video-lockup-renderer, ytd-hero-playlist-thumbnail-renderer, yt-lockup-view-model, ytm-shorts-lockup-view-model" - : "ytm-video-with-context-renderer, ytm-compact-radio-renderer, ytm-reel-item-renderer, ytm-channel-featured-video-renderer, ytm-compact-video-renderer, ytm-playlist-video-renderer, .playlist-immersive-header-content, ytm-compact-playlist-renderer, ytm-video-card-renderer, ytm-vertical-list-renderer, ytm-playlist-panel-video-renderer"; + : "ytm-video-with-context-renderer, ytm-compact-radio-renderer, ytm-reel-item-renderer, ytm-channel-featured-video-renderer, ytm-compact-video-renderer, ytm-playlist-video-renderer, .playlist-immersive-header-content, ytm-compact-playlist-renderer, ytm-video-card-renderer, ytm-vertical-list-renderer, ytm-playlist-panel-video-renderer, ytm-shorts-lockup-view-model"; export const watchPageThumbnailSelector = ".ytp-cued-thumbnail-overlay"; @@ -262,7 +263,7 @@ export function getLinkElement(element: HTMLElement, brandingLocation: BrandingL switch (brandingLocation) { case BrandingLocation.Related: if (!onMobile()) { - const link = element.querySelector("a#thumbnail, a.reel-item-endpoint, a.yt-lockup-metadata-view-model-wiz__title, a.yt-lockup-metadata-view-model-wiz__title-link, a.yt-lockup-view-model-wiz__content-image") as HTMLAnchorElement; + const link = element.querySelector("a#thumbnail, a.reel-item-endpoint, a.yt-lockup-metadata-view-model__title, a.yt-lockup-metadata-view-model__title-link, a.yt-lockup-view-model__content-image, a.yt-lockup-metadata-view-model-wiz__title") as HTMLAnchorElement; if (link) { return link; } else if (element.nodeName === "YTD-HERO-PLAYLIST-THUMBNAIL-RENDERER") { @@ -281,6 +282,7 @@ export function getLinkElement(element: HTMLElement, brandingLocation: BrandingL case BrandingLocation.EndRecommendations: case BrandingLocation.EmbedSuggestions: case BrandingLocation.UpNextPreview: + case BrandingLocation.EndAutonav: return element as HTMLAnchorElement; case BrandingLocation.Notification: case BrandingLocation.NotificationTitle: @@ -332,7 +334,8 @@ export async function extractVideoIDFromElement(element: HTMLElement, brandingLo function isPlaylistOrClipTitle(element: HTMLElement, link: HTMLAnchorElement) { return (link.href?.match(/list=/)?.[0] !== undefined - && link.href?.match(/index=/)?.[0] === undefined) + && link.href?.match(/index=/)?.[0] === undefined + && link.href?.match(/start_radio=/)?.[0] === undefined) || link.href?.match(/\/clip\//)?.[0] !== undefined; } diff --git a/src/videoBranding/watchPageBrandingHandler.ts b/src/videoBranding/watchPageBrandingHandler.ts index 05807a1a..60863ead 100644 --- a/src/videoBranding/watchPageBrandingHandler.ts +++ b/src/videoBranding/watchPageBrandingHandler.ts @@ -8,6 +8,12 @@ enum CheckType { AddedNodes } +let endscreenAutonavObserver: MutationObserver | null = null; +let endscreenAutonavObserverElement: HTMLElement | null = null; + +let endscreenAutonavSuggestionObserver: MutationObserver | null = null; +let endscreenAutonavSuggestionObserverElement: HTMLElement | null = null; + let autoplayObserver: MutationObserver | null = null; let autoplayObserverElement: HTMLElement | null = null; @@ -35,6 +41,7 @@ export async function replaceVideoPlayerSuggestionsBranding(): Promise { const endcardSelector = ".ytp-ce-element"; const autoplaySelector = ".ytp-autonav-endscreen-countdown-overlay"; const endRecommendationsSelector = ".html5-endscreen"; + const autonavSelector = ".ytp-autonav-endscreen-upnext-container"; // Setup initial listeners { @@ -74,6 +81,9 @@ export async function replaceVideoPlayerSuggestionsBranding(): Promise { setupAutoplayObserver(node); } else if (node.matches(endRecommendationsSelector)) { setupRecommendationsObserver(node); + } else if (node.matches(autonavSelector)) { + setupAutonavSuggestionsObserver(node); + setupAutonavObserver(node); } } } @@ -177,6 +187,38 @@ export function setupAutoplayObserver(element: HTMLElement): void { } } +export function setupAutonavObserver(element: HTMLElement): void { + const refNode = element.querySelector(".ytp-autonav-endscreen-upnext-container") as HTMLElement; + if (!endscreenAutonavObserver || endscreenAutonavObserverElement !== element && refNode) { + if (endscreenAutonavObserver) endscreenAutonavObserver.disconnect(); + + endscreenAutonavObserverElement = element as HTMLElement; + endscreenAutonavObserver = new MutationObserver((mutations) => observe(mutations, + ".ytp-autonav-endscreen-upnext-title:not(.cbCustomTitle)", BrandingLocation.EndAutonav, + CheckType.Target, ".ytp-autonav-endscreen-link-container")); + + endscreenAutonavObserver.observe(refNode, { + childList: true, + subtree: true + }); + } +} + +export function setupAutonavSuggestionsObserver(element: HTMLElement): void { + const refNode = element.querySelector(".ytp-suggestions-container") as HTMLElement; + + if (!endscreenAutonavSuggestionObserver || endscreenAutonavSuggestionObserverElement !== element && refNode) { + if (endscreenAutonavSuggestionObserver) endscreenAutonavSuggestionObserver.disconnect(); + + endscreenAutonavSuggestionObserverElement = element as HTMLElement; + endscreenAutonavSuggestionObserver = new MutationObserver((mutations) => observe(mutations, + ".ytp-autonav-suggestion-card", BrandingLocation.EndAutonav, CheckType.AddedNodes)); + + endscreenAutonavSuggestionObserver.observe(refNode, { + childList: true + }); + } +} export function setupRecommendationsObserver(element: HTMLElement): void { const refNode = element.querySelector(".ytp-endscreen-content") as HTMLElement; @@ -223,7 +265,8 @@ function setupVideoBrandReplacement(element: HTMLElement, brandingLocation: Bran replaceVideoCardBranding(element, brandingLocation).catch(logError); if (brandingLocation === BrandingLocation.EndRecommendations - || brandingLocation === BrandingLocation.EmbedSuggestions) { + || brandingLocation === BrandingLocation.EmbedSuggestions + || brandingLocation === BrandingLocation.EndAutonav) { if (element && !handledElements.has(element)) { handledElements.add(element); const observer = new MutationObserver((mutations) => { @@ -309,5 +352,7 @@ export function setupWatchPageBrandingCleanup() { mobileControlsObserver?.disconnect?.(); mobileAutoplayObserver?.disconnect?.(); mutationObserver?.disconnect?.(); + endscreenAutonavObserver?.disconnect?.(); + endscreenAutonavSuggestionObserver?.disconnect?.(); }); -} \ No newline at end of file +} From 11d8172a8746ebf59ead2223b91dd7c32c27e41e Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:10:34 +0000 Subject: [PATCH 5/6] =?UTF-8?q?DeArrow=202.1.6=E2=80=932.1.8:=20update=20f?= =?UTF-8?q?ancyTextConversions=20and=20title=20formatting=20flags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/titles/titleFormatter.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/titles/titleFormatter.ts b/src/titles/titleFormatter.ts index b189fdcc..06e137c9 100644 --- a/src/titles/titleFormatter.ts +++ b/src/titles/titleFormatter.ts @@ -24,21 +24,19 @@ if (LOAD_CLD) { */ export async function formatTitle(title: string, isCustom: boolean, videoID: VideoID | null): Promise { - const shouldOnlyFormatCustom = !Config.config!.formatOriginalTitles && Config.config!.formatCustomTitles; - return formatTitleInternal(title, isCustom, await getTitleFormatting(videoID), await shouldCleanEmojis(videoID), shouldOnlyFormatCustom); + return formatTitleInternal(title, isCustom, await getTitleFormatting(videoID), await shouldCleanEmojis(videoID), Config.config!.formatCustomTitles, Config.config!.formatOriginalTitles); } export async function formatTitleDefaultSettings(title: string, isCustom: boolean): Promise { - const shouldOnlyFormatCustom = !Config.config!.formatOriginalTitles && Config.config!.formatCustomTitles; - return await formatTitleInternal(title, isCustom, Config.config!.titleFormatting, Config.config!.shouldCleanEmojis, shouldOnlyFormatCustom); + return await formatTitleInternal(title, isCustom, Config.config!.titleFormatting, Config.config!.shouldCleanEmojis, Config.config!.formatCustomTitles, Config.config!.formatOriginalTitles); } -export async function formatTitleInternal(title: string, isCustom: boolean, titleFormatting: TitleFormatting, shouldCleanEmojis: boolean, onlyFormatCustomTitles = false): Promise { +export async function formatTitleInternal(title: string, isCustom: boolean, titleFormatting: TitleFormatting, shouldCleanEmojis: boolean, formatCustomTitles = true, formatOriginalTitles = true): Promise { if (shouldCleanEmojis) { title = cleanFancyText(cleanEmojis(title)); } - if (onlyFormatCustomTitles && !isCustom) { + if ((!formatCustomTitles && isCustom) || (!formatOriginalTitles && !isCustom)) { titleFormatting = TitleFormatting.Disable; } @@ -645,7 +643,7 @@ export function cleanFancyText(title: string): string { // Allow emoji variation selectors on either side of the replacement character // Supports emojis such as 🅱️ title = title.replace(new RegExp(`[\u{fe00}-\u{fe0f}\u{e0100}-\u{e01ef}]?${replacement[0]}[\u{fe00}-\u{fe0f}\u{e0100}-\u{e01ef}]?` - , "ug"), replacement[1].toUpperCase()); + , "ug"), replacement[1]); } return title.trim(); From 499cf07685386d4a3289c6a2163cded288f68610 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 17:32:42 +0000 Subject: [PATCH 6/6] =?UTF-8?q?DeArrow=202.1.8:=20thumbnails=20=E2=80=93?= =?UTF-8?q?=20add=20EndAutonav=20handling=20(selector=20+=20box=20mapping)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Erkin Alp Güney --- src/thumbnails/thumbnailRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/thumbnails/thumbnailRenderer.ts b/src/thumbnails/thumbnailRenderer.ts index e336c535..c5d31afa 100644 --- a/src/thumbnails/thumbnailRenderer.ts +++ b/src/thumbnails/thumbnailRenderer.ts @@ -521,6 +521,7 @@ export function getThumbnailImageSelector(brandingLocation: BrandingLocation): s case BrandingLocation.Endcards: return ".ytp-ce-covering-image"; case BrandingLocation.Autoplay: + case BrandingLocation.EndAutonav: return "div.ytp-autonav-endscreen-upnext-thumbnail"; case BrandingLocation.EndRecommendations: return "div.ytp-videowall-still-image"; @@ -551,6 +552,7 @@ function getThumbnailBox(image: HTMLElement, brandingLocation: BrandingLocation) return image; } case BrandingLocation.Autoplay: + case BrandingLocation.EndAutonav: case BrandingLocation.UpNextPreview: return image; default: