├── images ├── config.png ├── promo.png ├── netflix-skipper-128.png ├── netflix-skipper-16.png ├── netflix-skipper-32.png └── netflix-skipper-48.png ├── src ├── background.js ├── content_script.js ├── popup.html └── popup.js ├── README.md ├── manifest.json └── LICENSE /images/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/config.png -------------------------------------------------------------------------------- /images/promo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/promo.png -------------------------------------------------------------------------------- /images/netflix-skipper-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/netflix-skipper-128.png -------------------------------------------------------------------------------- /images/netflix-skipper-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/netflix-skipper-16.png -------------------------------------------------------------------------------- /images/netflix-skipper-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/netflix-skipper-32.png -------------------------------------------------------------------------------- /images/netflix-skipper-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ranrib/netflix-skipper/HEAD/images/netflix-skipper-48.png -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | chrome.runtime.onInstalled.addListener(() => { 2 | chrome.storage.local.set({ skipIntro: true, skipRecap: true, skipNext: true }); 3 | }); 4 | 5 | if (chrome.runtime) { 6 | chrome.runtime.setUninstallURL("https://docs.google.com/forms/d/e/1FAIpQLSdu75DIgQwiEYud7I8TgsAkyoUnFyLXRDAnELcN_QIeLGvh5w/viewform"); 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netflix-skipper 2 | Automatically skip Netflix intros, recaps, and next episode prompts ⚡⌛ 3 | 4 |  5 | 6 | Don't waste any more time waiting or clicking on intros, recaps, and next episode buttons. 7 | 8 | Once the extension is installed, reload or open a Netflix Tab, and it will work automatically. Through the popup, you can configure which events (intros, recaps, and next episode) you would like to skip. To exclude specific shows from your configured auto-skip settings, you can add the current show's title to an exemption list - or remove it from the list to restore the automatic skipping behavior. 9 | 10 |  11 | 12 | This extension supports ALL Netflix languages! 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Netflix Skipper", 3 | "description": "Automatically skip Netflix intros, recaps, and next episode prompts.", 4 | "author": "Ran Ribenzaft", 5 | "version": "1.0", 6 | "manifest_version": 3, 7 | "background": { 8 | "service_worker": "src/background.js" 9 | }, 10 | "content_scripts": [ 11 | { 12 | "matches": [ 13 | "*://*/*" 14 | ], 15 | "js": ["src/content_script.js"], 16 | "run_at": "document_end" 17 | } 18 | ], 19 | "permissions": ["storage", "activeTab"], 20 | "action": { 21 | "default_popup": "src/popup.html", 22 | "default_icon": { 23 | "16": "images/netflix-skipper-16.png", 24 | "32": "images/netflix-skipper-32.png", 25 | "48": "images/netflix-skipper-48.png", 26 | "128": "images/netflix-skipper-128.png" 27 | } 28 | }, 29 | "icons": { 30 | "16": "images/netflix-skipper-16.png", 31 | "32": "images/netflix-skipper-32.png", 32 | "48": "images/netflix-skipper-48.png", 33 | "128": "images/netflix-skipper-128.png" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Ran Ribenzaft 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/content_script.js: -------------------------------------------------------------------------------- 1 | const INTRO_UIA = "player-skip-intro"; 2 | const RECAP_UIA = "player-skip-recap"; 3 | const NEXT_UIA = "next-episode-seamless-button"; 4 | const NEXT_DRAIN_UIA = "next-episode-seamless-button-draining"; 5 | 6 | const BUTTONS = [INTRO_UIA, RECAP_UIA, NEXT_UIA, NEXT_DRAIN_UIA]; 7 | 8 | // Function to extract the current Netflix title 9 | function getCurrentTitle() { 10 | // Use the specific selector that works reliably 11 | const titleElement = document.querySelectorAll("[data-uia='video-title']")[0]; 12 | 13 | if (titleElement) { 14 | // Try to get the show title from the h4 element within the title element 15 | const h4Element = titleElement.querySelector('h4'); 16 | if (h4Element && h4Element.textContent.trim()) { 17 | return h4Element.textContent.trim(); 18 | } 19 | 20 | // Fallback to full text content if no h4 found 21 | if (titleElement.textContent.trim()) { 22 | return titleElement.textContent.trim(); 23 | } 24 | } 25 | 26 | // Fallback: try to get title from page title if the main selector fails 27 | const pageTitle = document.title; 28 | if (pageTitle && pageTitle !== 'Netflix' && !pageTitle.includes('Watch ')) { 29 | return pageTitle.replace(' - Netflix', '').trim(); 30 | } 31 | 32 | return null; 33 | } 34 | 35 | async function skipper() { 36 | try { 37 | chrome.storage.local.get( 38 | ["skipIntro", "skipRecap", "skipNext", "exemptTitles"], 39 | ({ skipIntro, skipRecap, skipNext, exemptTitles = [] }) => { 40 | // Check if current title is in exempt list 41 | const currentTitle = getCurrentTitle(); 42 | const isExempt = currentTitle && exemptTitles.includes(currentTitle); 43 | 44 | // If title is exempt, don't skip anything 45 | if (isExempt) { 46 | return; 47 | } 48 | 49 | const mapper = { 50 | [INTRO_UIA]: skipIntro, 51 | [RECAP_UIA]: skipRecap, 52 | [NEXT_UIA]: skipNext, 53 | [NEXT_DRAIN_UIA]: skipNext, 54 | }; 55 | BUTTONS.forEach((uia) => { 56 | const button = Object.values( 57 | document.getElementsByTagName("button") 58 | ).find((elem) => elem.getAttribute("data-uia") === uia); 59 | if (button && mapper[uia]) { 60 | button.click(); 61 | } 62 | }); 63 | } 64 | ); 65 | } catch (err) { 66 | console.error(err); 67 | } 68 | } 69 | 70 | // Function to add/remove current title from exempt list 71 | async function toggleExemptStatus() { 72 | const currentTitle = getCurrentTitle(); 73 | if (!currentTitle) { 74 | console.log('Netflix Skipper: Could not detect current title'); 75 | return; 76 | } 77 | 78 | try { 79 | const result = await chrome.storage.local.get(['exemptTitles']); 80 | const exemptTitles = result.exemptTitles || []; 81 | 82 | if (exemptTitles.includes(currentTitle)) { 83 | // Remove from exempt list 84 | const updatedTitles = exemptTitles.filter(title => title !== currentTitle); 85 | await chrome.storage.local.set({ exemptTitles: updatedTitles }); 86 | console.log(`Netflix Skipper: Removed "${currentTitle}" from exempt list`); 87 | } else { 88 | // Add to exempt list 89 | const updatedTitles = [...exemptTitles, currentTitle]; 90 | await chrome.storage.local.set({ exemptTitles: updatedTitles }); 91 | console.log(`Netflix Skipper: Added "${currentTitle}" to exempt list`); 92 | } 93 | } catch (err) { 94 | console.error('Netflix Skipper: Error toggling exempt status:', err); 95 | } 96 | } 97 | 98 | // Listen for messages from popup 99 | chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { 100 | if (request.action === 'toggleExempt') { 101 | toggleExemptStatus(); 102 | sendResponse({ success: true }); 103 | } else if (request.action === 'getCurrentTitle') { 104 | const title = getCurrentTitle(); 105 | sendResponse({ title }); 106 | } 107 | }); 108 | 109 | if (document.location.host.includes(".netflix.")) { 110 | setInterval(() => skipper(), 500); 111 | } 112 | -------------------------------------------------------------------------------- /src/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 106 | 107 | 108 |