videoplay.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. if(window.__videoplayer_enhancer__){
  2. window.__videoplayer_enhancer__();
  3. throw '';
  4. }else{
  5. window.__videoplayer_enhancer__ = function(){
  6. /* lib begin */
  7. function debounce(fn, ms = 0) {
  8. let timeoutId;
  9. return function(...args) {
  10. clearTimeout(timeoutId);
  11. timeoutId = setTimeout(() => fn.apply(this, args), ms);
  12. };
  13. };
  14. function throttle(fn, wait) {
  15. let inThrottle, lastFn, lastTime;
  16. return function(...args) {
  17. if (!inThrottle) {
  18. fn.apply(this, args);
  19. lastTime = Date.now();
  20. inThrottle = true;
  21. } else {
  22. clearTimeout(lastFn);
  23. lastFn = setTimeout(function() {
  24. if (Date.now() - lastTime >= wait) {
  25. fn.apply(this, args);
  26. lastTime = Date.now();
  27. }
  28. }, Math.max(wait - (Date.now() - lastTime), 0));
  29. }
  30. };
  31. };
  32. function is_same_size_position(ele1, ele2) {
  33. try {
  34. return ele1.clientWidth == ele2.clientWidth &&
  35. ele1.clientHeight == ele2.clientHeight &&
  36. ele1.scrollHeight == ele2.scrollHeight;
  37. } catch {
  38. return false;
  39. }
  40. }
  41. function find_top_wrap_ele(ele) {
  42. let origE = ele;
  43. let wrap = ele;
  44. do {
  45. ele = ele.parentElement;
  46. if (is_same_size_position(wrap, ele)) {
  47. wrap = ele;
  48. }
  49. }while (ele.tagName !== 'BODY');
  50. if(wrap == origE) {
  51. wrap = document.createElement('div');
  52. origE.parentElement.insertBefore(wrap, origE);
  53. wrap.append(origE);
  54. }
  55. return wrap;
  56. }
  57. function fullscreen2Element(ele) {
  58. if (document.fullscreen) {
  59. let curFS = document.fullscreenElement;
  60. if(curFS.tagName === 'VIDEO') {
  61. document.exitFullscreen();
  62. }
  63. }
  64. }
  65. function is_parent(ele, parent) {
  66. while (ele.tagName !== 'BODY' &&
  67. ele !== parent && ele.parentElement !== parent) {
  68. ele = ele.parentElement;
  69. }
  70. return ele.parentElement === parent;
  71. }
  72. function zero_padding(number, length = 2) {
  73. return Array(Math.max(length - number.toString().length, 0) + 1).join(0) + number;
  74. }
  75. function sec2HHMMSS(time, sec_base = 1) {
  76. const sec = sec_base, min = 60 * sec, hour = 60 * min;
  77. const h = Math.floor(time / hour);
  78. const m = Math.floor(time % hour / min);
  79. const s = Math.floor(time % min / sec);
  80. let result = zero_padding(m) + ':' + zero_padding(s);
  81. if (h) {
  82. return zero_padding(h) + ':' + result;
  83. } else {
  84. return result;
  85. }
  86. }
  87. function HHMMSS2sec(time, sec_base = 1) {
  88. const sec = sec_base, min = 60 * sec, hour = 60 * min;
  89. const split = time.split(':');
  90. if (split.length < 3) {
  91. split.unshift(0);
  92. }
  93. const h = split[0];
  94. const m = split[1];
  95. const s = split[2];
  96. return h * hour + m * min + s * sec;
  97. }
  98. function get_1cm_pixel_num() {
  99. const div = document.createElement('div');
  100. div.style.cssText = ['position: fixed', 'width: 1cm', 'visibility: hidden'].join('; ');
  101. document.body.append(div);
  102. const pixel = div.clientWidth;
  103. div.remove();
  104. get_1cm_pixel_num = () => pixel;
  105. return pixel;
  106. }
  107. function px2cm(px) {
  108. return px / get_1cm_pixel_num();
  109. }
  110. /* lib end */
  111. /* add custom event begin */
  112. function copy(obj) {
  113. const new_obj = {};
  114. for (let i in obj) {
  115. new_obj[i] = obj[i];
  116. }
  117. return new_obj;
  118. }
  119. function TapEvent(touch_event) {
  120. return new TouchEvent('tap',
  121. Object.assign(copy(touch_event),
  122. { bubbles: true, cancelable: true, composed: true }));
  123. }
  124. function DoubleTapEvent(touch_event) {
  125. return new TouchEvent('doubletap',
  126. Object.assign(copy(touch_event),
  127. { bubbles: true, cancelable: true, composed: true }));
  128. }
  129. function TouchEvent2MouseEvent(event_type, event) {
  130. const touch = event.targetTouches && event.targetTouches[0] || {};
  131. return new MouseEvent(event_type,
  132. Object.assign({}, copy(event), copy(touch),
  133. { bubbles: true, cancelable: true, composed: true }));
  134. }
  135. function add_tap_event_hook(element) {
  136. let start_touch, end_touch, end_event = {};
  137. const start = e => {
  138. start_touch = end_touch = copy(e.touches[0]);
  139. end_event = copy(e);
  140. };
  141. const move = e => {
  142. end_touch = copy(e.touches[0]);
  143. end_event = copy(e);
  144. };
  145. const end = e => {
  146. if (Math.abs(start_touch.clientX - end_touch.clientX) <= 10 &&
  147. Math.abs(start_touch.clientY - end_touch.clientY) <= 10) {
  148. e.target.dispatchEvent(new TapEvent(end_event));
  149. e.preventDefault();
  150. e.target.dispatchEvent(TouchEvent2MouseEvent('click', end_event));
  151. }
  152. };
  153. element.addEventListener('touchstart', start, true);
  154. element.addEventListener('touchmove', move, true);
  155. element.addEventListener('touchend', end, true);
  156. return function event_clearer() {
  157. element.removeEventListener('touchstart', start, true);
  158. element.removeEventListener('touchmove', move, true);
  159. element.removeEventListener('touchend', end, true);
  160. };
  161. }
  162. function add_doubletap_event_hook(element) {
  163. let start_time = 0;
  164. const doubletap_judge = e => {
  165. if (Date.now() - start_time <= 250) {
  166. start_time = 0;
  167. e.target.dispatchEvent(new DoubleTapEvent(e));
  168. } else {
  169. start_time = Date.now();
  170. }
  171. };
  172. element.addEventListener('tap', doubletap_judge, true);
  173. return function event_clearer() {
  174. element.removeEventListener('tap', doubletap_judge, true);
  175. };
  176. }
  177. /* add custom event end */
  178. function create_prompt_panel() {
  179. const prompt_div = document.createElement('div');
  180. const prompt_symbol_div = document.createElement('div');
  181. const prompt_time_div = document.createElement('div');
  182. const prompt_time_begin_span = document.createElement('span');
  183. const prompt_time_end_span = document.createElement('span');
  184. prompt_div.append(prompt_symbol_div, prompt_time_div);
  185. prompt_time_div.append(prompt_time_begin_span, ' / ', prompt_time_end_span);
  186. prompt_div.style.cssText = `
  187. width: 10em;
  188. position: absolute;
  189. z-index: 99999999;
  190. left: 50%;
  191. top: 50%;
  192. padding: 15px 0px;
  193. margin: calc(-0.5em - 15px) auto auto -5em;
  194. background-color: rgba(51, 51, 51, 0.8);
  195. border-radius: 15px;
  196. text-align: center;
  197. font-size: 0.5cm;
  198. color: white;
  199. display: none;
  200. `;
  201. prompt_time_begin_span.style.color = '#2fb3ff';
  202. return {
  203. div: prompt_div,
  204. symbol: prompt_symbol_div,
  205. left_time: prompt_time_begin_span,
  206. right_time: prompt_time_end_span,
  207. };
  208. }
  209. function create_control_panel() {
  210. const div = document.createElement('div');
  211. const content_divs = Array(5).fill().map(() => document.createElement('div'));
  212. div.append.apply(div, content_divs);
  213. div.style.cssText = `
  214. width: 50%;
  215. height: 100%;
  216. margin: 0;
  217. padding: 0;
  218. position: absolute;
  219. z-index: 9999999999;
  220. top: 0;
  221. left: 30%;
  222. background-color: rgba(51, 51, 51, 0.8);
  223. color: white;
  224. box-sizing: border-box;
  225. display: none;
  226. justify-content: center;
  227. align-items: center;
  228. font-size: 1cm;
  229. pointer-events: auto;
  230. `;
  231. return {
  232. display() {
  233. div.style.display = 'flex';
  234. },
  235. hidden() {
  236. div.style.display = 'none';
  237. },
  238. toggle() {
  239. if (div.style.display === 'flex') {
  240. this.hidden();
  241. } else {
  242. this.display();
  243. }
  244. },
  245. div,
  246. content_divs,
  247. };
  248. }
  249. const get_video_touch_hook = (video, e) => {
  250. let top_wrap = find_top_wrap_ele(video);
  251. if (top_wrap === video) {
  252. top_wrap = document.createElement('div');
  253. video.parentElement.insertBefore(top_wrap, video);
  254. top_wrap.append(video);
  255. }
  256. const event_clearer = [add_tap_event_hook, add_doubletap_event_hook].map(x => x(top_wrap));
  257. const hook_fn = {
  258. start: [],
  259. move: [],
  260. end: [],
  261. };
  262. let start_x, start_time;
  263. const touch_start = e => {
  264. start_x = e.touches[0].screenX;
  265. start_time = video.currentTime;
  266. hook_fn.start.forEach(fn => fn(e, start_time));
  267. //window.playbackRate = video.playbackRate;
  268. //video.playbackRate = 4.0;
  269. };
  270. if (e) {
  271. setTimeout(touch_start, 0, e);
  272. }
  273. const touch_move = e => {
  274. const end_x = e.touches[0].screenX;
  275. const x_distance_px = end_x - start_x;
  276. const time_length = px2cm(x_distance_px) * (this.sec_1cm || 1);
  277. hook_fn.move.forEach(fn => fn(e, start_time, x_distance_px, time_length));
  278. fullscreen2Element(top_wrap);
  279. };
  280. const touch_end = e => {
  281. hook_fn.end.forEach(fn => fn(e));
  282. /*
  283. video.playbackRate = window.playbackRate;
  284. const end_x = e.changedTouches[0].pageX;
  285. if(!(end_x - start_x === 0 ))
  286. fullscreen2Element(top_wrap);
  287. */
  288. };
  289. top_wrap.addEventListener('touchstart', touch_start, { passive: false });
  290. top_wrap.addEventListener('touchmove', touch_move, { passive: false });
  291. top_wrap.addEventListener('touchend', touch_end, { passive: false });
  292. const remove_hook = () => {
  293. top_wrap.removeEventListener('touchstart', touch_start);
  294. top_wrap.removeEventListener('touchmove', touch_move);
  295. top_wrap.removeEventListener('touchend', touch_end);
  296. };
  297. return { video, hook_fn, wrap: top_wrap, event_clearer: event_clearer.concat(remove_hook) };
  298. };
  299. const hook_video_move = hook => {
  300. const {video, hook_fn} = hook;
  301. hook_fn.start.push(() => {
  302. paused = video.paused;
  303. });
  304. let playing;
  305. const pause = () => {
  306. if (!video.paused) {
  307. video.pause();
  308. playing = true;
  309. }
  310. };
  311. const play = debounce(() => {
  312. if (playing) {
  313. video.play();
  314. playing = false;
  315. }
  316. }, 100);
  317. hook_fn.move.push((e, start_time, x_distance_px, time_length) => {
  318. pause();
  319. video.currentTime = Math.max(Math.min(start_time + time_length,
  320. video.duration),
  321. 0);
  322. play();
  323. });
  324. };
  325. const hook_video_time_change = hook => {
  326. const {video, hook_fn, event_clearer} = hook;
  327. const top_wrap = find_top_wrap_ele(video);
  328. const video_prompt = create_prompt_panel();
  329. top_wrap.append(video_prompt.div);
  330. event_clearer.push(() => video_prompt.div.remove());
  331. hook_fn.start.push(() => {
  332. video_prompt.right_time.innerText = sec2HHMMSS(video.duration);
  333. });
  334. hook_fn.move.push((e, start_time, x_distance_px, time_length) => {
  335. video_prompt.div.style.display = 'block';
  336. time_length = video.currentTime - start_time;
  337. video_prompt.symbol.innerText = time_length < 0 ? '-' : '+';
  338. video_prompt.symbol.innerText += sec2HHMMSS(Math.abs(time_length));
  339. video_prompt.left_time.innerText = sec2HHMMSS(video.currentTime);
  340. });
  341. hook_fn.end.push(() => {
  342. video_prompt.div.style.display = 'none';
  343. });
  344. };
  345. const hook_video_control = hook => {
  346. const {video, hook_fn, event_clearer} = hook;
  347. const top_wrap = find_top_wrap_ele(video);
  348. const wrap = document.createElement('div');
  349. const paddle_div = document.createElement('div');
  350. const speed_div = document.createElement('div');
  351. const jump_div = document.createElement('div');
  352. const skip_div = document.createElement('div');
  353. const fullscreen_div = document.createElement('div');
  354. const control = create_control_panel();
  355. top_wrap.append(wrap);
  356. const buttons = [paddle_div, speed_div, jump_div, skip_div, fullscreen_div];
  357. wrap.append.apply(wrap, buttons);
  358. wrap.append(control.div);
  359. wrap.style.cssText = `
  360. width: 100%;
  361. height: 100%;
  362. position: absolute;
  363. top: 0px;
  364. left: 0px;
  365. pointer-events: none;
  366. z-index: 9999999999;
  367. display: none;
  368. `;
  369. const update_wrap_size = () => {
  370. wrap.style.fontSize = `${Math.min(video.clientHeight, video.clientWidth) / buttons.length / 4}px`;
  371. };
  372. update_wrap_size();
  373. buttons.forEach(div => div.style.cssText =
  374. `
  375. width: 3em;
  376. height: 3em;
  377. line-height: 3em;
  378. text-align: center;
  379. border: solid white;
  380. border-radius: 100%;
  381. color: white;
  382. pointer-events: auto;
  383. `);
  384. paddle_div.style.pointerEvents = 'none';
  385. paddle_div.style.visibility = 'hidden';
  386. const skip_video = throttle(e => {
  387. video.currentTime = video.duration;
  388. }, 500);
  389. const toggle_play_state = throttle(e => {
  390. if (video.paused) {
  391. video.play();
  392. } else {
  393. video.pause();
  394. }
  395. }, 500);
  396. const toggle_fullscreen = throttle(e => {
  397. if (document.fullscreen) {
  398. document.exitFullscreen();
  399. } else {
  400. top_wrap.requestFullscreen();
  401. }
  402. }, 500);
  403. const update_button = () => {
  404. speed_div.innerText = video.playbackRate + 'x';
  405. jump_div.innerText = sec2HHMMSS(video.currentTime);
  406. };
  407. const hidden_wrap = () => {
  408. control.hidden();
  409. wrap.style.display = 'none';
  410. wrap.style.pointerEvents = 'none';
  411. };
  412. const hidden_wrap_delay = debounce(hidden_wrap, 3000);
  413. const display_wrap = throttle((e) => {
  414. update_wrap_size();
  415. wrap.style.display = 'block';
  416. update_button();
  417. hidden_wrap_delay();
  418. }, 1000);
  419. const prevent_event = e => {
  420. if (e.cancelable) {
  421. e.preventDefault();
  422. }
  423. e.stopImmediatePropagation();
  424. };
  425. skip_div.innerText = 'skip';
  426. fullscreen_div.innerText = 'FS';
  427. skip_div.addEventListener('tap', skip_video);
  428. fullscreen_div.addEventListener('tap', toggle_fullscreen);
  429. wrap.addEventListener('touchend', prevent_event);
  430. wrap.addEventListener('touchmove', e => {
  431. prevent_event(e);
  432. update_button();
  433. hidden_wrap_delay();
  434. });
  435. top_wrap.addEventListener('tap', display_wrap);
  436. top_wrap.addEventListener('doubletap', toggle_play_state);
  437. event_clearer.push(() => {
  438. top_wrap.removeEventListener('tap', display_wrap);
  439. top_wrap.removeEventListener('doubletap', toggle_play_state);
  440. wrap.remove();
  441. });
  442. function speed_activate() {
  443. video.playbackRate = parseFloat(control.div.innerText.replace(/[\r\n]+/g, ''));
  444. update_button();
  445. hidden_wrap();
  446. }
  447. function jump_activate() {
  448. video.currentTime = HHMMSS2sec(control.div.innerText.replace(/[\r\n]+/g, ''));
  449. update_button();
  450. hidden_wrap();
  451. }
  452. function clear_content() {
  453. control.content_divs.forEach(div => div.innerHTML = '');
  454. }
  455. const set_content_tap_fn = (() => {
  456. let bind_fn = [];
  457. return fn => {
  458. bind_fn.forEach(([div, fn]) => div.removeEventListener('tap', fn));
  459. bind_fn = [];
  460. control.content_divs.forEach(div => {
  461. bind_fn.push([div, fn]);
  462. div.addEventListener('tap', fn);
  463. });
  464. };
  465. })();
  466. let state = [];
  467. function set_state() {
  468. control.content_divs.forEach((div, i) => state[i] = div.innerText);
  469. }
  470. let start_y, modifier;
  471. const start = e => {
  472. start_y = e.touches[0].screenY;
  473. set_state();
  474. };
  475. const move = e => {
  476. const end_y = e.touches[0].screenY;
  477. const y_distance_px = start_y - end_y;
  478. const increase = Math.floor(px2cm(y_distance_px) * (this.increase_1cm || 1));
  479. const idx = Array.from(e.target.parentElement.children).indexOf(e.target);
  480. if (modifier[idx]) {
  481. modifier[idx](increase);
  482. }
  483. };
  484. const increase_helper = (state_idx, increase,
  485. limit, pre_ele_modifier, wrap = x => x) => {
  486. const v = parseFloat(state[state_idx]) + increase;
  487. if (limit !== undefined) {
  488. if (pre_ele_modifier) {
  489. if (v >= limit) {
  490. pre_ele_modifier(Math.floor(v / limit));
  491. control.content_divs[state_idx].innerText = wrap(v % limit);
  492. } else if (v < 0) {
  493. pre_ele_modifier(-1);
  494. control.content_divs[state_idx].innerText = wrap(limit + v);
  495. } else {
  496. control.content_divs[state_idx].innerText = wrap(v);
  497. }
  498. } else {
  499. control.content_divs[state_idx].innerText =
  500. wrap(Math.min(Math.max(v, 0), limit));
  501. }
  502. } else {
  503. control.content_divs[state_idx].innerText = wrap(v);
  504. }
  505. };
  506. const speed_modifier = [];
  507. speed_modifier[0] = (increase) => {
  508. increase_helper(0, increase);
  509. };
  510. speed_modifier[2] = (increase) => {
  511. increase_helper(2, increase, 10, speed_modifier[0]);
  512. };
  513. speed_modifier[3] = (increase) => {
  514. increase_helper(3, increase, 10, speed_modifier[2]);
  515. };
  516. const jump_modifier = [];
  517. jump_modifier[0] = (increase) => {
  518. increase_helper(0, increase, undefined, undefined, zero_padding);
  519. };
  520. jump_modifier[2] = (increase) => {
  521. increase_helper(2, increase, 60, jump_modifier[0], zero_padding);
  522. };
  523. jump_modifier[4] = (increase) => {
  524. increase_helper(4, increase, 60, jump_modifier[2], zero_padding);
  525. };
  526. speed_div.addEventListener('tap', throttle(e => {
  527. e.stopImmediatePropagation();
  528. clear_content();
  529. if(window.playbackRate>=4.0) window.playbackRate=0.5;
  530. else if(window.playbackRate<1.0) window.playbackRate=1.0;
  531. else if(window.playbackRate<1.5) window.playbackRate=1.5;
  532. else if(window.playbackRate>=2.0) window.playbackRate=4;
  533. else window.playbackRate = 2.0;
  534. const split = window.playbackRate.toString().split('.');
  535. control.content_divs[0].innerText = split[0];
  536. control.content_divs[1].innerText = '.';
  537. control.content_divs[2].innerText = (split[1] || '00').split('')[0] || 0;
  538. control.content_divs[3].innerText = (split[1] || '00').split('')[1] || 0;
  539. control.content_divs[4].innerText = 'x';
  540. modifier = speed_modifier;
  541. set_content_tap_fn(speed_activate);
  542. control.display();
  543. wrap.style.pointerEvents = 'auto';
  544. }, 500));
  545. jump_div.addEventListener('tap', throttle(e => {
  546. e.stopImmediatePropagation();
  547. clear_content();
  548. if(control.div.style.display === 'none'||!window.playbackPos)
  549. window.playbackPos = video.currentTime;
  550. window.playbackPos += 180;
  551. const time = sec2HHMMSS(window.playbackPos);
  552. const split = time.split(':');
  553. if (split.length < 3) {
  554. split.unshift('00');
  555. }
  556. control.content_divs[0].innerText = split[0];
  557. control.content_divs[1].innerText = ':';
  558. control.content_divs[2].innerText = split[1];
  559. control.content_divs[3].innerText = ':';
  560. control.content_divs[4].innerText = split[2];
  561. modifier = jump_modifier;
  562. set_content_tap_fn(jump_activate);
  563. control.display();
  564. wrap.style.pointerEvents = 'auto';
  565. }, 500));
  566. control.content_divs.forEach(div => {
  567. div.addEventListener('touchstart', start, { passive: false });
  568. div.addEventListener('touchmove', move, { passive: false });
  569. });
  570. };
  571. function find_hook(video) {
  572. window.__hook_video__ = window.__hook_video__ || [];
  573. const exist_video = window.__hook_video__.find(v => v.video === video);
  574. return exist_video;
  575. }
  576. function register_hook(hook) {
  577. if (!find_hook(hook.video)) {
  578. window.__hook_video__.push(hook);
  579. return hook;
  580. }
  581. }
  582. const hook_video = (video) => {
  583. const exist_video = find_hook(video);
  584. if (video.clientWidth && video.clientHeight &&
  585. (!exist_video || is_parent(exist_video.wrap, find_top_wrap_ele(video)))) {
  586. const hook = get_video_touch_hook(video);
  587. hook_video_move(hook);
  588. hook_video_time_change(hook);
  589. hook_video_control(hook);
  590. if (exist_video) {
  591. exist_video.event_clearer.forEach(x => x());
  592. Object.assign(exist_video, hook);
  593. console.log('video-improve: reloaded for ', video, ', wrapped by ', find_top_wrap_ele(video));
  594. } else {
  595. register_hook(hook);
  596. console.log('video-improve: loaded for ', video, ', wrapped by ', find_top_wrap_ele(video));
  597. }
  598. }
  599. };
  600. let videos;
  601. if (document.fullscreen) {
  602. videos = document.fullscreenElement.querySelectorAll('video');
  603. }else {
  604. function get_frames(window) {
  605. const frames = [window];
  606. for (let i = 0; i < window.frames.length; i++) {
  607. try {
  608. window.frames[i].document;
  609. } catch {
  610. continue;
  611. }
  612. frames.push(...get_frames(window.frames[i]))
  613. }
  614. return frames;
  615. }
  616. function flatten(array) {
  617. if (!Array.isArray(array)) {
  618. return [array];
  619. } else if (array.length == 0) {
  620. return [];
  621. } else {
  622. return flatten(array[0]).concat(flatten(array.slice(1)));
  623. }
  624. }
  625. function get_videos() {
  626. const frames = get_frames(window);
  627. const frame_video = frame => Array.from(frame.document.querySelectorAll('video'));
  628. const shadow = frame => Array.from(frame.document.querySelectorAll("shadow-output"));
  629. const shadow_video = shadow => Array.from(shadow.shadowRoot.querySelectorAll("video"));
  630. return flatten(frames.map(frame_video)
  631. .concat(frames.map(f => shadow(f).map(shadow_video))));
  632. }
  633. videos = get_videos();
  634. }
  635. videos.forEach(hook_video);
  636. };
  637. }
  638. window.__videoplayer_enhancer__();