From 2eaa32784caa88b4622520d7a5b6f69698668f69 Mon Sep 17 00:00:00 2001 From: chen3861229 Date: Fri, 17 May 2024 22:39:22 +0800 Subject: [PATCH 01/69] =?UTF-8?q?fix:=20=E6=94=B9=E7=94=A8embyCustomEvent?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ede.js | 1510 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 783 insertions(+), 727 deletions(-) diff --git a/ede.js b/ede.js index 110cc74..ce7db6e 100644 --- a/ede.js +++ b/ede.js @@ -3,7 +3,7 @@ // @description Emby弹幕插件 // @namespace https://github.com/RyoLee // @author RyoLee -// @version 1.12 +// @version 1.13 // @copyright 2022, RyoLee (https://github.com/RyoLee) // @license MIT; https://raw.githubusercontent.com/RyoLee/emby-danmaku/master/LICENSE // @icon https://github.githubassets.com/pinned-octocat.svg @@ -15,801 +15,857 @@ (async function () { 'use strict'; - if (document.querySelector('meta[name="application-name"]').content == 'Emby' || document.documentElement.classList.contains('accent-emby')) { - // ------ configs start------ - const check_interval = 200; - const chConverTtitle = ['当前状态: 未启用', '当前状态: 转换为简体', '当前状态: 转换为繁体']; - const danmuCache = {}; - const LOAD_TYPE = { - CHECK: 'check', - INIT: 'init', - REFRESH: 'refresh', - RELOAD: 'reload', - SEARCH: 'search', - }; - // 0:当前状态关闭 1:当前状态打开 - const danmaku_icons = ['\uE0B9', '\uE7A2']; - const search_icon = '\uE881'; - const translate_icon = '\uE927'; - const info_icon = '\uE0E0'; - const filter_icons = ['\uE3E0', '\uE3D0', '\uE3D1', '\uE3D2']; - const danmuStyleSetter_icon = '\uE0F0'; - const buttonOptions = { - class: 'paper-icon-button-light', - is: 'paper-icon-button-light', - }; - const uiAnchorStr = '\uE034'; - const mediaContainerQueryStr = "div[data-type='video-osd']"; - const mediaQueryStr = 'video'; - const displayButtonOpts = { - title: '弹幕开关', - id: 'displayDanmaku', - innerText: null, - onclick: () => { - if (window.ede.loading) { - console.log('正在加载,请稍后再试'); - return; - } - console.log('切换弹幕开关'); - window.ede.danmakuSwitch = (window.ede.danmakuSwitch + 1) % 2; - window.localStorage.setItem('danmakuSwitch', window.ede.danmakuSwitch); - document.querySelector('#displayDanmaku').children[0].innerText = danmaku_icons[window.ede.danmakuSwitch]; - if (window.ede.danmaku) { - window.ede.danmakuSwitch == 1 ? window.ede.danmaku.show() : window.ede.danmaku.hide(); - } - }, - }; - const searchButtonOpts = { - title: '搜索弹幕', - id: 'searchDanmaku', - innerText: search_icon, - onclick: () => { - if (window.ede.loading) { - console.log('正在加载,请稍后再试'); - return; - } - console.log('手动匹配弹幕'); - loadDanmaku(LOAD_TYPE.SEARCH); - }, - }; - const translateButtonOpts = { - title: null, - id: 'translateDanmaku', - innerText: translate_icon, - onclick: () => { - if (window.ede.loading) { - console.log('正在加载,请稍后再试'); - return; - } - console.log('切换简繁转换'); - window.ede.chConvert = (window.ede.chConvert + 1) % 3; - window.localStorage.setItem('chConvert', window.ede.chConvert); - document.querySelector('#translateDanmaku').setAttribute('title', chConverTtitle[window.ede.chConvert]); - loadDanmaku(LOAD_TYPE.REFRESH); - console.log(document.querySelector('#translateDanmaku').getAttribute('title')); - }, + // if (document.querySelector('meta[name="application-name"]').content == 'Emby' || document.documentElement.classList.contains('accent-emby')) { + // ------ configs start------ + const check_interval = 200; + const chConverTtitle = ['当前状态: 未启用', '当前状态: 转换为简体', '当前状态: 转换为繁体']; + const danmuCache = {}; + const LOAD_TYPE = { + CHECK: 'check', + INIT: 'init', + REFRESH: 'refresh', + RELOAD: 'reload', + SEARCH: 'search', + }; + // 0:当前状态关闭 1:当前状态打开 + const danmaku_icons = ['\uE0B9', '\uE7A2']; + const search_icon = '\uE881'; + const translate_icon = '\uE927'; + const info_icon = '\uE0E0'; + const filter_icons = ['\uE3E0', '\uE3D0', '\uE3D1', '\uE3D2']; + const danmuStyleSetter_icon = '\uE0F0'; + const buttonOptions = { + class: 'paper-icon-button-light', + is: 'paper-icon-button-light', + }; + const uiAnchorStr = '\uE034'; + // htmlVideoPlayerContainer + const mediaContainerQueryStr = ".graphicContentContainer"; + const mediaQueryStr = 'video'; + const displayButtonOpts = { + title: '弹幕开关', + id: 'displayDanmaku', + innerText: null, + onclick: () => { + if (window.ede.loading) { + console.log('正在加载,请稍后再试'); + return; + } + console.log('切换弹幕开关'); + window.ede.danmakuSwitch = (window.ede.danmakuSwitch + 1) % 2; + window.localStorage.setItem('danmakuSwitch', window.ede.danmakuSwitch); + document.querySelector('#displayDanmaku').children[0].innerText = danmaku_icons[window.ede.danmakuSwitch]; + if (window.ede.danmaku) { + window.ede.danmakuSwitch == 1 ? window.ede.danmaku.show() : window.ede.danmaku.hide(); + } + }, + }; + const searchButtonOpts = { + title: '搜索弹幕', + id: 'searchDanmaku', + innerText: search_icon, + onclick: () => { + if (window.ede.loading) { + console.log('正在加载,请稍后再试'); + return; + } + console.log('手动匹配弹幕'); + loadDanmaku(LOAD_TYPE.SEARCH); + }, + }; + const translateButtonOpts = { + title: null, + id: 'translateDanmaku', + innerText: translate_icon, + onclick: () => { + if (window.ede.loading) { + console.log('正在加载,请稍后再试'); + return; + } + console.log('切换简繁转换'); + window.ede.chConvert = (window.ede.chConvert + 1) % 3; + window.localStorage.setItem('chConvert', window.ede.chConvert); + document.querySelector('#translateDanmaku').setAttribute('title', chConverTtitle[window.ede.chConvert]); + loadDanmaku(LOAD_TYPE.REFRESH); + console.log(document.querySelector('#translateDanmaku').getAttribute('title')); + }, + }; + const infoButtonOpts = { + title: '弹幕信息', + id: 'printDanmakuInfo', + innerText: info_icon, + onclick: () => { + if (!window.ede.episode_info || window.ede.loading) { + console.log('正在加载,请稍后再试'); + return; + } + console.log('显示当前信息'); + let msg = '动画名称:' + window.ede.episode_info.animeTitle; + if (window.ede.episode_info.episodeTitle) { + msg += '\n分集名称:' + window.ede.episode_info.episodeTitle; + } + sendNotification('当前弹幕匹配', msg); + }, + }; + + const filterButtonOpts = { + title: '过滤等级(下次加载生效)', + id: 'filteringDanmaku', + innerText: null, + onclick: () => { + console.log('切换弹幕过滤等级'); + let level = window.localStorage.getItem('danmakuFilterLevel'); + level = ((level ? parseInt(level) : 0) + 1) % 4; + window.localStorage.setItem('danmakuFilterLevel', level); + document.querySelector('#filteringDanmaku').children[0].innerText = filter_icons[level]; + }, + }; + // ------ configs end------ + + /* eslint-disable */ + /* https://cdn.jsdelivr.net/npm/danmaku/dist/danmaku.min.js */ + // prettier-ignore + !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Danmaku=e()}(this,(function(){"use strict";var t=function(){if("undefined"==typeof document)return"transform";for(var t=["oTransform","msTransform","mozTransform","webkitTransform","transform"],e=document.createElement("div").style,i=0;i0&&a!==1/0?Math.ceil(a):1*!!o.strokeStyle,h.font=o.font,t.width=t.width||Math.max(1,Math.ceil(h.measureText(t.text).width)+2*a),t.height=t.height||Math.ceil(function(t,e){if(s[t])return s[t];var i=12,n=t.match(new RegExp('(\d+(?:\.\d+)?)(px|%|em|rem)(?:\\s*\\/\\s*(\d+(?:\.\d+)?)(px|%|em|rem)?)?'));if(n){var r=1*n[1]||10,h=n[2],o=1*n[3]||1.2,a=n[4];"%"===h&&(r*=e.container/100),"em"===h&&(r*=e.container),"rem"===h&&(r*=e.root),"px"===a&&(i=o),"%"===a&&(i=r*o/100),"em"===a&&(i=r*o),"rem"===a&&(i=e.root*o),void 0===a&&(i=r*o)}return s[t]=i,i}(o.font,e))+2*a,r.width=t.width*n,r.height=t.height*n,h.scale(n,n),o)h[d]=o[d];var u=0;switch(o.textBaseline){case"top":case"hanging":u=a;break;case"middle":u=t.height>>1;break;default:u=t.height-a}return o.strokeStyle&&h.strokeText(t.text,a,u),h.fillText(t.text,a,u),r}function h(t){return 1*window.getComputedStyle(t,null).getPropertyValue("font-size").match(/(.+)px/)[1]}var o={name:"canvas",init:function(t){var e=document.createElement("canvas");return e.context=e.getContext("2d"),e._fontSize={root:h(document.getElementsByTagName("html")[0]),container:h(t)},e},clear:function(t,e){t.context.clearRect(0,0,t.width,t.height);for(var i=0;ir)return!0;var h=e._.duration+t.time-i,o=e._.width+s.width,a=e.media?s.time:s._utc,d=o*(i-a)*n/e._.duration,u=e._.width-d;return h>e._.duration*u/(e._.width+s.width)}for(var r=this._.space[t.mode],h=0,o=0,a=1;a=u){o=a;break}s(d,t)&&(h=a)}var m=r[h].range,c={range:m+t.height,time:this.media?t.time:t._utc,width:t.width,height:t.height};return r.splice(h+1,o-h-1,c),"bottom"===t.mode?this._.height-t.height-m%this._.height:m%(this._.height-t.height)}var d="undefined"!=typeof window&&(window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame)||function(t){return setTimeout(t,50/3)},u="undefined"!=typeof window&&(window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame)||clearTimeout;function m(t,e,i){for(var n=0,s=0,r=t.length;s=t[n=s+r>>1][e]?s=n:r=n;return t[s]&&i=0;u--)o=this._.runningList[u],r-(d=this.media?o.time:o._utc)>this._.duration&&(n(this._.stage,o),this._.runningList.splice(u,1));for(var m=[];this._.position=r));)r-d>this._.duration||(this.media&&(o._utc=s-(this.media.currentTime-o.time)),m.push(o)),++this._.position;for(e(this._.stage,m),u=0;u>1),i(this._.stage,o)}}}(this._.engine.framing.bind(this),this._.engine.setup.bind(this),this._.engine.render.bind(this),this._.engine.remove.bind(this));return this._.requestID=d((function t(){n.call(i),i._.requestID=d(t)})),this}function g(){return!this._.visible||this._.paused||(this._.paused=!0,u(this._.requestID),this._.requestID=0),this}function _(){if(!this.media)return this;this.clear(),f(this._.space);var t=m(this.comments,"time",this.media.currentTime);return this._.position=Math.max(0,t-1),this}function v(t){t.play=p.bind(this),t.pause=g.bind(this),t.seeking=_.bind(this),this.media.addEventListener("play",t.play),this.media.addEventListener("pause",t.pause),this.media.addEventListener("playing",t.play),this.media.addEventListener("waiting",t.pause),this.media.addEventListener("seeking",t.seeking)}function w(t){this.media.removeEventListener("play",t.play),this.media.removeEventListener("pause",t.pause),this.media.removeEventListener("playing",t.play),this.media.removeEventListener("waiting",t.pause),this.media.removeEventListener("seeking",t.seeking),t.play=null,t.pause=null,t.seeking=null}function y(t){this._={},this.container=t.container||document.createElement("div"),this.media=t.media,this._.visible=!0,this.engine=(t.engine||"DOM").toLowerCase(),this._.engine="canvas"===this.engine?o:i,this._.requestID=0,this._.speed=Math.max(0,t.speed)||144,this._.duration=4,this.comments=t.comments||[],this.comments.sort((function(t,e){return t.time-e.time}));for(var e=0;e { + fontSizeInput.value = parseFloat(fontSizeSlider.value).toFixed(1); }; - const infoButtonOpts = { - title: '弹幕信息', - id: 'printDanmakuInfo', - innerText: info_icon, - onclick: () => { - if (!window.ede.episode_info || window.ede.loading) { - console.log('正在加载,请稍后再试'); - return; - } - console.log('显示当前信息'); - let msg = '动画名称:' + window.ede.episode_info.animeTitle; - if (window.ede.episode_info.episodeTitle) { - msg += '\n分集名称:' + window.ede.episode_info.episodeTitle; - } - sendNotification('当前弹幕匹配', msg); - }, + // 输入框 + let fontSizeInput = document.createElement('input'); + fontSizeInput.type = 'text'; + fontSizeInput.style.width = '30px'; + fontSizeInput.style.textAlign = 'center'; + fontSizeInput.oninput = () => { + var oldValue = window.localStorage.getItem('danmakuFontSizeMagnification') ?? '1.0'; + var fontSizeMagnification = parseFloat(fontSizeInput.value); + if (isNaN(fontSizeMagnification)) { + fontSizeInput.value = oldValue; + fontSizeSlider.value = oldValue; + alert('请输入有效的数字!'); + } else if (fontSizeMagnification < 0.1 || fontSizeMagnification > 3) { + fontSizeInput.value = oldValue; + fontSizeSlider.value = oldValue; + alert('请输入0.1到3之间的数字!'); + } else { + fontSizeSlider.value = fontSizeMagnification.toFixed(1); + } }; + // 标签 + let fontSizeLabel = document.createElement('span'); + fontSizeLabel.innerText = '大小'; - const filterButtonOpts = { - title: '过滤等级(下次加载生效)', - id: 'filteringDanmaku', - innerText: null, - onclick: () => { - console.log('切换弹幕过滤等级'); - let level = window.localStorage.getItem('danmakuFilterLevel'); - level = ((level ? parseInt(level) : 0) + 1) % 4; - window.localStorage.setItem('danmakuFilterLevel', level); - document.querySelector('#filteringDanmaku').children[0].innerText = filter_icons[level]; - }, + // 透明度调整SubPanel + let fontOpacitySetPopDiv = document.createElement('div'); + fontOpacitySetPopDiv.style.display = 'flex'; + fontOpacitySetPopDiv.style.flexDirection = 'column'; + fontOpacitySetPopDiv.style.flexWrap = 'nowrap'; + fontOpacitySetPopDiv.style.justifyContent = 'center'; + fontOpacitySetPopDiv.style.alignItems = 'center'; + // 滑动条 + let fontOpacitySlider = document.createElement('input'); + fontOpacitySlider.setAttribute('orient', 'vertical'); + fontOpacitySlider.setAttribute('type', 'range'); + fontOpacitySlider.setAttribute('min', '0.1'); + fontOpacitySlider.setAttribute('max', '1'); + fontOpacitySlider.setAttribute('step', '0.1'); + fontOpacitySlider.style.width = '30px'; + fontOpacitySlider.style.height = '60px'; + fontOpacitySlider.style.appearance = 'slider-vertical'; + fontOpacitySlider.oninput = () => { + fontOpacityInput.value = parseFloat(fontOpacitySlider.value).toFixed(1); }; - // ------ configs end------ - /* eslint-disable */ - /* https://cdn.jsdelivr.net/npm/danmaku/dist/danmaku.min.js */ - // prettier-ignore - !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).Danmaku=e()}(this,(function(){"use strict";var t=function(){for(var t=["oTransform","msTransform","mozTransform","webkitTransform","transform"],e=document.createElement("div").style,i=0;i0&&a!==1/0?Math.ceil(a):1*!!o.strokeStyle,h.font=o.font,t.width=t.width||Math.max(1,Math.ceil(h.measureText(t.text).width)+2*a),t.height=t.height||Math.ceil(function(t,e){if(s[t])return s[t];var i=12,n=t.match(/(\d+(?:\.\d+)?)(px|%|em|rem)(?:\s*\/\s*(\d+(?:\.\d+)?)(px|%|em|rem)?)?/);if(n){var r=1*n[1]||10,h=n[2],o=1*n[3]||1.2,a=n[4];"%"===h&&(r*=e.container/100),"em"===h&&(r*=e.container),"rem"===h&&(r*=e.root),"px"===a&&(i=o),"%"===a&&(i=r*o/100),"em"===a&&(i=r*o),"rem"===a&&(i=e.root*o),void 0===a&&(i=r*o)}return s[t]=i,i}(o.font,e))+2*a,r.width=t.width*n,r.height=t.height*n,h.scale(n,n),o)h[d]=o[d];var u=0;switch(o.textBaseline){case"top":case"hanging":u=a;break;case"middle":u=t.height>>1;break;default:u=t.height-a}return o.strokeStyle&&h.strokeText(t.text,a,u),h.fillText(t.text,a,u),r}function h(t){return 1*window.getComputedStyle(t,null).getPropertyValue("font-size").match(/(.+)px/)[1]}var o={name:"canvas",init:function(t){var e=document.createElement("canvas");return e.context=e.getContext("2d"),e._fontSize={root:h(document.getElementsByTagName("html")[0]),container:h(t)},e},clear:function(t,e){t.context.clearRect(0,0,t.width,t.height);for(var i=0;ir)return!0;var h=e._.duration+t.time-i,o=e._.width+s.width,a=e.media?s.time:s._utc,d=o*(i-a)*n/e._.duration,u=e._.width-d;return h>e._.duration*u/(e._.width+s.width)}for(var r=this._.space[t.mode],h=0,o=0,a=1;a=u){o=a;break}s(d,t)&&(h=a)}var m=r[h].range,c={range:m+t.height,time:this.media?t.time:t._utc,width:t.width,height:t.height};return r.splice(h+1,o-h-1,c),"bottom"===t.mode?this._.height-t.height-m%this._.height:m%(this._.height-t.height)}var d=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||function(t){return setTimeout(t,50/3)},u=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||clearTimeout;function m(t,e,i){for(var n=0,s=0,r=t.length;s=t[n=s+r>>1][e]?s=n:r=n;return t[s]&&i=0;u--)o=this._.runningList[u],r-(d=this.media?o.time:o._utc)>this._.duration&&(n(this._.stage,o),this._.runningList.splice(u,1));for(var m=[];this._.position=r));)r-d>this._.duration||(this.media&&(o._utc=s-(this.media.currentTime-o.time)),m.push(o)),++this._.position;for(e(this._.stage,m),u=0;u>1),i(this._.stage,o)}}}(this._.engine.framing.bind(this),this._.engine.setup.bind(this),this._.engine.render.bind(this),this._.engine.remove.bind(this));return this._.requestID=d((function t(){n.call(i),i._.requestID=d(t)})),this}function g(){return!this._.visible||this._.paused||(this._.paused=!0,u(this._.requestID),this._.requestID=0),this}function _(){if(!this.media)return this;this.clear(),f(this._.space);var t=m(this.comments,"time",this.media.currentTime);return this._.position=Math.max(0,t-1),this}function v(t){t.play=p.bind(this),t.pause=g.bind(this),t.seeking=_.bind(this),this.media.addEventListener("play",t.play),this.media.addEventListener("pause",t.pause),this.media.addEventListener("playing",t.play),this.media.addEventListener("waiting",t.pause),this.media.addEventListener("seeking",t.seeking)}function w(t){this.media.removeEventListener("play",t.play),this.media.removeEventListener("pause",t.pause),this.media.removeEventListener("playing",t.play),this.media.removeEventListener("waiting",t.pause),this.media.removeEventListener("seeking",t.seeking),t.play=null,t.pause=null,t.seeking=null}function y(t){this._={},this.container=t.container||document.createElement("div"),this.media=t.media,this._.visible=!0,this.engine=(t.engine||"DOM").toLowerCase(),this._.engine="canvas"===this.engine?o:i,this._.requestID=0,this._.speed=Math.max(0,t.speed)||144,this._.duration=4,this.comments=t.comments||[],this.comments.sort((function(t,e){return t.time-e.time}));for(var e=0;e { + var oldValue = window.localStorage.getItem('danmakuFontOpacity') ?? '1.0'; + var fontOpacity = parseFloat(fontOpacityInput.value); + if (isNaN(fontOpacity)) { + fontOpacityInput.value = oldValue; + fontOpacitySlider.value = oldValue; + alert('请输入有效的数字!'); + } else if (fontOpacity < 0 || fontOpacity > 1) { + fontOpacityInput.value = oldValue; + fontOpacitySlider.value = oldValue; + alert('请输入0到1之间的数字!'); + } else { + fontOpacitySlider.value = fontOpacity.toFixed(1); } - } + }; + // 标签 + let fontOpacityLabel = document.createElement('span'); + fontOpacityLabel.innerText = '透明度'; - function createButton(opt) { - let button = document.createElement('button', buttonOptions); - button.setAttribute('title', opt.title); - button.setAttribute('id', opt.id); - let icon = document.createElement('span'); - icon.className = 'md-icon'; - icon.innerText = opt.innerText; - button.appendChild(icon); - button.onclick = opt.onclick; - return button; - } + // 监听鼠标点击事件 + button.addEventListener('click', function (event) { + if (popDiv.style.display == 'none') { + // 赋初值 + let curFontSizeMag = window.localStorage.getItem('danmakuFontSizeMagnification'); + let curFontOpacity = window.localStorage.getItem('danmakuFontOpacity'); + curFontSizeMag = isNaN(parseFloat(curFontSizeMag)) ? '1.0' : curFontSizeMag; + curFontOpacity = isNaN(parseFloat(curFontOpacity)) ? '1.0' : curFontOpacity; + fontSizeSlider.value = curFontSizeMag; + fontSizeInput.value = curFontSizeMag; + fontOpacitySlider.value = curFontOpacity; + fontOpacityInput.value = curFontOpacity; + // CSS + var x = event.clientX - 36 > 0 ? event.clientX - 36 : 0; + var y = event.clientY - 160 > 0 ? event.clientY - 160 : 0; - /** 调整字幕大小和透明度的Button - * @returns - */ - function createResizeButton() { - // 创建按钮 - let button = document.createElement('button', buttonOptions); - button.setAttribute('title', '弹幕调整'); - button.setAttribute('id', 'danmuStyleSetter'); - //创建按钮图标 - let icon = document.createElement('span'); - icon.className = 'md-icon'; - icon.innerText = danmuStyleSetter_icon; - - // 创建弹出面板 - let popDiv = document.getElementById('danmuStyleSetterPopPanel'); - if (popDiv) popDiv.remove(); - popDiv = document.createElement('div'); - popDiv.setAttribute('id', 'danmuStyleSetterPopPanel'); - popDiv.startSetter = undefined; - popDiv.style.display = 'none'; - popDiv.style.position = 'absolute'; - popDiv.style.padding = '6px'; - popDiv.style.background = '#555'; - popDiv.style.flexWrap = 'nowrap'; - popDiv.style.justifyContent = 'center'; - popDiv.style.alignItems = 'center'; - - // 大小调整SubPanel - let fontSizeSetPopDiv = document.createElement('div'); - fontSizeSetPopDiv.style.display = 'flex'; - fontSizeSetPopDiv.style.flexDirection = 'column'; - fontSizeSetPopDiv.style.flexWrap = 'nowrap'; - fontSizeSetPopDiv.style.justifyContent = 'center'; - fontSizeSetPopDiv.style.alignItems = 'center'; - // 滑动条 - let fontSizeSlider = document.createElement('input'); - fontSizeSlider.setAttribute('orient', 'vertical'); - fontSizeSlider.setAttribute('type', 'range'); - fontSizeSlider.setAttribute('min', '0.1'); - fontSizeSlider.setAttribute('max', '3'); - fontSizeSlider.setAttribute('step', '0.1'); - fontSizeSlider.style.width = '30px'; - fontSizeSlider.style.height = '60px'; - fontSizeSlider.style.appearance = 'slider-vertical'; - fontSizeSlider.oninput = () => { - fontSizeInput.value = parseFloat(fontSizeSlider.value).toFixed(1); - }; - // 输入框 - let fontSizeInput = document.createElement('input'); - fontSizeInput.type = 'text'; - fontSizeInput.style.width = '30px'; - fontSizeInput.style.textAlign = 'center'; - fontSizeInput.oninput = () => { - var oldValue = window.localStorage.getItem('danmakuFontSizeMagnification') ?? '1.0'; - var fontSizeMagnification = parseFloat(fontSizeInput.value); - if (isNaN(fontSizeMagnification)) { - fontSizeInput.value = oldValue; - fontSizeSlider.value = oldValue; - alert('请输入有效的数字!'); - } else if (fontSizeMagnification < 0.1 || fontSizeMagnification > 3) { - fontSizeInput.value = oldValue; - fontSizeSlider.value = oldValue; - alert('请输入0.1到3之间的数字!'); - } else { - fontSizeSlider.value = fontSizeMagnification.toFixed(1); - } - }; - // 标签 - let fontSizeLabel = document.createElement('span'); - fontSizeLabel.innerText = '大小'; - - // 透明度调整SubPanel - let fontOpacitySetPopDiv = document.createElement('div'); - fontOpacitySetPopDiv.style.display = 'flex'; - fontOpacitySetPopDiv.style.flexDirection = 'column'; - fontOpacitySetPopDiv.style.flexWrap = 'nowrap'; - fontOpacitySetPopDiv.style.justifyContent = 'center'; - fontOpacitySetPopDiv.style.alignItems = 'center'; - // 滑动条 - let fontOpacitySlider = document.createElement('input'); - fontOpacitySlider.setAttribute('orient', 'vertical'); - fontOpacitySlider.setAttribute('type', 'range'); - fontOpacitySlider.setAttribute('min', '0.1'); - fontOpacitySlider.setAttribute('max', '1'); - fontOpacitySlider.setAttribute('step', '0.1'); - fontOpacitySlider.style.width = '30px'; - fontOpacitySlider.style.height = '60px'; - fontOpacitySlider.style.appearance = 'slider-vertical'; - fontOpacitySlider.oninput = () => { - fontOpacityInput.value = parseFloat(fontOpacitySlider.value).toFixed(1); - }; - // 输入框 - let fontOpacityInput = document.createElement('input'); - fontOpacityInput.type = 'text'; - fontOpacityInput.style.width = '30px'; - fontOpacityInput.style.textAlign = 'center'; - fontOpacityInput.oninput = () => { - var oldValue = window.localStorage.getItem('danmakuFontOpacity') ?? '1.0'; - var fontOpacity = parseFloat(fontOpacityInput.value); - if (isNaN(fontOpacity)) { - fontOpacityInput.value = oldValue; - fontOpacitySlider.value = oldValue; - alert('请输入有效的数字!'); - } else if (fontOpacity < 0 || fontOpacity > 1) { - fontOpacityInput.value = oldValue; - fontOpacitySlider.value = oldValue; - alert('请输入0到1之间的数字!'); - } else { - fontOpacitySlider.value = fontOpacity.toFixed(1); - } - }; - // 标签 - let fontOpacityLabel = document.createElement('span'); - fontOpacityLabel.innerText = '透明度'; - - // 监听鼠标点击事件 - button.addEventListener('click', function (event) { - if (popDiv.style.display == 'none') { - // 赋初值 - let curFontSizeMag = window.localStorage.getItem('danmakuFontSizeMagnification'); - let curFontOpacity = window.localStorage.getItem('danmakuFontOpacity'); - curFontSizeMag = isNaN(parseFloat(curFontSizeMag)) ? '1.0' : curFontSizeMag; - curFontOpacity = isNaN(parseFloat(curFontOpacity)) ? '1.0' : curFontOpacity; - fontSizeSlider.value = curFontSizeMag; - fontSizeInput.value = curFontSizeMag; - fontOpacitySlider.value = curFontOpacity; - fontOpacityInput.value = curFontOpacity; - // CSS - var x = event.clientX - 36 > 0 ? event.clientX - 36 : 0; - var y = event.clientY - 160 > 0 ? event.clientY - 160 : 0; - - popDiv.startSetter = '1'; - popDiv.style.left = x + 'px'; - popDiv.style.top = y + 'px'; - popDiv.style.display = 'flex'; - } else { - popDiv.style.display = 'none'; - if (popDiv.startSetter) { - popDiv.startSetter = undefined; - doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); - } - } - }); - // 监听页面点击事件 - document.addEventListener('click', function (event) { - // 检查点击事件的目标元素是否是div或div内的元素 - if (event.target === popDiv || popDiv.contains(event.target) || event.target === button || button.contains(event.target)) { - return; // 如果是,则不执行下面的代码 - } + popDiv.startSetter = '1'; + popDiv.style.left = x + 'px'; + popDiv.style.top = y + 'px'; + popDiv.style.display = 'flex'; + } else { popDiv.style.display = 'none'; if (popDiv.startSetter) { popDiv.startSetter = undefined; doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); } - }); - // 监听Input回车结束设置 - fontSizeInput.addEventListener('keydown', function (e) { - if (e.key === 'Enter') { - popDiv.style.display = 'none'; - if (popDiv.startSetter) { - popDiv.startSetter = undefined; - doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); - } - } - }); - fontOpacityInput.addEventListener('keydown', function (e) { - if (e.key === 'Enter') { - popDiv.style.display = 'none'; - if (popDiv.startSetter) { - popDiv.startSetter = undefined; - doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); - } - } - }); - - // icon插入button - button.appendChild(icon); - // 将滑动条和输入框添加到pop - fontSizeSetPopDiv.appendChild(fontSizeSlider); - fontSizeSetPopDiv.appendChild(fontSizeInput); - fontSizeSetPopDiv.appendChild(fontSizeLabel); - fontOpacitySetPopDiv.appendChild(fontOpacitySlider); - fontOpacitySetPopDiv.appendChild(fontOpacityInput); - fontOpacitySetPopDiv.appendChild(fontOpacityLabel); - popDiv.appendChild(fontSizeSetPopDiv); - popDiv.appendChild(fontOpacitySetPopDiv); - // 将POP插入body - document.body.appendChild(popDiv); - - return button; - } - /** 执行弹幕大小和透明度变换 */ - function doFontResizeAndOpacityChange(fontSize, fontOpacity) { - let oldFontSize = window.localStorage.getItem('danmakuFontSizeMagnification'); - let oldFontOpacity = window.localStorage.getItem('danmakuFontOpacity'); - if (fontSize != oldFontSize || fontOpacity != oldFontOpacity) { - window.localStorage.setItem('danmakuFontSizeMagnification', fontSize); - window.localStorage.setItem('danmakuFontOpacity', fontOpacity); - loadDanmaku(LOAD_TYPE.RELOAD); } - } - - function initListener() { - let container = document.querySelector(mediaQueryStr); - // 页面未加载 - if (!container) { - if (window.ede.episode_info) { - window.ede.episode_info = null; - } - return; + }); + // 监听页面点击事件 + document.addEventListener('click', function (event) { + // 检查点击事件的目标元素是否是div或div内的元素 + if (event.target === popDiv || popDiv.contains(event.target) || event.target === button || button.contains(event.target)) { + return; // 如果是,则不执行下面的代码 } - if (!container.getAttribute('ede_listening')) { - console.log('正在初始化Listener'); - container.setAttribute('ede_listening', true); - container.addEventListener('play', loadDanmaku); - console.log('Listener初始化完成'); + popDiv.style.display = 'none'; + if (popDiv.startSetter) { + popDiv.startSetter = undefined; + doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); } - } - - function getElementsByInnerText(tagType, innerStr, excludeChildNode = true) { - var temp = []; - var elements = document.getElementsByTagName(tagType); - if (!elements || 0 == elements.length) { - return temp; + }); + // 监听Input回车结束设置 + fontSizeInput.addEventListener('keydown', function (e) { + if (e.key === 'Enter') { + popDiv.style.display = 'none'; + if (popDiv.startSetter) { + popDiv.startSetter = undefined; + doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); + } } - for (let index = 0; index < elements.length; index++) { - var e = elements[index]; - if (e.innerText.includes(innerStr)) { - temp.push(e); + }); + fontOpacityInput.addEventListener('keydown', function (e) { + if (e.key === 'Enter') { + popDiv.style.display = 'none'; + if (popDiv.startSetter) { + popDiv.startSetter = undefined; + doFontResizeAndOpacityChange(fontSizeInput.value, fontOpacityInput.value); } } - if (!excludeChildNode) { - return temp; + }); + + // icon插入button + button.appendChild(icon); + // 将滑动条和输入框添加到pop + fontSizeSetPopDiv.appendChild(fontSizeSlider); + fontSizeSetPopDiv.appendChild(fontSizeInput); + fontSizeSetPopDiv.appendChild(fontSizeLabel); + fontOpacitySetPopDiv.appendChild(fontOpacitySlider); + fontOpacitySetPopDiv.appendChild(fontOpacityInput); + fontOpacitySetPopDiv.appendChild(fontOpacityLabel); + popDiv.appendChild(fontSizeSetPopDiv); + popDiv.appendChild(fontOpacitySetPopDiv); + // 将POP插入body + document.body.appendChild(popDiv); + + return button; + } + /** 执行弹幕大小和透明度变换 */ + function doFontResizeAndOpacityChange(fontSize, fontOpacity) { + let oldFontSize = window.localStorage.getItem('danmakuFontSizeMagnification'); + let oldFontOpacity = window.localStorage.getItem('danmakuFontOpacity'); + if (fontSize != oldFontSize || fontOpacity != oldFontOpacity) { + window.localStorage.setItem('danmakuFontSizeMagnification', fontSize); + window.localStorage.setItem('danmakuFontOpacity', fontOpacity); + loadDanmaku(LOAD_TYPE.RELOAD); + } + } + + function initListener() { + let container = document.querySelector(mediaQueryStr); + // 页面未加载 + if (!container) { + if (window.ede.episode_info) { + window.ede.episode_info = null; } - var res = []; - temp.forEach((e) => { - var e_copy = e.cloneNode(true); - while (e_copy.firstChild != e_copy.lastChild) { - e_copy.removeChild(e_copy.lastChild); - } - if (e_copy.innerText.includes(innerStr)) { - res.push(e); - } - }); - return res; + return; + } + if (!container.getAttribute('ede_listening')) { + console.log('正在初始化Listener'); + container.setAttribute('ede_listening', true); + container.addEventListener('play', loadDanmaku); + console.log('Listener初始化完成'); } + } - function initUI() { - // 页面未加载 - let uiAnchor = getElementsByInnerText('i', uiAnchorStr); - if (!uiAnchor || !uiAnchor[0]) { - return; + function getElementsByInnerText(tagType, innerStr, excludeChildNode = true) { + var temp = []; + var elements = document.getElementsByTagName(tagType); + if (!elements || 0 == elements.length) { + return temp; + } + for (let index = 0; index < elements.length; index++) { + var e = elements[index]; + if (e.innerText.includes(innerStr)) { + temp.push(e); } - // 已初始化 - if (document.getElementById('danmakuCtr')) { - return; + } + if (!excludeChildNode) { + return temp; + } + var res = []; + temp.forEach((e) => { + var e_copy = e.cloneNode(true); + while (e_copy.firstChild != e_copy.lastChild) { + e_copy.removeChild(e_copy.lastChild); } - console.log('正在初始化UI'); - // 弹幕按钮容器div - let parent = uiAnchor[0].parentNode.parentNode.parentNode; - let menubar = document.createElement('div'); - menubar.id = 'danmakuCtr'; - if (!window.ede.episode_info) { - menubar.style.opacity = 0.5; + if (e_copy.innerText.includes(innerStr)) { + res.push(e); } - parent.append(menubar); - // 弹幕开关 - displayButtonOpts.innerText = danmaku_icons[window.ede.danmakuSwitch]; - menubar.appendChild(createButton(displayButtonOpts)); - // 手动匹配 - menubar.appendChild(createButton(searchButtonOpts)); - // 简繁转换 - translateButtonOpts.title = chConverTtitle[window.ede.chConvert]; - menubar.appendChild(createButton(translateButtonOpts)); - // 屏蔽等级 - filterButtonOpts.innerText = filter_icons[parseInt(window.localStorage.getItem('danmakuFilterLevel') ? window.localStorage.getItem('danmakuFilterLevel') : 0)]; - menubar.appendChild(createButton(filterButtonOpts)); - // 弹幕信息 - menubar.appendChild(createButton(infoButtonOpts)); - // 弹幕大小调整 - menubar.appendChild(createResizeButton()); - console.log('UI初始化完成'); - } + }); + return res; + } - function sendNotification(title, msg) { - const Notification = window.Notification || window.webkitNotifications; - console.log(msg); - if (Notification.permission === 'granted') { - return new Notification(title, { - body: msg, - }); - } else { - Notification.requestPermission((permission) => { - if (permission === 'granted') { - return new Notification(title, { - body: msg, - }); - } - }); - } + function initUI() { + // 页面未加载 + // let uiAnchor = getElementsByInnerText('i', uiAnchorStr); + // if (!uiAnchor || !uiAnchor[0]) { + // return; + // } + // 已初始化 + if (document.getElementById('danmakuCtr')) { + return; } + console.log('正在初始化UI'); + // 弹幕按钮容器div + let parent = document.querySelector(".videoOsdBottom-maincontrols .videoOsdBottom-buttons"); + // let parent = uiAnchor[0].parentNode.parentNode.parentNode; + let menubar = document.createElement('div'); + menubar.id = 'danmakuCtr'; + if (!window.ede.episode_info) { + menubar.style.opacity = 0.5; + } + parent.append(menubar); + // 弹幕开关 + displayButtonOpts.innerText = danmaku_icons[window.ede.danmakuSwitch]; + menubar.appendChild(createButton(displayButtonOpts)); + // 手动匹配 + menubar.appendChild(createButton(searchButtonOpts)); + // 简繁转换 + translateButtonOpts.title = chConverTtitle[window.ede.chConvert]; + menubar.appendChild(createButton(translateButtonOpts)); + // 屏蔽等级 + filterButtonOpts.innerText = filter_icons[parseInt(window.localStorage.getItem('danmakuFilterLevel') ? window.localStorage.getItem('danmakuFilterLevel') : 0)]; + menubar.appendChild(createButton(filterButtonOpts)); + // 弹幕信息 + menubar.appendChild(createButton(infoButtonOpts)); + // 弹幕大小调整 + menubar.appendChild(createResizeButton()); + console.log('UI初始化完成'); + } - function getEmbyItemInfo() { - return window.require(['pluginManager']).then((items) => { - if (items) { - for (let i = 0; i < items.length; i++) { - const item = items[i]; - if (item.pluginsList) { - for (let j = 0; j < item.pluginsList.length; j++) { - const plugin = item.pluginsList[j]; - if (plugin && plugin.id == 'htmlvideoplayer') { - return plugin._currentPlayOptions ? plugin._currentPlayOptions.item : null; - } - } - } - } + function sendNotification(title, msg) { + const Notification = window.Notification || window.webkitNotifications; + console.log(msg); + if (Notification.permission === 'granted') { + return new Notification(title, { + body: msg, + }); + } else { + Notification.requestPermission((permission) => { + if (permission === 'granted') { + return new Notification(title, { + body: msg, + }); } - return null; }); } + } - async function getEpisodeInfo(is_auto = true) { - let item = await getEmbyItemInfo(); - if (!item) { - return null; - } - let _id; - let animeName; - let anime_id = -1; - let episode; - if (item.Type == 'Episode') { - _id = item.SeasonId; - animeName = item.SeriesName; - episode = item.IndexNumber; - let session = item.ParentIndexNumber; - if (session != 1) { - animeName += ' ' + session; - } - } else { - _id = item.Id; - animeName = item.Name; - episode = 'movie'; - } - let _id_key = '_anime_id_rel_' + _id; - let _name_key = '_anime_name_rel_' + _id; - let _episode_key = '_episode_id_rel_' + _id + '_' + episode; - if (is_auto) { - if (window.localStorage.getItem(_episode_key)) { - return JSON.parse(window.localStorage.getItem(_episode_key)); + function getEmbyItemInfo() { + return window.require(['pluginManager']).then((items) => { + if (items) { + for (let i = 0; i < items.length; i++) { + const item = items[i]; + if (item.pluginsList) { + for (let j = 0; j < item.pluginsList.length; j++) { + const plugin = item.pluginsList[j]; + if (plugin && plugin.id == 'htmlvideoplayer') { + return plugin._currentPlayOptions ? plugin._currentPlayOptions.item : null; + } + } + } } } - if (window.localStorage.getItem(_id_key)) { - anime_id = window.localStorage.getItem(_id_key); - } - if (window.localStorage.getItem(_name_key)) { - animeName = window.localStorage.getItem(_name_key); + return null; + }); + } + + async function getEpisodeInfo(is_auto = true) { + let item = await getEmbyItemInfo(); + if (!item) { + return null; + } + let _id; + let animeName; + let anime_id = -1; + let episode; + if (item.Type == 'Episode') { + _id = item.SeasonId; + animeName = item.SeriesName; + episode = item.IndexNumber; + let session = item.ParentIndexNumber; + if (session != 1) { + animeName += ' ' + session; } - if (!is_auto) { - animeName = prompt('确认动画名:', animeName); - if (animeName == null) throw new Error('用户取消确认动画名操作'); + } else { + _id = item.Id; + animeName = item.Name; + episode = 'movie'; + } + let _id_key = '_anime_id_rel_' + _id; + let _name_key = '_anime_name_rel_' + _id; + let _episode_key = '_episode_id_rel_' + _id + '_' + episode; + if (is_auto) { + if (window.localStorage.getItem(_episode_key)) { + return JSON.parse(window.localStorage.getItem(_episode_key)); } + } + if (window.localStorage.getItem(_id_key)) { + anime_id = window.localStorage.getItem(_id_key); + } + if (window.localStorage.getItem(_name_key)) { + animeName = window.localStorage.getItem(_name_key); + } + if (!is_auto) { + animeName = prompt('确认动画名:', animeName); + if (animeName == null) throw new Error('用户取消确认动画名操作'); + } - let searchUrl = 'https://api.dandanplay.net/api/v2/search/episodes?anime=' + animeName + '&withRelated=true'; - if (is_auto) { - searchUrl += '&episode=' + episode; - } - let animaInfo = await fetch(searchUrl, { - method: 'GET', - headers: { - 'Accept-Encoding': 'gzip', - Accept: 'application/json', - 'User-Agent': navigator.userAgent, - }, - }) - .then((response) => response.json()) - .catch((error) => { - console.log('查询失败:', error); - return null; - }); - console.log('查询成功'); - console.log(animaInfo); - let selecAnime_id = 1; - if (anime_id != -1) { - for (let index = 0; index < animaInfo.animes.length; index++) { - if (animaInfo.animes[index].animeId == anime_id) { - selecAnime_id = index + 1; - } + let searchUrl = 'https://api.dandanplay.net/api/v2/search/episodes?anime=' + animeName + '&withRelated=true'; + if (is_auto) { + searchUrl += '&episode=' + episode; + } + let animaInfo = await fetch(searchUrl, { + method: 'GET', + headers: { + 'Accept-Encoding': 'gzip', + Accept: 'application/json', + 'User-Agent': navigator.userAgent, + }, + }) + .then((response) => response.json()) + .catch((error) => { + console.log('查询失败:', error); + return null; + }); + console.log('查询成功'); + console.log(animaInfo); + let selecAnime_id = 1; + if (anime_id != -1) { + for (let index = 0; index < animaInfo.animes.length; index++) { + if (animaInfo.animes[index].animeId == anime_id) { + selecAnime_id = index + 1; } } - if (!is_auto) { - let anime_lists_str = list2string(animaInfo); - console.log(anime_lists_str); - selecAnime_id = prompt('选择:\n' + anime_lists_str, selecAnime_id); - if (selecAnime_id == null) throw new Error('用户取消选择集数操作'); - selecAnime_id = parseInt(selecAnime_id) - 1; - window.localStorage.setItem(_id_key, animaInfo.animes[selecAnime_id].animeId); - window.localStorage.setItem(_name_key, animaInfo.animes[selecAnime_id].animeTitle); - let episode_lists_str = ep2string(animaInfo.animes[selecAnime_id].episodes); - episode = prompt('确认集数:\n' + episode_lists_str, parseInt(episode)); - if (episode == null) throw new Error('用户取消确认集数操作'); - episode = parseInt(episode) - 1; - } else { - selecAnime_id = parseInt(selecAnime_id) - 1; - episode = 0; - } - let episodeInfo = { - episodeId: animaInfo.animes[selecAnime_id].episodes[episode].episodeId, - animeTitle: animaInfo.animes[selecAnime_id].animeTitle, - episodeTitle: animaInfo.animes[selecAnime_id].type == 'tvseries' ? animaInfo.animes[selecAnime_id].episodes[episode].episodeTitle : null, - }; - window.localStorage.setItem(_episode_key, JSON.stringify(episodeInfo)); - return episodeInfo; } + if (!is_auto) { + let anime_lists_str = list2string(animaInfo); + console.log(anime_lists_str); + selecAnime_id = prompt('选择:\n' + anime_lists_str, selecAnime_id); + if (selecAnime_id == null) throw new Error('用户取消选择集数操作'); + selecAnime_id = parseInt(selecAnime_id) - 1; + window.localStorage.setItem(_id_key, animaInfo.animes[selecAnime_id].animeId); + window.localStorage.setItem(_name_key, animaInfo.animes[selecAnime_id].animeTitle); + let episode_lists_str = ep2string(animaInfo.animes[selecAnime_id].episodes); + episode = prompt('确认集数:\n' + episode_lists_str, parseInt(episode)); + if (episode == null) throw new Error('用户取消确认集数操作'); + episode = parseInt(episode) - 1; + } else { + selecAnime_id = parseInt(selecAnime_id) - 1; + episode = 0; + } + let episodeInfo = { + episodeId: animaInfo.animes[selecAnime_id].episodes[episode].episodeId, + animeTitle: animaInfo.animes[selecAnime_id].animeTitle, + episodeTitle: animaInfo.animes[selecAnime_id].type == 'tvseries' ? animaInfo.animes[selecAnime_id].episodes[episode].episodeTitle : null, + }; + window.localStorage.setItem(_episode_key, JSON.stringify(episodeInfo)); + return episodeInfo; + } - function getComments(episodeId) { - let url = 'https://api.9-ch.com/cors/https://api.dandanplay.net/api/v2/comment/' + episodeId + '?withRelated=true&chConvert=' + window.ede.chConvert; - return fetch(url, { - method: 'GET', - headers: { - 'Accept-Encoding': 'gzip', - Accept: 'application/json', - 'User-Agent': navigator.userAgent, - }, + function getComments(episodeId) { + let url = 'https://api.9-ch.com/cors/https://api.dandanplay.net/api/v2/comment/' + episodeId + '?withRelated=true&chConvert=' + window.ede.chConvert; + return fetch(url, { + method: 'GET', + headers: { + 'Accept-Encoding': 'gzip', + Accept: 'application/json', + 'User-Agent': navigator.userAgent, + }, + }) + .then((response) => response.json()) + .then((data) => { + console.log('弹幕下载成功: ' + data.comments.length); + return data.comments; }) - .then((response) => response.json()) - .then((data) => { - console.log('弹幕下载成功: ' + data.comments.length); - return data.comments; - }) - .catch((error) => { - console.log('获取弹幕失败:', error); - return null; - }); + .catch((error) => { + console.log('获取弹幕失败:', error); + return null; + }); + } + + async function createDanmaku(comments) { + if (!comments) { + return; } + if (window.ede.danmaku != null) { + window.ede.danmaku.clear(); + window.ede.danmaku.destroy(); + window.ede.danmaku = null; + } + let _comments = danmakuFilter(danmakuParser(comments)); + console.log('弹幕加载成功: ' + _comments.length); - async function createDanmaku(comments) { - if (!comments) { - return; - } - if (window.ede.danmaku != null) { - window.ede.danmaku.clear(); - window.ede.danmaku.destroy(); - window.ede.danmaku = null; - } - let _comments = danmakuFilter(danmakuParser(comments)); - console.log('弹幕加载成功: ' + _comments.length); + // while (!document.querySelector(mediaContainerQueryStr)) { + // await new Promise((resolve) => setTimeout(resolve, 200)); + // } - while (!document.querySelector(mediaContainerQueryStr)) { - await new Promise((resolve) => setTimeout(resolve, 200)); + let _container = document.querySelector(mediaContainerQueryStr); + let _media = document.querySelector(mediaQueryStr); + if (!_media) throw new Error('用户已退出视频播放'); + window.ede.danmaku = new Danmaku({ + container: _container, + media: _media, + comments: _comments, + engine: 'canvas', + }); + window.ede.danmakuSwitch == 1 ? window.ede.danmaku.show() : window.ede.danmaku.hide(); + if (window.ede.ob) { + window.ede.ob.disconnect(); + } + window.ede.ob = new ResizeObserver(() => { + if (window.ede.danmaku) { + console.log('Resizing'); + window.ede.danmaku.resize(); } + }); + window.ede.ob.observe(_container); + } - let _container = document.querySelector(mediaContainerQueryStr); - let _media = document.querySelector(mediaQueryStr); - if (!_media) throw new Error('用户已退出视频播放'); - window.ede.danmaku = new Danmaku({ - container: _container, - media: _media, - comments: _comments, - engine: 'canvas', - }); - window.ede.danmakuSwitch == 1 ? window.ede.danmaku.show() : window.ede.danmaku.hide(); - if (window.ede.ob) { - window.ede.ob.disconnect(); - } - window.ede.ob = new ResizeObserver(() => { - if (window.ede.danmaku) { - console.log('Resizing'); - window.ede.danmaku.resize(); - } - }); - window.ede.ob.observe(_container); + function loadDanmaku(loadType = LOAD_TYPE.CHECK) { + if (window.ede.loading) { + console.log('正在重新加载'); + return; } - - function loadDanmaku(loadType = LOAD_TYPE.CHECK) { - if (window.ede.loading) { - console.log('正在重新加载'); - return; - } - window.ede.loading = true; - getEpisodeInfo(loadType !== LOAD_TYPE.SEARCH) - .then((info) => { - return new Promise((resolve, reject) => { - if (!info) { - if (loadType !== LOAD_TYPE.INIT) { - reject('播放器未完成加载'); - } else { - reject(null); - } - } - if ( - loadType !== LOAD_TYPE.SEARCH && - loadType !== LOAD_TYPE.REFRESH && - loadType !== LOAD_TYPE.RELOAD && - window.ede.danmaku && - window.ede.episode_info && - window.ede.episode_info.episodeId == info.episodeId - ) { - reject('当前播放视频未变动'); + window.ede.loading = true; + getEpisodeInfo(loadType !== LOAD_TYPE.SEARCH) + .then((info) => { + return new Promise((resolve, reject) => { + if (!info) { + if (loadType !== LOAD_TYPE.INIT) { + reject('播放器未完成加载'); } else { - window.ede.episode_info = info; - resolve(info.episodeId); + reject(null); } - }); - }) - .then( - (episodeId) => { - if (episodeId) { - if (loadType === LOAD_TYPE.RELOAD && danmuCache[episodeId]) { - createDanmaku(danmuCache[episodeId]) + } + if ( + loadType !== LOAD_TYPE.SEARCH && + loadType !== LOAD_TYPE.REFRESH && + loadType !== LOAD_TYPE.RELOAD && + window.ede.danmaku && + window.ede.episode_info && + window.ede.episode_info.episodeId == info.episodeId + ) { + reject('当前播放视频未变动'); + } else { + window.ede.episode_info = info; + resolve(info.episodeId); + } + }); + }) + .then( + (episodeId) => { + if (episodeId) { + if (loadType === LOAD_TYPE.RELOAD && danmuCache[episodeId]) { + createDanmaku(danmuCache[episodeId]) + .then(() => { + console.log('弹幕就位'); + }) + .catch((err) => { + console.log(err); + }); + } else { + getComments(episodeId).then((comments) => { + danmuCache[episodeId] = comments; + createDanmaku(comments) .then(() => { console.log('弹幕就位'); }) .catch((err) => { console.log(err); }); - } else { - getComments(episodeId).then((comments) => { - danmuCache[episodeId] = comments; - createDanmaku(comments) - .then(() => { - console.log('弹幕就位'); - }) - .catch((err) => { - console.log(err); - }); - }); - } + }); } - }, - (msg) => { - if (msg) { - console.log(msg); - } - }, - ) - .then(() => { - window.ede.loading = false; - if (document.getElementById('danmakuCtr').style.opacity != 1) { - document.getElementById('danmakuCtr').style.opacity = 1; } - }) - .catch((err) => { - console.log(err); - }); - } + }, + (msg) => { + if (msg) { + console.log(msg); + } + }, + ) + .then(() => { + window.ede.loading = false; + if (document.getElementById('danmakuCtr').style.opacity != 1) { + document.getElementById('danmakuCtr').style.opacity = 1; + } + }) + .catch((err) => { + console.log(err); + }); + } - function danmakuFilter(comments) { - let level = parseInt(window.localStorage.getItem('danmakuFilterLevel') ? window.localStorage.getItem('danmakuFilterLevel') : 0); - if (level == 0) { - return comments; + function danmakuFilter(comments) { + let level = parseInt(window.localStorage.getItem('danmakuFilterLevel') ? window.localStorage.getItem('danmakuFilterLevel') : 0); + if (level == 0) { + return comments; + } + let limit = 9 - level * 2; + let vertical_limit = 6; + let arr_comments = []; + let vertical_comments = []; + for (let index = 0; index < comments.length; index++) { + let element = comments[index]; + let i = Math.ceil(element.time); + let i_v = Math.ceil(element.time / 3); + if (!arr_comments[i]) { + arr_comments[i] = []; } - let limit = 9 - level * 2; - let vertical_limit = 6; - let arr_comments = []; - let vertical_comments = []; - for (let index = 0; index < comments.length; index++) { - let element = comments[index]; - let i = Math.ceil(element.time); - let i_v = Math.ceil(element.time / 3); - if (!arr_comments[i]) { - arr_comments[i] = []; - } - if (!vertical_comments[i_v]) { - vertical_comments[i_v] = []; - } - // TODO: 屏蔽过滤 - if (vertical_comments[i_v].length < vertical_limit) { - vertical_comments[i_v].push(element); - } else { - element.mode = 'rtl'; - } - if (arr_comments[i].length < limit) { - arr_comments[i].push(element); - } + if (!vertical_comments[i_v]) { + vertical_comments[i_v] = []; + } + // TODO: 屏蔽过滤 + if (vertical_comments[i_v].length < vertical_limit) { + vertical_comments[i_v].push(element); + } else { + element.mode = 'rtl'; + } + if (arr_comments[i].length < limit) { + arr_comments[i].push(element); } - return arr_comments.flat(); } + return arr_comments.flat(); + } - function danmakuParser($obj) { - //const $xml = new DOMParser().parseFromString(string, 'text/xml') - return $obj - .map(($comment) => { - const p = $comment.p; - //if (p === null || $comment.childNodes[0] === undefined) return null - const values = p.split(','); - const mode = { 6: 'ltr', 1: 'rtl', 5: 'top', 4: 'bottom' }[values[1]]; - if (!mode) return null; - //const fontSize = Number(values[2]) || 25 - // 弹幕大小 - const fontSizeMagnification = parseFloat(localStorage.getItem('danmakuFontSizeMagnification')) || 1; - const fontSize = Math.round((window.screen.height > window.screen.width ? window.screen.width : window.screen.height / 1080) * 18 * fontSizeMagnification); - // 弹幕透明度 - const fontOpacity = Math.round((parseFloat(localStorage.getItem('danmakuFontOpacity')) || 1.0) * 255).toString(16); - // 弹幕颜色+透明度 - const color = `000000${Number(values[2]).toString(16)}${fontOpacity}`.slice(-8); - return { - text: $comment.m, - mode, - time: values[0] * 1, - style: { - fontSize: `${fontSize}px`, - color: `#${color}`, - textShadow: - color === '00000' ? '-1px -1px #fff, -1px 1px #fff, 1px -1px #fff, 1px 1px #fff' : '-1px -1px #000, -1px 1px #000, 1px -1px #000, 1px 1px #000', - - font: `${fontSize}px sans-serif`, - fillStyle: `#${color}`, - strokeStyle: color === '000000' ? `#ffffff${fontOpacity}` : `#000000${fontOpacity}`, - lineWidth: 2.0, - }, - }; - }) - .filter((x) => x); - } + function danmakuParser($obj) { + //const $xml = new DOMParser().parseFromString(string, 'text/xml') + return $obj + .map(($comment) => { + const p = $comment.p; + //if (p === null || $comment.childNodes[0] === undefined) return null + const values = p.split(','); + const mode = { 6: 'ltr', 1: 'rtl', 5: 'top', 4: 'bottom' }[values[1]]; + if (!mode) return null; + //const fontSize = Number(values[2]) || 25 + // 弹幕大小 + const fontSizeMagnification = parseFloat( + localStorage.getItem('danmakuFontSizeMagnification') + ) || 1; + let fontSize = 25; + const h3Ele = document.querySelector('.videoOsdTitle') + if (h3Ele) { + fontSize = parseFloat(getComputedStyle(h3Ele).fontSize.replace('px', '')) * fontSizeMagnification + } else { + fontSize = Math.round( + (window.screen.height > window.screen.width + ? window.screen.width + : window.screen.height / 1080) * 18 * fontSizeMagnification + ); + } + // 弹幕透明度 + const fontOpacity = Math.round((parseFloat(localStorage.getItem('danmakuFontOpacity')) || 1.0) * 255).toString(16); + // 弹幕颜色+透明度 + const color = `000000${Number(values[2]).toString(16)}${fontOpacity}`.slice(-8); + return { + text: $comment.m, + mode, + time: values[0] * 1, + style: { + fontSize: `${fontSize}px`, + color: `#${color}`, + textShadow: + color === '00000' ? '-1px -1px #fff, -1px 1px #fff, 1px -1px #fff, 1px 1px #fff' : '-1px -1px #000, -1px 1px #000, 1px -1px #000, 1px 1px #000', - function list2string($obj2) { - const $animes = $obj2.animes; - let anime_lists = $animes.map(($single_anime) => { - return $single_anime.animeTitle + ' 类型:' + $single_anime.typeDescription; - }); - let anime_lists_str = '1:' + anime_lists[0]; - for (let i = 1; i < anime_lists.length; i++) { - anime_lists_str = anime_lists_str + '\n' + (i + 1).toString() + ':' + anime_lists[i]; - } - return anime_lists_str; - } + font: `${fontSize}px sans-serif`, + fillStyle: `#${color}`, + strokeStyle: color === '000000' ? `#ffffff${fontOpacity}` : `#000000${fontOpacity}`, + lineWidth: 2.0, + }, + }; + }) + .filter((x) => x); + } - function ep2string($obj3) { - const $animes = $obj3; - let anime_lists = $animes.map(($single_ep) => { - return $single_ep.episodeTitle; - }); - let ep_lists_str = '1:' + anime_lists[0]; - for (let i = 1; i < anime_lists.length; i++) { - ep_lists_str = ep_lists_str + '\n' + (i + 1).toString() + ':' + anime_lists[i]; - } - return ep_lists_str; + function list2string($obj2) { + const $animes = $obj2.animes; + let anime_lists = $animes.map(($single_anime) => { + return $single_anime.animeTitle + ' 类型:' + $single_anime.typeDescription; + }); + let anime_lists_str = '1:' + anime_lists[0]; + for (let i = 1; i < anime_lists.length; i++) { + anime_lists_str = anime_lists_str + '\n' + (i + 1).toString() + ':' + anime_lists[i]; } + return anime_lists_str; + } - while (!window.require) { - await new Promise((resolve) => setTimeout(resolve, 200)); + function ep2string($obj3) { + const $animes = $obj3; + let anime_lists = $animes.map(($single_ep) => { + return $single_ep.episodeTitle; + }); + let ep_lists_str = '1:' + anime_lists[0]; + for (let i = 1; i < anime_lists.length; i++) { + ep_lists_str = ep_lists_str + '\n' + (i + 1).toString() + ':' + anime_lists[i]; } - if (!window.ede) { + return ep_lists_str; + } + + function init() { + if (!window.ede || (!!window.ede && !window.ede.danmaku)) { window.ede = new EDE(); - setInterval(() => { - initUI(); - }, check_interval); - while (!(await getEmbyItemInfo())) { - await new Promise((resolve) => setTimeout(resolve, 200)); - } + initUI(); loadDanmaku(LOAD_TYPE.INIT); - setInterval(() => { - initListener(); - }, check_interval); + initListener(); } } + + // emby/jellyfin CustomEvent + // see: https://github.com/MediaBrowser/emby-web-defaultskin/blob/822273018b82a4c63c2df7618020fb837656868d/nowplaying/videoosd.js#L691 + document.addEventListener("viewbeforeshow", function (e) { + console.log("viewbeforeshow", e); + // if (isEmby === "") { + // isEmby = !!e.detail.contextPath; + // } + let isTargetPage; + // if (isEmby) { + isTargetPage = e.detail.type === "video-osd"; + // } else { + // isTargetPage = !!e.detail.state && !!e.detail.state.item.Id; + // } + if (isTargetPage) { + // const mutation = new MutationObserver(function() { + init(); + // mutation.disconnect(); + // }) + // mutation.observe(document.body, { + // childList: true, + // characterData: true, + // subtree: true, + // }) + } + }); + + // while (!window.require) { + // await new Promise((resolve) => setTimeout(resolve, check_interval)); + // } + // if (!window.ede) { + // window.ede = new EDE(); + // while (!window.ConnectionManager) { + // await new Promise((resolve) => setTimeout(resolve, check_interval)); + // } + // setInterval(() => { + // initUI(); + // }, check_interval); + // // while (!(await getEmbyItemInfo())) { + // // await new Promise((resolve) => setTimeout(resolve, check_interval)); + // // } + // loadDanmaku(LOAD_TYPE.INIT); + // setInterval(() => { + // initListener(); + // }, check_interval); + // } + + // } })(); From 6a547013c9251364c5c897bf70ced856b20f0740 Mon Sep 17 00:00:00 2001 From: chen3861229 Date: Fri, 24 May 2024 23:18:05 +0800 Subject: [PATCH 02/69] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E6=83=85=E5=86=B5=E7=9A=84=E4=BE=9D=E8=B5=96=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ede.js | 149 +++++++++++++++++++++------------------------------------ 1 file changed, 54 insertions(+), 95 deletions(-) diff --git a/ede.js b/ede.js index ce7db6e..13c5919 100644 --- a/ede.js +++ b/ede.js @@ -3,7 +3,7 @@ // @description Emby弹幕插件 // @namespace https://github.com/RyoLee // @author RyoLee -// @version 1.13 +// @version 1.14 // @copyright 2022, RyoLee (https://github.com/RyoLee) // @license MIT; https://raw.githubusercontent.com/RyoLee/emby-danmaku/master/LICENSE // @icon https://github.githubassets.com/pinned-octocat.svg @@ -15,9 +15,13 @@ (async function () { 'use strict'; - // if (document.querySelector('meta[name="application-name"]').content == 'Emby' || document.documentElement.classList.contains('accent-emby')) { - // ------ configs start------ - const check_interval = 200; + // ------ user configs start ------ + // Danmaku 依赖路径,index.html 引入的和篡改猴环境不用填,依赖已内置,被 eval() 执行的特殊环境使用,支持相对绝对网络路径 + const requireDanmakuPath = "danmaku.min.js"; // 默认是相对路径等同 https://emby/web/ 和 /system/dashboard-ui/ + // ------ user configs start ------ + // ------ inner configs start ------ + let embyItemId = ''; + // const check_interval = 200; const chConverTtitle = ['当前状态: 未启用', '当前状态: 转换为简体', '当前状态: 转换为繁体']; const danmuCache = {}; const LOAD_TYPE = { @@ -38,9 +42,14 @@ class: 'paper-icon-button-light', is: 'paper-icon-button-light', }; - const uiAnchorStr = '\uE034'; // htmlVideoPlayerContainer - const mediaContainerQueryStr = ".graphicContentContainer"; + const appVersion = parseFloat(ApiClient.appVersion()); + const isVersionOld = appVersion < 4.8; + let mediaContainerQueryStr = ".graphicContentContainer"; + if (isVersionOld) { + mediaContainerQueryStr = "div[data-type='video-osd']"; + } + // not webClient no