- // ==UserScript==
- // @name Netflix Enchantments
- // @namespace http://tampermonkey.net/
- // @version 0.4.4
- // @description Enhancements for Netflix video player: skip intro, skip outro, and more.
- // @author JJJ
- // @match https://www.netflix.com/*
- // @icon https://www.google.com/s2/favicons?sz=64&domain=netflix.com
- // @require https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_registerMenuCommand
- // @license MIT
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- const CONFIG = {
- enableSkipRecap: GM_getValue('enableSkipRecap', true),
- enableSkipIntro: GM_getValue('enableSkipIntro', true),
- enableSkipOutro: GM_getValue('enableSkipOutro', true),
- cancelFullscreen: GM_getValue('cancelFullscreen', false),
- hideGames: GM_getValue('hideGames', true),
- };
-
- const SELECTORS = {
- skipRecapButton: '.button-primary.watch-video--skip-content-button.medium.hasLabel.ltr-1mjzmhv[data-uia="player-skip-recap"]',
- skipIntroButton: '.button-primary.watch-video--skip-content-button.medium.hasLabel.ltr-1mjzmhv[data-uia="player-skip-intro"]',
- skipOutroButton: '.color-primary.hasLabel.hasIcon.ltr-1jtux27',
- fullscreenView: '.watch-video--player-view',
- gamesSection: '.lolomoRow[data-list-context*="games"]'
- };
-
- function createSettingsDialog() {
- const dialogHTML = `
- <div id="netflixEnchantmentsDialog" class="dpe-dialog">
- <h3>Netflix Enchantments</h3>
- ${createToggle('enableSkipRecap', 'Skip Recap', 'Automatically skip episode recaps')}
- ${createToggle('enableSkipIntro', 'Skip Intro', 'Automatically skip the intro of episodes')}
- ${createToggle('enableSkipOutro', 'Skip Outro', 'Automatically skip the outro of episodes')}
- ${createToggle('cancelFullscreen', 'Cancel Fullscreen', 'Automatically exit fullscreen mode')}
- ${createToggle('hideGames', 'Hide Games', 'Hide the games section')}
- <div class="dpe-button-container">
- <button id="saveSettingsButton" class="dpe-button dpe-button-save">Save</button>
- <button id="cancelSettingsButton" class="dpe-button dpe-button-cancel">Cancel</button>
- </div>
- </div>
- `;
-
- const styleSheet = `
- <style>
- .dpe-dialog {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: rgba(0, 0, 0, 0.8);
- border: 1px solid #444;
- border-radius: 8px;
- padding: 20px;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
- z-index: 9999;
- color: white;
- width: 300px;
- font-family: Arial, sans-serif;
- }
- .dpe-dialog h3 {
- margin-top: 0;
- font-size: 1.4em;
- text-align: center;
- margin-bottom: 20px;
- }
- .dpe-checkbox-container {
- display: flex;
- align-items: center;
- margin-bottom: 15px;
- }
- .dpe-checkbox-container input[type="checkbox"] {
- margin-right: 10px;
- }
- .dpe-button-container {
- display: flex;
- justify-content: space-between;
- margin-top: 20px;
- }
- .dpe-button {
- padding: 8px 16px;
- border: none;
- border-radius: 4px;
- cursor: pointer;
- font-size: 1em;
- transition: background-color 0.3s;
- }
- .dpe-button-save {
- background-color: #0078d4;
- color: white;
- }
- .dpe-button-save:hover {
- background-color: #005a9e;
- }
- .dpe-button-cancel {
- background-color: #d41a1a;
- color: white;
- }
- .dpe-button-cancel:hover {
- background-color: #a61515;
- }
- .dpe-toggle-container {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 15px;
- }
- .dpe-toggle-label {
- flex-grow: 1;
- }
- .dpe-toggle {
- position: relative;
- display: inline-block;
- width: 50px;
- height: 24px;
- }
- .dpe-toggle input {
- position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0;
- cursor: pointer;
- margin: 0;
- }
- .dpe-toggle-slider {
- position: absolute;
- cursor: pointer;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: #ccc;
- transition: .4s;
- border-radius: 24px;
- }
- .dpe-toggle-slider:before {
- position: absolute;
- content: "";
- height: 16px;
- width: 16px;
- left: 4px;
- bottom: 4px;
- background-color: white;
- transition: .4s;
- border-radius: 50%;
- }
- .dpe-toggle input:checked + .dpe-toggle-slider {
- background-color: #0078d4;
- }
- .dpe-toggle input:checked + .dpe-toggle-slider:before {
- transform: translateX(26px);
- }
- </style>
- `;
-
- const dialogWrapper = document.createElement('div');
- dialogWrapper.innerHTML = styleSheet + dialogHTML;
- document.body.appendChild(dialogWrapper);
-
- // Add event listeners to toggles
- document.querySelectorAll('.dpe-toggle input').forEach(toggle => {
- toggle.addEventListener('change', (event) => {
- const { id, checked } = event.target;
- CONFIG[id] = checked; // Update the CONFIG object with the new value
- });
- });
-
- // Add event listeners to buttons
- document.getElementById('saveSettingsButton').addEventListener('click', saveAndCloseDialog);
- document.getElementById('cancelSettingsButton').addEventListener('click', closeDialog);
- }
-
- function createToggle(id, label, title) {
- return `
- <div class="dpe-toggle-container" title="${title}">
- <label class="dpe-toggle">
- <input type="checkbox" id="${id}" ${CONFIG[id] ? 'checked' : ''}>
- <span class="dpe-toggle-slider"></span>
- </label>
- <label for="${id}" class="dpe-toggle-label">${label}</label>
- </div>
- `;
- }
-
- function saveAndCloseDialog() {
- Object.keys(CONFIG).forEach(key => {
- CONFIG[key] = document.getElementById(key).checked;
- GM_setValue(key, CONFIG[key]);
- });
- closeDialog();
- }
-
- function closeDialog() {
- const dialog = document.getElementById('netflixEnchantmentsDialog');
- if (dialog) {
- dialog.remove();
- if (document.fullscreenElement) {
- document.exitFullscreen();
- }
- }
- }
-
- function clickButton(selector) {
- const button = document.querySelector(selector);
- if (button) {
- button.click();
- }
- }
-
- function enterFullscreen() {
- if (!document.fullscreenElement) {
- document.documentElement.requestFullscreen();
- }
- }
-
- function exitFullscreen() {
- if (document.fullscreenElement) {
- document.exitFullscreen();
- }
- }
-
- function hideGamesSection() {
- const gamesSections = document.querySelectorAll(SELECTORS.gamesSection);
- gamesSections.forEach(section => {
- if (section && CONFIG.hideGames) {
- section.style.display = 'none';
- }
- });
- }
-
- function handleSkipActions() {
- try {
- if (CONFIG.enableSkipRecap) {
- clickButton(SELECTORS.skipRecapButton);
- }
-
- if (CONFIG.enableSkipIntro) {
- clickButton(SELECTORS.skipIntroButton);
- }
-
- if (CONFIG.enableSkipOutro) {
- clickButton(SELECTORS.skipOutroButton);
- }
-
- if (document.querySelector(SELECTORS.fullscreenView)) {
- enterFullscreen();
- }
-
- if (CONFIG.cancelFullscreen && document.fullscreenElement) {
- exitFullscreen();
- }
-
- if (CONFIG.hideGames) {
- hideGamesSection();
- }
- } catch (error) {
- console.error('An error occurred:', error);
- }
- }
-
- const observer = new MutationObserver(handleSkipActions);
- observer.observe(document.body, { childList: true, subtree: true });
-
- GM_registerMenuCommand('Netflix Enchantments Settings', createSettingsDialog);
-
- let isSettingsDialogOpen = false;
-
- function toggleSettingsDialog() {
- const dialog = document.getElementById('netflixEnchantmentsDialog');
- if (dialog) {
- dialog.remove();
- } else {
- createSettingsDialog();
- }
- }
-
- document.addEventListener('keyup', (event) => {
- if (event.key === 'F2') {
- toggleSettingsDialog();
- } else if (event.key === 'Escape') {
- exitFullscreen();
- closeDialog();
- }
- });
-
- })();