Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #55

Merged
merged 8 commits into from
Oct 12, 2023
Merged

Dev #55

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
}
],
"no-useless-escape": "off",
"no-empty": ["error", { "allowEmptyCatch": true }]
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"]
},
"globals": {
"chrome": "readonly"
Expand Down
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# YouTube Enhancer Extension
# YouTube Enhancer Extension 🚀

The YouTube Enhancer Extension is a powerful browser extension designed to enhance your YouTube experience. This README provides an overview of the extension's features and how to make the most out of it.
The YouTube Enhancer Extension is a powerful browser extension designed to enhance your YouTube experience.

## Table of Contents

- [Introduction](#introduction)
- [Features](#features)
- [Building the Extension](#building-the-extension)
- [Configuration](#configuration)
- [Usage](#usage)
- [Contributing](#contributing)
- [License](#license)
- [Introduction](#🌟-introduction)
- [Features](#🎛️-features)
- [Building the Extension](#🛠️-building-the-extension)
- [Configuration](#⚙-configuration)
- [Usage](#🔧-usage)
- [Contributing](#📝-contributing)
- [License](#📜-license)

## Introduction
## 🌟 Introduction

YouTube Enhancer is a browser extension that aims to improve your YouTube experience by providing a set of customizable features and enhancements. Whether you want to fine-tune your video settings, improve navigation, or simplify common tasks, this extension has you covered.

## Features
## 🎛️ Features

### 1. Miscellaneous Settings

Expand All @@ -30,7 +30,7 @@ YouTube Enhancer is a browser extension that aims to improve your YouTube experi

### 2. Scroll Wheel Volume Control Settings

- **Enable Scroll Wheel Volume Control:** Allows users to use the scroll wheel to control the volume of the video they're watching.
- **Enable Scroll Wheel Volume Control:** Control video volume with your mouse's scroll wheel for quick and easy adjustments.

- **OSD Color:** Choose the color of the On-Screen Display (OSD) for volume control.

Expand All @@ -42,7 +42,7 @@ YouTube Enhancer is a browser extension that aims to improve your YouTube experi

- **Amount to Adjust Volume per Scroll:** Define how much the volume should change with each scroll.

- **Time to Hide OSD:** Set the time delay before hiding the OSD.
- **Time to Hide OSD:** Set the time delay before the OSD disappears.

- **Padding for OSD:** Define the amount of padding to add to the OSD (applies to corner OSD).

Expand Down Expand Up @@ -72,7 +72,7 @@ YouTube Enhancer is a browser extension that aims to improve your YouTube experi

- **Screenshot Format:** Define the format in which screenshots should be saved (PNG, JPEG, or WEBP).

## Building the Extension
## 🛠️ Building the Extension

To build the YouTube Enhancer extension from scratch, follow these steps. We'll start with installing the necessary dependencies:

Expand Down Expand Up @@ -136,11 +136,11 @@ Your extension should now be loaded. Test it on YouTube to ensure it functions a

That's it! You've successfully built the YouTube Enhancer extension from scratch.

## Configuration
## Configuration

The extension provides a range of configuration options to tailor your YouTube experience to your liking. Simply access the extension's settings page to customize its behavior.

## Usage
## 🔧 Usage

Using the YouTube Enhancer Extension is straightforward:

Expand All @@ -154,10 +154,10 @@ Using the YouTube Enhancer Extension is straightforward:

5. Enjoy an enhanced YouTube experience with the extension's features working seamlessly in the background.

## Contributing
## 📝 Contributing

Contributions to the YouTube Enhancer Extension are welcome! If you'd like to contribute to the development of this extension or report issues, please refer to the project's GitHub repository.

## License
## 📜 License

The YouTube Enhancer Extension is open-source and available under the [MIT License](LICENSE). Feel free to explore, modify, and share it as needed.
2 changes: 1 addition & 1 deletion src/components/Inputs/CheckBox/CheckBox.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ChangeEvent } from "react";
import React, { type ChangeEvent } from "react";

interface CheckboxProps {
id?: string;
Expand Down
3 changes: 2 additions & 1 deletion src/components/Inputs/Number/Number.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { ChangeEvent, MutableRefObject, useRef } from "react";
import React, { useRef } from "react";
import type { ChangeEvent, MutableRefObject } from "react";
import "./Number.css";
import Arrow from "./Arrow";
interface NumberInputProps {
Expand Down
3 changes: 2 additions & 1 deletion src/components/Inputs/Select/Select.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useComponentVisible } from "@/hooks";
import React, { ChangeEvent, Dispatch, SetStateAction } from "react";
import React from "react";
import type { ChangeEvent, Dispatch, SetStateAction } from "react";
import Arrow from "../Number/Arrow";

interface OptionProps {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Inputs/Slider/Slider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { ChangeEvent, useState } from "react";
import React, { type ChangeEvent, useState } from "react";

type SliderProps = {
min: number;
Expand Down
17 changes: 14 additions & 3 deletions src/components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import "@/assets/styles/tailwind.css";
import "@/components/Settings/Settings.css";

import { useNotifications } from "@/hooks";
import { configuration, configurationKeys, youtubePlayerSpeedRate } from "@/src/types";
import type { configuration, configurationKeys } from "@/src/types";
import { youtubePlayerSpeedRate } from "@/src/types";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import React, { ChangeEvent, Dispatch, SetStateAction, useEffect, useState } from "react";
import { Checkbox, NumberInput, Select, SelectOption } from "../Inputs";
import React, { useEffect, useState } from "react";
import type { ChangeEvent, Dispatch, SetStateAction } from "react";
import { Checkbox, NumberInput, Select, type SelectOption } from "../Inputs";
import { settingsAreDefault } from "@/src/utils/utilities";
import { configurationSchema } from "@/src/utils/constants";
import { generateErrorMessage } from "zod-error";
Expand Down Expand Up @@ -336,6 +338,15 @@ export default function Settings({
onChange={setCheckboxOption("enable_remaining_time")}
/>
</div>
<div className="mx-2 mb-1" title="Adds a button to the player to loop the video you're watching">
<Checkbox
id="enable_loop_button"
title="Adds a button to the player to loop the video you're watching"
label="Enable loop button"
checked={settings.enable_loop_button.toString() === "true"}
onChange={setCheckboxOption("enable_loop_button")}
/>
</div>
</fieldset>
<fieldset className="mx-1">
<legend className="mb-1 text-lg sm:text-xl md:text-2xl">Scroll wheel volume control settings</legend>
Expand Down
2 changes: 1 addition & 1 deletion src/components/SettingsWrapper/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Loader from "@/src/components/Loader";
import Settings from "@/src/components/Settings/Settings";
import { configuration } from "@/src/types";
import type { configuration } from "@/src/types";
import { defaultConfiguration } from "@/src/utils/constants";
import { parseStoredValue } from "@/src/utils/utilities";
import React, { useEffect, useState } from "react";
Expand Down
28 changes: 28 additions & 0 deletions src/features/loopButton/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { waitForSpecificMessage } from "@/src/utils/utilities";
import { makeLoopOffButton } from "./utils";

export async function addLoopButton() {
// Wait for the "options" message from the content script
const optionsData = await waitForSpecificMessage("options", "request_data", "content");
if (!optionsData) return;
const {
data: { options }
} = optionsData;
// Extract the necessary properties from the options object
const { enable_loop_button } = options;
// If the loop button option is disabled, return
if (!enable_loop_button) return;
const loopButtonExists = document.querySelector("button#yte-loop-button") as HTMLButtonElement | null;
if (loopButtonExists) return;
// Get the volume control element
const volumeControl = document.querySelector("div.ytp-chrome-controls > div.ytp-left-controls > span.ytp-volume-area") as HTMLSpanElement | null;
// If volume control element is not available, return
if (!volumeControl) return;
const loopButton = makeLoopOffButton();
volumeControl.before(loopButton);
}
export function removeLoopButton() {
const loopButton = document.querySelector("button#yte-loop-button") as HTMLButtonElement | null;
if (!loopButton) return;
loopButton.remove();
}
131 changes: 131 additions & 0 deletions src/features/loopButton/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import eventManager from "@/src/utils/EventManager";
import { createTooltip } from "@/src/utils/utilities";

async function loopButtonClickListener() {
const videoElement = document.querySelector("video.html5-main-video") as HTMLVideoElement | null;
if (!videoElement) return;
const loopButton = document.querySelector("button#yte-loop-button") as HTMLButtonElement | null;
if (!loopButton) return;
const loop = videoElement.hasAttribute("loop");
if (loop) {
videoElement.removeAttribute("loop");
} else {
videoElement.setAttribute("loop", "");
}
const loopButtonSVG = loop ? makeLoopOffSVG() : makeLoopOnSVG();
loopButton.removeChild(loopButton.firstChild as Node);
loopButton.appendChild(loopButtonSVG);
if (loop) {
loopButton.dataset.title = "Loop Off";
} else {
loopButton.dataset.title = "Loop On";
}
}
export function makeLoopOnButton() {
const loopOnButton = document.createElement("button");
loopOnButton.classList.add("ytp-button");
loopOnButton.id = "yte-loop-button";
loopOnButton.dataset.title = "Loop On";
loopOnButton.style.padding = "0px";
loopOnButton.style.display = "flex";
loopOnButton.style.alignItems = "center";
loopOnButton.style.justifyContent = "center";
const loopOnButtonSVG = makeLoopOnSVG();
loopOnButton.appendChild(loopOnButtonSVG);
eventManager.addEventListener(loopOnButton, "click", loopButtonClickListener, "loopButton");
eventManager.addEventListener(
loopOnButton,
"mouseover",
createTooltip({
element: loopOnButton,
id: "yte-loop-button-tooltip",
featureName: "loopButton"
}),
"loopButton"
);
return loopOnButton;
}
export function makeLoopOffButton() {
const loopOffButton = document.createElement("button");
loopOffButton.classList.add("ytp-button");
loopOffButton.id = "yte-loop-button";
loopOffButton.dataset.title = "Loop Off";
loopOffButton.style.padding = "0px";
loopOffButton.style.display = "flex";
loopOffButton.style.alignItems = "center";
loopOffButton.style.justifyContent = "center";
const loopOffButtonSVG = makeLoopOffSVG();
loopOffButton.appendChild(loopOffButtonSVG);
eventManager.addEventListener(loopOffButton, "click", loopButtonClickListener, "loopButton");
eventManager.addEventListener(
loopOffButton,
"mouseover",
createTooltip({
element: loopOffButton,
id: "yte-loop-button-tooltip",
featureName: "loopButton"
}),
"loopButton"
);
return loopOffButton;
}
function makeLoopOnSVG(): SVGElement {
const loopOnSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
loopOnSVG.setAttributeNS(null, "stroke", "currentColor");
loopOnSVG.setAttributeNS(null, "stroke-width", "1.5");
loopOnSVG.setAttributeNS(null, "fill", "currentColor");
loopOnSVG.setAttributeNS(null, "height", "36");
loopOnSVG.setAttributeNS(null, "width", "36");
loopOnSVG.setAttributeNS(null, "viewBox", "0 0 36 36");
const loopOnGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
loopOnGroup.setAttributeNS(null, "transform", "matrix(0.0943489,0,0,-0.09705882,-1.9972187,36.735291)");
const loopOnTopArrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
loopOnTopArrowPath.setAttributeNS(
null,
"d",
"m 120.59273,172.42419 v 20.60606 20.60606 l -1e-5,20.60608 v 20.60606 h 40.55254 40.55253 40.55255 40.55253 v 30.9091 l 50.69068,-41.21213 -50.69068,-51.51516 v 30.9091 h -32.94893 -32.94893 -32.94895 -32.94893 v -12.8788 -12.87879 -12.87879 -12.87879 h -7.6036 -7.6036 -7.6036 z"
);
loopOnTopArrowPath.setAttributeNS(null, "transform", "matrix(1.0454545,0,0,0.99999979,-14.814644,20.606103)");
const loopOnBottomArrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
loopOnBottomArrowPath.setAttributeNS(
null,
"d",
"m 313.21727,172.4242 1e-5,-82.42427 H 151.00712 V 59.09087 l -50.69065,41.21209 50.69065,51.51516 v -30.90909 h 131.79575 v 51.51517 z"
);
loopOnBottomArrowPath.setAttributeNS(null, "transform", "matrix(1.0454545,0,0,0.99999979,-14.814644,20.606103)");
loopOnGroup.appendChild(loopOnTopArrowPath);
loopOnGroup.appendChild(loopOnBottomArrowPath);
loopOnSVG.appendChild(loopOnGroup);
return loopOnSVG;
}
function makeLoopOffSVG(): SVGElement {
const loopOffSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
loopOffSVG.setAttributeNS(null, "stroke", "currentColor");
loopOffSVG.setAttributeNS(null, "stroke-width", "1.5");
loopOffSVG.setAttributeNS(null, "fill", "currentColor");
loopOffSVG.setAttributeNS(null, "height", "36");
loopOffSVG.setAttributeNS(null, "width", "36");
loopOffSVG.setAttributeNS(null, "viewBox", "0 0 36 36");
const loopOffG = document.createElementNS("http://www.w3.org/2000/svg", "g");
loopOffG.setAttributeNS(null, "transform", "matrix(0.09863748,0,0,-0.0970588,-3.3949621,34.735285)");
const loopOffTopArrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
loopOffTopArrowPath.setAttributeNS(
null,
"d",
"M 282.80287,285.75755 V 270.303 254.84845 h -81.10508 -57.44282 l 35.48347,-30.9091 h 103.06443 v -15.45454 -15.45455 l 25.34533,25.75758 25.34534,25.75758 -25.34534,20.60607 z M 151.00712,211.73026 v -39.30607 h -15.2072 -15.2072 v 65.93941 z"
);
const loopOffBottomArrowPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
loopOffBottomArrowPath.setAttributeNS(
null,
"d",
"m 282.80287,172.42419 v -25.75758 -12.51658 l 30.4144,-26.48201 v 23.54404 41.21213 h -15.2072 z M 151.00712,151.81812 125.66179,126.06054 100.31645,100.30296 125.66179,79.696895 151.00712,59.090829 v 15.45455 15.454549 h 81.10508 58.45648 l -35.48347,30.909102 h -38.18022 -65.89787 v 15.45455 z"
);
const loopOffLinePath = document.createElementNS("http://www.w3.org/2000/svg", "path");
loopOffLinePath.setAttributeNS(null, "d", "M 7,10 28,28 29.981167,25.855305 8.9811674,7.8553045 Z");
loopOffLinePath.setAttributeNS(null, "transform", "matrix(10.138134,0,0,-10.303033,29.349514,357.87878)");
loopOffG.appendChild(loopOffTopArrowPath);
loopOffG.appendChild(loopOffBottomArrowPath);
loopOffG.appendChild(loopOffLinePath);
loopOffSVG.appendChild(loopOffG);
return loopOffSVG;
}
Loading
Loading