ally

ali

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/478868/1303647/ally.js

  1. //v3版本
  2. window.dpPlugins = window.dpPlugins || function(t) {
  3. var obj = {
  4. version: '1.1.2'
  5. };
  6.  
  7. obj.init = function(player, option) {
  8. obj = Object.assign(option || {}, obj);
  9.  
  10. window.m3u8Parser || obj.loadJs("https://cdn.staticfile.org/m3u8-parser/7.1.0/m3u8-parser.min.js");
  11. window.localforage || obj.loadJs("https://cdn.staticfile.org/localforage/1.10.0/localforage.min.js");
  12.  
  13. obj.ready(player).then(() => {
  14. t.forEach((k) => {
  15. new k(player, obj);
  16. });
  17. });
  18. };
  19.  
  20. obj.ready = function(player) {
  21. return new Promise(function(resolve, reject) {
  22. if (player.isReady) {
  23. resolve();
  24. } else if (player.video.duration > 0 || player.video.readyState > 2) {
  25. player.isReady = true;
  26. resolve();
  27. } else {
  28. player.video.ondurationchange = function() {
  29. player.video.ondurationchange = null;
  30. player.isReady = true;
  31. resolve();
  32. }
  33. }
  34. });
  35. };
  36.  
  37. obj.query = function(selector, parent = document) {
  38. return parent.querySelector(selector);
  39. };
  40.  
  41. obj.queryAll = function(selector, parent = document) {
  42. return parent.querySelectorAll(selector);
  43. };
  44.  
  45. obj.append = function(parent, child) {
  46. if (child instanceof Element) {
  47. Node.prototype.appendChild.call(parent, child);
  48. } else {
  49. parent.insertAdjacentHTML("beforeend", String(child));
  50. }
  51. return parent.lastElementChild || parent.lastChild;
  52. };
  53.  
  54. obj.prepend = function(parent, child) {
  55. if (child instanceof Element) {
  56. Node.prototype.insertBefore.call(parent, child, parent.firstElementChild || parent.firstChild);
  57. } else {
  58. parent.insertAdjacentHTML("afterbegin", String(child));
  59. }
  60. return parent.firstElementChild || parent.firstChild;
  61. };
  62.  
  63. obj.insertAfter = function(targetElement, child) {
  64. var parent = targetElement.parentNode;
  65. if (parent.lastChild == targetElement) {
  66. return obj.append(parent, child);
  67. } else {
  68. child = obj.append(parent, child);
  69. return Node.prototype.insertBefore.call(parent, child, targetElement.nextSibling);
  70. }
  71. };
  72.  
  73. obj.remove = function(child) {
  74. return Node.prototype.removeChild.call(child?.parentNode || document, child);
  75. };
  76.  
  77. obj.setStyle = function(element, key, value) {
  78. if (typeof key === 'object') {
  79. for (const k in key) {
  80. element.style[k] = key[k];
  81. }
  82. return element;
  83. }
  84. element.style[key] = value;
  85. return element;
  86. };
  87.  
  88. console.info("\n %c dpPlugins v" + obj.version + " %c https://gf.qytechs.cn/scripts/483448/ \n", "color: #7367F0; background: #fff; padding:5px 0;", "color: #fff;background: #7367F0; padding:5px 0;");
  89.  
  90. return obj;
  91. }([
  92. class HlsEvents {
  93. constructor(player) {
  94. this.player = player;
  95. this.hls = this.player.plugins.hls;
  96. this.now = Date.now();
  97. this.currentTime = 0;
  98.  
  99. if (!this.player.events.type('video_end')) {
  100. this.player.events.playerEvents.push('video_end');
  101. }
  102. this.player.on('video_end', () => {
  103. this.switchUrl();
  104. });
  105.  
  106. this.player.on('quality_end', () => {
  107. if (this.hls) {
  108. this.hls.destroy();
  109. this.hls = this.player.plugins.hls;
  110. this.onEvents();
  111. localStorage.setItem("dplayer-defaultQuality", this.player.quality.name);
  112. }
  113. });
  114.  
  115. this.player.on('destroy', () => {
  116. if (this.hls) {
  117. this.hls.destroy();
  118. }
  119. });
  120.  
  121. this.onEvents();
  122. }
  123.  
  124. switchUrl() {
  125. if (this.hls && this.hls.hasOwnProperty('data') && this.hls.levelController && !this.hls.levelController.currentLevelIndex) {
  126. const url = this.player.quality.url;
  127. this.hls.url = this.hls.levelController.currentLevel.url[0] = url;
  128. fetch(url).then((response) => {
  129. return response.ok ? response.text() : Promise.reject();
  130. }).then((data) => {
  131. window.m3u8Parser = window.m3u8Parser || unsafeWindow.m3u8Parser;
  132. const parser = new window.m3u8Parser.Parser();
  133. parser.push(data);
  134. parser.end();
  135.  
  136. const vidUrl = url.replace(/media.m3u8.+/, "");
  137. const segments = parser.manifest.segments;
  138. const fragments = this.hls.levelController.currentLevel.details.fragments;
  139. fragments.forEach(function(item, index) {
  140. const segment = segments[index];
  141. Object.assign(item, {
  142. baseurl: url,
  143. relurl: segment.uri,
  144. url: vidUrl + segment.uri,
  145. });
  146. });
  147.  
  148. this.hls.startLoad(this.player.video.currentTime);
  149. this.onEvents();
  150. });
  151. }
  152. }
  153.  
  154. onEvents() {
  155. const Hls = window.Hls || unsafeWindow.Hls;
  156. if (!this.hls) {
  157. this.player = window.player || unsafeWindow.player;
  158. this.hls = this.player.plugins.hls;
  159. }
  160. this.hls.once(Hls.Events.ERROR, (event, data) => {
  161. if (this.hls.media.currentTime > 0) {
  162. this.currentTime = this.hls.media.currentTime;
  163. }
  164. if (data.fatal) {
  165. this.player.notice(`当前带宽: ${Math.round(this.hls.bandwidthEstimate / 1024 / 1024 / 8 * 100) / 100} MB/s`);
  166. setTimeout(this.onEvents, 3e3);
  167. switch (data.type) {
  168. case Hls.ErrorTypes.NETWORK_ERROR:
  169. if (data.details === Hls.ErrorDetails.MANIFEST_LOAD_ERROR || data.details === Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT || data.details === Hls.ErrorDetails.MANIFEST_PARSING_ERROR) {
  170. this.hls.loadSource(this.hls.url)
  171. } else if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) {
  172. this.hls.loadSource(this.hls.url);
  173. this.hls.media.currentTime = this.currentTime;
  174. this.hls.media.play();
  175. } else {
  176. this.hls.startLoad();
  177. }
  178. break;
  179. case Hls.ErrorTypes.MEDIA_ERROR:
  180. this.hls.recoverMediaError();
  181. break;
  182. default:
  183. this.player.notice('视频播放异常,请刷新重试');
  184. this.hls.destroy();
  185. break;
  186. }
  187. } else {
  188. switch (data.type) {
  189. case Hls.ErrorTypes.NETWORK_ERROR:
  190. if (data.details === Hls.ErrorDetails.FRAG_LOAD_ERROR) {
  191. if (this.isUrlExpires(this.hls.url)) {
  192. this.now = Date.now();
  193. this.player.events.trigger('video_start');
  194. return;
  195. }
  196. }
  197. this.onEvents();
  198. break;
  199. default:
  200. this.onEvents();
  201. break;
  202. }
  203. }
  204. });
  205. }
  206.  
  207. isUrlExpires(e) {
  208. var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 6e3,
  209. n = e.match(/&x-oss-expires=(\d+)&/);
  210. if (n) {
  211. return +"".concat(n[1], "000") - t < Date.now();
  212. } else {
  213. return Date.now() - this.now > 300 * 1000 - t;
  214. }
  215. }
  216. },
  217. class ImageEnhancer {
  218. constructor(player, obj) {
  219. this.player = player;
  220.  
  221. Object.assign(this.player.user.storageName, {
  222. imageenhancer: "dplayer-imageenhancer"
  223. });
  224. Object.assign(this.player.user.default, {
  225. imageenhancer: 0
  226. });
  227. this.player.user.init();
  228. this.imageenhancer = this.player.user.get("imageenhancer");
  229. if (this.imageenhancer) {
  230. this.player.video.style.filter = 'contrast(1.01) brightness(1.05) saturate(1.1)';
  231. }
  232.  
  233. this.player.template.imageEnhancer = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-imageenhancer"><span class="dplayer-label">画质增强</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>');
  234. this.player.template.imageEnhancerToggle = obj.query('input', this.player.template.imageEnhancer);
  235. this.player.template.imageEnhancerToggle.checked = this.imageenhancer;
  236.  
  237. this.player.template.imageEnhancer.addEventListener('click', () => {
  238. this.imageenhancer = this.player.template.imageEnhancerToggle.checked = !this.player.template.imageEnhancerToggle.checked;
  239. this.player.user.set("imageenhancer", Number(this.imageenhancer));
  240. this.player.video.style.filter = this.imageenhancer ? 'contrast(1.01) brightness(1.05) saturate(1.1)' : '';
  241. this.player.notice(`画质增强: ${this.imageenhancer ? '开启' : '关闭'}`);
  242. });
  243.  
  244. this.player.on("playing", () => {
  245. if (this.imageenhancer) {
  246. this.player.video.style.filter = 'contrast(1.01) brightness(1.05) saturate(1.1)';
  247. }
  248. });
  249. }
  250. },
  251. class SoundEnhancer {
  252. constructor(player, obj) {
  253. this.player = player;
  254. this.Joysound = window.Joysound || unsafeWindow.Joysound;
  255. this.localforage = window.localforage || unsafeWindow.localforage;
  256. this.joySound = null;
  257. this.offset = null;
  258.  
  259. Object.assign(this.player.user.storageName, {
  260. soundenhancer: "dplayer-soundenhancer",
  261. volumeenhancer: "dplayer-volumeenhancer"
  262. });
  263. Object.assign(this.player.user.default, {
  264. soundenhancer: 0,
  265. volumeenhancer: 0
  266. });
  267. this.player.user.init();
  268.  
  269. /*** SoundEnhancer ***/
  270. this.player.template.soundEnhancer = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-soundenhancer"><span class="dplayer-label">音质增强</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>');
  271. this.player.template.soundEnhancerToggle = obj.query('input', this.player.template.soundEnhancer);
  272. this.player.template.soundEnhancerToggle.checked = !!this.player.user.get("soundenhancer");
  273.  
  274. this.player.template.soundEnhancer.addEventListener('click', () => {
  275. let checked = this.player.template.soundEnhancerToggle.checked = !this.player.template.soundEnhancerToggle.checked;
  276. this.player.user.set("soundenhancer", Number(checked));
  277. this.switchJoysound(checked);
  278. });
  279.  
  280. /*** VolumeEnhancer ***/
  281. this.player.template.gainBox = obj.prepend(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-danmaku dplayer-setting-gain" style="display: block;"><span class="dplayer-label">音量增强</span><div class="dplayer-danmaku-bar-wrap dplayer-gain-bar-wrap"><div class="dplayer-danmaku-bar dplayer-gain-bar"><div class="dplayer-danmaku-bar-inner dplayer-gain-bar-inner" style="width: 0%;"><span class="dplayer-thumb"></span></div></div></div></div>');
  282. this.player.template.gainBarWrap = this.player.template.gainBox.querySelector('.dplayer-gain-bar-wrap');
  283. this.player.bar.elements.gain = this.player.template.gainBox.querySelector('.dplayer-gain-bar-inner');
  284.  
  285. const gainMove = (event) => {
  286. const e = event || window.event;
  287. let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.getElementViewLeft(this.player.template.gainBarWrap)) / 130;
  288. this.switchGainValue(percentage);
  289. };
  290. const gainUp = () => {
  291. document.removeEventListener("touchend", gainUp);
  292. document.removeEventListener("touchmove", gainMove);
  293. document.removeEventListener("mouseup", gainUp);
  294. document.removeEventListener("mousemove", gainMove);
  295. this.player.template.gainBox.classList.remove('dplayer-setting-danmaku-active');
  296. };
  297.  
  298. this.player.template.gainBarWrap.addEventListener('click', (event) => {
  299. const e = event || window.event;
  300. let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.getElementViewLeft(this.player.template.gainBarWrap)) / 130;
  301. this.switchGainValue(percentage);
  302. });
  303. this.player.template.gainBarWrap.addEventListener("touchstart", () => {
  304. document.addEventListener("touchmove", gainMove);
  305. document.addEventListener("touchend", gainUp);
  306. this.player.template.gainBox.classList.add('dplayer-setting-danmaku-active');
  307. });
  308. this.player.template.gainBarWrap.addEventListener("mousedown", () => {
  309. document.addEventListener("mousemove", gainMove);
  310. document.addEventListener("mouseup", gainUp);
  311. this.player.template.gainBox.classList.add('dplayer-setting-danmaku-active');
  312. });
  313.  
  314. this.player.on("playing", () => {
  315. this.localforage.getItem("playing").then((data) => {
  316. if ((data = data || 0, ++data < 1e3)) {
  317. this.player.plugins.hls.data = true;
  318. this.localforage.setItem("playing", data);
  319. }
  320. });
  321. if (!this.player.video.joySound) {
  322. this.init();
  323. }
  324. });
  325. }
  326.  
  327. init() {
  328. if (this.Joysound && this.Joysound.isSupport()) {
  329. this.joySound = new this.Joysound();
  330. this.joySound.init(this.player.video);
  331. this.player.video.joySound = true;
  332. let isJoysound = this.player.user.get("soundenhancer");
  333. if (isJoysound) {
  334. this.switchJoysound(isJoysound);
  335. }
  336. let percentage = this.player.user.get("volumeenhancer");
  337. if (percentage) {
  338. this.switchGainValue(percentage);
  339. }
  340. }
  341. }
  342.  
  343. switchJoysound(o) {
  344. if (this.joySound) {
  345. this.joySound.setEnabled(o);
  346. this.player.notice(`音质增强: ${o ? '开启' : '关闭'}`);
  347. } else {
  348. this.player.notice('Joysound 未完成初始化');
  349. }
  350. }
  351.  
  352. switchGainValue(percentage) {
  353. if (this.joySound) {
  354. percentage = Math.min(Math.max(percentage, 0), 1);
  355. this.player.bar.set("gain", percentage, "width");
  356. this.player.user.set("volumeenhancer", percentage);
  357. this.joySound.setVolume(percentage);
  358. this.player.notice(`音量增强: ${100 + Math.floor(percentage * 100)}%`);
  359. } else {
  360. this.player.notice('Joysound 未完成初始化');
  361. }
  362. }
  363.  
  364. getElementViewLeft(element) {
  365. const scrollTop = window.scrollY || window.pageYOffset || document.body.scrollTop + ((document.documentElement && document.documentElement.scrollTop) || 0);
  366. if (element.getBoundingClientRect) {
  367. if (typeof this.offset !== 'number') {
  368. let temp = document.createElement('div');
  369. temp.style.cssText = 'position:absolute;top:0;left:0;';
  370. document.body.appendChild(temp);
  371. this.offset = -temp.getBoundingClientRect().top - scrollTop;
  372. document.body.removeChild(temp);
  373. temp = null;
  374. }
  375. const rect = element.getBoundingClientRect();
  376. return rect.left + this.offset;
  377. } else {
  378. let actualLeft = element.offsetLeft;
  379. let current = element.offsetParent;
  380. const elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
  381. if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
  382. while (current !== null) {
  383. actualLeft += current.offsetLeft;
  384. current = current.offsetParent;
  385. }
  386. } else {
  387. while (current !== null && current !== element) {
  388. actualLeft += current.offsetLeft;
  389. current = current.offsetParent;
  390. }
  391. }
  392. return actualLeft - elementScrollLeft;
  393. }
  394. }
  395. },
  396. class AspectRatio {
  397. constructor(player) {
  398. this.player = player;
  399. this.value = null;
  400.  
  401. this.player.template.controller.querySelector('.dplayer-icons-right').insertAdjacentHTML("afterbegin", '<div class="dplayer-quality dplayer-aspectRatio"><button class="dplayer-icon dplayer-quality-icon">画面比例</button><div class="dplayer-quality-mask"><div class="dplayer-quality-list dplayer-aspectRatio-list"><div class="dplayer-quality-item" data-value="none">原始比例</div><div class="dplayer-quality-item" data-value="cover">自动裁剪</div><div class="dplayer-quality-item" data-value="fill">拉伸填充</div><div class="dplayer-quality-item" data-value="">系统默认</div></div></div></div>');
  402. this.player.template.aspectRatioButton = this.player.template.controller.querySelector('.dplayer-aspectRatio button');
  403. this.player.template.aspectRatioList = this.player.template.controller.querySelector('.dplayer-aspectRatio-list');
  404.  
  405. this.player.template.aspectRatioList.addEventListener('click', (e) => {
  406. if (e.target.classList.contains('dplayer-quality-item')) {
  407. this.value = e.target.dataset.value;
  408. this.player.video.style['object-fit'] = e.target.dataset.value;
  409. this.player.template.aspectRatioButton.innerText = e.target.innerText;
  410. }
  411. });
  412.  
  413. this.player.on("playing", () => {
  414. if (this.value) {
  415. this.player.video.style['object-fit'] = this.value;
  416. }
  417. });
  418. }
  419. },
  420. class SelectEpisode {
  421. constructor(player, obj) {
  422. this.player = player;
  423.  
  424. if (!(Array.isArray(this.player.options.fileList) && this.player.options.fileList.length > 1 && this.player.options.file)) return;
  425.  
  426. if (!this.player.events.type('episode_end')) {
  427. this.player.events.playerEvents.push('episode_end');
  428. }
  429. this.player.on('episode_end', () => {
  430. this.switchVideo();
  431. });
  432.  
  433. this.player.fileIndex = (this.player.options.fileList || []).findIndex((item, index) => {
  434. return item.file_id === this.player.options.file.file_id;
  435. });
  436.  
  437. obj.prepend(this.player.template.controller.querySelector('.dplayer-icons-right'), '<style>.episode .content{max-width: 360px;max-height: 330px;width: auto;height: auto;box-sizing: border-box;overflow: hidden auto;position: absolute;left: 0px;transition: all 0.38s ease-in-out 0s;bottom: 52px;transform: scale(0);z-index: 2;}.episode .content .list{background-color: rgba(0,0,0,.3);height: 100%;}.episode .content .video-item{color: #fff;cursor: pointer;font-size: 14px;line-height: 35px;overflow: hidden;padding: 0 10px;text-overflow: ellipsis;text-align: center;white-space: nowrap;}.episode .content .active{background-color: rgba(0,0,0,.3);color: #0df;}</style><div class="dplayer-quality episode"><button class="dplayer-icon prev-icon" title="上一集"><svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M757.527273 190.138182L382.510545 490.123636a28.020364 28.020364 0 0 0 0 43.752728l375.016728 299.985454a28.020364 28.020364 0 0 0 45.474909-21.876363V212.014545a28.020364 28.020364 0 0 0-45.474909-21.876363zM249.949091 221.509818a28.020364 28.020364 0 0 0-27.973818 27.973818v525.032728a28.020364 28.020364 0 1 0 55.994182 0V249.483636a28.020364 28.020364 0 0 0-28.020364-27.973818zM747.054545 270.242909v483.514182L444.834909 512l302.173091-241.757091z"></path></svg></button><button class="dplayer-icon dplayer-quality-icon episode-icon">选集</button><button class="dplayer-icon next-icon" title="下一集"><svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M248.506182 190.138182l374.970182 299.985454a28.020364 28.020364 0 0 1 0 43.752728L248.552727 833.861818a28.020364 28.020364 0 0 1-45.521454-21.876363V212.014545c0-23.505455 27.182545-36.538182 45.521454-21.876363z m507.485091 31.371636c15.453091 0 28.020364 12.567273 28.020363 27.973818v525.032728a28.020364 28.020364 0 1 1-55.994181 0V249.483636c0-15.453091 12.520727-27.973818 27.973818-27.973818zM258.978909 270.242909v483.514182L561.198545 512 258.978909 270.242909z"></path></svg></button><div class="content"><div class="list"></div></div></div>')
  438. this.player.template.episodeButton = this.player.template.controller.querySelector('.episode .episode-icon');
  439. this.player.template.episodePrevButton = this.player.template.controller.querySelector('.episode .prev-icon');
  440. this.player.template.episodeNextButton = this.player.template.controller.querySelector('.episode .next-icon');
  441. this.player.template.episodeContent = this.player.template.controller.querySelector('.episode .content');
  442. this.player.template.episodeList = this.player.template.controller.querySelector('.episode .list');
  443. this.player.options.fileList.forEach((item, index) => {
  444. obj.append(this.player.template.episodeList, '<div class="video-item" data-index="' + index + '" title="' + item.name + '">' + item.name + '</div>');
  445. });
  446. this.player.template.episodeVideoItems = this.player.template.controller.querySelectorAll('.episode .video-item');
  447. if (this.player.template.episodeVideoItems.length && this.player.fileIndex >= 0) {
  448. this.player.template.episodeVideoItems[this.player.fileIndex].classList.add('active');
  449. }
  450.  
  451. this.player.template.mask.addEventListener('click', () => {
  452. this.hide();
  453. });
  454.  
  455. this.player.template.episodeButton.addEventListener('click', (e) => {
  456. if (this.player.template.episodeContent.style.transform === 'scale(1)') {
  457. this.hide();
  458. } else {
  459. this.show();
  460. }
  461. });
  462.  
  463. this.player.template.episodeList.addEventListener('click', (e) => {
  464. if (e.target.classList.contains('video-item') && !e.target.classList.contains('active')) {
  465. this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
  466. e.target.classList.add('active');
  467. this.player.fileIndex = e.target.dataset.index * 1;
  468. this.player.options.file = this.player.options.fileList[this.player.fileIndex];
  469.  
  470. this.hide();
  471. this.player.events.trigger('episode_start');
  472. this.player.notice('准备播放:' + this.player.options.file.name, 5000);
  473. }
  474. });
  475.  
  476. this.player.template.episodePrevButton.addEventListener('click', (e) => {
  477. const index = this.player.fileIndex - 1;
  478. if (index >= 0) {
  479. this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
  480. this.player.template.episodeVideoItems[index].classList.add('active');
  481. this.player.fileIndex = index;
  482. this.player.options.file = this.player.options.fileList[this.player.fileIndex];
  483.  
  484. this.hide();
  485. this.player.events.trigger('episode_start');
  486. this.player.notice('准备播放:' + this.player.options.file.name, 5000);
  487. } else {
  488. this.player.notice('没有上一集了');
  489. }
  490. });
  491.  
  492. this.player.template.episodeNextButton.addEventListener('click', (e) => {
  493. const index = this.player.fileIndex + 1;
  494. if (index <= this.player.options.fileList.length - 1) {
  495. this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
  496. this.player.template.episodeVideoItems[index].classList.add('active');
  497. this.player.fileIndex = index;
  498. this.player.options.file = this.player.options.fileList[this.player.fileIndex];
  499.  
  500. this.hide();
  501. this.player.events.trigger('episode_start');
  502. this.player.notice('准备播放:' + this.player.options.file.name, 5000);
  503. } else {
  504. this.player.notice('没有下一集了');
  505. }
  506. });
  507. }
  508.  
  509. switchVideo() {
  510. this.player.switchVideo({
  511. url: this.player.quality.url,
  512. type: 'hls'
  513. });
  514.  
  515. this.player.video.oncanplay = () => {
  516. this.player.video.oncanplay = null;
  517. this.player.play();
  518.  
  519. this.player.events.trigger('quality_end');
  520. };
  521. }
  522.  
  523. show() {
  524. this.player.template.episodeContent.style.transform = 'scale(1)';
  525. this.player.template.mask.classList.add('dplayer-mask-show');
  526. }
  527.  
  528. hide() {
  529. this.player.template.episodeContent.style.transform = 'scale(0)';
  530. this.player.template.mask.classList.remove('dplayer-mask-show');
  531. }
  532. },
  533. class AutoNextEpisode {
  534. constructor(player, obj) {
  535. this.player = player;
  536.  
  537. Object.assign(this.player.user.storageName, {
  538. autonextepisode: "dplayer-autonextepisode"
  539. });
  540. Object.assign(this.player.user.default, {
  541. autonextepisode: 0
  542. });
  543. this.player.user.init();
  544.  
  545. this.autonextepisode = this.player.user.get("autonextepisode");
  546.  
  547. this.player.template.autoNextEpisode = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-autonextepisode"><span class="dplayer-label">自动下一集</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>');
  548. this.player.template.autoNextEpisodeToggle = obj.query('input', this.player.template.autoNextEpisode);
  549. this.player.template.autoNextEpisodeToggle.checked = this.autonextepisode;
  550.  
  551. this.player.template.autoNextEpisode.addEventListener('click', () => {
  552. this.autonextepisode = this.player.template.autoNextEpisodeToggle.checked = !this.player.template.autoNextEpisodeToggle.checked;
  553. this.player.user.set("autonextepisode", Number(this.autonextepisode));
  554. this.player.notice(`自动播放下集: ${this.autonextepisode ? '开启' : '关闭'}`);
  555. });
  556.  
  557. this.player.on("ended", () => {
  558. if (this.autonextepisode) {
  559. this.player.template.episodeNextButton && this.player.template.episodeNextButton.click();
  560. }
  561. });
  562. }
  563. },
  564. class MemoryPlay {
  565. constructor(player, obj) {
  566. this.player = player;
  567. this.file_id = this.player.options?.file?.file_id;
  568. this.hasMemoryDisplay = false;
  569.  
  570. Object.assign(this.player.user.storageName, {
  571. automemoryplay: "dplayer-automemoryplay"
  572. });
  573. Object.assign(this.player.user.default, {
  574. automemoryplay: 0
  575. });
  576. this.player.user.init();
  577. this.automemoryplay = this.player.user.get("automemoryplay");
  578.  
  579. this.player.template.autoMemoryPlay = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-automemoryplay"><span class="dplayer-label">自动记忆播放</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>');
  580. this.player.template.autoMemoryPlayToggle = obj.query('input', this.player.template.autoMemoryPlay);
  581. this.player.template.autoMemoryPlayToggle.checked = this.automemoryplay;
  582.  
  583. this.player.template.autoMemoryPlay.addEventListener('click', () => {
  584. this.automemoryplay = this.player.template.autoMemoryPlayToggle.checked = !this.player.template.autoMemoryPlayToggle.checked;
  585. this.player.user.set("automemoryplay", Number(this.automemoryplay));
  586. this.player.notice(`自动记忆播放: ${this.automemoryplay ? '开启' : '关闭'}`);
  587. });
  588.  
  589. this.player.on('quality_end', () => {
  590. if (this.file_id !== this.player.options?.file?.file_id) {
  591. this.file_id = this.player.options?.file?.file_id;
  592. this.hasMemoryDisplay = false;
  593. }
  594. this.run();
  595. });
  596.  
  597. document.onvisibilitychange = () => {
  598. if (document.visibilityState === 'hidden') {
  599. const {
  600. video: {
  601. currentTime, duration
  602. }
  603. } = this.player;
  604. this.setTime(this.file_id, currentTime, duration);
  605. }
  606. };
  607.  
  608. window.onbeforeunload = () => {
  609. const {
  610. video: {
  611. currentTime, duration
  612. }
  613. } = this.player;
  614. this.setTime(this.file_id, currentTime, duration);
  615. };
  616.  
  617. this.run();
  618. }
  619.  
  620. run() {
  621. if (this.hasMemoryDisplay === false) {
  622. this.hasMemoryDisplay = true;
  623.  
  624. const {
  625. video: {
  626. currentTime, duration
  627. }
  628. } = this.player;
  629. const memoryTime = this.getTime(this.file_id);
  630. if (memoryTime && memoryTime > currentTime) {
  631. if (this.automemoryplay) {
  632. this.player.seek(memoryTime);
  633.  
  634. if (this.player.video.paused) {
  635. this.player.play();
  636. }
  637. } else {
  638. const fTime = this.formatTime(memoryTime);
  639. let memoryNode = document.createElement('div');
  640. memoryNode.setAttribute('class', 'memory-play-wrap');
  641. memoryNode.setAttribute('style', 'display: block;position: absolute;left: 33px;bottom: 66px;font-size: 15px;padding: 7px;border-radius: 3px;color: #fff;z-index:100;background: rgba(0,0,0,.5);');
  642. memoryNode.innerHTML = '上次播放到:' + fTime + '&nbsp;&nbsp;<a href="javascript:void(0);" class="play-jump" style="text-decoration: none;color: #06c;"> 跳转播放 &nbsp;</a><em class="close-btn" style="display: inline-block;width: 15px;height: 15px;vertical-align: middle;cursor: pointer;background: url(https://nd-static.bdstatic.com/m-static/disk-share/widget/pageModule/share-file-main/fileType/video/img/video-flash-closebtn_15f0e97.png) no-repeat;"></em>';
  643. this.player.container.insertBefore(memoryNode, null);
  644.  
  645. let memoryTimeout = setTimeout(() => {
  646. this.player.container.removeChild(memoryNode);
  647. }, 15000);
  648.  
  649. memoryNode.querySelector('.close-btn').onclick = () => {
  650. this.player.container.removeChild(memoryNode);
  651. clearTimeout(memoryTimeout);
  652. };
  653.  
  654. memoryNode.querySelector('.play-jump').onclick = () => {
  655. this.player.seek(memoryTime);
  656. this.player.container.removeChild(memoryNode);
  657. clearTimeout(memoryTimeout);
  658. }
  659. }
  660. }
  661. }
  662. }
  663.  
  664. getTime(e) {
  665. return localStorage.getItem("video_" + e) || 0;
  666. }
  667.  
  668. setTime(e, t, o) {
  669. e && t && (e = "video_" + e, t <= 60 || t + 120 >= o || 0 ? localStorage.removeItem(e) : localStorage.setItem(e, t));
  670. }
  671.  
  672. formatTime(seconds) {
  673. var secondTotal = Math.round(seconds),
  674. hour = Math.floor(secondTotal / 3600),
  675. minute = Math.floor((secondTotal - hour * 3600) / 60),
  676. second = secondTotal - hour * 3600 - minute * 60;
  677. minute < 10 && (minute = "0" + minute);
  678. second < 10 && (second = "0" + second);
  679. return hour === 0 ? minute + ":" + second : hour + ":" + minute + ":" + second;
  680. }
  681. },
  682. class SkipPosition {
  683. constructor(player, obj) {
  684. this.player = player;
  685. this.file_id = this.player.options?.file?.file_id;
  686. this.timer = null;
  687.  
  688. Object.assign(this.player.user.storageName, {
  689. skipposition: "dplayer-skipposition",
  690. skipstarttime: "dplayer-skipstarttime",
  691. skipendtime: "dplayer-endtime"
  692. });
  693. Object.assign(this.player.user.default, {
  694. skipposition: 0,
  695. skipstarttime: 0,
  696. skipendtime: 0
  697. });
  698. this.player.user.init();
  699. this.skipposition = this.player.user.get("skipposition");
  700. this.skipstarttime = this.player.user.get("skipstarttime");
  701. this.skipendtime = this.player.user.get("skipendtime");
  702.  
  703. this.player.template.skipPosition = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-skipposition"><span class="dplayer-label">跳过片头片尾</span><div class="dplayer-toggle"><input class="dplayer-toggle-setting-input" type="checkbox" name="dplayer-toggle"><label for="dplayer-toggle"></label></div></div>');
  704. this.player.template.skipPositionToggle = obj.query('input', this.player.template.skipPosition);
  705. this.player.template.skipPositionToggle.checked = this.skipposition;
  706.  
  707. this.player.template.skipPositionBox = obj.insertAfter(this.player.template.settingBox, '<div class="dplayer-setting-skipposition-item" style="display: none;right: 155px;position: absolute;bottom: 50px;width: 150px;border-radius: 2px;background: rgba(28, 28, 28, 0.9);padding: 7px 0px;transition: all 0.3s ease-in-out 0s;overflow: hidden;z-index: 2;"><div class="dplayer-skipposition-item" style="padding: 5px 10px;box-sizing: border-box;cursor: pointer;position: relative;"><span class="dplayer-skipposition-label" title="双击设置当前时间为跳过片头时间" style="color: #eee;font-size: 13px;display: inline-block;vertical-align: middle;white-space: nowrap;">片头时间:</span><input type="number" style="width: 55px;height: 15px;top: 3px;font-size: 13px;border: 1px solid #fff;border-radius: 3px;text-align: center;" step="1" min="0" value="60"></div><div class="dplayer-skipposition-item" style="padding: 5px 10px;box-sizing: border-box;cursor: pointer;position: relative;"><span class="dplayer-skipposition-label" title="双击设置剩余时间为跳过片尾时间" style="color: #eee;font-size: 13px;display: inline-block;vertical-align: middle;white-space: nowrap;">片尾时间:</span><input type="number" style="width: 55px;height: 15px;top: 3px;font-size: 13px;border: 1px solid #fff;border-radius: 3px;text-align: center;" step="1" min="0" value="120"></div></div>');
  708. this.player.template.skipPositionItems = obj.queryAll('.dplayer-skipposition-item', this.player.template.skipPositionBox);
  709. this.player.template.jumpStartSpan = obj.query('span', this.player.template.skipPositionItems[0]);
  710. this.player.template.jumpStartInput = obj.query('input', this.player.template.skipPositionItems[0]);
  711. this.player.template.jumpEndSpan = obj.query('span', this.player.template.skipPositionItems[1]);
  712. this.player.template.jumpEndInput = obj.query('input', this.player.template.skipPositionItems[1]);
  713. this.player.template.jumpStartInput.value = this.skipstarttime;
  714. this.player.template.jumpEndInput.value = this.skipendtime;
  715.  
  716. this.player.template.jumpStartSpan.addEventListener('dblclick', (event) => {
  717. this.player.template.jumpStartInput.value = this.player.video.currentTime;
  718. this.skipstarttime = this.player.video.currentTime;
  719. });
  720. this.player.template.jumpStartInput.addEventListener('input', (event) => {
  721. this.skipstarttime = event.target.value * 1;
  722. this.player.user.set("skipstarttime", this.skipstarttime);
  723. });
  724. this.player.template.jumpEndSpan.addEventListener('dblclick', (event) => {
  725. this.skipendtime = this.player.video.duration - this.player.video.currentTime;
  726. this.player.template.jumpEndInput.value = this.skipendtime;
  727. });
  728. this.player.template.jumpEndInput.addEventListener('input', (event) => {
  729. this.skipendtime = event.target.value * 1;
  730. this.player.user.set("skipendtime", this.skipendtime);
  731. });
  732.  
  733. this.player.template.skipPosition.addEventListener('click', () => {
  734. this.skipposition = this.player.template.skipPositionToggle.checked = !this.player.template.skipPositionToggle.checked;
  735. this.player.user.set("skipposition", Number(this.skipposition));
  736. this.skipposition ? this.show() : this.hide();
  737. this.player.notice(`跳过片头片尾: ${this.skipposition ? '开启' : '关闭'}`);
  738. });
  739. this.player.template.skipPosition.addEventListener('mouseenter', () => {
  740. if (this.skipposition) {
  741. this.show();
  742. }
  743. });
  744.  
  745. this.player.template.mask.addEventListener('click', () => {
  746. this.hide();
  747. });
  748.  
  749. this.player.on('quality_end', () => {
  750. if (this.file_id !== this.player.options?.file?.file_id) {
  751. this.file_id = this.player.options?.file?.file_id;
  752. this.jumpStart();
  753. this.jumpEnd();
  754. }
  755. });
  756.  
  757. if (this.skipposition) {
  758. this.jumpStart();
  759. this.jumpEnd();
  760. }
  761. }
  762.  
  763. jumpStart() {
  764. if (this.skipposition && this.skipstarttime > this.player.video.currentTime) {
  765. this.player.video.currentTime = this.skipstarttime;
  766. }
  767. }
  768.  
  769. jumpEnd() {
  770. if (!this.timer) {
  771. this.timer = setInterval(() => {
  772. if (this.skipposition && this.skipendtime >= (this.player.video.duration - this.player.video.currentTime)) {
  773. this.player.video.currentTime = this.player.video.duration;
  774. clearInterval(this.timer);
  775. this.timer = null;
  776. }
  777. }, 3000);
  778. }
  779. }
  780.  
  781. show() {
  782. this.player.template.skipPositionBox.style.display = 'block';
  783. }
  784.  
  785. hide() {
  786. this.player.template.skipPositionBox.style.display = 'none';
  787. }
  788.  
  789. },
  790. class Subtitle {
  791. constructor(player, obj) {
  792. this.player = player;
  793. this.offset = 0;
  794. this.offsetStep = 1;
  795. this.color = this.get('color') || '#fff';
  796. this.bottom = this.get('bottom') || '40px';
  797. this.fontSize = this.get('fontSize') || '20px';
  798.  
  799. if (!player.events.type('subtitle_end')) {
  800. player.events.playerEvents.push('subtitle_end');
  801. }
  802. player.on('subtitle_end', () => {
  803. this.add(this.player.options.subtitles);
  804. });
  805. this.player.events.trigger('subtitle_start');
  806.  
  807. this.player.on('quality_end', () => {
  808. this.player.template.subtitle.innerHTML = `<p></p>`;
  809. if (this.player.options.subtitle.url.length && this.player.options.subtitles.length) {
  810. this.switch(this.player.options.subtitles[this.player.options.subtitle.index]);
  811. } else {
  812. this.player.events.trigger('subtitle_start');
  813. }
  814. });
  815.  
  816. this.player.on('episode_end', () => {
  817. this.clear();
  818.  
  819. this.style({
  820. color: this.color,
  821. bottom: this.bottom,
  822. fontSize: this.fontSize,
  823. });
  824. });
  825.  
  826. this.player.on('video_end', () => {
  827. this.style({
  828. color: this.color,
  829. bottom: this.bottom,
  830. fontSize: this.fontSize,
  831. });
  832. });
  833.  
  834. this.player.template.subtitleSettingBox = obj.append(this.player.template.controller, '<div class="dplayer-icons dplayer-comment-box subtitle-setting-box" style="bottom: 10px;left: auto;right: 400px !important;display: block;"><div class="dplayer-comment-setting-box"><div class="dplayer-comment-setting-color"><div class="dplayer-comment-setting-title">字幕颜色<button type="text" class="color-custom" style="line-height: 16px;font-size: 12px;top: 12px;right: 12px;color: #fff;background: rgba(28, 28, 28, 0.9);position: absolute;">自定义</button></div><label><input type="radio" name="dplayer-danmaku-color-1" value="#fff" checked=""><span style="background: #fff;"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#e54256"><span style="background: #e54256"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#ffe133"><span style="background: #ffe133"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#64DD17"><span style="background: #64DD17"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#39ccff"><span style="background: #39ccff"></span></label><label><input type="radio" name="dplayer-danmaku-color-1" value="#D500F9"><span style="background: #D500F9"></span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕位置</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>上移</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0" checked=""><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>下移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕大小</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>加大</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>默认</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>减小</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">字幕偏移<div style="margin-top: -30px;right: 14px;position: absolute;">偏移量<input type="number" class="subtitle-offset-step" style="height: 14px;width: 50px;margin-left: 4px;border: 1px solid #fff;border-radius: 3px;color: #fff;background: rgba(28, 28, 28, 0.9);text-align: center;" value="1" step="1" min="1"></div></div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>前移</span></label><label><span><input type="text" class="subtitle-offset" style="width: 94%;height: 14px;background: rgba(28, 28, 28, 0.9);border: 0px solid #fff;text-align: center;" value="0" title="双击恢复默认"></span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>后移</span></label></div><div class="dplayer-comment-setting-type"><div class="dplayer-comment-setting-title">更多字幕功能</div><label><input type="radio" name="dplayer-danmaku-type-1" value="1"><span>本地字幕</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="0"><span>待定</span></label><label><input type="radio" name="dplayer-danmaku-type-1" value="2"><span>待定</span></label></div></div></div>');
  835. this.player.template.subtitleCommentSettingBox = obj.query('.dplayer-comment-setting-box', this.player.template.subtitleSettingBox);
  836. this.player.template.subtitleSetting = obj.append(obj.query('.dplayer-setting-origin-panel', this.player.template.settingBox), '<div class="dplayer-setting-item dplayer-setting-subtitle"><span class="dplayer-label">字幕设置</span><div class="dplayer-toggle"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 32 32"><path d="M22 16l-10.105-10.6-1.895 1.987 8.211 8.613-8.211 8.612 1.895 1.988 8.211-8.613z"></path></svg></div></div>');
  837. this.player.template.mask.addEventListener('click', () => {
  838. this.hide();
  839. });
  840. this.player.template.subtitleSetting.addEventListener('click', () => {
  841. this.toggle();
  842. });
  843.  
  844. this.player.template.subtitleColorPicker = obj.append(this.player.template.container, '<input type="color" id="colorPicker">');
  845. this.player.template.subtitleColorCustom = obj.query('.color-custom', this.player.template.subtitleCommentSettingBox);
  846. this.player.template.subtitleColorCustom.addEventListener('click', () => {
  847. this.player.template.subtitleColorPicker.click();
  848. });
  849. this.player.template.subtitleColorPicker.addEventListener('input', (event) => {
  850. this.color = event.target.value;
  851. this.set("color", this.color);
  852. this.style({
  853. color: this.color
  854. });
  855. });
  856.  
  857. this.player.template.subtitleSettingColor = obj.query('.dplayer-comment-setting-color', this.player.template.subtitleCommentSettingBox);
  858. this.player.template.subtitleSettingColor.addEventListener('click', (event) => {
  859. if (event.target.nodeName === "INPUT") {
  860. this.color = event.target.value;
  861. this.set("color", this.color);
  862. this.style({
  863. color: this.color
  864. });
  865. }
  866. });
  867.  
  868. this.player.template.subtitleSettingItem = obj.queryAll('.dplayer-comment-setting-type', this.player.template.subtitleCommentSettingBox);
  869. this.player.template.subtitleSettingItem[0].addEventListener('click', (event) => {
  870. if (event.target.nodeName === "INPUT") {
  871. if (event.target.value == "1") {
  872. this.bottom = parseFloat(this.bottom) + 1 + this.bottom.replace(/[\d\.]+/, '');
  873. } else if (event.target.value == "2") {
  874. this.bottom = parseFloat(this.bottom) - 1 + this.bottom.replace(/[\d\.]+/, '');
  875. } else {
  876. this.bottom = '20px';
  877. }
  878. this.set('bottom', this.bottom);
  879. this.style({
  880. bottom: this.bottom
  881. });
  882. }
  883. });
  884.  
  885. this.player.template.subtitleSettingItem[1].addEventListener('click', (event) => {
  886. if (event.target.nodeName === "INPUT") {
  887. if (event.target.value == "1") {
  888. this.fontSize = parseFloat(this.fontSize) + 1 + this.fontSize.replace(/[\d\.]+/, '');
  889. } else if (event.target.value == "2") {
  890. this.fontSize = parseFloat(this.fontSize) - 1 + this.fontSize.replace(/[\d\.]+/, '');
  891. } else {
  892. this.fontSize = '40px';
  893. }
  894. this.set("fontSize", this.fontSize);
  895. this.style({
  896. fontSize: this.fontSize
  897. });
  898. }
  899. });
  900.  
  901. this.player.template.subtitleOffsetStep = obj.query('.subtitle-offset-step', this.player.template.subtitleSettingItem[2]);
  902. this.player.template.subtitleOffsetStep.addEventListener('input', (event) => {
  903. this.offsetStep = event.target.value * 1;
  904. });
  905.  
  906. this.player.template.subtitleOffset = obj.query('.subtitle-offset', this.player.template.subtitleSettingItem[2]);
  907. this.player.template.subtitleOffset.addEventListener('input', (event) => {
  908. this.offset = event.target.value * 1;
  909. this.subtitleOffset();
  910. });
  911. this.player.template.subtitleOffset.addEventListener('dblclick', (event) => {
  912. if (this.offset != 0) {
  913. this.offset = 0;
  914. event.target.value = 0;
  915. this.subtitleOffset();
  916. }
  917. });
  918.  
  919. this.player.template.subtitleSettingItem[2].addEventListener('click', (event) => {
  920. if (event.target.nodeName === "INPUT") {
  921. if (event.target.type === "radio") {
  922. let value = this.player.template.subtitleOffset.value *= 1;
  923. if (event.target.value == "1") {
  924. value += this.offsetStep || 1;
  925. } else if (event.target.value == "2") {
  926. value -= this.offsetStep || 1;
  927. } else {
  928. value = 0;
  929. }
  930. this.offset = value;
  931. this.player.template.subtitleOffset.value = value;
  932. this.subtitleOffset();
  933. }
  934. }
  935. });
  936.  
  937. this.player.template.subtitleLocalFile = obj.append(this.player.template.container, '<input class="subtitleLocalFile" type="file" accept="webvtt,.vtt,.srt,.ssa,.ass" style="display: none;">');
  938. this.player.template.subtitleSettingItem[3].addEventListener('click', (event) => {
  939. if (event.target.nodeName === "INPUT") {
  940. if (event.target.value == "1") {
  941. this.player.template.subtitleLocalFile.click();
  942. this.hide();
  943. }
  944. }
  945. });
  946. this.player.template.subtitleLocalFile.addEventListener("change", (event) => {
  947. if (event.target.files.length) {
  948. const file = event.target.files[0];
  949. const file_ext = file.name.split(".").pop().toLowerCase();
  950. this.blobToText(file).then((text) => {
  951. let subtitleOption = {};
  952. subtitleOption.url = '';
  953. subtitleOption.sarr = this.subParser(text, file_ext);
  954. subtitleOption.lang = this.getlangBySarr(subtitleOption.sarr);
  955. subtitleOption.name = subtitleOption.name || this.langToLabel(subtitleOption.lang);
  956. this.add([subtitleOption]);
  957. this.switch(subtitleOption);
  958. });
  959. }
  960. event.target.value = "";
  961. });
  962.  
  963. const lastItemIndex = this.player.template.subtitlesItem.length - 1;
  964. this.player.template.subtitlesItem[lastItemIndex].addEventListener("click", () => {
  965. this.player.options.subtitle.index = lastItemIndex;
  966. this.player.template.subtitle.innerHTML = `<p></p>`;
  967. this.player.subtitles.subContainerHide();
  968. });
  969.  
  970. this.style({
  971. color: this.color,
  972. bottom: this.bottom,
  973. fontSize: this.fontSize,
  974. });
  975. }
  976.  
  977. add(sublist) {
  978. if (!(Array.isArray(sublist) && sublist.length)) {
  979. return;
  980. }
  981. if (!(this.player.template.subtitlesBox && this.player.template.subtitlesItem.length)) {
  982. return;
  983. }
  984.  
  985. const lastItemIndex = this.player.template.subtitlesItem.length - 1;
  986. sublist.forEach((item, index) => {
  987. const i = lastItemIndex + index;
  988. this.player.options.subtitle.url.splice(i, 0, item);
  989.  
  990. let itemNode = document.createElement('div');
  991. itemNode.setAttribute('class', 'dplayer-subtitles-item');
  992. itemNode.innerHTML = '<span class="dplayer-label">' + (item.name + ' ' + (item.language || item.lang || "")) + '</span>';
  993. this.player.template.subtitlesBox.insertBefore(itemNode, this.player.template.subtitlesBox.childNodes[i]);
  994.  
  995. itemNode.addEventListener('click', (event) => {
  996. this.player.subtitles.hide();
  997. if (this.player.options.subtitle.index !== i + 1) {
  998. this.player.options.subtitle.index = i + 1;
  999.  
  1000. this.player.template.subtitle.innerHTML = `<p></p>`;
  1001. this.switch(item);
  1002.  
  1003. if (this.player.template.subtitle.classList.contains('dplayer-subtitle-hide')) {
  1004. this.player.subtitles.subContainerShow();
  1005. }
  1006. }
  1007. });
  1008. });
  1009. this.player.template.subtitlesItem = this.player.template.subtitlesBox.querySelectorAll('.dplayer-subtitles-item');
  1010.  
  1011. if (!(this.player.video.textTracks.length && this.player.video.textTracks[0]?.cues && this.player.video.textTracks[0].cues.length)) {
  1012. this.player.options.subtitle.index = this.player.options.subtitles.findIndex((item) => {
  1013. return ['cho', 'chi'].includes(item.language);
  1014. });
  1015. if (this.player.options.subtitle.index < 0) {
  1016. this.player.options.subtitle.index = 0;
  1017. }
  1018. this.switch(this.player.options.subtitle.url[this.player.options.subtitle.index]);
  1019. }
  1020. }
  1021.  
  1022. switch (newOption = {}) {
  1023. return this.initCues(newOption).then((subArr) => {
  1024. if (newOption.name) {
  1025. //this.player.notice(`切换字幕: ${newOption.name}`);
  1026. }
  1027. });
  1028. }
  1029.  
  1030. restart() {
  1031. this.clear();
  1032. this.add(this.player.options.subtitles);
  1033. }
  1034.  
  1035. clear() {
  1036. this.player.template.subtitle.innerHTML = `<p></p>`;
  1037. this.player.options.subtitles = [];
  1038. this.player.options.subtitle.url = [];
  1039. for (let i = 0; i < this.player.template.subtitlesItem.length - 1; i++) {
  1040. this.player.template.subtitlesBox.removeChild(this.player.template.subtitlesItem[i]);
  1041. }
  1042. this.player.template.subtitlesItem = this.player.template.subtitlesBox.querySelectorAll('.dplayer-subtitles-item');
  1043. }
  1044.  
  1045. initCues(subtitleOption) {
  1046. return this.urlToArray(subtitleOption).then((subtitleOption) => {
  1047. return this.createTrack(subtitleOption);
  1048. });
  1049. }
  1050.  
  1051. urlToArray(subtitleOption) {
  1052. if (Array.isArray(subtitleOption?.sarr) && subtitleOption.sarr.length) {
  1053. return Promise.resolve(subtitleOption);
  1054. } else {
  1055. const url = subtitleOption?.url || subtitleOption?.download_url || subtitleOption?.uri || subtitleOption?.surl;
  1056. const extension = subtitleOption.sext || subtitleOption.file_extension;
  1057. if (url) {
  1058. return this.requestFile(url).then((text) => {
  1059. subtitleOption.sarr = this.subParser(text, extension, subtitleOption.delay || 0);
  1060. subtitleOption.lang = subtitleOption.lang || this.getlangBySarr(subtitleOption.sarr);
  1061. subtitleOption.name = subtitleOption.name || this.langToLabel(subtitleOption.lang);
  1062. return subtitleOption;
  1063. });
  1064. } else {
  1065. return Promise.reject();
  1066. }
  1067. }
  1068. }
  1069.  
  1070. createTrack(subtitleOption) {
  1071. const {
  1072. video
  1073. } = this.player;
  1074. const textTracks = video.textTracks;
  1075. const textTrack = textTracks[0];
  1076.  
  1077. textTrack.mode === "hidden" || (textTrack.mode = "hidden");
  1078. if (textTrack.cues && textTrack.cues.length) {
  1079. for (let i = textTrack.cues.length - 1; i >= 0; i--) {
  1080. textTrack.removeCue(textTrack.cues[i]);
  1081. }
  1082. }
  1083.  
  1084. subtitleOption.sarr.forEach(function(item, index) {
  1085. const textTrackCue = new VTTCue(item.startTime, item.endTime, item.text);
  1086. textTrackCue.id = item.index;
  1087. textTrack.addCue(textTrackCue);
  1088. });
  1089. }
  1090.  
  1091. requestFile(url) {
  1092. return fetch(url, {
  1093. referrer: "https://www.aliyundrive.com/",
  1094. referrerPolicy: "origin",
  1095. body: null,
  1096. method: "GET",
  1097. mode: "cors",
  1098. credentials: "omit"
  1099. }).then(data => data.blob()).then(blob => {
  1100. return this.blobToText(blob)
  1101. });
  1102. };
  1103.  
  1104. blobToText(blob) {
  1105. return new Promise(function(resolve, reject) {
  1106. var reader = new FileReader();
  1107. reader.readAsText(blob, "UTF-8");
  1108. reader.onload = function(e) {
  1109. var result = reader.result;
  1110. if (result.indexOf("�") > -1 && !reader.markGBK) {
  1111. reader.markGBK = true;
  1112. return reader.readAsText(blob, "GBK");
  1113. } else if (result.indexOf("") > -1 && !reader.markBIG5) {
  1114. reader.markBIG5 = true;
  1115. return reader.readAsText(blob, "BIG5");
  1116. }
  1117. resolve(result);
  1118. };
  1119. reader.onerror = function(error) {
  1120. reject(error);
  1121. };
  1122. });
  1123. }
  1124.  
  1125. subParser(stext, sext, delay = 0) {
  1126. if (!sext) {
  1127. sext = (stext.indexOf("->") > 0 ? "srt" : stext.indexOf("Dialogue:") > 0 ? "ass" : "").toLowerCase();
  1128. }
  1129. var regex, data = [],
  1130. items = [];
  1131. switch (sext) {
  1132. case "webvtt":
  1133. case "vtt":
  1134. case "srt":
  1135. stext = stext.replace(/\r/g, "");
  1136. regex = /(\d+)?\n?(\d{0,2}:?\d{2}:\d{2}.\d{3}) -?-> (\d{0,2}:?\d{2}:\d{2}.\d{3})/g;
  1137. data = stext.split(regex);
  1138. data.shift();
  1139. for (let i = 0; i < data.length; i += 4) {
  1140. items.push({
  1141. index: items.length,
  1142. startTime: parseTimestamp(data[i + 1]) + +delay,
  1143. endTime: parseTimestamp(data[i + 2]) + +delay,
  1144. text: data[i + 3].trim().replace(/(\\N|\\n)/g, "\n").replace(/{.*?}/g, "").replace(/[a-z]+\:.*\d+\.\d+\%\s/, "")
  1145. });
  1146. }
  1147. return items;
  1148. case "ssa":
  1149. case "ass":
  1150. stext = stext.replace(/\r\n/g, "");
  1151. regex = /Dialogue: .*?\d+,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),.*?,\d+,\d+,\d+,.*?,/g;
  1152. data = stext.split(regex);
  1153. data.shift();
  1154. for (let i = 0; i < data.length; i += 3) {
  1155. items.push({
  1156. index: items.length,
  1157. startTime: parseTimestamp(data[i]) + +delay,
  1158. endTime: parseTimestamp(data[i + 1]) + +delay,
  1159. text: data[i + 2].trim().replace(/(\\N|\\n)/g, "\n").replace(/{.*?}/g, "")
  1160. });
  1161. }
  1162. return items;
  1163. default:
  1164. console.error("未知字幕格式,无法解析");
  1165. console.info(sext, stext);
  1166. return items;
  1167. }
  1168.  
  1169. function parseTimestamp(e) {
  1170. var t = e.split(":"),
  1171. n = parseFloat(t.length > 0 ? t.pop().replace(/,/g, ".") : "00.000") || 0,
  1172. r = parseFloat(t.length > 0 ? t.pop() : "00") || 0;
  1173. return 3600 * (parseFloat(t.length > 0 ? t.pop() : "00") || 0) + 60 * r + n;
  1174. }
  1175. };
  1176.  
  1177. getlangBySarr(sarr) {
  1178. var t = [
  1179. sarr[parseInt(sarr.length / 3)].text,
  1180. sarr[parseInt(sarr.length / 2)].text,
  1181. sarr[parseInt(sarr.length / 3 * 2)].text
  1182. ].join("").replace(/[<bi\/>\r?\n]*/g, "");
  1183.  
  1184. var e = "eng",
  1185. i = (t.match(/[一-龥]/g) || []).length / t.length;
  1186. (t.match(/[〠-〿]|[぀-ゟ]|[゠-ヿ]|[ㇰ-ㇿ]/g) || []).length / t.length > .03 ? e = "jpn" : i > .1 && (e = "zho");
  1187. return e;
  1188. };
  1189.  
  1190. langToLabel(lang) {
  1191. return {
  1192. chi: "中文字幕",
  1193. zho: "中文字幕",
  1194. eng: "英文字幕",
  1195. en: "英文字幕",
  1196. jpn: "日文字幕",
  1197. "zh-CN": "简体中文",
  1198. "zh-TW": "繁体中文"
  1199. }[lang] || "未知语言";
  1200. };
  1201.  
  1202. subtitleOffset() {
  1203. const {
  1204. video, subtitle, events
  1205. } = this.player;
  1206. const textTrack = video.textTracks[0];
  1207. if (textTrack && textTrack.cues) {
  1208. const cues = Array.from(textTrack.cues);
  1209. const sarr = this.player.options.subtitle.url.find((item) => {
  1210. return item.sarr && item.sarr[parseInt(item.sarr.length / 2)].text === cues[parseInt(cues.length / 2)].text;
  1211. })?.sarr;
  1212. if (!sarr) {
  1213. return;
  1214. }
  1215.  
  1216. for (let index = 0; index < cues.length; index++) {
  1217. const cue = cues[index];
  1218. cue.startTime = sarr[index].startTime + this.offset;
  1219. cue.endTime = sarr[index].endTime + this.offset;
  1220. }
  1221.  
  1222. events.trigger('subtitle_change');
  1223.  
  1224. this.player.notice(`字幕偏移: ${this.offset} 秒`);
  1225. } else {
  1226. this.offset = 0;
  1227. }
  1228. }
  1229.  
  1230. toggle() {
  1231. if (this.player.template.subtitleCommentSettingBox.classList.contains('dplayer-comment-setting-open')) {
  1232. this.hide();
  1233. } else {
  1234. this.show();
  1235. }
  1236. }
  1237.  
  1238. show() {
  1239. this.player.template.subtitleCommentSettingBox.classList.add('dplayer-comment-setting-open');
  1240. this.player.template.mask.classList.add('dplayer-mask-show');
  1241. }
  1242.  
  1243. hide() {
  1244. this.player.template.subtitleCommentSettingBox.classList.remove('dplayer-comment-setting-open');
  1245. this.player.template.settingBox.classList.remove('dplayer-setting-box-open');
  1246. this.player.template.mask.classList.remove('dplayer-mask-show');
  1247. }
  1248.  
  1249. get(key) {
  1250. return localStorage.getItem("dplayer-subtitle-" + key);
  1251. }
  1252.  
  1253. set(key, value) {
  1254. key && value && localStorage.setItem("dplayer-subtitle-" + key, value);
  1255. }
  1256.  
  1257. style(key, value) {
  1258. const {
  1259. subtitle
  1260. } = this.player.template;
  1261. if (typeof key === 'object') {
  1262. for (const k in key) {
  1263. subtitle.style[k] = key[k];
  1264. }
  1265. return subtitle;
  1266. }
  1267. subtitle.style[key] = value;
  1268. return subtitle;
  1269. }
  1270. },
  1271. class Appreciation {
  1272. constructor(player, obj) {
  1273. this.player = player;
  1274. this.now = Date.now();
  1275. this.localforage = window.localforage || unsafeWindow.localforage;
  1276.  
  1277. const { contextmenu,
  1278. container: { offsetWidth, offsetHeight }
  1279. } = this.player;
  1280. }
  1281.  
  1282. getItem(n) {
  1283. n = localStorage.getItem(n);
  1284. if (!n) {
  1285. return null;
  1286. }
  1287. try {
  1288. return JSON.parse(n);
  1289. } catch (e) {
  1290. return n;
  1291. }
  1292. }
  1293.  
  1294. setItem(n, t) {
  1295. n && t != undefined && localStorage.setItem(n, t instanceof Object ? JSON.stringify(t) : t);
  1296. }
  1297.  
  1298. removeItem(n) {
  1299. n != undefined && localStorage.removeItem(n);
  1300. }
  1301. },
  1302. class DoHotKey {
  1303. constructor(player) {
  1304. this.player = player;
  1305.  
  1306. this.player.template.videoWrap.addEventListener('dblclick', (event) => {
  1307. this.player.fullScreen.toggle();
  1308. });
  1309.  
  1310. document.addEventListener("wheel", (event) => {
  1311. if (this.player.focus) {
  1312. event = event || window.event;
  1313. var o, t = this.player;
  1314. if (event.deltaY < 0) {
  1315. o = t.volume() + .01;
  1316. t.volume(o);
  1317. } else if (event.deltaY > 0) {
  1318. o = t.volume() - .01;
  1319. t.volume(o);
  1320. }
  1321. }
  1322. });
  1323. }
  1324. },
  1325. ]);

QingJ © 2025

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