ally

ali

目前为 2023-11-02 提交的版本。查看 最新版本

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

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

QingJ © 2025

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