├── WebExtension ├── data │ ├── options │ │ ├── matched.js │ │ ├── matched.json │ │ ├── index.html │ │ └── index.js │ ├── icons │ │ ├── 128.png │ │ ├── 16.png │ │ ├── 19.png │ │ ├── 256.png │ │ ├── 32.png │ │ ├── 38.png │ │ ├── 48.png │ │ ├── 512.png │ │ └── 64.png │ └── inject.js ├── common.js └── manifest.json └── XUL ├── icon.png ├── icon64.png ├── data ├── icons │ ├── 128.png │ ├── 16.png │ ├── 32.png │ ├── 48.png │ └── 64.png └── content_script │ └── inject.js ├── lib ├── config.js ├── common.js └── firefox │ └── firefox.js └── package.json /WebExtension/data/options/matched.js: -------------------------------------------------------------------------------- 1 | ../../../../../shared/matched/matched.js -------------------------------------------------------------------------------- /WebExtension/data/options/matched.json: -------------------------------------------------------------------------------- 1 | ../../../../../shared/matched/matched.json -------------------------------------------------------------------------------- /XUL/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/icon.png -------------------------------------------------------------------------------- /XUL/icon64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/icon64.png -------------------------------------------------------------------------------- /XUL/data/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/data/icons/128.png -------------------------------------------------------------------------------- /XUL/data/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/data/icons/16.png -------------------------------------------------------------------------------- /XUL/data/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/data/icons/32.png -------------------------------------------------------------------------------- /XUL/data/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/data/icons/48.png -------------------------------------------------------------------------------- /XUL/data/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/XUL/data/icons/64.png -------------------------------------------------------------------------------- /WebExtension/data/icons/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/128.png -------------------------------------------------------------------------------- /WebExtension/data/icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/16.png -------------------------------------------------------------------------------- /WebExtension/data/icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/19.png -------------------------------------------------------------------------------- /WebExtension/data/icons/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/256.png -------------------------------------------------------------------------------- /WebExtension/data/icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/32.png -------------------------------------------------------------------------------- /WebExtension/data/icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/38.png -------------------------------------------------------------------------------- /WebExtension/data/icons/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/48.png -------------------------------------------------------------------------------- /WebExtension/data/icons/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/512.png -------------------------------------------------------------------------------- /WebExtension/data/icons/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/james-fray/YouTube-No-Buffer/HEAD/WebExtension/data/icons/64.png -------------------------------------------------------------------------------- /XUL/lib/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var app = require('./firefox/firefox'); 4 | var config = exports; 5 | 6 | config.welcome = { 7 | get version () { 8 | return app.storage.read('version'); 9 | }, 10 | set version (val) { 11 | app.storage.write('version', val); 12 | }, 13 | timeout: 3, 14 | get show () { 15 | return app.storage.read('show') === 'false' ? false : true; // default is true 16 | }, 17 | set show (val) { 18 | app.storage.write('show', val); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /XUL/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "YouTube No Buffer", 3 | "name": "iynbuffer", 4 | "description": "A bulletproof approach to prevent YouTube from buffering videos", 5 | "id": "jid0-zxGf4jM5hHg1dJ5Gf1H7NfFfe76@jetpack", 6 | "license": "Mozilla Public License, version 2.0", 7 | "version": "0.1.7", 8 | "author": "James Fray", 9 | "main": "lib/common.js", 10 | "url": "http://firefox.add0n.com/no-buffer.html", 11 | "permissions": { 12 | "private-browsing": true, 13 | "multiprocess": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /XUL/lib/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var app = require('./firefox/firefox'); 4 | var config = require('./config'); 5 | 6 | /* welcome page */ 7 | (function () { 8 | var version = config.welcome.version; 9 | if (app.version() !== version) { 10 | app.timer.setTimeout(function () { 11 | app.tab.open( 12 | 'http://firefox.add0n.com/no-buffer.html?v=' + app.version() + 13 | (version ? '&p=' + version + '&type=upgrade' : '&type=install') 14 | ); 15 | config.welcome.version = app.version(); 16 | }, config.welcome.timeout); 17 | } 18 | })(); 19 | -------------------------------------------------------------------------------- /WebExtension/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | { 4 | const {onInstalled, setUninstallURL, getManifest} = chrome.runtime; 5 | const {name, version} = getManifest(); 6 | const page = getManifest().homepage_url; 7 | onInstalled.addListener(({reason, previousVersion}) => { 8 | chrome.storage.local.get({ 9 | 'faqs': true, 10 | 'last-update': 0 11 | }, prefs => { 12 | if (reason === 'install' || (prefs.faqs && reason === 'update')) { 13 | const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45; 14 | if (doUpdate && previousVersion !== version) { 15 | chrome.tabs.create({ 16 | url: page + '&version=' + version + 17 | (previousVersion ? '&p=' + previousVersion : '') + 18 | '&type=' + reason, 19 | active: reason === 'install' 20 | }); 21 | chrome.storage.local.set({'last-update': Date.now()}); 22 | } 23 | } 24 | }); 25 | }); 26 | setUninstallURL(page + '&rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version); 27 | } 28 | -------------------------------------------------------------------------------- /WebExtension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "YouTube™ No Buffer (Stop Auto-playing)", 3 | "description": "Prevents YouTube HTML5 and Flash players from auto-buffering (auto-playing) videos", 4 | "author": "James Fray", 5 | "version": "0.3.0", 6 | "manifest_version": 2, 7 | "permissions": [ 8 | "storage" 9 | ], 10 | "background": { 11 | "persistent": false, 12 | "scripts": [ 13 | "common.js" 14 | ] 15 | }, 16 | "content_scripts": [{ 17 | "matches": [ 18 | "*://www.youtube.com/*" 19 | ], 20 | "js": ["data/inject.js"], 21 | "run_at": "document_start", 22 | "all_frames": false 23 | }], 24 | "homepage_url": "https://add0n.com/youtube-tools.html?from=buffer", 25 | "icons": { 26 | "16": "data/icons/16.png", 27 | "19": "data/icons/19.png", 28 | "32": "data/icons/32.png", 29 | "38": "data/icons/38.png", 30 | "48": "data/icons/48.png", 31 | "64": "data/icons/64.png", 32 | "128": "data/icons/128.png", 33 | "256": "data/icons/256.png", 34 | "512": "data/icons/512.png" 35 | }, 36 | "options_ui": { 37 | "page": "data/options/index.html", 38 | "chrome_style": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /XUL/lib/firefox/firefox.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Load Firefox based resources 4 | var self = require('sdk/self'), 5 | data = self.data, 6 | sp = require('sdk/simple-prefs'), 7 | prefs = sp.prefs, 8 | pageMod = require('sdk/page-mod'), 9 | tabs = require('sdk/tabs'), 10 | timers = require('sdk/timers'); 11 | 12 | pageMod.PageMod({ 13 | include: [ 14 | 'https://www.youtube.com/*' 15 | ], 16 | contentScriptFile: [data.url('./content_script/inject.js')], 17 | contentScriptWhen: 'start', 18 | attachTo: ['top', 'existing'] 19 | }); 20 | 21 | exports.storage = { 22 | read: function (id) { 23 | return (prefs[id] || prefs[id] + '' === 'false' || !isNaN(prefs[id])) ? (prefs[id] + '') : null; 24 | }, 25 | write: function (id, data) { 26 | data = data + ''; 27 | if (data === 'true' || data === 'false') { 28 | prefs[id] = data === 'true' ? true : false; 29 | } 30 | else if (parseInt(data) + '' === data) { 31 | prefs[id] = parseInt(data); 32 | } 33 | else { 34 | prefs[id] = data + ''; 35 | } 36 | } 37 | }; 38 | 39 | exports.tab = { 40 | open: function (url) { 41 | tabs.open({url}); 42 | } 43 | }; 44 | 45 | exports.version = function () { 46 | return self.version; 47 | }; 48 | 49 | exports.timer = timers; 50 | -------------------------------------------------------------------------------- /WebExtension/data/options/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Options Page :: YouTube™ No Buffer 5 | 13 | 14 | 15 | 16 | 17 |

18 | 19 |
20 | 21 |
22 | 23 |
24 | 25 |

26 |

27 | 1: If this option is checked and [2] is unchecked, the YouTube player is released from the no-buffer state only once. 28 |
29 | 2: If this option is checked and [1] is checked too, the YouTube player gets paused when the tab is hidden and it gets resumed when the tab is active again. 30 |
31 | 3: Recently, Ad-blocker extensions do not allow YouTube from fetching video information when the page is already loaded, so stopping the video is not working anymore since the player needs to fetch the video information later. To be compatible with ad-blocker extensions, this extension by default pauses videos instead. If you have whitelisted the blocking rule on your ad-blocker extension, uncheck this option for real no-buffering. 32 |

33 |
34 |

35 | 36 | - 37 | 38 |

39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /WebExtension/data/options/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const status = document.getElementById('status'); 4 | 5 | function restore() { 6 | // Use default value color = 'red' and likesColor = true. 7 | chrome.storage.local.get({ 8 | playlist: false, 9 | visible: false, 10 | hidden: false, 11 | method: 'pauseVideo' 12 | }, prefs => { 13 | document.getElementById('playlist').checked = prefs.playlist; 14 | document.getElementById('visible').checked = prefs.visible; 15 | document.getElementById('hidden').checked = prefs.hidden; 16 | document.getElementById('method').checked = prefs.method === 'pauseVideo'; 17 | }); 18 | } 19 | 20 | function save() { 21 | const playlist = document.getElementById('playlist').checked; 22 | const visible = document.getElementById('visible').checked; 23 | const hidden = document.getElementById('hidden').checked; 24 | const method = document.getElementById('method').checked ? 'pauseVideo' : 'stopVideo'; 25 | chrome.storage.local.set({ 26 | playlist, 27 | visible, 28 | hidden, 29 | method 30 | }, () => { 31 | status.textContent = 'Options saved.'; 32 | setTimeout(() => status.textContent = '', 750); 33 | }); 34 | } 35 | 36 | document.addEventListener('DOMContentLoaded', restore); 37 | document.getElementById('save').addEventListener('click', save); 38 | 39 | // support 40 | document.getElementById('support').addEventListener('click', () => chrome.tabs.create({ 41 | url: chrome.runtime.getManifest().homepage_url + '&rd=donate' 42 | })); 43 | // reset 44 | document.getElementById('reset').addEventListener('click', e => { 45 | if (e.detail === 1) { 46 | status.textContent = 'Double-click to reset!'; 47 | window.setTimeout(() => status.textContent = '', 750); 48 | } 49 | else { 50 | localStorage.clear(); 51 | chrome.storage.local.clear(() => { 52 | chrome.runtime.reload(); 53 | window.close(); 54 | }); 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /XUL/data/content_script/inject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var script = document.createElement('script'); 4 | script.textContent = ` 5 | var yttools = yttools || []; 6 | yttools.push(function (e) { 7 | try { 8 | e.stopVideo(); 9 | } 10 | catch(e) {} 11 | }); 12 | function onYouTubePlayerReady (e) { 13 | yttools.forEach(c => c(e)); 14 | } 15 | 16 | (function (observe) { 17 | observe(window, 'ytplayer', (ytplayer) => { 18 | observe(ytplayer, 'config', (config) => { 19 | if (config && config.args) { 20 | Object.defineProperty(config.args, 'autoplay', { 21 | configurable: true, 22 | get: () => '0' 23 | }); 24 | config.args.fflags = config.args.fflags.replace("legacy_autoplay_flag=true", "legacy_autoplay_flag=false"); 25 | config.args.jsapicallback = 'onYouTubePlayerReady'; 26 | delete config.args.ad3_module; 27 | } 28 | }); 29 | }); 30 | })(function (object, property, callback) { 31 | let value; 32 | let descriptor = Object.getOwnPropertyDescriptor(object, property); 33 | Object.defineProperty(object, property, { 34 | enumerable: true, 35 | configurable: true, 36 | get: () => value, 37 | set: (v) => { 38 | callback(v); 39 | if (descriptor && descriptor.set) { 40 | descriptor.set(v); 41 | } 42 | value = v; 43 | return value; 44 | } 45 | }); 46 | }); 47 | // HTML5 spf forward 48 | document.addEventListener('spfpartprocess', function (e) { 49 | if (e.detail && e.detail.part && e.detail.part.data && e.detail.part.data.swfcfg) { 50 | delete e.detail.part.data.swfcfg.args.ad3_module; 51 | if (document.location.href.indexOf('&list=') !== -1 && document.location.href.indexOf('&index=') !== -1) { 52 | return; 53 | } 54 | e.detail.part.data.swfcfg.args.autoplay = '0'; 55 | } 56 | }); 57 | `; 58 | document.documentElement.appendChild(script); 59 | -------------------------------------------------------------------------------- /WebExtension/data/inject.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // preferences 4 | const prefs = { 5 | playlist: false, 6 | visible: false, 7 | hidden: false, 8 | method: 'pauseVideo' 9 | }; 10 | const script = document.createElement('script'); 11 | Object.assign(script.dataset, prefs); 12 | { 13 | const play = () => { 14 | script.dispatchEvent(new Event('stop')); 15 | // document.removeEventListener('play', play, true); 16 | }; 17 | document.addEventListener('canplay', play, true); 18 | document.addEventListener('yt-navigate-finish', () => { 19 | document.removeEventListener('canplay', play, true); 20 | document.addEventListener('canplay', play, true); 21 | script.dispatchEvent(new Event('stop')); 22 | }); 23 | document.addEventListener('mousedown', () => document.removeEventListener('canplay', play, true)); 24 | document.addEventListener('keydown', () => document.removeEventListener('canplay', play, true)); 25 | script.addEventListener('release', () => document.removeEventListener('canplay', play, true)); 26 | } 27 | 28 | script.textContent = `{ 29 | const script = document.currentScript; 30 | const prefs = script.dataset; 31 | const player = () => document.querySelector('.html5-video-player') || { 32 | stopVideo: () => {}, 33 | pauseVideo: () => {}, 34 | playVideo: () => {} 35 | }; 36 | const policy = () => { 37 | const href = location.href; 38 | return prefs.playlist === 'true' || href.indexOf('&list=') === -1 || href.indexOf('&index=') === -1; 39 | }; 40 | const stop = () => { 41 | const method = script.dataset.method; 42 | if (player().getPlayerState() === -1) { 43 | script.dispatchEvent(new Event('release')); 44 | } 45 | player()[method](); 46 | }; 47 | script.addEventListener('stop', () => policy() && stop()); 48 | // visibility 49 | document.addEventListener('visibilitychange', () => { 50 | if (prefs.visible === 'true' && document.visibilityState === 'visible') { 51 | player().playVideo(); 52 | if (prefs.hidden === 'false') { 53 | prefs.visible = 'false'; 54 | } 55 | } 56 | if (prefs.hidden === 'true' && document.visibilityState === 'hidden') { 57 | player().pauseVideo(); 58 | } 59 | }); 60 | }`; 61 | document.documentElement.appendChild(script); 62 | script.remove(); 63 | // prefs 64 | chrome.storage.local.get(prefs, prefs => Object.assign(script.dataset, prefs)); 65 | chrome.storage.onChanged.addListener(prefs => { 66 | Object.entries(prefs).forEach(([key, value]) => script.dataset[key] = value.newValue); 67 | }); 68 | --------------------------------------------------------------------------------