Custom Native HTML5 Player with Shortcuts

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

目前為 2020-07-30 提交的版本,檢視 最新版本

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