Greasy Fork is available in English.

Custom Native HTML5 Player with Shortcuts

Custom html5 player with shortcuts and v.redd.it videos with audio

Verze ze dne 09. 12. 2020. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name Custom Native HTML5 Player with Shortcuts
  3. // @namespace https://gist.github.com/narcolepticinsomniac
  4. // @version 1.2
  5. // @description Custom html5 player with shortcuts and v.redd.it videos with audio
  6. // @author narcolepticinsomniac
  7. // @include *
  8. // @require https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
  9. // @run-at document-start
  10. // @grant GM_xmlhttpRequest
  11. // @connect v.redd.it
  12. // ==/UserScript==
  13.  
  14. let imagusAudio;
  15. let audioSync;
  16. let audioError;
  17. let ytID;
  18. let ytTimeChecked;
  19. const $ = document.querySelector.bind(document);
  20. const $$ = document.querySelectorAll.bind(document);
  21.  
  22. const settings = {
  23. // delay to hide contols and cursor if inactive (set to 3000 milliseconds)
  24. hideControls: 3000,
  25. // delay for fullscreen double-click (set to 300 milliseconds)
  26. clickDelay: 300,
  27. // right-click delay to match imagus user setting (set to 0 milliseconds)
  28. imagusStickyDelay: 0,
  29. // right/left arrows keys or inner skip buttons (set to 10 seconds)
  30. skipNormal: 10,
  31. // Shift + Arrow keys or outer skip buttons (set to 30 seconds)
  32. skipShift: 30,
  33. // Ctrl + Arrow keys skip (set to 1 minute)
  34. skipCtrl: 1,
  35. };
  36.  
  37. const shortcutFuncs = {
  38. toggleCaptions: v => {
  39. const validTracks = [];
  40. for (let i = 0; i < v.textTracks.length; ++i) {
  41. const tt = v.textTracks[i];
  42. if (tt.mode === 'showing') {
  43. tt.mode = 'disabled';
  44. if (v.textTracks.addEventListener) {
  45. // If text track event listeners are supported
  46. // (they are on the most recent Chrome), add
  47. // a marker to remember the old track. Use a
  48. // listener to delete it if a different track
  49. // is selected.
  50. v.cbhtml5vsLastCaptionTrack = tt.label;
  51.  
  52. function cleanup(e) {
  53. for (let i = 0; i < v.textTracks.length; ++i) {
  54. const ott = v.textTracks[i];
  55. if (ott.mode === 'showing') {
  56. delete v.cbhtml5vsLastCaptionTrack;
  57. v.textTracks.removeEventListener('change', cleanup);
  58. return;
  59. }
  60. }
  61. }
  62. v.textTracks.addEventListener('change', cleanup);
  63. }
  64. return;
  65. } else if (tt.mode !== 'hidden') {
  66. validTracks.push(tt);
  67. }
  68. }
  69. // If we got here, none of the tracks were selected.
  70. if (validTracks.length === 0) {
  71. return true; // Do not prevent default if no UI activated
  72. }
  73. // Find the best one and select it.
  74. validTracks.sort((a, b) => {
  75. if (v.cbhtml5vsLastCaptionTrack) {
  76. const lastLabel = v.cbhtml5vsLastCaptionTrack;
  77.  
  78. if (a.label === lastLabel && b.label !== lastLabel) {
  79. return -1;
  80. } else if (b.label === lastLabel && a.label !== lastLabel) {
  81. return 1;
  82. }
  83. }
  84.  
  85. const aLang = a.language.toLowerCase();
  86. const bLang = b.language.toLowerCase();
  87. const navLang = navigator.language.toLowerCase();
  88.  
  89. if (aLang === navLang && bLang !== navLang) {
  90. return -1;
  91. } else if (bLang === navLang && aLang !== navLang) {
  92. return 1;
  93. }
  94.  
  95. const aPre = aLang.split('-')[0];
  96. const bPre = bLang.split('-')[0];
  97. const navPre = navLang.split('-')[0];
  98.  
  99. if (aPre === navPre && bPre !== navPre) {
  100. return -1;
  101. } else if (bPre === navPre && aPre !== navPre) {
  102. return 1;
  103. }
  104.  
  105. return 0;
  106. })[0].mode = 'showing';
  107. },
  108.  
  109. togglePlay: v => {
  110. v.paused ? v.play() : v.pause();
  111. },
  112.  
  113. toStart: v => {
  114. v.currentTime = 0;
  115. },
  116.  
  117. toEnd: v => {
  118. v.currentTime = v.duration;
  119. },
  120.  
  121. skipLeft: (v, key, shift, ctrl) => {
  122. if (shift) {
  123. v.currentTime -= settings.skipShift;
  124. } else if (ctrl) {
  125. v.currentTime -= settings.skipCtrl;
  126. } else {
  127. v.currentTime -= settings.skipNormal;
  128. }
  129. },
  130.  
  131. skipRight: (v, key, shift, ctrl) => {
  132. if (shift) {
  133. v.currentTime += settings.skipShift;
  134. } else if (ctrl) {
  135. v.currentTime += settings.skipCtrl;
  136. } else {
  137. v.currentTime += settings.skipNormal;
  138. }
  139. },
  140.  
  141. increaseVol: v => {
  142. if (audioError) return;
  143. if (v.nextSibling.querySelector('volume.disabled')) {
  144. v.volume = 0;
  145. return;
  146. }
  147. const increase = (v.volume + 0.1).toFixed(1);
  148. if (v.muted) {
  149. v.muted = !v.muted;
  150. v.volume = 0.1;
  151. } else {
  152. v.volume <= 0.9 ? v.volume = increase : v.volume = 1;
  153. }
  154. },
  155.  
  156. decreaseVol: v => {
  157. if (audioError) return;
  158. if (v.nextSibling.querySelector('volume.disabled')) {
  159. v.volume = 0;
  160. return;
  161. }
  162. const decrease = (v.volume - 0.1).toFixed(1);
  163. v.volume >= 0.1 ? v.volume = decrease : v.volume = 0;
  164. },
  165.  
  166. toggleMute: v => {
  167. v.muted = !v.muted;
  168. if (audioSync) imagusAudio.muted = v.muted;
  169. },
  170.  
  171. toggleFS: v => {
  172. if (document.fullscreenElement) {
  173. document.exitFullscreen();
  174. v.parentElement.classList.remove('native-fullscreen');
  175. } else {
  176. v.parentElement.classList.add('native-fullscreen');
  177. v.parentElement.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  178. }
  179. },
  180.  
  181. reloadVideo: v => {
  182. const currTime = v.currentTime;
  183. v.load();
  184. v.currentTime = currTime;
  185. },
  186.  
  187. slowOrPrevFrame: (v, key, shift) => {
  188. if (shift) { // Less-Than
  189. v.currentTime -= 1 / 60;
  190. } else { // Comma
  191. if (v.playbackRate >= 0.1) {
  192. const decrease = (v.playbackRate - 0.1).toFixed(2);
  193. const rate = v.nextSibling.querySelector('rate');
  194. v.playbackRate = decrease;
  195. rate.textContent = `${v.playbackRate}x`;
  196. if (v.playbackRate !== 1) {
  197. rate.setAttribute('data-current-rate', `${v.playbackRate}x`);
  198. }
  199. if (v.playbackRate === 0.9) {
  200. v.classList.add('playback-rate-decreased');
  201. } else if (v.playbackRate === 1.1) {
  202. v.classList.add('playback-rate-increased');
  203. } else if (v.playbackRate === 1) {
  204. v.classList.remove('playback-rate-decreased');
  205. v.classList.remove('playback-rate-increased');
  206. rate.removeAttribute('data-current-rate');
  207. }
  208. } else {
  209. v.playbackRate = 0;
  210. }
  211. if (audioSync) imagusAudio.playbackRate = v.playbackRate;
  212. }
  213. },
  214.  
  215. fastOrNextFrame: (v, key, shift) => {
  216. if (shift) { // Greater-Than
  217. v.currentTime += 1 / 60;
  218. } else { // Period
  219. if (v.playbackRate <= 15.9) {
  220. const increase = (v.playbackRate += 0.1).toFixed(2);
  221. const rate = v.nextSibling.querySelector('rate');
  222. v.playbackRate = increase;
  223. rate.textContent = `${v.playbackRate}x`;
  224. if (v.playbackRate !== 1) {
  225. rate.setAttribute('data-current-rate', `${v.playbackRate}x`);
  226. }
  227. if (v.playbackRate === 0.9) {
  228. v.classList.add('playback-rate-decreased');
  229. } else if (v.playbackRate === 1.1) {
  230. v.classList.add('playback-rate-increased');
  231. } else if (v.playbackRate === 1) {
  232. v.classList.remove('playback-rate-decreased');
  233. v.classList.remove('playback-rate-increased');
  234. rate.removeAttribute('data-current-rate');
  235. }
  236. } else {
  237. v.playbackRate = 16;
  238. }
  239. if (audioSync) imagusAudio.playbackRate = v.playbackRate;
  240. }
  241. },
  242.  
  243. normalSpeed: v => { // ?
  244. v.playbackRate = v.defaultPlaybackRate;
  245. if (audioSync) imagusAudio.playbackRate = v.playbackRate;
  246. v.classList.remove('playback-rate-decreased');
  247. v.classList.remove('playback-rate-increased');
  248. v.nextSibling.querySelector('rate').textContent = '1x';
  249. v.nextSibling.querySelector('rate').removeAttribute('data-current-rate');
  250. },
  251.  
  252. toPercentage: (v, key) => {
  253. v.currentTime = (v.duration * (key - 48)) / 10.0;
  254. },
  255. };
  256.  
  257. const keyFuncs = {
  258. 32: shortcutFuncs.togglePlay, // Space
  259. 75: shortcutFuncs.togglePlay, // K
  260. 35: shortcutFuncs.toEnd, // End
  261. 48: shortcutFuncs.toStart, // 0
  262. 36: shortcutFuncs.toStart, // Home
  263. 37: shortcutFuncs.skipLeft, // Left arrow
  264. 74: shortcutFuncs.skipLeft, // J
  265. 39: shortcutFuncs.skipRight, // Right arrow
  266. 76: shortcutFuncs.skipRight, // L
  267. 38: shortcutFuncs.increaseVol, // Up arrow
  268. 40: shortcutFuncs.decreaseVol, // Down arrow
  269. 77: shortcutFuncs.toggleMute, // M
  270. 70: shortcutFuncs.toggleFS, // F
  271. 67: shortcutFuncs.toggleCaptions, // C
  272. 82: shortcutFuncs.reloadVideo, // R
  273. 188: shortcutFuncs.slowOrPrevFrame, // Comma or Less-Than
  274. 190: shortcutFuncs.fastOrNextFrame, // Period or Greater-Than
  275. 191: shortcutFuncs.normalSpeed, // Forward slash or ?
  276. 49: shortcutFuncs.toPercentage, // 1
  277. 50: shortcutFuncs.toPercentage, // 2
  278. 51: shortcutFuncs.toPercentage, // 3
  279. 52: shortcutFuncs.toPercentage, // 4
  280. 53: shortcutFuncs.toPercentage, // 5
  281. 54: shortcutFuncs.toPercentage, // 6
  282. 55: shortcutFuncs.toPercentage, // 7
  283. 56: shortcutFuncs.toPercentage, // 8
  284. 57: shortcutFuncs.toPercentage, // 9
  285. };
  286.  
  287. function customPlayer(v) {
  288. let videoWrapper;
  289. let savedTimeKey;
  290. let mouseDown;
  291. let isPlaying;
  292. let isSeeking;
  293. let earlyXposPercent;
  294. let preventMouseMove;
  295. let controlsTimeout;
  296. let imagusMouseTimeout;
  297. let imagusVid;
  298. let muteTillSync;
  299. let loaded;
  300. let error;
  301. let elToFocus;
  302. let clickCount = 0;
  303. let repeat = 0;
  304. const directVideo = /video/.test(document.contentType) &&
  305. document.body.firstElementChild === v;
  306. const controls = document.createElement('controls');
  307. const imagus = v.classList.contains('imagus');
  308. if (imagus && !imagusVid) {
  309. imagusVid = v;
  310. imagusAudio = document.createElement('video');
  311. imagusAudio.preload = 'auto';
  312. imagusAudio.autoplay = 'true';
  313. imagusAudio.className = 'imagus imagus-audio';
  314. imagusAudio.style = 'display: none!important;';
  315. imagusVid.parentElement.insertBefore(imagusAudio, imagusVid);
  316. }
  317. if (directVideo) {
  318. elToFocus = document.body;
  319. self === top ? document.body.classList.add('direct-video-top-level') :
  320. document.body.classList.add('direct-video-embed');
  321. } else {
  322. elToFocus = v;
  323. videoWrapper = document.createElement('videowrapper');
  324. v.parentNode.insertBefore(videoWrapper, v);
  325. videoWrapper.appendChild(v);
  326. if (!imagus) {
  327. const compStyles = getComputedStyle(v);
  328. const position = compStyles.getPropertyValue('position');
  329. const zIndex = compStyles.getPropertyValue('z-index');
  330. if (position === 'absolute') {
  331. videoWrapper.style.setProperty('--wrapper-position', `${position}`);
  332. }
  333. if (zIndex !== 'auto') {
  334. controls.style.setProperty('--controls-z-index', `calc(${zIndex} + 1)`);
  335. }
  336. }
  337. }
  338. v.parentNode.insertBefore(controls, v.nextSibling);
  339. const playButton = document.createElement('btn');
  340. playButton.className = 'toggle-play';
  341. controls.appendChild(playButton);
  342. const beginButton = document.createElement('btn');
  343. beginButton.className = 'begin';
  344. controls.appendChild(beginButton);
  345. const skipLongLeft = document.createElement('btn');
  346. skipLongLeft.className = 'skip-long left';
  347. controls.appendChild(skipLongLeft);
  348. const skipShortLeft = document.createElement('btn');
  349. skipShortLeft.className = 'skip-short left';
  350. controls.appendChild(skipShortLeft);
  351. const skipShortRight = document.createElement('btn');
  352. skipShortRight.className = 'skip-short right';
  353. controls.appendChild(skipShortRight);
  354. const skipLongRight = document.createElement('btn');
  355. skipLongRight.className = 'skip-long right';
  356. controls.appendChild(skipLongRight);
  357. const timelineWrapper = document.createElement('timelinewrapper');
  358. controls.appendChild(timelineWrapper);
  359. const currentTime = document.createElement('currenttime');
  360. currentTime.textContent = '0:00';
  361. timelineWrapper.appendChild(currentTime);
  362. const timeline = document.createElement('timeline');
  363. timelineWrapper.appendChild(timeline);
  364. const timeBar = document.createElement('timebar');
  365. timeline.appendChild(timeBar);
  366. const timeBuffer = document.createElement('timebuffer');
  367. timeBar.appendChild(timeBuffer);
  368. const timeProgress = document.createElement('timeprogress');
  369. timeBar.appendChild(timeProgress);
  370. const timeSlider = document.createElement('input');
  371. timeSlider.type = 'range';
  372. timeSlider.value = 0;
  373. timeSlider.min = 0;
  374. timeSlider.max = 100;
  375. timeSlider.step = 0.01;
  376. timeSlider.textContent = '';
  377. timeline.appendChild(timeSlider);
  378. const timeTooltip = document.createElement('timetooltip');
  379. timeTooltip.className = 'hidden';
  380. timeTooltip.textContent = '-:-';
  381. timeline.appendChild(timeTooltip);
  382. const totalTime = document.createElement('totaltime');
  383. totalTime.textContent = '-:-';
  384. timelineWrapper.appendChild(totalTime);
  385. const rateDecrease = document.createElement('btn');
  386. rateDecrease.className = 'rate-decrease';
  387. controls.appendChild(rateDecrease);
  388. const rate = document.createElement('rate');
  389. rate.textContent = '1x';
  390. controls.appendChild(rate);
  391. const rateIncrease = document.createElement('btn');
  392. rateIncrease.className = 'rate-increase';
  393. controls.appendChild(rateIncrease);
  394. const volume = document.createElement('volume');
  395. controls.appendChild(volume);
  396. const volumeBar = document.createElement('volumebar');
  397. volume.appendChild(volumeBar);
  398. const volumeTrail = document.createElement('volumetrail');
  399. volumeBar.appendChild(volumeTrail);
  400. const volumeSlider = document.createElement('input');
  401. volumeSlider.type = 'range';
  402. volumeSlider.min = 0;
  403. volumeSlider.max = 1;
  404. volumeSlider.step = 0.01;
  405. volumeSlider.textContent = '';
  406. volume.appendChild(volumeSlider);
  407. const volumeTooltip = document.createElement('volumetooltip');
  408. volumeTooltip.className = 'hidden';
  409. volumeTooltip.textContent = '0%';
  410. volume.appendChild(volumeTooltip);
  411. const muteButton = document.createElement('btn');
  412. muteButton.className = 'mute';
  413. controls.appendChild(muteButton);
  414. const expandButton = document.createElement('btn');
  415. expandButton.className = 'expand';
  416. controls.appendChild(expandButton);
  417. v.classList.remove('custom-native-player-hidden');
  418. if (v.querySelector('source')) v.classList.add('contains-source');
  419. if (videoWrapper) enforcePosition();
  420. volumeValues();
  421.  
  422. v.onloadedmetadata = () => {
  423. loaded = true;
  424. shortcutFuncs.normalSpeed(v);
  425. savedTimeKey = `${location.pathname}${location.search}${v.duration}`;
  426. const savedTime = localStorage.getItem(savedTimeKey);
  427. if (timeSlider.value === '0') {
  428. if (savedTime) v.currentTime = savedTime;
  429. } else if (earlyXposPercent) {
  430. const time = (earlyXposPercent * v.duration) / 100;
  431. v.currentTime = time;
  432. }
  433. currentTime.textContent = formatTime(v.currentTime);
  434. totalTime.textContent = formatTime(v.duration);
  435. v.classList.remove('disabled');
  436. sliderValues();
  437. };
  438.  
  439. v.onloadeddata = () => {
  440. const imagusVreddit = /v(cf)?\.redd\.it/.test(v.src);
  441. const vHasAudio = hasAudio(v);
  442. if (!vHasAudio && !imagusVreddit) {
  443. v.classList.add('muted');
  444. volumeSlider.value = 0;
  445. muteButton.classList.add('disabled');
  446. volume.classList.add('disabled');
  447. } else if (vHasAudio && !imagusVreddit) {
  448. if (v.volume && !v.muted) v.classList.remove('muted');
  449. volumeValues();
  450. if (volume.classList.contains('disabled')) {
  451. muteButton.classList.remove('disabled');
  452. volume.classList.remove('disabled');
  453. }
  454. }
  455. elToFocus.focus({preventScroll: true});
  456. if (v.duration <= settings.skipNormal) {
  457. skipShortLeft.classList.add('disabled');
  458. skipShortRight.classList.add('disabled');
  459. } else {
  460. skipShortLeft.classList.remove('disabled');
  461. skipShortRight.classList.remove('disabled');
  462. }
  463. if (v.duration <= settings.skipShift) {
  464. skipLongLeft.classList.add('disabled');
  465. skipLongRight.classList.add('disabled');
  466. } else {
  467. skipLongLeft.classList.remove('disabled');
  468. skipLongRight.classList.remove('disabled');
  469. }
  470. if (v.paused) {
  471. v.classList.add('paused');
  472. if (videoWrapper) videoWrapper.classList.add('paused');
  473. }
  474. if (imagus) v.currentTime = 0;
  475. };
  476.  
  477. v.oncanplay = () => {
  478. v.oncanplay = null;
  479. if (!loaded) {
  480. v.load();
  481. console.log('Custom native player reloaded');
  482. }
  483. };
  484.  
  485. v.onprogress = () => {
  486. if (v.readyState > 1 && v.duration > 0) {
  487. const buffer = (v.buffered.end(v.buffered.length - 1) / v.duration) * 100;
  488. timeBuffer.style.width = `${buffer}%`;
  489. }
  490. };
  491.  
  492. v.ontimeupdate = () => {
  493. if (v.readyState > 0) {
  494. if (v.duration > 0 && !mouseDown) {
  495. sliderValues();
  496. totalTime.textContent = formatTime(v.duration);
  497. if (!imagus && savedTimeKey) localStorage.setItem(savedTimeKey, v.currentTime)
  498. }
  499. }
  500. };
  501.  
  502. v.onvolumechange = () => {
  503. if (audioError) return;
  504. if (audioSync) imagusAudio.volume = v.volume;
  505. if (v.muted || !v.volume) {
  506. v.classList.add('muted');
  507. volumeSlider.value = 0;
  508. volumeTrail.style.width = '0';
  509. localStorage.setItem('videomuted', 'true');
  510. } else {
  511. v.classList.remove('muted');
  512. sliderValues();
  513. v.volume > 0.1 ? localStorage.setItem('videovolume', v.volume) :
  514. localStorage.setItem('videovolume', 0.1);
  515. localStorage.setItem('videomuted', 'false');
  516. }
  517. };
  518.  
  519. v.onplay = () => {
  520. if (v === imagusVid && audioSync) imagusAudio.play();
  521. v.classList.remove('paused');
  522. if (videoWrapper) videoWrapper.classList.remove('paused');
  523. v.classList.add('playing');
  524. };
  525.  
  526. v.onpause = () => {
  527. if (v === imagusVid && audioSync) imagusAudio.pause();
  528. if (!isSeeking) {
  529. v.classList.remove('playing');
  530. v.classList.add('paused');
  531. if (videoWrapper) videoWrapper.classList.add('paused');
  532. }
  533. };
  534.  
  535. v.onended = () => {
  536. if (localStorage.getItem(savedTimeKey)) localStorage.removeItem(savedTimeKey);
  537. savedTimeKey = false;
  538. };
  539.  
  540. v.onemptied = () => {
  541. if (v === imagusVid) {
  542. if (v.src !== '') {
  543. if (/v(cf)?\.redd\.it/.test(v.src)) {
  544. const prefix = v.src.split('DASH')[0].replace('vcf.', 'v.');
  545. const audioSrc = `${prefix}DASH_audio.mp4`;
  546. GM_xmlhttpRequest({
  547. method: 'GET',
  548. url: audioSrc,
  549. onload: xhr => {
  550. imagusAudio.src = xhr.status >= 200 && xhr.status < 300 ? audioSrc : `${prefix}audio`;;
  551. },
  552. onerror: () => {
  553. imagusAudio.src = `${prefix}audio`;
  554. },
  555. });
  556. if (!imagusAudio.muted) {
  557. muteTillSync = true;
  558. imagusAudio.muted = true;
  559. }
  560. if (imagusVid.hasAttribute('loop')) imagusAudio.setAttribute('loop', 'true');
  561. }
  562. v.parentElement.parentElement.classList.add('imagus-video-wrapper');
  563. window.addEventListener('click', imagusClick, true);
  564. document.addEventListener('keyup', imagusKeys, true);
  565. document.addEventListener('mousedown', imagusMouseDown, true);
  566. document.addEventListener('mouseup', imagusMouseUp, true);
  567. } else {
  568. audioSync = false;
  569. audioError = false;
  570. imagusAudio.pause();
  571. imagusAudio.removeAttribute('src');
  572. imagusAudio.load();
  573. imagusAudio.removeAttribute('loop');
  574. v.parentElement.parentElement.removeAttribute('class');
  575. timeTooltip.classList.add('hidden');
  576. window.removeEventListener('click', imagusClick, true);
  577. document.removeEventListener('keyup', imagusKeys, true);
  578. document.removeEventListener('mousedown', imagusMouseDown, true);
  579. document.removeEventListener('mouseup', imagusMouseUp, true);
  580. }
  581. }
  582. };
  583.  
  584. v.onerror = () => {
  585. error = true;
  586. elToFocus.blur();
  587. v.classList.add('disabled');
  588. };
  589.  
  590. v.onmousedown = e => {
  591. if (error && e.button !== 2) return;
  592. e.stopPropagation();
  593. e.stopImmediatePropagation();
  594. if (e.button === 0) {
  595. clickCount++;
  596. const checkState = v.paused;
  597. if (clickCount === 1) {
  598. setTimeout(() => {
  599. if (clickCount === 1) {
  600. // avoid conflicts with existing click listeners
  601. const recheckState = v.paused;
  602. if (checkState === recheckState) shortcutFuncs.togglePlay(v);
  603. } else {
  604. shortcutFuncs.toggleFS(v);
  605. }
  606. clickCount = 0;
  607. }, settings.clickDelay);
  608. }
  609. } else if (e.button === 2) {
  610. window.addEventListener('contextmenu', preventHijack, true);
  611. }
  612. };
  613.  
  614. v.onmouseup = e => {
  615. if (e.button === 2) {
  616. setTimeout(() => {
  617. window.removeEventListener('contextmenu', preventHijack, true);
  618. }, 100);
  619. }
  620. if (error) elToFocus.blur();
  621. };
  622.  
  623. v.onmousemove = () => {
  624. controlsTimeout ? clearTimeout(controlsTimeout) :
  625. v.classList.add('active');
  626. if (videoWrapper) videoWrapper.classList.add('active');
  627. controlsTimeout = setTimeout(() => {
  628. controlsTimeout = false;
  629. v.classList.remove('active');
  630. if (videoWrapper) videoWrapper.classList.remove('active');
  631. }, settings.hideControls);
  632. };
  633.  
  634. new ResizeObserver(() => {
  635. compactControls();
  636. }).observe(v);
  637.  
  638. controls.onmouseup = () => {
  639. if (error) return;
  640. elToFocus.focus({preventScroll: true});
  641. };
  642.  
  643. timeSlider.onmousemove = () => sliderValues();
  644.  
  645. timeSlider.oninput = () => sliderValues();
  646.  
  647. timeSlider.onmousedown = e => {
  648. if (e.button > 0) return;
  649. mouseDown = true;
  650. isSeeking = true;
  651. if (timeTooltip.classList.contains('hidden')) sliderValues();
  652. if (v.readyState > 0) {
  653. if (!v.paused) {
  654. isPlaying = true;
  655. v.pause();
  656. } else {
  657. isPlaying = false;
  658. }
  659. }
  660. };
  661.  
  662. timeSlider.onmouseup = e => {
  663. if (e.button > 0) return;
  664. mouseDown = false;
  665. isSeeking = false;
  666. if (v.readyState > 0) {
  667. sliderValues();
  668. if (isPlaying) {
  669. v.play();
  670. isPlaying = false;
  671. }
  672. }
  673. };
  674.  
  675. volumeSlider.onmousemove = () => sliderValues();
  676.  
  677. volumeSlider.oninput = () => {
  678. if (v.muted) shortcutFuncs.toggleMute(v);
  679. sliderValues();
  680. };
  681.  
  682. muteButton.onmouseup = e => {
  683. if (e.button > 0) return;
  684. const lastVolume = localStorage.getItem('videovolume');
  685. if (v.muted || v.volume) shortcutFuncs.toggleMute(v);
  686. v.volume = lastVolume;
  687. if (audioSync) imagusAudio.muted = v.muted;
  688. };
  689.  
  690. playButton.onmouseup = e => {
  691. if (e.button > 0) return;
  692. shortcutFuncs.togglePlay(v);
  693. };
  694.  
  695. skipShortLeft.onmouseup = e => {
  696. if (e.button > 0) return;
  697. shortcutFuncs.skipLeft(v);
  698. };
  699.  
  700. skipShortRight.onmouseup = e => {
  701. if (e.button > 0) return;
  702. shortcutFuncs.skipRight(v);
  703. };
  704.  
  705. skipLongLeft.onmouseup = e => {
  706. if (e.button > 0) return;
  707. v.currentTime -= settings.skipShift;
  708. };
  709.  
  710. skipLongRight.onmouseup = e => {
  711. if (e.button > 0) return;
  712. v.currentTime += settings.skipShift;
  713. };
  714.  
  715. beginButton.onmouseup = e => {
  716. if (e.button > 0) return;
  717. v.currentTime = 0;
  718. timeSlider.value = 0;
  719. timeProgress.style.width = '0';
  720. currentTime.textContent = '0:00';
  721. };
  722.  
  723. rateDecrease.onmouseup = e => {
  724. if (e.button > 0) return;
  725. shortcutFuncs.slowOrPrevFrame(v);
  726. };
  727.  
  728. rateIncrease.onmouseup = e => {
  729. if (e.button > 0) return;
  730. shortcutFuncs.fastOrNextFrame(v);
  731. };
  732.  
  733. rate.onmouseup = e => {
  734. if (e.button > 0) return;
  735. shortcutFuncs.normalSpeed(v);
  736. };
  737.  
  738. rate.onmouseenter = () => {
  739. rate.textContent = '1x?';
  740. };
  741.  
  742. rate.onmouseleave = () => {
  743. const currentRate = rate.getAttribute('data-current-rate');
  744. if (currentRate) rate.textContent = currentRate;
  745. };
  746.  
  747. expandButton.onmouseup = e => {
  748. if (e.button > 0) return;
  749. shortcutFuncs.toggleFS(v);
  750. };
  751.  
  752. // exiting fullscreen by escape key or other browser provided method
  753. document.onfullscreenchange = () => {
  754. if (!document.fullscreenElement) {
  755. const nativeFS = $('.native-fullscreen');
  756. if (nativeFS) nativeFS.classList.remove('native-fullscreen');
  757. }
  758. };
  759.  
  760. if (imagusVid) {
  761. imagusAudio.onloadedmetadata = () => {
  762. audioSync = true;
  763. if (v.hasAttribute('autoplay')) imagusAudio.play();
  764. };
  765.  
  766. imagusAudio.onloadeddata = () => {
  767. if (v.volume && !v.muted) v.classList.remove('muted');
  768. volumeValues(v);
  769. if (volume.classList.contains('disabled')) {
  770. muteButton.classList.remove('disabled');
  771. volume.classList.remove('disabled');
  772. }
  773. };
  774.  
  775. imagusAudio.onended = () => {
  776. imagusAudio.currentTime = 0;
  777. if (imagusVid.hasAttribute('loop')) imagusAudio.play();
  778. };
  779.  
  780. imagusAudio.onerror = () => {
  781. audioError = true;
  782. v.classList.add('muted');
  783. volumeSlider.value = 0;
  784. muteButton.classList.add('disabled');
  785. volume.classList.add('disabled');
  786. };
  787. }
  788.  
  789. if (directVideo) {
  790. v.removeAttribute('tabindex');
  791. document.body.setAttribute('tabindex', '0');
  792. document.addEventListener('keydown', docHandleKeyDown, true);
  793. document.addEventListener('keypress', docHandleKeyOther, true);
  794. document.addEventListener('keyup', docHandleKeyOther, true);
  795. } else {
  796. v.addEventListener('keydown', handleKeyDown, false);
  797. v.addEventListener('keypress', handleKeyOther, false);
  798. v.addEventListener('keyup', handleKeyOther, false);
  799. }
  800.  
  801. function sliderValues() {
  802. let slider;
  803. let xPosition;
  804. const vid = audioSync ? imagusAudio && v : v;
  805. const eType = event && event.type;
  806. const eTime = eType === 'timeupdate';
  807. const eVolume = eType === 'volumechange';
  808. const eMeta = eType === 'loadedmetadata';
  809. const eData = eType === 'loadeddata';
  810. const eInput = eType === 'input';
  811. const eMouseUp = eType === 'mouseup';
  812. const eMouseMove = eType === 'mousemove';
  813. const eMouseDown = eType === 'mousedown';
  814. if (eMeta || eTime || eVolume || eData || !event) {
  815. slider = eMeta || eTime ? timeSlider : volumeSlider;
  816. } else {
  817. slider = event.target;
  818. }
  819. const tooltip = slider.nextSibling;
  820. const timeTarget = slider === timeSlider;
  821. const sliderWidth = slider.clientWidth;
  822. const halfSlider = sliderWidth / 2;
  823. const slider14ths = halfSlider / 7;
  824. const eX = event && event.offsetX;
  825. const start7 = eX <= 7;
  826. const end7 = eX >= sliderWidth - 7;
  827. if (eMouseMove || eMouseDown) {
  828. if (start7 || end7) {
  829. xPosition = start7 ? 0 : sliderWidth;
  830. } else {
  831. xPosition = eX < halfSlider ? (eX + (-7 + (eX / slider14ths))).toFixed(1) :
  832. (eX + ((eX - halfSlider) / slider14ths)).toFixed(1);
  833. }
  834. }
  835. if (eMeta || eTime || eVolume || eData || !event) {
  836. xPosition = eMeta || eTime ?
  837. ((((100 / v.duration) * v.currentTime) * sliderWidth) / 100).toFixed(1) :
  838. (v.volume * sliderWidth).toFixed(1);
  839. }
  840. if (eTime && event.target === imagusVid && audioSync) {
  841. if (imagusVid.currentTime - imagusAudio.currentTime >= 0.1 ||
  842. imagusVid.currentTime - imagusAudio.currentTime <= -0.1) {
  843. imagusAudio.currentTime = imagusVid.currentTime + 0.06;
  844. console.log('time sync corrected');
  845. if (muteTillSync && imagusAudio.readyState > 2) {
  846. imagusAudio.muted = false;
  847. muteTillSync = false;
  848. console.log('unmuted after time correct');
  849. }
  850. } else if (muteTillSync && imagusAudio.readyState > 2) {
  851. imagusAudio.muted = false;
  852. muteTillSync = false;
  853. console.log('unmuted');
  854. }
  855. }
  856. if (eInput || eMouseUp) xPosition = +tooltip.getAttribute('data-x-position');
  857. const xPosPercent = timeTarget ? (xPosition / sliderWidth) * 100 :
  858. Math.round((xPosition / sliderWidth) * 100);
  859. let time = (xPosPercent * v.duration) / 100;
  860. if (eInput || eMeta || eTime || eVolume || eData || !event) {
  861. const valueTrail = timeTarget ? timeProgress : volumeTrail;
  862. const offset = halfSlider < xPosition ? -7 + (xPosition / slider14ths) :
  863. (xPosition - halfSlider) / slider14ths;
  864. slider.value = timeTarget ? xPosPercent : xPosPercent / 100;
  865. valueTrail.style.width = `calc(${xPosPercent}% - ${offset}px)`;
  866. if (eInput && !timeTarget) {
  867. if (start7 || end7) {
  868. vid.volume = start7 ? 0 : 1;
  869. } else {
  870. vid.volume = xPosPercent / 100;
  871. }
  872. }
  873. if (eInput && timeTarget && v.readyState > 0) currentTime.textContent = formatTime(time);
  874. if (eTime) currentTime.textContent = formatTime(v.currentTime);
  875. if (eInput && timeTarget && v.readyState < 1) earlyXposPercent = xPosPercent;
  876. if (eMeta && !tooltip.classList.contains('hidden')) {
  877. xPosition = +tooltip.getAttribute('data-x-position');
  878. time = (xPosition / sliderWidth) * v.duration;
  879. tooltip.textContent = formatTime(time);
  880. }
  881. } else if (eMouseUp) {
  882. if (audioSync) {
  883. if (start7 || end7) {
  884. imagusAudio.currentTime = start7 ? 0 : v.duration;
  885. } else {
  886. imagusAudio.currentTime = time;
  887. }
  888. }
  889. if (start7 || end7) {
  890. v.currentTime = start7 ? 0 : v.duration;
  891. } else {
  892. v.currentTime = time;
  893. }
  894. preventMouseMove = true;
  895. setTimeout(() => {
  896. preventMouseMove = false;
  897. }, 10);
  898. } else if (eMouseMove || eMouseDown) {
  899. if (!preventMouseMove || eMouseDown) {
  900. tooltip.dataset.xPosition = xPosition;
  901. tooltip.style.left = `${eX}px`;
  902. if (v.readyState > 0 && timeTarget) tooltip.textContent = formatTime(time);
  903. if (!timeTarget) tooltip.textContent = `${xPosPercent}%`;
  904. }
  905. tooltip.classList.remove('hidden');
  906. preventMouseMove = false;
  907. }
  908. }
  909.  
  910. function formatTime(t) {
  911. let seconds = Math.round(t);
  912. const minutes = Math.floor(seconds / 60);
  913. if (minutes > 0) seconds -= minutes * 60;
  914. if (seconds.toString().length === 1) seconds = `0${seconds}`;
  915. return `${minutes}:${seconds}`;
  916. }
  917.  
  918. function volumeValues() {
  919. const videovolume = localStorage.getItem('videovolume');
  920. const videomuted = localStorage.getItem('videomuted');
  921. if ((!videovolume && !videomuted) ||
  922. (videovolume && videovolume === '1' &&
  923. videomuted && videomuted !== 'true')) {
  924. v.volume = 1;
  925. volumeSlider.value = 1;
  926. volumeTrail.style.width = '100%';
  927. localStorage.setItem('videovolume', v.volume);
  928. localStorage.setItem('videomuted', 'false');
  929. } else if (videomuted && videomuted === 'true') {
  930. v.classList.add('muted');
  931. volumeSlider.value = 0;
  932. volumeTrail.style.width = '0';
  933. v.muted = true;
  934. } else {
  935. v.volume = videovolume;
  936. if (audioSync) imagusAudio.volume = v.volume;
  937. sliderValues();
  938. if (!volumeSlider.clientWidth) {
  939. new MutationObserver((_, observer) => {
  940. const volumeWidthSet = v.parentElement.querySelector('volume input').clientWidth;
  941. if (volumeWidthSet) {
  942. sliderValues();
  943. observer.disconnect();
  944. }
  945. }).observe(v.parentElement, {childList: true, subtree: true, attributes: true});
  946. }
  947. }
  948. }
  949.  
  950. function hasAudio() {
  951. return v.mozHasAudio ||
  952. Boolean(v.webkitAudioDecodedByteCount) ||
  953. Boolean(v.audioTracks && v.audioTracks.length);
  954. }
  955.  
  956. function compactControls() {
  957. const width = v.clientWidth;
  958. width && width < 892 ? v.classList.add('compact') : v.classList.remove('compact');
  959. width && width < 412 ? v.classList.add('compact-2') : v.classList.remove('compact-2');
  960. width && width < 316 ? v.classList.add('compact-3') : v.classList.remove('compact-3');
  961. width && width < 246 ? v.classList.add('compact-4') : v.classList.remove('compact-4');
  962. }
  963.  
  964. function imagusMouseDown(e) {
  965. const vid = $('.imagus-video-wrapper');
  966. if (vid && e.button === 2) {
  967. e.stopImmediatePropagation();
  968. imagusMouseTimeout = setTimeout(() => {
  969. imagusMouseTimeout = 'sticky';
  970. }, settings.imagusStickyDelay);
  971. }
  972. }
  973.  
  974. function imagusMouseUp(e) {
  975. const vid = $('.imagus-video-wrapper');
  976. if (vid && e.button === 2) {
  977. if (imagusMouseTimeout === 'sticky') {
  978. vid.classList.add('stickied');
  979. setTimeout(() => {
  980. v.removeAttribute('controls');
  981. });
  982. if (volume.classList.contains('disabled')) volumeSlider.value = 0;
  983. document.removeEventListener('mousedown', imagusMouseDown, true);
  984. document.removeEventListener('mouseup', imagusMouseUp, true);
  985. } else {
  986. clearInterval(imagusMouseTimeout);
  987. imagusMouseTimeout = false;
  988. }
  989. }
  990. }
  991.  
  992. function imagusClick(e) {
  993. const imagusStickied = $('.imagus-video-wrapper.stickied');
  994. if (imagusStickied) {
  995. if (e.target.closest('.imagus-video-wrapper.stickied')) {
  996. e.stopImmediatePropagation();
  997. } else {
  998. imagusStickied.removeAttribute('class');
  999. e.preventDefault();
  1000. }
  1001. }
  1002. }
  1003.  
  1004. function imagusKeys(e) {
  1005. const vid = $('.imagus-video-wrapper');
  1006. if (vid) {
  1007. if (e.keyCode === 13 || e.keyCode === 90) {
  1008. vid.classList.add('stickied');
  1009. setTimeout(() => {
  1010. v.removeAttribute('controls');
  1011. });
  1012. if (volume.classList.contains('disabled')) volumeSlider.value = 0;
  1013. document.removeEventListener('keyup', imagusKeys, true);
  1014. document.removeEventListener('mousedown', imagusMouseDown, true);
  1015. document.removeEventListener('mouseup', imagusMouseUp, true);
  1016. }
  1017. }
  1018. }
  1019.  
  1020. function handleKeyDown(e) {
  1021. if (e.altKey || e.metaKey) return true; // Do not activate
  1022. const func = keyFuncs[e.keyCode];
  1023. if (func) {
  1024. if ((func.length < 3 && e.shiftKey) ||
  1025. (func.length < 4 && e.ctrlKey)) return true; // Do not activate
  1026. func(e.target, e.keyCode, e.shiftKey, e.ctrlKey);
  1027. e.preventDefault();
  1028. e.stopPropagation();
  1029. e.stopImmediatePropagation();
  1030. return false;
  1031. }
  1032. }
  1033.  
  1034. function handleKeyOther(e) {
  1035. if (e.altKey || e.metaKey) return true; // Do not prevent default
  1036. const func = keyFuncs[e.keyCode];
  1037. if (func) {
  1038. if ((func.length < 3 && e.shiftKey) ||
  1039. (func.length < 4 && e.ctrlKey)) return true; // Do not prevent default
  1040. e.preventDefault();
  1041. e.stopPropagation();
  1042. e.stopImmediatePropagation();
  1043. return false;
  1044. }
  1045. }
  1046.  
  1047. function docHandleKeyDown(e) {
  1048. if (document.body !== document.activeElement ||
  1049. e.altKey || e.metaKey) return true; // Do not activate
  1050. const func = keyFuncs[e.keyCode];
  1051. if (func) {
  1052. if ((func.length < 3 && e.shiftKey) ||
  1053. (func.length < 4 && e.ctrlKey)) return true; // Do not activate
  1054. func(v, e.keyCode, e.shiftKey, e.ctrlKey);
  1055. e.preventDefault();
  1056. e.stopPropagation();
  1057. return false;
  1058. }
  1059. }
  1060.  
  1061. function docHandleKeyOther(e) {
  1062. if (document.body !== document.activeElement ||
  1063. e.altKey || e.metaKey) return true; // Do not prevent default
  1064. const func = keyFuncs[e.keyCode];
  1065. if (func) {
  1066. if ((func.length < 3 && e.shiftKey) ||
  1067. (func.length < 4 && e.ctrlKey)) return true; // Do not prevent default
  1068. e.preventDefault();
  1069. e.stopPropagation();
  1070. return false;
  1071. }
  1072. }
  1073.  
  1074. // circumvent any scripts attempting to hijack video context menus
  1075. function preventHijack(e) {
  1076. e.stopPropagation();
  1077. e.stopImmediatePropagation();
  1078. const redirectEvent = e.target.ownerDocument.createEvent('MouseEvents');
  1079. redirectEvent.initMouseEvent(e, e.bubbles, e.cancelable);
  1080. return e;
  1081. }
  1082.  
  1083. function enforcePosition() {
  1084. setTimeout(() => {
  1085. let controlsDisplaced = controls !== v.nextSibling;
  1086. const vidDisplaced = videoWrapper !== v.parentNode;
  1087. if (vidDisplaced || controlsDisplaced) {
  1088. if (vidDisplaced) videoWrapper.appendChild(v);
  1089. controlsDisplaced = v !== controls.previousSibling;
  1090. if (controlsDisplaced) videoWrapper.insertBefore(controls, v.nextSibling);
  1091. const bs =
  1092. videoWrapper.querySelectorAll('videowrapper > *:not(video):not(controls)');
  1093. for (let i = 0; i < bs.length; ++i) {
  1094. bs[i].remove();
  1095. }
  1096. }
  1097. repeat++;
  1098. if (repeat < 10) enforcePosition.call(this);
  1099. }, 100);
  1100. }
  1101. }
  1102.  
  1103. function ytSaveCurrentTime(v) {
  1104. v.addEventListener('loadstart', ytCheckSavedTime);
  1105. v.addEventListener('loadeddata', ytCheckSavedTime);
  1106.  
  1107. v.ontimeupdate = () => {
  1108. if (v.currentTime > 0 && ytTimeChecked) localStorage.setItem(ytID, v.currentTime);
  1109. };
  1110.  
  1111. v.onended = () => {
  1112. if (localStorage.getItem(ytID)) localStorage.removeItem(ytID);
  1113. };
  1114. }
  1115.  
  1116. function ytCheckSavedTime(e) {
  1117. ytID = location.href.replace(/.*?\/(watch\?v=|embed\/)(.*?)(\?|&|$).*/, '$2');
  1118. const savedTime = localStorage.getItem(ytID);
  1119. const timeURL = /(\?|&)(t(ime_continue)?|start)=[1-9]/.test(location.href);
  1120. const ytStart = $('.ytp-clip-start:not([style*="left: 0%;"])');
  1121. if (e.type === 'loadstart') {
  1122. ytTimeChecked = false;
  1123. if ((!ytStart || !savedTime) && !timeURL) ytTimeChecked = true;
  1124. if (ytStart) ytStart.click();
  1125. if (ytTimeChecked && savedTime) e.target.currentTime = savedTime;
  1126. e.target.focus({preventScroll: true});
  1127. if (self === top) window.scroll({top: 0, behavior: 'smooth'});
  1128. } else if (e.type === 'loadeddata' && !ytTimeChecked) {
  1129. if (savedTime) e.target.currentTime = savedTime;
  1130. ytTimeChecked = true;
  1131. }
  1132. }
  1133.  
  1134. window.addEventListener('DOMContentLoaded', () => {
  1135. const style = $('#custom-native-player-style');
  1136. if (style) {
  1137. const docEls = $$('html > *');
  1138. for (let i = 0; i < docEls.length; i++) {
  1139. if (docEls[i].tagName === 'STYLE' &&
  1140. docEls[i].classList.contains('stylus') &&
  1141. docEls[i].id !== 'custom-native-player-style') {
  1142. $('html').insertBefore(style, docEls[i]);
  1143. break;
  1144. } else if (docEls[i] === docEls[docEls.length - 1]) {
  1145. $('html').insertBefore(style, docEls[i].nextSibling);
  1146. }
  1147. }
  1148. }
  1149.  
  1150. document.arrive(
  1151. 'video[controls], video[style*="visibility: inherit !important"]',
  1152. {fireOnAttributesModification: true, existing: true}, v => {
  1153. if (!v.parentNode.parentNode) return;
  1154. const vP = v.parentNode;
  1155. const vPP = v.parentNode.parentNode;
  1156. const imagus = !v.hasAttribute('controls') &&
  1157. $('html > div[style*="z-index: 2147483647"]') === v.parentNode;
  1158. const vidOrParentsIdOrClass =
  1159. `${v.id}${v.classList}${vP.id}${vP.classList}${vPP.id}${vPP.classList}`;
  1160. const exclude = v.classList.contains('custom-native-player') ||
  1161. v.classList.contains('imagus') ||
  1162. /(v(ideo)?|me)(-|_)?js|jw|jplay|plyr|kalt|flowp|wisti/i.test(vidOrParentsIdOrClass);
  1163. if (imagus || (v.hasAttribute('controls') && !exclude)) {
  1164. if (imagus) v.classList.add('imagus');
  1165. v.classList.add('custom-native-player');
  1166. v.classList.add('custom-native-player-hidden');
  1167. v.setAttribute('tabindex', '0');
  1168. v.setAttribute('preload', 'auto');
  1169. v.removeAttribute('controls');
  1170. customPlayer(v);
  1171. }
  1172. });
  1173. });
  1174.  
  1175. if (/^https?:\/\/www\.youtube\.com/.test(location.href)) {
  1176. document.arrive(
  1177. 'video[src*="youtube.com"]',
  1178. {fireOnAttributesModification: true, existing: true}, v => {
  1179. ytSaveCurrentTime(v);
  1180. });
  1181. }
  1182.  
  1183. const css = `.imagus-video-wrapper {
  1184. height: min-content!important;
  1185. position: fixed!important;
  1186. left: 0!important;
  1187. right: 0!important;
  1188. top: 0!important;
  1189. bottom: 0!important;
  1190. margin: auto!important;
  1191. box-shadow: none!important;
  1192. background-color: hsl(0, 0%, 0%)!important;
  1193. width: calc(100% - 100px)!important;
  1194. }
  1195. .imagus-video-wrapper.stickied {
  1196. box-shadow: 0 0 0 100000px hsla(0, 0%, 0%, .7)!important;
  1197. }
  1198. .imagus-video-wrapper videowrapper {
  1199. height: 0!important;
  1200. padding-top: 56.25%!important;
  1201. }
  1202. .imagus-video-wrapper videowrapper video.custom-native-player {
  1203. position: absolute!important;
  1204. }
  1205. @media (min-width: 177.778vh) {
  1206. .imagus-video-wrapper {
  1207. margin: 18px auto!important;
  1208. height: calc(100vh - 18px)!important;
  1209. width: calc(((100vh - 18px) * 16) / 9)!important;
  1210. }
  1211. .imagus-video-wrapper videowrapper {
  1212. height: 100%!important;
  1213. padding-top: 0!important;
  1214. }
  1215. .imagus-video-wrapper videowrapper video.custom-native-player {
  1216. position: relative!important;
  1217. }
  1218. }
  1219. html > div[style*="2147483647"] > img[style*="display: block"] ~ videowrapper {
  1220. display: none!important;
  1221. }
  1222. html > div[style*="2147483647"] {
  1223. background: none!important;
  1224. box-shadow: none!important;
  1225. border: 0!important;
  1226. }
  1227. html > div[style*="2147483647"] videowrapper + div {
  1228. -webkit-text-fill-color: hsl(0, 0%, 90%)!important;
  1229. box-shadow: none!important;
  1230. width: 100%!important;
  1231. max-width: 100%!important;
  1232. box-sizing: border-box!important;
  1233. overflow: hidden!important;
  1234. text-overflow: ellipsis!important;
  1235. }
  1236. html > div:not(.stickied) video.custom-native-player + controls,
  1237. video[controls]:not(.custom-native-player) {
  1238. opacity: 0!important;
  1239. pointer-events: none!important;
  1240. }
  1241. videowrapper {
  1242. --wrapper-position: relative;
  1243. position: var(--wrapper-position)!important;
  1244. height: 100%!important;
  1245. display: block!important;
  1246. font-size: 0px!important;
  1247. top: 0!important;
  1248. bottom: 0!important;
  1249. left: 0!important;
  1250. right: 0!important;
  1251. background-color: hsl(0, 0%, 0%)!important;
  1252. overflow: hidden!important;
  1253. }
  1254. video.custom-native-player + controls timetooltip,
  1255. video.custom-native-player + controls volumetooltip {
  1256. position: absolute!important;
  1257. display: none!important;
  1258. top: -25px!important;
  1259. height: 22px!important;
  1260. line-height: 22px!important;
  1261. text-align: center!important;
  1262. border-radius: 4px!important;
  1263. font-size: 12px!important;
  1264. background: hsla(0, 0%, 0%, .7)!important;
  1265. box-shadow: 0 0 4px hsla(0, 0%, 100%, .5)!important;
  1266. color: hsl(0, 0%, 100%)!important;
  1267. pointer-events: none!important;
  1268. }
  1269. video.custom-native-player + controls timetooltip {
  1270. margin-left: -25px!important;
  1271. width: 50px!important;
  1272. }
  1273. video.custom-native-player + controls volumetooltip {
  1274. margin-left: -20px!important;
  1275. width: 40px!important;
  1276. }
  1277. video.custom-native-player.compact + controls timeline timetooltip {
  1278. top: -25px!important;
  1279. }
  1280. video.custom-native-player.compact + controls btn,
  1281. video.custom-native-player.compact + controls rate,
  1282. video.custom-native-player.compact + controls volume {
  1283. height: 24px!important;
  1284. line-height: 22px!important;
  1285. }
  1286. video.custom-native-player.compact + controls volume input {
  1287. padding-bottom: 2px!important;
  1288. }
  1289. video.custom-native-player.compact + controls btn:before {
  1290. margin-top: -2px!important;
  1291. }
  1292. video.custom-native-player.compact + controls volume > volumebar {
  1293. top: 6px!important;
  1294. }
  1295. video.custom-native-player + controls timelinewrapper {
  1296. line-height: 20px!important;
  1297. }
  1298. video.custom-native-player + controls timeline:hover timetooltip:not(.hidden),
  1299. video.custom-native-player + controls volume:hover volumetooltip:not(.hidden) {
  1300. display: inline!important;
  1301. }
  1302. video.custom-native-player {
  1303. cursor: none!important;
  1304. max-height: 100%!important;
  1305. height: 100%!important;
  1306. width: 100%!important;
  1307. margin: 0!important;
  1308. padding: 0!important;
  1309. top: 0!important;
  1310. bottom: 0!important;
  1311. left: 0!important;
  1312. right: 0!important;
  1313. background-color: hsl(0, 0%, 0%)!important;
  1314. border-radius: 0!important;
  1315. }
  1316. video.custom-native-player:not(.contains-source):not([src*="/"]) {
  1317. cursor: auto!important;
  1318. }
  1319. video.custom-native-player:not(.contains-source):not([src*="/"]) + controls {
  1320. display: none!important;
  1321. }
  1322. video.custom-native-player + controls > * {
  1323. background: none!important;
  1324. outline: none!important;
  1325. line-height: 32px!important;
  1326. font-family: monospace!important;
  1327. }
  1328. video.custom-native-player.compact + controls > * {
  1329. line-height: 24px!important;
  1330. }
  1331. video.custom-native-player + controls {
  1332. --controls-z-index: 1;
  1333. white-space: nowrap!important;
  1334. transition: opacity .5s ease 0s!important;
  1335. background-color: hsla(0, 0%, 0%, .85)!important;
  1336. height: 32px !important;
  1337. width: 100%!important;
  1338. cursor: default !important;
  1339. font-size: 18px !important;
  1340. user-select: none!important;
  1341. z-index: var(--controls-z-index)!important;
  1342. flex: none!important;
  1343. position: absolute!important;
  1344. display: flex!important;
  1345. flex-wrap: wrap!important;
  1346. opacity: 0!important;
  1347. margin: 0!important;
  1348. bottom: 0!important;
  1349. left: 0!important;
  1350. right: 0!important;
  1351. }
  1352. video.custom-native-player.custom-native-player-hidden,
  1353. video.custom-native-player.custom-native-player-hidden + controls {
  1354. opacity: 0!important;
  1355. pointer-events: none!important;
  1356. }
  1357. video.custom-native-player.paused + controls,
  1358. video.custom-native-player.active + controls,
  1359. video.custom-native-player + controls:hover {
  1360. opacity: 1!important;
  1361. }
  1362. video.custom-native-player + controls timeline {
  1363. flex-grow: 1!important;
  1364. position: relative!important;
  1365. align-items: center!important;
  1366. flex-direction: column!important;
  1367. height: 100%!important;
  1368. }
  1369. video.custom-native-player + controls timelinewrapper {
  1370. flex: 1 0 480px!important;
  1371. position: relative!important;
  1372. align-items: center!important;
  1373. }
  1374. video.custom-native-player.compact + controls timelinewrapper {
  1375. order: -1;
  1376. flex-basis: 100%!important;
  1377. height: 20px!important;
  1378. }
  1379. video.custom-native-player.compact + controls timeline timebar {
  1380. top: 5px!important;
  1381. }
  1382. video.custom-native-player.compact + controls currenttime,
  1383. video.custom-native-player.compact + controls totaltime {
  1384. line-height: 20px!important;
  1385. }
  1386. video.custom-native-player.compact + controls {
  1387. height: 44px!important;
  1388. }
  1389. video.custom-native-player.compact-2 + controls btn.begin,
  1390. video.custom-native-player.compact-2 + controls btn.skip-short,
  1391. video.custom-native-player.compact-3 + controls rate,
  1392. video.custom-native-player.compact-3 + controls btn.rate-increase,
  1393. video.custom-native-player.compact-3 + controls btn.rate-decrease,
  1394. video.custom-native-player.compact-4 + controls btn.skip-long {
  1395. display: none!important;
  1396. }
  1397. video.custom-native-player + controls > * {
  1398. display: inline-flex!important;
  1399. }
  1400. video.custom-native-player.compact-2 + controls btn.rate-increase,
  1401. video.custom-native-player.compact-4 + controls btn.toggle-play {
  1402. margin-right: auto!important;
  1403. }
  1404. video.custom-native-player + controls timeline > timebar > timebuffer,
  1405. video.custom-native-player + controls timeline > timebar > timeprogress,
  1406. video.custom-native-player + controls volume > volumebar > volumetrail {
  1407. position: absolute!important;
  1408. flex: none!important;
  1409. pointer-events: none!important;
  1410. height: 100%!important;
  1411. border-radius: 20px!important;
  1412. }
  1413. video.custom-native-player + controls timeline > timebar,
  1414. video.custom-native-player + controls volume > volumebar {
  1415. position: absolute!important;
  1416. height: 10px!important;
  1417. border-radius: 20px!important;
  1418. overflow: hidden!important;
  1419. background-color: hsla(0, 0%, 16%, .85)!important;
  1420. top: 11px!important;
  1421. left: 0!important;
  1422. right: 0!important;
  1423. pointer-events: none!important;
  1424. z-index: -1!important;
  1425. box-shadow: inset 0 0 0 1px hsla(0, 0%, 40%), inset 0 0 5px hsla(0, 0%, 40%, .85)!important;
  1426. }
  1427. video.custom-native-player + controls volume.disabled,
  1428. video.custom-native-player + controls btn.disabled {
  1429. -webkit-filter: brightness(.4);
  1430. filter: brightness(.4);
  1431. pointer-events: none!important;
  1432. }
  1433. video.custom-native-player.disabled,
  1434. video.custom-native-player.active.disabled {
  1435. cursor: default!important;
  1436. }
  1437. video.custom-native-player.disabled + controls {
  1438. opacity: 1!important;
  1439. -webkit-filter: brightness(.3)sepia(1)hue-rotate(320deg)saturate(5);
  1440. filter: brightness(.3)sepia(1)hue-rotate(320deg)saturate(5);
  1441. }
  1442. video.custom-native-player.disabled + controls > * {
  1443. pointer-events: none!important;
  1444. }
  1445. video.custom-native-player + controls volume {
  1446. max-width: 70px!important;
  1447. flex: 1 0 48px!important;
  1448. position: relative!important;
  1449. margin: 0 12px!important;
  1450. }
  1451. video.custom-native-player + controls timeline > timebar > timebuffer {
  1452. background-color: hsla(0, 0%, 100%, .2)!important;
  1453. border-top-right-radius: 20px!important;
  1454. border-bottom-right-radius: 20px!important;
  1455. left: 0!important;
  1456. }
  1457. video.custom-native-player + controls timeline > timebar > timeprogress,
  1458. video.custom-native-player + controls volume > volumebar > volumetrail {
  1459. background-color: hsla(0, 0%, 100%, .4)!important;
  1460. left: 0!important;
  1461. }
  1462. video.custom-native-player + controls volume.disabled volumetrail {
  1463. width: 0!important;
  1464. }
  1465. video.custom-native-player + controls timeline > input {
  1466. height: 100%!important;
  1467. width: 100%!important;
  1468. }
  1469. video.custom-native-player.active {
  1470. cursor: pointer!important;
  1471. }
  1472. video.custom-native-player + controls btn {
  1473. border: none !important;
  1474. cursor: pointer!important;
  1475. background-color: transparent!important;
  1476. font-family: "Segoe UI Symbol"!important;
  1477. font-size: 0!important;
  1478. margin: 0!important;
  1479. align-items: center!important;
  1480. justify-content: center!important;
  1481. height: 32px!important;
  1482. padding: 0!important;
  1483. flex: 1 1 32px!important;
  1484. max-width: 46px!important;
  1485. box-sizing: content-box!important;
  1486. position: relative!important;
  1487. opacity: .86!important;
  1488. text-shadow: none!important;
  1489. transition: opacity .3s, text-shadow .3s!important;
  1490. -webkit-text-fill-color: hsl(0, 0%, 100%)!important;
  1491. }
  1492. video.custom-native-player + controls btn.toggle-play {
  1493. flex: 1 1 46px!important
  1494. }
  1495. video.custom-native-player + controls btn:hover {
  1496. opacity: 1!important;
  1497. text-shadow: 0 0 8px hsla(0, 0%, 100%)!important;
  1498. }
  1499. video.custom-native-player.playback-rate-increased + controls btn.rate-increase,
  1500. video.custom-native-player.playback-rate-decreased + controls btn.rate-decrease {
  1501. -webkit-text-fill-color: cyan!important;
  1502. }
  1503. video.custom-native-player + controls rate {
  1504. height: 32px!important;
  1505. width: 42px!important;
  1506. margin: 0!important;
  1507. display: unset!important;
  1508. text-align: center!important;
  1509. font-size: 14px!important;
  1510. flex-shrink: 0!important;
  1511. font-weight: bold!important;
  1512. letter-spacing: .5px!important;
  1513. -webkit-text-fill-color: hsl(0, 0%, 100%)!important;
  1514. user-select: none!important;
  1515. pointer-events: none!important;
  1516. opacity: .86!important;
  1517. }
  1518. video.custom-native-player + controls rate[data-current-rate] {
  1519. pointer-events: all!important;
  1520. cursor: pointer!important;
  1521. }
  1522. video.custom-native-player + controls rate[data-current-rate]:hover {
  1523. transition: opacity .3s, text-shadow .3s!important;
  1524. opacity: 1!important;
  1525. text-shadow: 0 0 8px hsla(0, 0%, 100%)!important;
  1526. }
  1527. video.custom-native-player + controls input[type=range] {
  1528. -webkit-appearance: none!important;
  1529. background-color: transparent !important;
  1530. outline: none!important;
  1531. border: 0!important;
  1532. border-radius: 6px !important;
  1533. margin: 0!important;
  1534. top: 0!important;
  1535. bottom: 0!important;
  1536. left: 0!important;
  1537. right: 0!important;
  1538. padding: 0!important;
  1539. width: 100%!important;
  1540. position: relative!important;
  1541. }
  1542. video.custom-native-player + controls input[type='range']::-webkit-slider-thumb {
  1543. -webkit-appearance: none!important;
  1544. background-color: hsl(0, 0%, 86%)!important;
  1545. border: 0!important;
  1546. height: 14px!important;
  1547. width: 14px!important;
  1548. border-radius: 50%!important;
  1549. pointer-events: none!important;
  1550. }
  1551. video.custom-native-player.muted + controls volume input[type='range']::-webkit-slider-thumb {
  1552. background-color: hsl(0, 0%, 50%)!important;
  1553. }
  1554. video.custom-native-player + controls input[type='range']::-moz-range-thumb {
  1555. -moz-appearance: none!important;
  1556. background-color: hsl(0, 0%, 86%)!important;
  1557. border: 0!important;
  1558. height: 14px!important;
  1559. width: 14px!important;
  1560. border-radius: 50%!important;
  1561. pointer-events: none!important;
  1562. }
  1563. video.custom-native-player.muted + controls volume input[type='range']::-moz-range-thumb {
  1564. background-color: hsl(0, 0%, 50%)!important;
  1565. }
  1566. video.custom-native-player + controls currenttime,
  1567. video.custom-native-player + controls totaltime {
  1568. font-family: monospace, arial!important;
  1569. font-weight: bold!important;
  1570. font-size: 14px!important;
  1571. letter-spacing: .5px!important;
  1572. height: 100%!important;
  1573. line-height: 32px!important;
  1574. min-width: 58px!important;
  1575. display: unset!important;
  1576. -webkit-text-fill-color: hsl(0, 0%, 86%)!important;
  1577. }
  1578. video.custom-native-player + controls btn.rate-decrease,
  1579. video.custom-native-player + controls btn.rate-increase {
  1580. padding: 0!important;
  1581. flex: 1 0 14px!important;
  1582. max-width: 24px!important;
  1583. }
  1584. video.custom-native-player + controls btn.rate-decrease {
  1585. margin-left: auto!important;
  1586. }
  1587. video.custom-native-player + controls currenttime {
  1588. padding: 0 12px 0 0!important;
  1589. margin-right: 2px!important;
  1590. text-align: right!important;
  1591. }
  1592. video.custom-native-player + controls totaltime {
  1593. padding: 0 0 0 12px!important;
  1594. margin-left: 2px!important;
  1595. text-align: left!important;
  1596. }
  1597. .direct-video-top-level {
  1598. margin: 0!important;
  1599. padding: 0!important;
  1600. display: flex!important;
  1601. align-items: center!important;
  1602. justify-content: center!important;
  1603. }
  1604. .direct-video-top-level video {
  1605. height: calc(((100vw - 30px) * 9) / 16)!important;
  1606. min-height: calc(((100vw - 30px) * 9) / 16)!important;
  1607. max-height: calc(((100vw - 30px) * 9) / 16)!important;
  1608. width: calc(100vw - 30px)!important;
  1609. min-width: calc(100vw - 30px)!important;
  1610. max-width: calc(100vw - 30px)!important;
  1611. margin: auto!important;
  1612. }
  1613. .direct-video-top-level > video.custom-native-player + controls {
  1614. position: absolute!important;
  1615. left: 0!important;
  1616. right: 0!important;
  1617. margin: 0 auto !important;
  1618. width: calc(100vw - 30px)!important;
  1619. bottom: calc((100vh - (((100vw - 30px) * 9) / 16)) / 2)!important;
  1620. }
  1621. @media (min-width: 177.778vh) {
  1622. .direct-video-top-level video {
  1623. position: unset!important;
  1624. height: calc(100vh - 30px)!important;
  1625. min-height: calc(100vh - 30px)!important;
  1626. max-height: calc(100vh - 30px)!important;
  1627. width: calc(((100vh - 30px) * 16) / 9)!important;
  1628. min-width: calc(((100vh - 30px) * 16) / 9)!important;
  1629. max-width: calc(((100vh - 30px) * 16) / 9)!important;
  1630. margin: 0 auto!important;
  1631. padding: 0!important;
  1632. background-color: hsl(0, 0%, 0%)!important;
  1633. }
  1634. .direct-video-top-level > video.custom-native-player + controls {
  1635. width: calc(((100vh - 30px) * 16) / 9)!important;
  1636. min-width: calc(((100vh - 30px) * 16) / 9)!important;
  1637. max-width: calc(((100vh - 30px) * 16) / 9)!important;
  1638. bottom: 15px!important;
  1639. }
  1640. }
  1641. video::-webkit-media-controls,
  1642. .native-fullscreen > *:not(video):not(controls) {
  1643. display: none!important;
  1644. }
  1645. .native-fullscreen video.custom-native-player,
  1646. .direct-video-top-level .native-fullscreen video.custom-native-player {
  1647. height: 100vh!important;
  1648. width: 100vw!important;
  1649. max-height: 100vh!important;
  1650. max-width: 100vw!important;
  1651. min-height: 100vh!important;
  1652. min-width: 100vw!important;
  1653. margin: 0!important;
  1654. }
  1655. .native-fullscreen video.custom-native-player + controls {
  1656. position: fixed!important;
  1657. bottom: 0!important;
  1658. left: 0!important;
  1659. right: 0!important;
  1660. margin: 0!important;
  1661. width: 100vw!important;
  1662. max-width: 100vw!important;
  1663. }
  1664. video.custom-native-player + controls btn:before {
  1665. font-family: 'customNativePlayer'!important;
  1666. font-weight: 400!important;
  1667. -webkit-font-smoothing: subpixel-antialiased!important;
  1668. -moz-osx-font-smoothing: subpixel-antialiased!important;
  1669. font-size: 20px!important;
  1670. }
  1671. video.custom-native-player + controls btn.skip-short.right:before {
  1672. content: '\\e90c'!important;
  1673. }
  1674. video.custom-native-player + controls btn.skip-short.left:before {
  1675. content: '\\e90b'!important;
  1676. }
  1677. video.custom-native-player + controls btn.skip-long.right:before {
  1678. content: '\\e901'!important;
  1679. }
  1680. video.custom-native-player + controls btn.skip-long.left:before {
  1681. content: '\\e902'!important;
  1682. }
  1683. video.custom-native-player + controls btn.begin:before {
  1684. content: '\\e908'!important;
  1685. }
  1686. video.custom-native-player + controls btn.toggle-play:before {
  1687. content: '\\e906'!important;
  1688. font-size: 26px!important;
  1689. }
  1690. video.custom-native-player.playing + controls btn.toggle-play:before {
  1691. content: '\\e905'!important;
  1692. font-size: 24px!important;
  1693. }
  1694. video.custom-native-player + controls btn.rate-decrease:before {
  1695. content: '\\ea0b'!important;
  1696. font-size: 10px!important;
  1697. }
  1698. video.custom-native-player + controls btn.rate-increase:before {
  1699. content: '\\ea0a'!important;
  1700. font-size: 10px!important;
  1701. }
  1702. video.custom-native-player + controls btn.mute:before {
  1703. content: '\\e90a'!important;
  1704. font-size: 22px!important;
  1705. }
  1706. video.custom-native-player.muted + controls btn.mute:before {
  1707. content: '\\e909'!important;
  1708. font-size: 22px!important;
  1709. }
  1710. video.custom-native-player + controls btn.mute.disabled:before {
  1711. content: '\\e909'!important;
  1712. }
  1713. video.custom-native-player + controls btn.expand:before {
  1714. content: '\\e904'!important;
  1715. font-size: 24px!important;
  1716. }
  1717. .native-fullscreen video.custom-native-player + controls btn.expand:before {
  1718. content: '\\e903'!important;
  1719. font-size: 24px!important;
  1720. }
  1721. @font-face {
  1722. font-family: 'customNativePlayer';
  1723. src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAe8AAsAAAAAC2gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAEAAAABgDxIHPmNtYXAAAAFIAAAAUQAAAHTquqeaZ2FzcAAAAZwAAAAIAAAACAAAABBnbHlmAAABpAAABG0AAAcgC+w8l2hlYWQAAAYUAAAALAAAADYWP5TBaGhlYQAABkAAAAAcAAAAJAgCBBhobXR4AAAGXAAAAC8AAABcUkALAGxvY2EAAAaMAAAAMAAAADAN9g+EbWF4cAAABrwAAAAYAAAAIAAcAIxuYW1lAAAG1AAAANoAAAGGmUoJ+3Bvc3QAAAewAAAADAAAACAAAwAAeNpjYGZ+xTiBgZWBgWkm0xkGBoZ+CM34msGYkZMBFTAKoAkwODAwvNJiPvD/AIMD8wEQj4ERSVaBgQEAdCILXHjaY2BgYGaAYBkGRijNDGSBaBaGCCAtxCAAFGECiim85HnZ84r7ldorrf9///9nAAGFlwwvu19xwcUY/z8WZxFrE+MUfS/6BmoSGgAA0DQY1QAAAAABAAH//wAPeNqNVD1s20YUfo+UdJYd/dAiRdtKJVOMScWyKVs0SRuuGQ6xA8QI4CKQ4p+kMAJkSAx0SacOBdGtKNBNnTUFhTQUKNDOHDp5l5cu3r0nSyz1kZSNGHCCHqS7e3/f+967OwLC1eAAnI1I/P+6AXT4OncBUyQogiooliKYgsLXR9Aekb2NgJ3xZjSO7kPAd7gAeGCElEYBhTT28c3wN/TDOaAYGJLjEDBOy8EJxbQohoMkwIKACkUN4oCAI+RRyAoS13xSkIECzAIUTMm0VKmgRguaFi0FK5QGfvvM98+IWJvm9hlKoUAbf7jok5YkuIGZpCoFkKnSCIyPsMZ7KUyDdQpuExoXBvsEckKIBDYEgvfJENZCFXV4ILyo/gVTUMOWIfT72Op3uPZljwsTI7bGeakyqhZbeMZdXPawHvUdyYYhBvXdon6HUdhph7Y+eHyL70CDBIvJVlMuo1yURJZFllKruoG6ZqlipDWbjouOba1FWpWDwcBqGDsijR2jYcX71lzphes+euS6L0pz8Z676A0GPVHcbpCT0diWRFHabhjWzgP3eYnGc/fBTuRfinvoEyef92ACKtAEm5itaboku2iZYoqFa8xAl4oxW2SyKpiyIBNpiSjKDiapFi7YXHmNeHJnypNkubjnOF5D1zfy+ctf7NPT/uAvaaW0tFd9Zl/a+PjgAIONo5lvX7MMK6+XvNrBykPXfamq2f3M3dKuYZjo26cjambl7/zcxP5krfTM5k7rBwd/AnXWh8fE2Y7u0hLdpJAOU5NEXHCRvyIat5VJ9qeN1P3+YNDnvM2Vlc2TmGA+v6HrDc9x9opj4pxHqbnewCeOw99njvCPK1qmYeyW7mb2s6r60nUfjkmHd+JrCLh30TuAhTRy7+gJvIneC9kOyfbPtQ0Pr99SqBkFCeCDqBa6TTrTHZ1nsiLITgK6wXHQ7Qbd4XE34INwJvmS/kja8Yu/JR7jeAwif/48BkB/DIDn1wB4Ha9G34k1rY7VlCQo1dRXKBZNRRCLm9i0LUFp2lt0NfjzYbeQCTKFYTdTKGTwOBLwmATOi5bMbQ7j7xR6CeA8yNGZSSF6jKlSNihk+CAM+OhlCtx8tA2n6I6Gk8f/CHX4Br6Dn6mLVU3X1pybJxsqmvLNw8+iql/52mufd1q93asoRmZW1RqoVjVLWLM3kZJSuCSIoYn/IT3Nsllldq6aplGdm1Wy2WwtWytX7k/RuF8p19h0ujcpkNfqzOzszCrZ9WxlRp5PT0yk5+WZChPS/QilnM/l8uUofkkuFuUlNv1r6k7y/duwG2/fs0I6PTWV5lMaY+SiaNrT5WXDWF5+qmkKKShu2Xhl2+vrtv3KWK4xdsgmKFdzy/1py23SLpcrq/eeLC7W64uLT+6p5Ql2FEGVdW1P08sRxtLG+vfrG0uM/ZtMfKADpPP4kErwifzkx2Ayn8Dxd58GH9CZ5GCRzlVSdaZajm6ZsmNKDL/QsKB1cnL1G+7eVh62PnXxPkPjP6LOXdEAAAB42mNgZAADZqYpmfH8Nl8ZuFnA/JsFK5QQ9P8DLA7MB4BcDgYmkCgA/hcJqHjaY2BkYGA+8P8AAwOLAwMDmGRkQAXiAFdpAyR42mNhQAAmIGZhYLgKxKuBOBvKBmJGoDhjKJJcAwQz2gBxFAtEHwI7QGgAfJcHlwAAAAAAAAoAFAAeADgAUgBmAJAAuADMANoA6AEyAYwB1gHkAfICEgIyAmgChANOA5B42mNgZGBgEGfoYmBhAAEmBjQAABCkAKl42m3OwWrCQBSF4T8aLbXgri5czRMEhdJdt4IUNy5cN8YhBHQGxmQh9An6HF33GXuMd5mBDF/O3Ll3gDl/ZNxXxlO/39dI/jKP5XdzLnfmCS+8mqfKP80zlvzoVpY/K5nr5OGRXJvH8oc5l7/NExY481T53jzjjd+mipcYAw0VkYu+SDj4dG1icOtixQFP4qoCHajPmoLV4K3BcO/r7lwmDfV6aMeZkjRYuYmhdbUPPpWtP7njzW2ruFNZwaaf3Wp6rTahf1Gpf89J2ZGb9m3fa/foRfEP3IM9twAAeNpjYGbACwAAfQAE) format('woff'), url('customNativePlayer.ttf') format('truetype'), url('customNativePlayer.svg#customNativePlayer') format('svg');
  1724. font-weight: normal;
  1725. font-style: normal;
  1726. }`;
  1727.  
  1728. const style = document.createElement('style');
  1729. style.id = 'custom-native-player-style';
  1730. style.type = 'text/css';
  1731. style.className = 'stylus'; // Dark Reader won't brute force override stylus
  1732. style.textContent = css;
  1733. document.documentElement.appendChild(style);