HTML5 video settings

Change on load default HTML5 video behavior

  1. // ==UserScript==
  2. // @name HTML5 video settings
  3. // @name:ru Настройки HTML5 видео
  4. // @namespace html5-video-settings
  5. // @author smut
  6. // @version 2020.09.30.1
  7. // @icon https://img.icons8.com/color/344/video.png
  8. // @description Change on load default HTML5 video behavior
  9. // @description:ru Изменение дефолтных параметров воспроизведения HTML5 видео
  10. // @grant GM_log
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @grant GM_listValues
  14. // @grant GM_registerMenuCommand
  15. // @grant GM.cookie
  16. // @grant GM_util
  17. // @grant GM_util.timeout
  18. // @grant unsafeWindow
  19. // @grant window.close
  20. // @exclude /^https?:\/\/([^.]+\.)*?(youtube\.com|coub\.com|youtu\.be|pikabu\.ru)([:/]|$)/
  21. // @match *://*/*
  22. // ==/UserScript==
  23.  
  24. (function() {
  25.  
  26. 'use strict';
  27.  
  28. const win = (unsafeWindow || window);
  29.  
  30. const
  31. _Document = Object.getPrototypeOf(HTMLDocument.prototype),
  32. _Element = Object.getPrototypeOf(HTMLElement.prototype);
  33. const
  34. _Node = Object.getPrototypeOf(_Element);
  35.  
  36. const
  37. isSafari =
  38. Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 ||
  39. (function (p) {
  40. return p.toString() === "[object SafariRemoteNotification]";
  41. })(!window.safari || window.safari.pushNotification),
  42. isFirefox = 'InstallTrigger' in win,
  43. inIFrame = (win.self !== win.top);
  44. const
  45. _bindCall = fun => Function.prototype.call.bind(fun),
  46. _getAttribute = _bindCall(_Element.getAttribute),
  47. _setAttribute = _bindCall(_Element.setAttribute),
  48. _removeAttribute = _bindCall(_Element.removeAttribute),
  49. _hasOwnProperty = _bindCall(Object.prototype.hasOwnProperty),
  50. _toString = _bindCall(Function.prototype.toString),
  51. _document = win.document,
  52. _de = _document.documentElement,
  53. _appendChild = _Document.appendChild.bind(_de),
  54. _removeChild = _Document.removeChild.bind(_de),
  55. _createElement = _Document.createElement.bind(_document),
  56. _querySelector = _Document.querySelector.bind(_document),
  57. _querySelectorAll = _Document.querySelectorAll.bind(_document),
  58. _attachShadow = ('attachShadow' in _Element) ? _bindCall(_Element.attachShadow) : null,
  59. _apply = Reflect.apply,
  60. _construct = Reflect.construct;
  61.  
  62. let skipLander = true;
  63. try {
  64. skipLander = !(isFirefox && 'StopIteration' in win);
  65. } catch (ignore) {}
  66.  
  67. const jsf = (function () {
  68. const opts = {};
  69. let getValue = (a, b) => b,
  70. setValue = () => null,
  71. listValues = () => [];
  72. try {
  73. [getValue, setValue, listValues] = [GM_getValue, GM_setValue, GM_listValues];
  74. } catch (ignore) {}
  75. // defaults
  76. opts.Lang = 'eng';
  77. opts.controls = true;
  78. opts.loop = false;
  79. opts.autoplay = false;
  80. opts.muted = false;
  81. // load actual values
  82. for (let name of listValues())
  83. opts[name] = getValue(name, opts[name]);
  84. const checkName = name => {
  85. if (!_hasOwnProperty(opts, name))
  86. throw new Error('Attempt to access missing option value.');
  87. return true;
  88. };
  89. return new Proxy(opts, {
  90. get(opts, name) {
  91. if (name === 'toString')
  92. return () => JSON.stringify(opts);
  93. if (checkName(name))
  94. return opts[name];
  95. },
  96. set(opts, name, value) {
  97. if (checkName(name)) {
  98. opts[name] = value;
  99. setValue(name, value);
  100. }
  101. return true;
  102. }
  103. });
  104. })();
  105.  
  106. if (isFirefox && _document.constructor.prototype.toString() === '[object ImageDocumentPrototype]')
  107. return;
  108.  
  109. if (!NodeList.prototype[Symbol.iterator])
  110. NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
  111. if (!HTMLCollection.prototype[Symbol.iterator])
  112. HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
  113.  
  114. if (GM.cookie === undefined)
  115. GM.cookie = {
  116. list: () => ({
  117. then: () => null
  118. })
  119. };
  120.  
  121. const
  122. batchLand = [],
  123. batchPrepend = new Set(),
  124. _APIString = `const win = window, isFirefox = ${isFirefox}, inIFrame = ${inIFrame}, _document = win.document, _de = _document.documentElement,
  125. _Document = Object.getPrototypeOf(HTMLDocument.prototype), _Element = Object.getPrototypeOf(HTMLElement.prototype), _Node = Object.getPrototypeOf(_Element),
  126. _appendChild = _Document.appendChild.bind(_de), _removeChild = _Document.removeChild.bind(_de), skipLander = ${skipLander},
  127. _createElement = _Document.createElement.bind(_document), _querySelector = _Document.querySelector.bind(_document),
  128. _querySelectorAll = _Document.querySelectorAll.bind(_document), _bindCall = fun => Function.prototype.call.bind(fun),
  129. _getAttribute = _bindCall(_Element.getAttribute), _setAttribute = _bindCall(_Element.setAttribute),
  130. _removeAttribute = _bindCall(_Element.removeAttribute), _hasOwnProperty = _bindCall(Object.prototype.hasOwnProperty),
  131. _toString = _bindCall(Function.prototype.toString), _apply = Reflect.apply, _construct = Reflect.construct;
  132. const GM = { info: { version: '0.0', scriptHandler: null }, cookie: { list: () => ({ then: () => null }) } };
  133. const jsf = ${jsf.toString()}`,
  134. landScript = (f, pre) => {
  135. const script = _createElement('script');
  136. script.textContent = `(()=>{${_APIString}${[...pre].join(';')};(${f.join(')();(')})();})();`;
  137. _appendChild(script);
  138. _removeChild(script);
  139. },
  140. startdelay = 2000,
  141. clickdelay = 1000,
  142. playdelay = 200;
  143. var first_load_mute = false;
  144. var play_click_iframe = false;
  145. var play_click_timeout;
  146.  
  147. let scriptLander = f => f();
  148. if (!skipLander) {
  149. scriptLander = (func, ...prepend) => {
  150. prepend.forEach(x => batchPrepend.add(x));
  151. batchLand.push(func);
  152. };
  153. _document.addEventListener(
  154. 'DOMContentLoaded', () => void(scriptLander = (f, ...prep) => landScript([f], prep)), false
  155. );
  156. }
  157.  
  158. function play_click_switch(play_switch) {
  159. play_click_iframe = play_switch;
  160. }
  161. function html5_video_set(play_click) {
  162. for (var e of document.getElementsByTagName('video')){
  163. if (jsf.controls){
  164. e.setAttribute('controls', '');
  165. e.controls = "controls";
  166. if (play_click){
  167. if (window.location.hostname === 'www.instagram.com'){
  168. var instaoverlay = document.querySelector('.PyenC');
  169. var isntacontrol = document.querySelector('.fXIG0');
  170. //console.log (instaoverlay);
  171. if(document.querySelector('.PyenC')){
  172. instaoverlay.parentNode.removeChild(instaoverlay);
  173. }
  174. if(document.querySelector('.fXIG0')){
  175. isntacontrol.parentNode.removeChild(isntacontrol);
  176. }
  177. }
  178. }
  179. }else{
  180. e.removeAttribute('controls');
  181. e.controls = "";
  182. }
  183. if (jsf.loop){
  184. e.setAttribute('loop', '');
  185. e.loop = "loop";
  186. }else{
  187. e.removeAttribute('loop');
  188. e.loop = "";
  189. }
  190. if (jsf.muted && !play_click){
  191. e.setAttribute('muted', '');
  192. e.muted = "muted";
  193. first_load_mute = true;
  194. }else{
  195. e.removeAttribute('muted');
  196. }
  197. if (jsf.autoplay && !play_click){
  198. //console.log("autoplay");
  199. e.setAttribute('autoplay', '');
  200. e.autoplay = "autoplay";
  201. e.play();
  202. }else if (!play_click){
  203. e.removeAttribute('autoplay');
  204. e.autoplay = "";
  205. e.pause();
  206. //console.log("pause");
  207. }
  208. };
  209. }
  210.  
  211. const createStyle = (function createStyleModule() {
  212. function createStyleElement(rules, opts) {
  213. const style = _createElement('style');
  214. Object.assign(style, opts.props);
  215. opts.root.appendChild(style);
  216.  
  217. if (style.sheet) // style.sheet is only available when style attached to DOM
  218. rules.forEach(style.sheet.insertRule.bind(style.sheet));
  219. else
  220. style.textContent = rules.join('\n');
  221.  
  222. if (opts.protect) {
  223. Object.defineProperty(style, 'sheet', {
  224. value: null,
  225. enumerable: true
  226. });
  227. Object.defineProperty(style, 'disabled', { //pretend to be disabled
  228. enumerable: true,
  229. set() {},
  230. get() {
  231. return true;
  232. }
  233. });
  234. (new MutationObserver(
  235. () => opts.root.removeChild(style)
  236. )).observe(style, {
  237. childList: true
  238. });
  239. }
  240.  
  241. return style;
  242. }
  243.  
  244. // functions to parse object-based rulesets
  245. function parseRule(rec) {
  246. /* jshint validthis: true */
  247. return this.concat(rec[0], ' {\n', Object.entries(rec[1]).map(parseProperty, this + '\t').join('\n'), '\n', this, '}');
  248. }
  249.  
  250. function parseProperty(rec) {
  251. /* jshint validthis: true */
  252. return rec[1] instanceof Object ? parseRule.call(this, rec) : `${this}${rec[0].replace(/_/g, '-')}: ${rec[1]};`;
  253. }
  254.  
  255. // main
  256. const createStyle = (rules, opts) => {
  257. // parse options
  258. opts = Object.assign({
  259. protect: true,
  260. root: _de,
  261. type: 'text/css'
  262. }, opts);
  263. // move style properties into separate property
  264. // { a, b, ...rest } construction is not available in Fx 52
  265. opts.props = Object.assign({}, opts);
  266. delete opts.props.protect;
  267. delete opts.props.root;
  268. // store binded methods instead of element
  269. opts.root = {
  270. appendChild: opts.root.appendChild.bind(opts.root),
  271. removeChild: opts.root.removeChild.bind(opts.root)
  272. };
  273.  
  274. // convert rules set into an array if it isn't one already
  275. rules = Array.isArray(rules) ? rules : rules instanceof Object ? Object.entries(rules).map(parseRule, '') : [rules];
  276.  
  277. // could be reassigned when protection triggered
  278. let style = createStyleElement(rules, opts);
  279.  
  280. if (!opts.protect)
  281. return style;
  282.  
  283. const replaceStyle = () => new Promise(
  284. resolve => setTimeout(re => re(createStyleElement(rules, opts)), 0, resolve)
  285. ).then(st => (style = st)); // replace poiner to style object with a new style object
  286.  
  287. (new MutationObserver(ms => {
  288. for (let m of ms)
  289. for (let node of m.removedNodes)
  290. if (node === style) replaceStyle();
  291. })).observe(_de, {
  292. childList: true
  293. });
  294.  
  295.  
  296. return style;
  297. };
  298. createStyle.toString = () => `const createStyle = (${createStyleModule.toString()})();`;
  299. return createStyle;
  300. })();
  301.  
  302. const lines = {
  303. linked: [],
  304. MenuOptions: {
  305. eng: 'Options',
  306. rus: 'Настройки'
  307. },
  308. langs: {
  309. eng: 'English',
  310. rus: 'Русский'
  311. },
  312. HeaderName: {
  313. eng: 'HTML5 video settings',
  314. rus: 'Настройки HTML5 видео'
  315. },
  316. HeaderTools: {
  317. eng: 'Tools',
  318. rus: 'Инструменты'
  319. },
  320. HeaderOptions: {
  321. eng: 'Options',
  322. rus: 'Настройки'
  323. },
  324. controlsLabel: {
  325. eng: 'Show controls',
  326. rus: 'Отображать элементы управления'
  327. },
  328. loopLabel: {
  329. eng: 'Loop video',
  330. rus: 'Повтор видео'
  331. },
  332. autoplayLabel: {
  333. eng: 'Autoplay video',
  334. rus: 'Автоматическое воспроизведение видео'
  335. },
  336. autoplayTip: {
  337. eng: 'Autoplay may not working if "Mute sound" not enabled',
  338. rus: 'Автовоспроизведение может не работать, если не установлен режим \"отключить звук\"'
  339. },
  340. mutedLabel: {
  341. eng: 'Mute sound',
  342. rus: 'Отключить звук'
  343. },
  344. reg(el, name) {
  345. this[name].link = el;
  346. this.linked.push(name);
  347. },
  348. setLang(lang = 'eng') {
  349. for (let name of this.linked) {
  350. const el = this[name].link;
  351. const label = this[name][lang];
  352. el.textContent = label;
  353. }
  354. this.langs.link.value = lang;
  355. jsf.Lang = lang;
  356. }
  357. };
  358.  
  359. const _createTextNode = _Document.createTextNode.bind(_document);
  360. const createOptionsWindow = () => {
  361. const root = _createElement('div'),
  362. shadow = _attachShadow ? _attachShadow(root, {
  363. mode: 'closed'
  364. }) : root,
  365. overlay = _createElement('div'),
  366. inner = _createElement('div');
  367.  
  368. overlay.id = 'overlay';
  369. overlay.appendChild(inner);
  370. shadow.appendChild(overlay);
  371.  
  372. inner.id = 'inner';
  373. inner.br = function appendBreakLine() {
  374. return this.appendChild(_createElement('br'));
  375. };
  376.  
  377. createStyle({
  378. 'h2': {
  379. margin_top: 0,
  380. white_space: 'nowrap'
  381. },
  382. 'h2, h3': {
  383. margin_block_end: '0.5em'
  384. },
  385. 'h4': {
  386. margin_block_start: '0em',
  387. margin_block_end: '0.5em',
  388. margin_left: '0.4em',
  389. font_family: 'Helvetica, Arial, sans-serif',
  390. font_size: '8pt',
  391. font_style: 'italic',
  392. font_weight: 'normal'
  393. },
  394. 'div, button, select, input': {
  395. font_family: 'Helvetica, Arial, sans-serif',
  396. font_size: '12pt'
  397. },
  398. 'select': {
  399. border: '1px solid darkgrey',
  400. border_radius: '0px 0px 5px 5px',
  401. border_top: '0px'
  402. },
  403. '#overlay': {
  404. position: 'fixed',
  405. top: 0,
  406. left: 0,
  407. bottom: 0,
  408. right: 0,
  409. background: 'rgba(0,0,0,0.65)',
  410. z_index: 2147483647 // Highest z-index: Math.pow(2, 31) - 1
  411. },
  412. '#inner': {
  413. background: 'whitesmoke',
  414. color: 'black',
  415. padding: '1.5em 1em 1.5em 1em',
  416. max_width: '150ch',
  417. position: 'absolute',
  418. top: '50%',
  419. left: '50%',
  420. transform: 'translate(-50%, -50%)',
  421. border: '1px solid darkgrey',
  422. border_radius: '5px'
  423. },
  424. '#closeOptionsButton': {
  425. float: 'right',
  426. transform: 'translate(1em, -1.5em)',
  427. border: 0,
  428. border_radius: 0,
  429. background: 'none',
  430. box_shadow: 'none'
  431. },
  432. '#selectLang': {
  433. float: 'right',
  434. transform: 'translate(0, -1.5em)'
  435. },
  436. '.optionsLabel': {
  437. padding_left: '1.5em',
  438. text_indent: '-1em',
  439. display: 'block'
  440. },
  441. '.optionsCheckbox': {
  442. left: '-0.25em',
  443. width: '1em',
  444. height: '1em',
  445. padding: 0,
  446. margin: 0,
  447. position: 'relative',
  448. vertical_align: 'middle'
  449. },
  450. '@media (prefers-color-scheme: dark)': {
  451. '#inner': {
  452. background_color: '#292a2d',
  453. color: 'white',
  454. border: '1px solid #1a1b1e'
  455. },
  456. 'input': {
  457. filter: 'invert(100%)'
  458. },
  459. 'select': {
  460. background_color: '#303030',
  461. color: '#f0f0f0',
  462. border: '1px solid #1a1b1e',
  463. border_radius: '0px 0px 5px 5px',
  464. border_top: '0px'
  465. },
  466. '#overlay': {
  467. background: 'rgba(0,0,0,.85)',
  468. }
  469. }
  470. }, {
  471. root: shadow,
  472. protect: false
  473. });
  474.  
  475. // components
  476. function createCheckbox(name) {
  477. const checkbox = _createElement('input'),
  478. label = _createElement('label');
  479. checkbox.type = 'checkbox';
  480. checkbox.classList.add('optionsCheckbox');
  481. checkbox.checked = jsf[name];
  482. checkbox.id = name+'_checkbox';
  483. checkbox.onclick = e => {
  484. jsf[name] = e.target.checked;
  485. return true;
  486. };
  487. label.classList.add('optionsLabel');
  488. label.appendChild(checkbox);
  489. const text = _createTextNode('');
  490. label.appendChild(text);
  491. Object.defineProperty(label, 'textContent', {
  492. set(title) {
  493. text.textContent = title;
  494. }
  495. });
  496. return label;
  497. }
  498.  
  499. // language & close
  500. const closeBtn = _createElement('button');
  501. closeBtn.onclick = () => _removeChild(root);
  502. closeBtn.textContent = '\u2715';
  503. closeBtn.id = 'closeOptionsButton';
  504. inner.appendChild(closeBtn);
  505.  
  506. overlay.addEventListener('click', e => {
  507. if (e.target === overlay) {
  508. _removeChild(root);
  509. e.preventDefault();
  510. }
  511. e.stopPropagation();
  512. }, false);
  513.  
  514. const selectLang = _createElement('select');
  515. for (let name in lines.langs) {
  516. const langOption = _createElement('option');
  517. langOption.value = name;
  518. langOption.innerText = lines.langs[name];
  519. selectLang.appendChild(langOption);
  520. }
  521. selectLang.id = 'selectLang';
  522. lines.langs.link = selectLang;
  523. inner.appendChild(selectLang);
  524.  
  525. selectLang.onchange = e => {
  526. const lang = e.target.value;
  527. lines.setLang(lang);
  528. };
  529.  
  530. // fill options form
  531.  
  532. lines.reg(inner.appendChild(_createElement('h2')), 'HeaderName');
  533.  
  534. lines.reg(inner.appendChild(_createElement('h3')), 'HeaderOptions');
  535.  
  536. lines.reg(inner.appendChild(createCheckbox('controls')), 'controlsLabel');
  537. lines.reg(inner.appendChild(createCheckbox('loop')), 'loopLabel');
  538. lines.reg(inner.appendChild(createCheckbox('autoplay')), 'autoplayLabel');
  539.  
  540. lines.reg(inner.appendChild(_createElement('h4')), 'autoplayTip');
  541.  
  542. lines.reg(inner.appendChild(createCheckbox('muted')), 'mutedLabel');
  543.  
  544. lines.setLang(jsf.Lang);
  545.  
  546. return root;
  547. };
  548.  
  549. let optionsWindow;
  550. GM_registerMenuCommand(lines.MenuOptions[jsf.Lang], () => _appendChild(optionsWindow = optionsWindow || createOptionsWindow()));
  551.  
  552. if( document.readyState !== 'loading' ) {
  553. setTimeout (function () {html5_video_set;}, startdelay);
  554. } else {
  555. document.addEventListener('DOMContentLoaded', function () {
  556. setTimeout (function () {html5_video_set;}, startdelay);
  557. });
  558. }
  559. function video_click(){
  560. play_click_iframe = true;
  561. //console.log("play_click_iframe = " + play_click_iframe);
  562. if(play_click_iframe) {
  563. clearTimeout(play_click_timeout);
  564. play_click_timeout = setTimeout(function () {play_click_switch(false);}, clickdelay);
  565. //setTimeout(function () {console.log("play_click_iframe = " + play_click_iframe);}, clickdelay+100);
  566. }
  567. //console.log("clicked");
  568.  
  569. };
  570. document.addEventListener('play', function(e){
  571.  
  572. document.addEventListener('click', video_click, true);
  573. setTimeout (function () {html5_video_set(play_click_iframe);}, playdelay);
  574. //console.log("play");
  575. }, true);
  576.  
  577. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址