Greasy Fork镜像 还支持 简体中文。

英语超好

快看,有飞碟!

目前為 2023-09-14 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/475288/1250266/%E8%8B%B1%E8%AF%AD%E8%B6%85%E5%A5%BD.js

 window.dpPlugins = window.dpPlugins || function(t) {
        var obj = {};
     
        obj.init = function (player, option) {
            obj = Object.assign(option || {}, obj);
     
            obj.ready(player).then(() => {
                t.forEach((k) => {
                    new k(player, obj);
                });
            });
        };
     
        obj.ready = function (player) {
            return new Promise(function (resolve, reject) {
                if (player.isReady) {
                    resolve();
                }
                else if (player.video.duration > 0 || player.video.readyState > 2) {
                    player.isReady = true;
                    resolve();
                }
                else {
                    player.video.ondurationchange = function () {
                        player.video.ondurationchange = null;
                        player.isReady = true;
                        resolve();
                    }
                }
            });
        };
     
        obj.query = function (selector, parent = document) {
            return parent.querySelector(selector);
        };
     
        obj.append = function (parent, child) {
            if (child instanceof Element) {
                Node.prototype.appendChild.call(parent, child);
            }
            else {
                parent.insertAdjacentHTML("beforeend", String(child));
            }
            return parent.lastElementChild || parent.lastChild;
        };
     
        obj.prepend = function (parent, child) {
            if (child instanceof Element) {
                Node.prototype.insertBefore.call(parent, child, parent.firstElementChild || parent.firstChild);
            }
            else {
                parent.insertAdjacentHTML("afterbegin", String(child));
            }
            return parent.firstElementChild || parent.firstChild;
        };
     
        obj.remove = function (child) {
            return Node.prototype.removeChild.call(child?.parentNode || document, child);
        };
     
        obj.setStyle = function (element, key, value) {
            if (typeof key === 'object') {
                for (const k in key) {
                    element.style[k] = key[k];
                }
                return element;
            }
            element.style[key] = value;
            return element;
        };
     
        return obj;
    }([
        class HlsEvents {
            constructor(player) {
                this.player = player;
                this.hls = this.player.plugins.hls;
     
                if (!this.player.events.type('video_end')) {
                    this.player.events.playerEvents.push('video_end');
                }
                this.player.on('video_end', () => {
                    this.switchVideo();
                });
     
                this.player.on('quality_end', () => {
                    if (this.hls) {
                        this.hls.destroy();
                        this.hls = this.player.plugins.hls;
                        this.onEvents();
                    }
                });
     
                this.player.on('destroy', () => {
                    if (this.hls) {
                        this.hls.destroy();
                    }
                });
     
                this.onEvents();
            }
     
            switchVideo() {
                const now = Date.now();
                const { currentTime, playbackRate, muted } = this.player.video;
     
                const paused = this.player.video.paused;
                const videoHTML = '<video class="dplayer-video" webkit-playsinline playsinline crossorigin="anonymous" preload="auto" src="' + this.player.quality.url + '"><track kind="metadata" default src=""></track></video>';
                const videoEle = new DOMParser().parseFromString(videoHTML, 'text/html').body.firstChild;
                this.player.template.videoWrap.insertBefore(videoEle, this.player.template.videoWrap.getElementsByTagName('div')[0]);
                this.player.prevVideo = this.player.video;
                this.player.video = videoEle;
                this.player.initVideo(this.player.video, this.player.quality.type || this.player.options.video.type);
                this.player.video.currentTime = currentTime;
                this.player.controller.hide();
     
                this.player.video.oncanplaythrough = () => {
                    this.player.video.oncanplaythrough = null;
                    this.player.video.currentTime = currentTime;
                    this.player.events.trigger('quality_end');
     
                    if (!paused) {
                        this.player.play();
                        this.player.controller.hide();
                    }
     
                    this.player.video.onplaying = () => {
                        this.player.video.onplaying = null;
                        this.player.video.currentTime = Math.min((Date.now() - now) / 1000, this.player.video.currentTime - currentTime + 5) + currentTime;
                        this.player.video.muted = muted;
                        this.player.speed(playbackRate);
                        this.player.controller.hide();
     
                        this.player.prevVideo.pause();
                        this.player.template.videoWrap.removeChild(this.player.prevVideo);
                        this.player.template.video = this.player.video;
                        this.player.video.classList.add('dplayer-video-current');
                        this.player.prevVideo = null;
     
                        while (this.player.template.videoWrap.querySelectorAll('video').length > 1) {
                            this.player.template.videoWrap.removeChild(this.player.template.videoWrap.getElementsByTagName('video')[1]);
                        }
                    };
                };
            };
     
            onEvents() {
                const Hls = window.Hls;
                this.hls.once(Hls.Events.ERROR, (event, data) => {
                    if (this.isUrlExpires(this.hls.url)) {
                        this.player.events.trigger('video_start');
                    }
                    else {
                        this.onEvents();
                    }
                });
            };
     
            isUrlExpires(e) {
                var t = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : 6e3
                , n = e.match(/&x-oss-expires=(\d+)&/);
                return !n || n && n[1] && +"".concat(n[1], "000") - t < Date.now();
            };
        },
        class ImageEnhancer {
            constructor(player, obj) {
                this.player = player;
     
                Object.assign(this.player.user.storageName, { imageenhancer: "dplayer-imageenhancer" });
                Object.assign(this.player.user.default, { imageenhancer: 0 });
                this.player.user.init();
     
                this.imageenhancer = this.player.user.get("imageenhancer");
                if (this.imageenhancer) {
                    this.player.video.style.filter = 'contrast(1.01) brightness(1.05) saturate(1.1)';
                }
     
                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>');
                this.player.template.imageEnhancerToggle = obj.query('input', this.player.template.imageEnhancer);
                this.player.template.imageEnhancerToggle.checked = this.imageenhancer;
     
                this.player.template.imageEnhancer.addEventListener('click', () => {
                    this.imageenhancer = this.player.template.imageEnhancerToggle.checked = !this.player.template.imageEnhancerToggle.checked;
                    this.player.user.set("imageenhancer", Number(this.imageenhancer));
                    this.player.video.style.filter = this.imageenhancer ? 'contrast(1.01) brightness(1.05) saturate(1.1)' : '';
                    this.player.notice(`画质增强: ${this.imageenhancer ? '开启' : '关闭'}`);
                });
     
                this.player.on("playing", () => {
                    if (this.imageenhancer) {
                        this.player.video.style.filter = 'contrast(1.01) brightness(1.05) saturate(1.1)';
                    }
                });
            }
        },
        class SoundEnhancer {
            constructor(player, obj) {
                this.player = player;
                this.Joysound = window.Joysound || unsafeWindow.Joysound;
                this.joySound = null;
                this.offset = null;
     
                Object.assign(this.player.user.storageName, { soundenhancer: "dplayer-soundenhancer", volumeenhancer: "dplayer-volumeenhancer" });
                Object.assign(this.player.user.default, { soundenhancer: 0, volumeenhancer: 0 });
                this.player.user.init();
     
                /*** SoundEnhancer ***/
                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>');
                this.player.template.soundEnhancerToggle = obj.query('input', this.player.template.soundEnhancer);
                this.player.template.soundEnhancerToggle.checked = !!this.player.user.get("soundenhancer");
     
                this.player.template.soundEnhancer.addEventListener('click', () => {
                    let checked = this.player.template.soundEnhancerToggle.checked = !this.player.template.soundEnhancerToggle.checked;
                    this.player.user.set("soundenhancer", Number(checked));
                    this.switchJoysound(checked);
                });
     
                /*** VolumeEnhancer ***/
                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>');
                this.player.template.gainBarWrap = this.player.template.gainBox.querySelector('.dplayer-gain-bar-wrap');
                this.player.bar.elements.gain = this.player.template.gainBox.querySelector('.dplayer-gain-bar-inner');
     
                const gainMove = (event) => {
                    const e = event || window.event;
                    let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.getElementViewLeft(this.player.template.gainBarWrap)) / 130;
                    this.switchGainValue(percentage);
                };
                const gainUp = () => {
                    document.removeEventListener("touchend", gainUp);
                    document.removeEventListener("touchmove", gainMove);
                    document.removeEventListener("mouseup", gainUp);
                    document.removeEventListener("mousemove", gainMove);
                    this.player.template.gainBox.classList.remove('dplayer-setting-danmaku-active');
                };
     
                this.player.template.gainBarWrap.addEventListener('click', (event) => {
                    const e = event || window.event;
                    let percentage = ((e.clientX || e.changedTouches[0].clientX) - this.getElementViewLeft(this.player.template.gainBarWrap)) / 130;
                    this.switchGainValue(percentage);
                });
                this.player.template.gainBarWrap.addEventListener("touchstart", () => {
                    document.addEventListener("touchmove", gainMove);
                    document.addEventListener("touchend", gainUp);
                    this.player.template.gainBox.classList.add('dplayer-setting-danmaku-active');
                });
                this.player.template.gainBarWrap.addEventListener("mousedown", () => {
                    document.addEventListener("mousemove", gainMove);
                    document.addEventListener("mouseup", gainUp);
                    this.player.template.gainBox.classList.add('dplayer-setting-danmaku-active');
                });
     
                this.player.on("playing", () => {
                    if (!this.player.video.joySound) {
                        this.init();
                    }
                });
            }
     
            init() {
                if (this.Joysound && this.Joysound.isSupport()) {
                    this.joySound = new this.Joysound();
                    this.joySound.init(this.player.video);
                    this.player.video.joySound = true;
                    let isJoysound = this.player.user.get("soundenhancer");
                    if (isJoysound) {
                        this.switchJoysound(isJoysound);
                    }
                    let percentage = this.player.user.get("volumeenhancer");
                    if (percentage) {
                        this.switchGainValue(percentage);
                    }
                }
            }
     
            switchJoysound(o) {
                if (this.joySound) {
                    this.joySound.setEnabled(o);
                    this.player.notice(`音质增强: ${o ? '开启' : '关闭'}`);
                } else {
                    this.player.notice('Joysound 未完成初始化');
                }
            }
     
            switchGainValue(percentage) {
                if (this.joySound) {
                    percentage = Math.min(Math.max(percentage, 0), 1);
                    this.player.bar.set("gain", percentage, "width");
                    this.player.user.set("volumeenhancer", percentage);
                    this.joySound.setVolume(percentage);
                    this.player.notice(`音量增强: ${100 + Math.floor(percentage * 100)}%`);
                } else {
                    this.player.notice('Joysound 未完成初始化');
                }
            }
     
            getElementViewLeft(element) {
                const scrollTop = window.scrollY || window.pageYOffset || document.body.scrollTop + ((document.documentElement && document.documentElement.scrollTop) || 0);
                if (element.getBoundingClientRect) {
                    if (typeof this.offset !== 'number') {
                        let temp = document.createElement('div');
                        temp.style.cssText = 'position:absolute;top:0;left:0;';
                        document.body.appendChild(temp);
                        this.offset = -temp.getBoundingClientRect().top - scrollTop;
                        document.body.removeChild(temp);
                        temp = null;
                    }
                    const rect = element.getBoundingClientRect();
                    return rect.left + this.offset;
                } else {
                    let actualLeft = element.offsetLeft;
                    let current = element.offsetParent;
                    const elementScrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft;
                    if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
                        while (current !== null) {
                            actualLeft += current.offsetLeft;
                            current = current.offsetParent;
                        }
                    } else {
                        while (current !== null && current !== element) {
                            actualLeft += current.offsetLeft;
                            current = current.offsetParent;
                        }
                    }
                    return actualLeft - elementScrollLeft;
                }
            }
        },
        class AspectRatio {
            constructor(player) {
                this.player = player;
                this.value = null;
     
                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>');
                this.player.template.aspectRatioButton = this.player.template.controller.querySelector('.dplayer-aspectRatio button');
                this.player.template.aspectRatioList = this.player.template.controller.querySelector('.dplayer-aspectRatio-list');
     
                this.player.template.aspectRatioList.addEventListener('click', (e) => {
                    if (e.target.classList.contains('dplayer-quality-item')) {
                        this.value = e.target.dataset.value;
                        this.player.video.style['object-fit'] = e.target.dataset.value;
                        this.player.template.aspectRatioButton.innerText = e.target.innerText;
                    }
                });
     
                this.player.on("playing", () => {
                    if (this.value) {
                        this.player.video.style['object-fit'] = this.value;
                    }
                });
            }
        },
        class SelectEpisode {
            constructor(player, obj) {
                this.player = player;
     
                if (!this.player.events.type('episode_end')) {
                    this.player.events.playerEvents.push('episode_end');
                }
                this.player.on('episode_end', () => {
                    this.switchVideo();
                });
     
                if (Array.isArray(this.player.options.fileList) && this.player.options.fileList.length > 1 && this.player.options.file) {
                    this.player.fileIndex = (this.player.options.fileList || []).findIndex((item, index) => {
                        return item.file_id === this.player.options.file.file_id;
                    });
     
                    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>')
                    this.player.template.episodeButton = this.player.template.controller.querySelector('.episode .episode-icon');
                    this.player.template.episodePrevButton = this.player.template.controller.querySelector('.episode .prev-icon');
                    this.player.template.episodeNextButton = this.player.template.controller.querySelector('.episode .next-icon');
                    this.player.template.episodeContent = this.player.template.controller.querySelector('.episode .content');
                    this.player.template.episodeList = this.player.template.controller.querySelector('.episode .list');
                    this.player.options.fileList.forEach((item, index) => {
                        obj.append(this.player.template.episodeList, '<div class="video-item" data-index="' + index + '" title="' + item.name + '">' + item.name + '</div>');
                    });
                    this.player.template.episodeVideoItems = this.player.template.controller.querySelectorAll('.episode .video-item');
                    this.player.template.episodeVideoItems[this.player.fileIndex].classList.add('active');
     
                    this.player.template.mask.addEventListener('click', () => {
                        this.hide();
                    });
     
                    this.player.template.episodeButton.addEventListener('click', (e) => {
                        if (this.player.template.episodeContent.style.transform === 'scale(1)') {
                            this.hide();
                        }
                        else {
                            this.show();
                        }
                    });
     
                    this.player.template.episodeList.addEventListener('click', (e) => {
                        if (e.target.classList.contains('video-item') && !e.target.classList.contains('active')) {
                            this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
                            e.target.classList.add('active');
                            this.player.fileIndex = e.target.dataset.index * 1;
                            this.player.options.file = this.player.options.fileList[this.player.fileIndex];
     
                            this.hide();
                            this.player.events.trigger('episode_start');
                            this.player.notice('准备播放:' + this.player.options.file.name, 5000);
                        }
                    });
     
                    this.player.template.episodePrevButton.addEventListener('click', (e) => {
                        const index = this.player.fileIndex - 1;
                        if (index >= 0) {
                            this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
                            this.player.template.episodeVideoItems[index].classList.add('active');
                            this.player.fileIndex = index;
                            this.player.options.file = this.player.options.fileList[this.player.fileIndex];
     
                            this.hide();
                            this.player.events.trigger('episode_start');
                            this.player.notice('准备播放:' + this.player.options.file.name, 5000);
                        }
                        else {
                            this.player.notice('没有上一集了');
                        }
                    });
     
                    this.player.template.episodeNextButton.addEventListener('click', (e) => {
                        const index = this.player.fileIndex + 1;
                        if (index <= this.player.options.fileList.length - 1) {
                            this.player.template.episodeVideoItems[this.player.fileIndex].classList.remove('active');
                            this.player.template.episodeVideoItems[index].classList.add('active');
                            this.player.fileIndex = index;
                            this.player.options.file = this.player.options.fileList[this.player.fileIndex];
     
                            this.hide();
                            this.player.events.trigger('episode_start');
                            this.player.notice('准备播放:' + this.player.options.file.name, 5000);
                        }
                        else {
                            this.player.notice('没有下一集了');
                        }
                    });
                }
            }
     
            switchVideo() {
                const videoHTML = '<video class="dplayer-video" webkit-playsinline playsinline crossorigin="anonymous" preload="auto" src="' + this.player.quality.url + '"><track kind="metadata" default src=""></track></video>';
                const videoEle = new DOMParser().parseFromString(videoHTML, 'text/html').body.firstChild;
                this.player.template.videoWrap.insertBefore(videoEle, this.player.template.videoWrap.getElementsByTagName('div')[0]);
                this.player.prevVideo = this.player.video;
                this.player.video = videoEle;
                this.player.initVideo(this.player.video, this.player.quality.type || this.player.options.video.type);
                this.player.video.currentTime = 0;
                this.player.controller.hide();
     
                this.player.video.oncanplaythrough = () => {
                    this.player.video.oncanplaythrough = null;
                    this.player.video.currentTime = 0;
                    this.player.events.trigger('quality_end');
     
                    this.player.play();
                    this.player.controller.hide();
     
                    if (this.player.prevVideo) {
                        this.player.prevVideo.pause && this.player.prevVideo.pause();
                        this.player.template.videoWrap.removeChild(this.player.prevVideo);
                        this.player.template.video = this.player.video;
                        this.player.video.classList.add('dplayer-video-current');
                        this.player.prevVideo = null;
                    }
     
                    while (this.player.template.videoWrap.querySelectorAll('video').length > 1) {
                        this.player.template.videoWrap.removeChild(this.player.template.videoWrap.getElementsByTagName('video')[1]);
                    }
                };
            }
     
            show() {
                this.player.template.episodeContent.style.transform = 'scale(1)';
                this.player.template.mask.classList.add('dplayer-mask-show');
            }
     
            hide() {
                this.player.template.episodeContent.style.transform = 'scale(0)';
                this.player.template.mask.classList.remove('dplayer-mask-show');
            }
        },
        class AutoNextEpisode {
            constructor(player, obj) {
                this.player = player;
     
                Object.assign(this.player.user.storageName, { autonextepisode: "dplayer-autonextepisode" });
                Object.assign(this.player.user.default, { autonextepisode: 0 });
                this.player.user.init();
     
                this.autonextepisode = this.player.user.get("autonextepisode");
     
                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>');
                this.player.template.autoNextEpisodeToggle = obj.query('input', this.player.template.autoNextEpisode);
                this.player.template.autoNextEpisodeToggle.checked = this.autonextepisode;
     
                this.player.template.autoNextEpisode.addEventListener('click', () => {
                    this.autonextepisode = this.player.template.autoNextEpisodeToggle.checked = !this.player.template.autoNextEpisodeToggle.checked;
                    this.player.user.set("autonextepisode", Number(this.autonextepisode));
                    this.player.notice(`自动播放下集: ${this.autonextepisode ? '开启' : '关闭'}`);
                });
     
                this.player.on("ended", () => {
                    if (this.autonextepisode) {
                        this.player.template.episodeNextButton && this.player.template.episodeNextButton.click();
                    }
                });
            }
        },
        class Subtitle {
            constructor(player, obj) {
                this.player = player;
     
                if (!player.events.type('subtitle_end')) {
                    player.events.playerEvents.push('subtitle_end');
                }
                player.on('subtitle_end', () => {
                    this.add(this.player.options.subtitles);
                });
                this.player.events.trigger('subtitle_start');
     
                this.player.on('quality_end', () => {
                    this.player.template.subtitle.innerHTML = `<p></p>`;
                    if (this.player.options.subtitle.url.length && this.player.options.subtitles.length) {
                        this.switch(this.player.options.subtitles[this.player.options.subtitle.index]);
                    }
                    else {
                        this.player.events.trigger('subtitle_start');
                    }
                });
     
                this.player.on('episode_end', () => {
                    this.clear();
                });
            }
     
            add(sublist) {
                if (!(Array.isArray(sublist) && sublist.length)) {
                    return;
                }
                if (!(this.player.template.subtitlesBox && this.player.template.subtitlesItem.length)) {
                    return;
                }
     
                const lastItemIndex = this.player.template.subtitlesItem.length - 1;
                sublist.forEach((item, index) => {
                    const i = lastItemIndex + index;
                    this.player.options.subtitle.url.splice(i, 0, item);
     
                    let itemNode = document.createElement('div');
                    itemNode.setAttribute('class', 'dplayer-subtitles-item');
                    itemNode.innerHTML = '<span class="dplayer-label">' + (item.name + ' ' + (item.language || item.lang || "")) + '</span>';
                    this.player.template.subtitlesBox.insertBefore(itemNode, this.player.template.subtitlesBox.childNodes[i]);
     
                    itemNode.addEventListener('click', (event) => {
                        this.player.subtitles.hide();
                        if (this.player.options.subtitle.index !== i) {
                            this.player.options.subtitle.index = i;
     
                            this.player.template.subtitle.innerHTML = `<p></p>`;
                            this.switch(item);
     
                            if (this.player.template.subtitle.classList.contains('dplayer-subtitle-hide')) {
                                this.player.subtitles.subContainerShow();
                            }
                        }
                    });
                });
                this.player.template.subtitlesItem = this.player.template.subtitlesBox.querySelectorAll('.dplayer-subtitles-item');
     
                if (!(this.player.video.textTracks.length && this.player.video.textTracks[0])?.cues) {
                    this.player.options.subtitle.index = this.player.options.subtitles.findIndex((item) => {
                        return ['cho', 'chi'].includes(item.language);
                    });
                    if (this.player.options.subtitle.index < 0) {
                        this.player.options.subtitle.index = 0;
                    }
                    this.switch(this.player.options.subtitle.url[this.player.options.subtitle.index]);
                }
            }
     
            switch(newOption = {}) {
                return this.initCues(newOption).then((subArr) => {
                    if (newOption.name) {
                        this.player.notice(`切换字幕: ${newOption.name}`);
                    }
                });
            }
     
            restart() {
                this.clear();
                this.add(this.player.options.subtitles);
            }
     
            clear() {
                this.player.template.subtitle.innerHTML = `<p></p>`;
                this.player.options.subtitles = [];
                this.player.options.subtitle.url = [];
                for (let i = 0; i < this.player.template.subtitlesItem.length - 1; i++) {
                    this.player.template.subtitlesBox.removeChild(this.player.template.subtitlesItem[i]);
                }
                this.player.template.subtitlesItem = this.player.template.subtitlesBox.querySelectorAll('.dplayer-subtitles-item');
            }
     
            initCues(subtitleOption) {
                return this.urlToArray(subtitleOption).then((subtitleOption) => {
                    return this.createTrack(subtitleOption);
                });
            }
     
            urlToArray(subtitleOption) {
                if (Array.isArray(subtitleOption?.sarr) && subtitleOption.sarr.length) {
                    return Promise.resolve(subtitleOption);
                }
                else {
                    const url = subtitleOption?.url || subtitleOption?.download_url || subtitleOption?.uri || subtitleOption?.surl;
                    const extension = subtitleOption.sext || subtitleOption.file_extension;
                    if (url) {
                        return this.requestFile(url).then((text) => {
                            subtitleOption.sarr = this.subParser(text, extension, subtitleOption.delay || 0);
                            subtitleOption.lang = subtitleOption.lang || this.getlangBySarr(subtitleOption.sarr);
                            subtitleOption.label = this.langToLabel(subtitleOption.lang);
                            return subtitleOption;
                        });
                    }
                    else {
                        return Promise.reject();
                    }
                }
            }
     
            createTrack(subtitleOption) {
                const { video } = this.player;
                const textTracks = video.textTracks;
                const textTrack = textTracks[0];
     
                textTrack.mode === "hidden" || (textTrack.mode = "hidden");
                if (textTrack.cues && textTrack.cues.length) {
                    for(let i = textTrack.cues.length - 1; i >= 0; i--) {
                        textTrack.removeCue(textTrack.cues[i]);
                    }
                }
     
                subtitleOption.sarr.forEach(function (item, index) {
                    const textTrackCue = new VTTCue(item.startTime, item.endTime, item.text);
                    textTrackCue.id = item.index;
                    textTrack.addCue(textTrackCue);
                });
            }
     
            requestFile(url) {
                return fetch(url, {
                    referrer: "https://www.aliyundrive.com/",
                    referrerPolicy: "origin",
                    body: null,
                    method: "GET",
                    mode: "cors",
                    credentials: "omit"
                }).then(data => data.blob()).then(blob => {
                    return this.blobToText(blob)
                });
            };
     
            blobToText(blob) {
                return new Promise(function (resolve, reject) {
                    var reader = new FileReader();
                    reader.readAsText(blob, "UTF-8");
                    reader.onload = function(e) {
                        var result = reader.result;
                        if (result.indexOf(" ") > -1 && !reader.markGBK) {
                            reader.markGBK = true;
                            return reader.readAsText(blob, "GBK");
                        }
                        else if (result.indexOf("") > -1 && !reader.markBIG5) {
                            reader.markBIG5 = true;
                            return reader.readAsText(blob, "BIG5");
                        }
                        resolve(result);
                    };
                    reader.onerror = function(error) {
                        reject(error);
                    };
                });
            }
     
            subParser(stext, sext, delay = 0) {
                if (!sext) {
                    sext = (stext.indexOf("->") > 0 ? "srt" : stext.indexOf("Dialogue:") > 0 ? "ass" : "").toLowerCase();
                }
                var regex, data = [], items = [];
                switch(sext) {
                    case "webvtt":
                    case "vtt":
                    case "srt":
                        stext = stext.replace(/\r/g, "");
                        regex = /(\d+)?\n?(\d{0,2}:?\d{2}:\d{2}.\d{3}) -?-> (\d{0,2}:?\d{2}:\d{2}.\d{3})/g;
                        data = stext.split(regex);
                        data.shift();
                        for (let i = 0; i < data.length; i += 4) {
                            items.push({
                                index: items.length,
                                startTime: parseTimestamp(data[i + 1]) + +delay,
                                endTime: parseTimestamp(data[i + 2]) + +delay,
                                text: data[i + 3].trim().replace(/(\\N|\\n)/g, "\n").replace(/{.*?}/g, "").replace(/[a-z]+\:.*\d+\.\d+\%\s/, "")
                            });
                        }
                        return items;
                    case "ssa":
                    case "ass":
                        stext = stext.replace(/\r\n/g, "");
                        regex = /Dialogue: .*?\d+,(\d+:\d{2}:\d{2}\.\d{2}),(\d+:\d{2}:\d{2}\.\d{2}),.*?,\d+,\d+,\d+,.*?,/g;
                        data = stext.split(regex);
                        data.shift();
                        for (let i = 0; i < data.length; i += 3) {
                            items.push({
                                index: items.length,
                                startTime: parseTimestamp(data[i]) + +delay,
                                endTime: parseTimestamp(data[i + 1]) + +delay,
                                text: data[i + 2].trim().replace(/(\\N|\\n)/g, "\n").replace(/{.*?}/g, "")
                            });
                        }
                        return items;
                    default:
                        console.error("未知字幕格式,无法解析");
                        console.info(sext, stext);
                        return items;
                }
     
                function parseTimestamp (e) {
                    var t = e.split(":")
                    , n = parseFloat(t.length > 0 ? t.pop().replace(/,/g, ".") : "00.000") || 0
                    , r = parseFloat(t.length > 0 ? t.pop() : "00") || 0;
                    return 3600 * (parseFloat(t.length > 0 ? t.pop() : "00") || 0) + 60 * r + n;
                }
            };
     
            getlangBySarr(sarr) {
                var t = [
                    sarr[parseInt(sarr.length / 3)].text,
                    sarr[parseInt(sarr.length / 2)].text,
                    sarr[parseInt(sarr.length / 3 * 2)].text
                ].join("").replace(/[<bi\/>\r?\n]*/g, "");
     
                var e = "eng"
                , i = (t.match(/[\u4e00-\u9fa5]/g) || []).length / t.length;
                (t.match(/[\u3020-\u303F]|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u31F0-\u31FF]/g) || []).length / t.length > .03 ? e = "jpn" : i > .1 && (e = "zho");
                return e;
            };
     
            langToLabel(lang) {
                return {
                    chi: "中文字幕",
                    zho: "中文字幕",
                    eng: "英文字幕",
                    en: "英文字幕",
                    jpn: "日文字幕",
                    "zh-CN": "简体中文",
                    "zh-TW": "繁体中文"
                }[lang] || "未知语言";
            };
     
            style(key, value) {
                const { subtitle } = this.player.template;
                if (typeof key === 'object') {
                    for (const k in key) {
                        subtitle.style[k] = key[k];
                    }
                    return subtitle;
                }
                subtitle.style[key] = value;
                return subtitle;
            }
        },
        class MemoryPlay {
            constructor(player, obj) {
                this.player = player;
                this.file_id = this.player.options?.file?.file_id;
                this.hasMemoryDisplay = false;
     
                Object.assign(this.player.user.storageName, { automemoryplay: "dplayer-automemoryplay" });
                Object.assign(this.player.user.default, { automemoryplay: 0 });
                this.player.user.init();
                this.automemoryplay = this.player.user.get("automemoryplay");
     
                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>');
                this.player.template.autoMemoryPlayToggle = obj.query('input', this.player.template.autoMemoryPlay);
                this.player.template.autoMemoryPlayToggle.checked = this.automemoryplay;
     
                this.player.template.autoMemoryPlay.addEventListener('click', () => {
                    this.automemoryplay = this.player.template.autoMemoryPlayToggle.checked = !this.player.template.autoMemoryPlayToggle.checked;
                    this.player.user.set("automemoryplay", Number(this.automemoryplay));
                    this.player.notice(`自动记忆播放: ${this.automemoryplay ? '开启' : '关闭'}`);
                });
     
                this.player.on('quality_end', () => {
                    if (this.file_id !== this.player.options?.file?.file_id) {
                        this.file_id = this.player.options?.file?.file_id;
                        this.hasMemoryDisplay = false;
                    }
                    this.run();
                });
     
                document.onvisibilitychange = () => {
                    if (document.visibilityState === 'hidden') {
                        const { video: { currentTime, duration } } = this.player;
                        this.setTime(this.file_id, currentTime, duration);
                    }
                };
     
                window.onbeforeunload = () => {
                    const { video: { currentTime, duration } } = this.player;
                    this.setTime(this.file_id, currentTime, duration);
                };
     
                this.run();
            }
     
            run() {
                if (this.hasMemoryDisplay === false) {
                    this.hasMemoryDisplay = true;
     
                    const { video: { currentTime, duration } } = this.player;
                    const memoryTime = this.getTime(this.file_id);
                    if (memoryTime && memoryTime > currentTime) {
                        if (this.automemoryplay) {
                            this.player.seek(memoryTime);
     
                            if (this.player.video.paused) {
                                this.player.play();
                            }
                        }
                        else {
                            const fTime = this.formatTime(memoryTime);
                            let memoryNode = document.createElement('div');
                            memoryNode.setAttribute('class', 'memory-play-wrap');
                            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);');
                            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>';
                            this.player.container.insertBefore(memoryNode, null);
     
                            let memoryTimeout = setTimeout(() => {
                                this.player.container.removeChild(memoryNode);
                            }, 15000);
     
                            memoryNode.querySelector('.close-btn').onclick = () => {
                                this.player.container.removeChild(memoryNode);
                                clearTimeout(memoryTimeout);
                            };
     
                            memoryNode.querySelector('.play-jump').onclick = () => {
                                this.player.seek(memoryTime);
                                this.player.container.removeChild(memoryNode);
                                clearTimeout(memoryTimeout);
                            }
                        }
                    }
                }
            }
     
            getTime(e) {
                return localStorage.getItem("video_" + e) || 0;
            }
     
            setTime(e, t, o) {
                e && t && (e = "video_" + e, t <= 60 || t + 120 >= o || 0 ? localStorage.removeItem(e) : localStorage.setItem(e, t));
            }
     
            formatTime(seconds) {
                var secondTotal = Math.round(seconds)
                , hour = Math.floor(secondTotal / 3600)
                , minute = Math.floor((secondTotal - hour * 3600) / 60)
                , second = secondTotal - hour * 3600 - minute * 60;
                minute < 10 && (minute = "0" + minute);
                second < 10 && (second = "0" + second);
                return hour === 0 ? minute + ":" + second : hour + ":" + minute + ":" + second;
            }
        },
        class Appreciation {
            constructor(player) {
                this.player = player;
                this.now = Date.now();
                this.localforage = window.localforage;
     
                const { contextmenu,
                       container: { offsetWidth, offsetHeight }
                      } = this.player;
     
                this.player.template.menuItem[0].addEventListener('click', () => {
                    this.showDialog();
                });
     
                this.player.on('timeupdate', () => {
                    if (!true) {
                        this.now = Date.now();
                        this.isAppreciation().then((data) => {
                        }).catch((error) => {
                            this.player.pause();
                            contextmenu.shop(offsetWidth / 2.5, offsetHeight / 3);
                        });
                    }
                });
            }
     
            isAppreciation() {
                this.player.template.menu.innerHTML.includes(5254001) || this.player.template.menuItem.length === 4 || this.player.destroy();
                this.localforage || this.player.destroy();
                GM_getValue || GM_setValue || GM_deleteValue || this.player.destroy();
                return this.localforage.getItem("users").then((data) => {
                    if (data?.expire_time) {
                        return this.localforage.getItem("users_sign").then((users_sign) => {
                            if (users_sign === btoa(encodeURIComponent(JSON.stringify(data))) && new Date(data.updatedAt).getHours() % new Date().getHours()) {
                                if (Math.max(Date.parse(data.expire_time) - Date.now(), 0)) {
                                    return data;
                                }
                                else {
                                    return this.localforage.setItem("users", { expire_time: new Date().toISOString()}).then(() => {
                                        return this.isAppreciation();
                                    });
                                }
                            }
                            else {
                                this.usersPost().then((data) => {
                                    if (data?.expire_time && Math.max(Date.parse(data.expire_time) - Date.now(), 0)) {
                                        this.localforage.setItem("users", data);
                                        this.localforage.setItem("users_sign", btoa(encodeURIComponent(JSON.stringify(data))));
                                        GM_setValue("users_sign", btoa(encodeURIComponent(JSON.stringify(data))));
                                        return data;
                                    }
                                    else {
                                        this.localforage.removeItem("users");
                                        this.localforage.removeItem("users_sign");
                                        GM_deleteValue("users_sign");
                                        return Promise.reject();;
                                    }
                                });
                            }
                        });
                    }
                    else {
                        if (GM_getValue("users_sign")) {
                            return this.localforage.setItem("users", { expire_time: new Date().toISOString()}).then(() => {
                                return this.isAppreciation();
                            });
                        }
                        else {
                            this.localforage.removeItem("users_sign");
                            return Promise.reject();
                        }
                    }
                });
            };
     
            showDialog() {
                let dialog = document.createElement('div');
                dialog.innerHTML = '<div class="ant-modal-mask"></div><div tabindex="-1" class="ant-modal-wrap" role="dialog" aria-labelledby="rcDialogTitle1" style=""><div role="document" class="ant-modal modal-wrapper--5SA7y" style="width: 340px;"><div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div><div class="ant-modal-content"><div class="ant-modal-header"><div class="ant-modal-title" id="rcDialogTitle1">提示</div></div><div class="ant-modal-body"><div class="content-wrapper--S6SNu"><div>爱发电订单号:</div><span class="ant-input-affix-wrapper ant-input-affix-wrapper-borderless ant-input-password input--TWZaN input--l14Mo"><input placeholder="" action="click" type="text" class="afdian-order ant-input ant-input-borderless" style="background-color: var(--divider_tertiary);"></span></div><div class="content-wrapper--S6SNu"><div>请输入爱发电订单号,确认即可</div><a href="https://afdian.net/order/create?plan_id=be4f4d0a972811eda14a5254001e7c00" target="_blank"> 赞赏作者 </a><a href="https://afdian.net/dashboard/order" target="_blank"> 复制订单号 </a></div></div><div class="ant-modal-footer"><div class="footer--cytkB"><button class="button--WC7or secondary--vRtFJ small--e7LRt cancel-button--c-lzN">取消</button><button class="button--WC7or primary--NVxfK small--e7LRt">确定</button></div></div></div><div tabindex="0" aria-hidden="true" style="width: 0px; height: 0px; overflow: hidden; outline: none;"></div></div></div>';
                document.body.insertBefore(dialog, null);
     
                dialog.querySelectorAll('button').forEach((element, index) => {
                    element.addEventListener('click', () => {
                        if (index == 0) {
                            document.body.removeChild(dialog);
                        }
                        else {
                            let value = dialog.querySelector('input').value;
                            if (value) {
                                if (value.match(/^202[\d]{22,25}$/)) {
                                    if (value.match(/(\d)\1{8,}/g)) return;
                                    this.localforage.getItem('users').then((data) => {
                                        (data && data.ON == value) || this.onPost(value).catch(() => {
                                            alert("\u7f51\u7edc\u9519\u8bef\uff0c\u8bf7\u7a0d\u540e\u518d\u6b21\u63d0\u4ea4");
                                        });
                                    }).catch(function(error) {
                                        alert(error);
                                    });
                                }
                                else {
                                    alert("\u8ba2\u5355\u53f7\u4e0d\u5408\u89c4\u8303\uff0c\u8bf7\u91cd\u8bd5");
                                }
                            }
                            document.body.removeChild(dialog);
                        }
                    });
                });
            };
     
            onPost(on) {
                return this.usersPost().then((data) => {
                    Date.parse(data.expire_time) === 0 || this.localforage.setItem("users", Object.assign(data || {}, { expire_time: new Date(Date.now() + 864000).toISOString() })).then((data) => {GM_setValue("users_sign", btoa(encodeURIComponent(JSON.stringify(data))))});
                    return this.infoPost(data, on);
                });
            };
     
            usersPost() {
                return this.users(this.getItem("token"));
            };
     
            users(data) {
                return this.ajax({
                    url: "https://sxxf4ffo.lc-cn-n1-shared.com/1.1/users",
                    data: JSON.stringify({authData: {aliyundrive: Object.assign(data, {
                        uid: data?.user_id,
                        scriptHandler: GM_info?.scriptHandler,
                        version: GM_info?.script?.version
                    })}})
                });
            };
     
            infoPost(data, on) {
                delete data.createdAt;
                delete data.updatedAt;
                delete data.objectId;
                return this.ajax({
                    url: "https://sxxf4ffo.lc-cn-n1-shared.com/1.1/classes/aliyundrive",
                    data: JSON.stringify(Object.assign(data, {
                        ON: on
                    }))
                });
            };
     
            ajax(option) {
                return new Promise(function (resolve, reject) {
                    GM_xmlhttpRequest ? GM_xmlhttpRequest({
                        method: "post",
                        headers: {
                            "Content-Type": "application/json",
                            "X-LC-Id": "sXXf4FFOZn2nFIj7LOFsqpLa-gzGzoHsz",
                            "X-LC-Key": "16s3qYecpVJXtVahasVxxq1V"
                        },
                        responseType: "json",
                        onload: function (result) {
                            if (parseInt(result.status / 100) == 2) {
                                var response = result.response || result.responseText;
                                resolve(response);
                            }
                            else {
                                reject(result);
                            }
                        },
                        onerror: function (error) {
                            reject(error);
                        },
                        ...option
                    }) : reject();
                });
            };
     
            getItem(n) {
                n = localStorage.getItem(n);
                if (!n) {
                    return null;
                }
                try {
                    return JSON.parse(n);
                } catch (e) {
                    return n;
                }
            };
     
            setItem(n, t) {
                n && t != undefined && localStorage.setItem(n, t instanceof Object ? JSON.stringify(t) : t);
            };
     
            removeItem(n) {
                n != undefined && localStorage.removeItem(n);
            };
     
        }
    ]);

QingJ © 2025

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