diff --git a/public/contentStyle.css b/public/contentStyle.css
index 22073d1e..ba0f4d38 100644
--- a/public/contentStyle.css
+++ b/public/contentStyle.css
@@ -63,4 +63,8 @@ body.no-scroll .yte-button-tooltip {
.ytp-right-controls {
display: flex !important;
+}
+
+.yte-hide-shorts {
+ display: none !important;
}
\ No newline at end of file
diff --git a/public/locales/en-US.json b/public/locales/en-US.json
index 0afd8d0a..9dfbe38c 100644
--- a/public/locales/en-US.json
+++ b/public/locales/en-US.json
@@ -186,6 +186,10 @@
"label": "Hide scrollbar",
"title": "Hides the pages scrollbar"
},
+ "hideShorts": {
+ "label": "Hide shorts",
+ "title": "Hides all shorts"
+ },
"loopButton": {
"label": "Loop button",
"title": "Adds a button to the feature menu to loop the video you're watching"
diff --git a/public/locales/en-US.json.d.ts b/public/locales/en-US.json.d.ts
index 7d766d06..0ea41e80 100644
--- a/public/locales/en-US.json.d.ts
+++ b/public/locales/en-US.json.d.ts
@@ -130,6 +130,7 @@ interface EnUS {
title: "Automatically enables theater mode when you load a video";
};
hideScrollbar: { label: "Hide scrollbar"; title: "Hides the pages scrollbar" };
+ hideShorts: { label: "Hide shorts"; title: "Hides all shorts" };
loopButton: {
label: "Loop button";
title: "Adds a button to the feature menu to loop the video you're watching";
diff --git a/src/components/Settings/Settings.tsx b/src/components/Settings/Settings.tsx
index 143b1e36..8fd36948 100644
--- a/src/components/Settings/Settings.tsx
+++ b/src/components/Settings/Settings.tsx
@@ -587,6 +587,14 @@ export default function Settings() {
title={t("settings.sections.miscellaneous.features.shortsAutoScroll.title")}
type="checkbox"
/>
+
diff --git a/src/features/hideShorts/index.ts b/src/features/hideShorts/index.ts
new file mode 100644
index 00000000..e4246d22
--- /dev/null
+++ b/src/features/hideShorts/index.ts
@@ -0,0 +1,26 @@
+import { hideShorts, observeShortsElements, showShorts } from "@/src/features/hideShorts/utils";
+import { waitForSpecificMessage } from "@/src/utils/utilities";
+let shortsObserver: MutationObserver | null = null;
+export async function enableHideShorts() {
+ // Wait for the "options" message from the content script
+ const optionsData = await waitForSpecificMessage("options", "request_data", "content");
+ const {
+ data: {
+ options: { enable_hide_shorts }
+ }
+ } = optionsData;
+ // If the hide shorts option is disabled, return
+ if (!enable_hide_shorts) return;
+ hideShorts();
+ // Observe changes to the short sections and hides them
+ shortsObserver = observeShortsElements();
+}
+
+export function disableHideShorts() {
+ showShorts();
+ // Disconnect the observer
+ if (shortsObserver) {
+ shortsObserver.disconnect();
+ shortsObserver = null;
+ }
+}
diff --git a/src/features/hideShorts/utils.ts b/src/features/hideShorts/utils.ts
new file mode 100644
index 00000000..d7a887d0
--- /dev/null
+++ b/src/features/hideShorts/utils.ts
@@ -0,0 +1,129 @@
+const sideBarOpenedShortsButtonSelector = "ytd-guide-entry-renderer:has(a[title=Shorts])";
+const sideBarClosedShortsButtonSelector = "ytd-guide-entry-renderer:has(a[title=Shorts])";
+const homePageShortsSectionSelector = "ytd-rich-section-renderer:has(#rich-shelf-header)";
+const channelHomePageShortsSectionSelector = "ytd-reel-shelf-renderer:has(#title-container)";
+const channelPageShortsTabSelector = "yt-tab-shape[tab-title=Shorts]";
+const searchResultsShortsTabSelector = "yt-chip-cloud-chip-renderer:has(yt-formatted-string[title=Shorts])";
+const shortsVideoRendererSelector = "ytd-video-renderer:has([overlay-style=SHORTS])";
+
+type ElementVisibilityAction = (element: HTMLElement) => void;
+
+function toggleElementVisibility(selector: string, action: ElementVisibilityAction) {
+ const elements = document.querySelectorAll(selector);
+ if (elements.length === 0) return;
+ elements.forEach((element) => action(element));
+}
+
+function hideElement(element: HTMLElement) {
+ element.classList.add("yte-hide-shorts");
+}
+
+function showElement(element: HTMLElement) {
+ element.classList.remove("yte-hide-shorts");
+}
+
+function hideSideBarShortsButton() {
+ toggleElementVisibility(sideBarOpenedShortsButtonSelector, hideElement);
+ toggleElementVisibility(sideBarClosedShortsButtonSelector, hideElement);
+}
+
+function showSideBarShortsButton() {
+ toggleElementVisibility(sideBarOpenedShortsButtonSelector, showElement);
+ toggleElementVisibility(sideBarClosedShortsButtonSelector, showElement);
+}
+function hideShortsTabOnSearchResultsPage() {
+ toggleElementVisibility(searchResultsShortsTabSelector, hideElement);
+}
+function showShortsTabOnSearchResultsPage() {
+ toggleElementVisibility(searchResultsShortsTabSelector, showElement);
+}
+function hideShortsSectionOnHomePage() {
+ toggleElementVisibility(homePageShortsSectionSelector, hideElement);
+}
+
+function showShortsSectionOnHomePage() {
+ toggleElementVisibility(homePageShortsSectionSelector, showElement);
+}
+
+function hideShortsSectionOnChannelHomePage() {
+ toggleElementVisibility(channelHomePageShortsSectionSelector, hideElement);
+}
+
+function showShortsSectionOnChannelHomePage() {
+ toggleElementVisibility(channelHomePageShortsSectionSelector, showElement);
+}
+
+function hideShortsTabOnChannelPage() {
+ toggleElementVisibility(channelPageShortsTabSelector, hideElement);
+}
+
+function showShortsTabOnChannelPage() {
+ toggleElementVisibility(channelPageShortsTabSelector, showElement);
+}
+function hideShortsVideoRenderers() {
+ toggleElementVisibility(shortsVideoRendererSelector, hideElement);
+}
+function showShortsVideoRenderers() {
+ toggleElementVisibility(shortsVideoRendererSelector, showElement);
+}
+export function hideShorts() {
+ // Hide the shorts tab on the channel page
+ hideShortsTabOnChannelPage();
+ // Hide the shorts tab on the search results page
+ hideShortsTabOnSearchResultsPage();
+ // Hide the shorts section on the homepage
+ hideShortsSectionOnHomePage();
+ // Hide the shorts section on the channel home page
+ hideShortsSectionOnChannelHomePage();
+ // Hide the shorts sidebar items
+ hideSideBarShortsButton();
+ // Hide the shorts video renderers
+ hideShortsVideoRenderers();
+}
+export function showShorts() {
+ // Show the shorts sidebar items
+ showSideBarShortsButton();
+ // Show the shorts section on the homepage
+ showShortsSectionOnHomePage();
+ // Show the shorts section on the channel home page
+ showShortsSectionOnChannelHomePage();
+ // Show the shorts tab on the channel page
+ showShortsTabOnChannelPage();
+ // Show the shorts tab on the search results page
+ showShortsTabOnSearchResultsPage();
+ // Show the shorts video renderers
+ showShortsVideoRenderers();
+}
+
+export function observeShortsElements() {
+ const observerOptions: MutationObserverInit = {
+ childList: true,
+ subtree: true
+ };
+
+ const observer = new MutationObserver((mutations) => {
+ // Check if any mutation contains one of the specified selectors
+ const containsShortsSelector = mutations.some((mutation) => {
+ return (
+ mutation.target instanceof Element &&
+ (mutation.target.matches(sideBarOpenedShortsButtonSelector) ||
+ mutation.target.matches(sideBarClosedShortsButtonSelector) ||
+ mutation.target.matches(homePageShortsSectionSelector) ||
+ mutation.target.matches(channelHomePageShortsSectionSelector) ||
+ mutation.target.matches(channelPageShortsTabSelector) ||
+ mutation.target.matches(searchResultsShortsTabSelector) ||
+ mutation.target.matches(shortsVideoRendererSelector))
+ );
+ });
+
+ // Only call hideShorts if one of the mutations contains one of the selectors
+ if (containsShortsSelector) {
+ hideShorts();
+ }
+ });
+
+ observer.observe(document.body, observerOptions);
+
+ // Return the observer so it can be disconnected later
+ return observer;
+}
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
index 39c9ff5a..08baf625 100644
--- a/src/i18n/index.ts
+++ b/src/i18n/index.ts
@@ -21,10 +21,10 @@ export const availableLocales = [
"zh-TW"
] as const;
export const localePercentages: Record = {
- "en-US": 100,
"ca-ES": 0,
"cs-CZ": 0,
"de-DE": 44,
+ "en-US": 100,
"es-ES": 77,
"fa-IR": 0,
"fr-FR": 81,
diff --git a/src/pages/content/index.tsx b/src/pages/content/index.tsx
index 86f2d295..5b78930a 100644
--- a/src/pages/content/index.tsx
+++ b/src/pages/content/index.tsx
@@ -10,6 +10,7 @@ import { enableFeatureMenu, setupFeatureMenuEventListeners } from "@/src/feature
import { featuresInMenu, updateFeatureMenuItemLabel, updateFeatureMenuTitle } from "@/src/features/featureMenu/utils";
import { enableHideScrollBar } from "@/src/features/hideScrollBar";
import { hideScrollBar, showScrollBar } from "@/src/features/hideScrollBar/utils";
+import { disableHideShorts, enableHideShorts } from "@/src/features/hideShorts";
import { addLoopButton, removeLoopButton } from "@/src/features/loopButton";
import { addMaximizePlayerButton, removeMaximizePlayerButton } from "@/src/features/maximizePlayerButton";
import { maximizePlayer } from "@/src/features/maximizePlayerButton/utils";
@@ -108,6 +109,7 @@ window.addEventListener("DOMContentLoaded", function () {
eventManager.removeAllEventListeners(["featureMenu"]);
void enableFeatureMenu();
void enableOpenYouTubeSettingsOnHover();
+ void enableHideShorts();
void enableShortsAutoScroll();
void removeRedirect();
void enableShareShortener();
@@ -313,6 +315,17 @@ window.addEventListener("DOMContentLoaded", function () {
}
break;
}
+ case "hideShortsChange": {
+ const {
+ data: { hideShortsEnabled }
+ } = message;
+ if (hideShortsEnabled) {
+ await enableHideShorts();
+ } else {
+ disableHideShorts();
+ }
+ break;
+ }
case "languageChange": {
const {
data: { language }
diff --git a/src/pages/inject/index.tsx b/src/pages/inject/index.tsx
index 9bf6daec..777b5c02 100644
--- a/src/pages/inject/index.tsx
+++ b/src/pages/inject/index.tsx
@@ -241,6 +241,11 @@ const storageChangeHandler = async (changes: StorageChanges, areaName: string) =
hideScrollBarEnabled: newValue
});
},
+ enable_hide_shorts: (__oldValue, newValue) => {
+ sendExtensionOnlyMessage("hideShortsChange", {
+ hideShortsEnabled: newValue
+ });
+ },
enable_loop_button: (__oldValue, newValue) => {
sendExtensionOnlyMessage("loopButtonChange", {
loopButtonEnabled: newValue
diff --git a/src/types/index.ts b/src/types/index.ts
index 332af6ae..cebbb0a4 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -71,6 +71,7 @@ export type configuration = {
enable_custom_css: boolean;
enable_forced_playback_speed: boolean;
enable_hide_scrollbar: boolean;
+ enable_hide_shorts: boolean;
enable_loop_button: boolean;
enable_maximize_player_button: boolean;
enable_open_transcript_button: boolean;
@@ -163,6 +164,7 @@ export type ExtensionSendOnlyMessageMappings = {
customCSSChange: DataResponseMessage<"customCSSChange", { customCSSCode: string; customCSSEnabled: boolean }>;
featureMenuOpenTypeChange: DataResponseMessage<"featureMenuOpenTypeChange", { featureMenuOpenType: FeatureMenuOpenType }>;
hideScrollBarChange: DataResponseMessage<"hideScrollBarChange", { hideScrollBarEnabled: boolean }>;
+ hideShortsChange: DataResponseMessage<"hideShortsChange", { hideShortsEnabled: boolean }>;
languageChange: DataResponseMessage<"languageChange", { language: AvailableLocales }>;
loopButtonChange: DataResponseMessage<"loopButtonChange", { loopButtonEnabled: boolean }>;
maximizeButtonChange: DataResponseMessage<"maximizeButtonChange", { maximizePlayerButtonEnabled: boolean }>;
diff --git a/src/utils/EventManager.ts b/src/utils/EventManager.ts
index 5a07f539..6d7a6c63 100644
--- a/src/utils/EventManager.ts
+++ b/src/utils/EventManager.ts
@@ -2,6 +2,7 @@ export type FeatureName =
| "automaticTheaterMode"
| "featureMenu"
| "hideScrollBar"
+ | "hideShorts"
| "loopButton"
| "maximizePlayerButton"
| "openTranscriptButton"
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 596306e5..3d3fb2c8 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -33,6 +33,7 @@ export const defaultConfiguration = {
enable_custom_css: false,
enable_forced_playback_speed: false,
enable_hide_scrollbar: false,
+ enable_hide_shorts: false,
enable_loop_button: false,
enable_maximize_player_button: false,
enable_open_transcript_button: false,
@@ -95,6 +96,7 @@ export const configurationImportSchema: TypeToPartialZodSchema<
enable_custom_css: z.boolean().optional(),
enable_forced_playback_speed: z.boolean().optional(),
enable_hide_scrollbar: z.boolean().optional(),
+ enable_hide_shorts: z.boolean().optional(),
enable_loop_button: z.boolean().optional(),
enable_maximize_player_button: z.boolean().optional(),
enable_open_transcript_button: z.boolean().optional(),