/** * 视频画中画UI库 * 基于 WinBox.js 实现 * 功能: * 1. 可拖拽、可调整大小的视频窗口 * 2. 根据视频宽高自动调整窗口尺寸 * 3. 支持最小化、最大化、全屏 * 4. 抖音风格视频滑动切换 * 5. 右侧操作栏、底部信息栏(自动隐藏) * * 使用方法: * 1. 引入 WinBox.js (https://rawcdn.githack.com/nextapps-de/winbox/0.2.82/dist/winbox.bundle.min.js) * 2. 引入 HLS.js (https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.6.13/hls.min.js) * 3. 使用 VideoPipLibrary.create() 创建视频窗口 * 4. 使用 player.add() 追加新视频 */ /** * WinBox.js v0.2.82 (Bundle) * Author and Copyright: Thomas Wilkerling * Licence: Apache-2.0 * Hosted by Nextapps GmbH * https://github.com/nextapps-de/winbox * */ (function(){'use strict';var e,aa=document.createElement("style");aa.innerHTML="@keyframes wb-fade-in{0%{opacity:0}to{opacity:.85}}.winbox{position:fixed;left:0;top:0;background:#0050ff;box-shadow:0 14px 28px rgba(0,0,0,.25),0 10px 10px rgba(0,0,0,.22);transition:width .3s,height .3s,left .3s,top .3s;transition-timing-function:cubic-bezier(.3,1,.3,1);contain:layout size;text-align:left;touch-action:none}.wb-body,.wb-header{position:absolute;left:0}.wb-header{top:0;width:100%;height:35px;line-height:35px;color:#fff;overflow:hidden;z-index:1}.wb-body{top:35px;right:0;bottom:0;overflow:auto;-webkit-overflow-scrolling:touch;overflow-scrolling:touch;will-change:contents;background:#fff;margin-top:0!important;contain:strict;z-index:0}.wb-control *,.wb-icon{background-repeat:no-repeat}.wb-drag{height:100%;padding-left:10px;cursor:move}.wb-title{font-family:Arial,sans-serif;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wb-icon{display:none;width:20px;height:100%;margin:-1px 8px 0-3px;float:left;background-size:100%;background-position:center}.wb-e,.wb-w{width:10px;top:0}.wb-n,.wb-s{left:0;height:10px;position:absolute}.wb-n{top:-5px;right:0;cursor:n-resize;z-index:2}.wb-e{position:absolute;right:-5px;bottom:0;cursor:w-resize;z-index:2}.wb-s{bottom:-5px;right:0;cursor:n-resize;z-index:2}.wb-nw,.wb-sw,.wb-w{left:-5px}.wb-w{position:absolute;bottom:0;cursor:w-resize;z-index:2}.wb-ne,.wb-nw,.wb-sw{width:15px;height:15px;z-index:2;position:absolute}.wb-nw{top:-5px;cursor:nw-resize}.wb-ne,.wb-sw{cursor:ne-resize}.wb-ne{top:-5px;right:-5px}.wb-se,.wb-sw{bottom:-5px}.wb-se{position:absolute;right:-5px;width:15px;height:15px;cursor:nw-resize;z-index:2}.wb-control{float:right;height:100%;max-width:100%;text-align:center}.wb-control *{display:inline-block;width:30px;height:100%;max-width:100%;background-position:center;cursor:pointer}.no-close .wb-close,.no-full .wb-full,.no-header .wb-header,.no-max .wb-max,.no-min .wb-min,.no-resize .wb-body~div,.wb-body .wb-hide,.wb-show,.winbox.hide,.winbox.min .wb-body>*,.winbox.min .wb-full,.winbox.min .wb-min,.winbox.modal .wb-full,.winbox.modal .wb-max,.winbox.modal .wb-min{display:none}.winbox.max .wb-drag,.winbox.min .wb-drag{cursor:default}.wb-min{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAyIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNOCAwaDdhMSAxIDAgMCAxIDAgMkgxYTEgMSAwIDAgMSAwLTJoN3oiLz48L3N2Zz4=);background-size:14px auto;background-position:center calc(50% + 6px)}.wb-max{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9IiNmZmYiIHZpZXdCb3g9IjAgMCA5NiA5NiI+PHBhdGggZD0iTTIwIDcxLjMxMUMxNS4zNCA2OS42NyAxMiA2NS4yMyAxMiA2MFYyMGMwLTYuNjMgNS4zNy0xMiAxMi0xMmg0MGM1LjIzIDAgOS42NyAzLjM0IDExLjMxMSA4SDI0Yy0yLjIxIDAtNCAxLjc5LTQgNHY1MS4zMTF6Ii8+PHBhdGggZD0iTTkyIDc2VjM2YzAtNi42My01LjM3LTEyLTEyLTEySDQwYy02LjYzIDAtMTIgNS4zNy0xMiAxMnY0MGMwIDYuNjMgNS4zNyAxMiAxMiAxMmg0MGM2LjYzIDAgMTItNS4zNyAxMi0xMnptLTUyIDRjLTIuMjEgMC00LTEuNzktNC00VjM2YzAtMi4yMSAxLjc5LTQgNC00aDQwYzIuMjEgMCA0IDEuNzkgNCA0djQwYzAgMi4yMS0xLjc5IDQtNCA0SDQweiIvPjwvc3ZnPg==);background-size:17px auto}.wb-close{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xIC0xIDE4IDE4Ij48cGF0aCBmaWxsPSIjZmZmIiBkPSJtMS42MTMuMjEuMDk0LjA4M0w4IDYuNTg1IDE0LjI5My4yOTNsLjA5NC0uMDgzYTEgMSAwIDAgMSAxLjQwMyAxLjQwM2wtLjA4My4wOTRMOS40MTUgOGw2LjI5MiA2LjI5M2ExIDEgMCAwIDEtMS4zMiAxLjQ5N2wtLjA5NC0uMDgzTDggOS40MTVsLTYuMjkzIDYuMjkyLS4wOTQuMDgzQTEgMSAwIDAgMSAuMjEgMTQuMzg3bC4wODMtLjA5NEw2LjU4NSA4IC4yOTMgMS43MDdBMSAxIDAgMCAxIDEuNjEzLjIxeiIvPjwvc3ZnPg==);background-size:15px auto;background-position:5px center}.wb-full{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2Utd2lkdGg9IjIuNSIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNOCAzSDVhMiAyIDAgMCAwLTIgMnYzbTE4IDBWNWEyIDIgMCAwIDAtMi0yaC0zbTAgMThoM2EyIDIgMCAwIDAgMi0ydi0zTTMgMTZ2M2EyIDIgMCAwIDAgMiAyaDMiLz48L3N2Zz4=);background-size:16px auto}.winbox.max .wb-body~div,.winbox.min .wb-body~div,.winbox.modal .wb-body~div,.winbox.modal .wb-drag,body.wb-lock iframe{pointer-events:none}.winbox.max{box-shadow:none}.winbox.max .wb-body{margin:0!important}.winbox iframe{position:absolute;width:100%;height:100%;border:0}body.wb-lock .winbox{will-change:left,top,width,height;transition:none}.winbox.modal:before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:inherit;border-radius:inherit}.winbox.modal:after{content:'';position:absolute;top:-50vh;left:-50vw;right:-50vw;bottom:-50vh;background:#0d1117;animation:wb-fade-in .2s ease-out forwards;z-index:-1}.no-animation{transition:none}.no-shadow{box-shadow:none}.no-header .wb-body{top:0}.no-move:not(.min) .wb-title{pointer-events:none}.wb-body .wb-show{display:revert}"; var h=document.getElementsByTagName("head")[0];h.firstChild?h.insertBefore(aa,h.firstChild):h.appendChild(aa);var ba=document.createElement("div");ba.innerHTML="
";function k(a,b,c,f){a&&a.addEventListener(b,c,f||!1)}function l(a,b){var c=window,f=m;c&&c.removeEventListener(a,b,f||!1)}function t(a,b){a.stopPropagation();b&&a.preventDefault()}function u(a,b,c){c=""+c;a["_s_"+b]!==c&&(a.style.setProperty(b,c),a["_s_"+b]=c)};/* self.max &&*/ var x=[],A=[],ca={capture:!0,passive:!1},m={capture:!0,passive:!0},B,da=0,E=10,F,J,ha,K,P,ia; function U(a,b){if(!(this instanceof U))return new U(a);B||ja();if(a){if(b){var c=a;a=b}if("string"===typeof a)c=a;else{var f=a.id;var d=a.index;var n=a.root;var p=a.template;c=c||a.title;var v=a.icon;var L=a.mount;var Q=a.html;var g=a.url;var q=a.width;var r=a.height;var w=a.minwidth;var C=a.minheight;var y=a.maxwidth;var z=a.maxheight;var ea=a.autosize;var D=a.overflow;var G=a.min;var H=a.max;var I=a.hidden;var fa=a.modal;var X=a.x||(fa?"center":0);var Y=a.y||(fa?"center":0);var M=a.top;var N=a.left; var R=a.bottom;var S=a.right;var la=a.background;var O=a.border;var T=a.header;var Z=a["class"];var ma=a.oncreate;var ra=a.onclose;var sa=a.onfocus;var ta=a.onblur;var ua=a.onmove;var va=a.onresize;var wa=a.onfullscreen;var xa=a.onmaximize;var ya=a.onminimize;var za=a.onrestore;var Aa=a.onhide;var Ba=a.onshow;var Ca=a.onload}}this.g=(p||ba).cloneNode(!0);this.g.id=this.id=f||"winbox-"+ ++da;this.g.className="winbox"+(Z?" "+("string"===typeof Z?Z:Z.join(" ")):"")+(fa?" modal":"");this.g.winbox=this; this.window=this.g;this.body=this.g.getElementsByClassName("wb-body")[0];this.h=T||35;A.push(this);la&&this.setBackground(la);O?u(this.body,"margin",O+(isNaN(O)?"":"px")):O=0;T&&(b=this.g.getElementsByClassName("wb-header")[0],u(b,"height",T+"px"),u(b,"line-height",T+"px"),u(this.body,"top",T+"px"));c&&this.setTitle(c);v&&this.setIcon(v);L?this.mount(L):Q?this.body.innerHTML=Q:g&&this.setUrl(g,Ca);M=M?V(M,P):0;R=R?V(R,P):0;N=N?V(N,K):0;S=S?V(S,K):0;c=K-N-S;v=P-M-R;y=y?V(y,c):c;z=z?V(z,v):v;w=w?V(w, y):150;C=C?V(C,z):this.h;ea?((n||B).appendChild(this.body),q=Math.max(Math.min(this.body.clientWidth+2*O+1,y),w),r=Math.max(Math.min(this.body.clientHeight+this.h+O+1,z),C),this.g.appendChild(this.body)):(q=q?V(q,y):Math.max(y/2,w)|0,r=r?V(r,z):Math.max(z/2,C)|0);X=X?V(X,c,q):N;Y=Y?V(Y,v,r):M;this.x=X;this.y=Y;this.width=q;this.height=r;this.s=w;this.o=C;this.m=y;this.l=z;this.top=M;this.right=S;this.bottom=R;this.left=N;this.index=d;this.j=D;this.focused=this.hidden=this.full=this.max=this.min=!1; this.onclose=ra;this.onfocus=sa;this.onblur=ta;this.onmove=ua;this.onresize=va;this.onfullscreen=wa;this.onmaximize=xa;this.onminimize=ya;this.onrestore=za;this.onhide=Aa;this.onshow=Ba;I?this.hide():this.focus();if(d||0===d)this.index=d,u(this.g,"z-index",d),d>E&&(E=d);H?this.maximize():G?this.minimize():this.resize().move();ka(this);(n||B).appendChild(this.g);ma&&ma.call(this,a)}U["new"]=function(a){return new U(a)};U.stack=function(){return A}; function V(a,b,c){"string"===typeof a&&("center"===a?a=(b-c)/2+.5|0:"right"===a||"bottom"===a?a=b-c:(c=parseFloat(a),a="%"===(""+c!==a&&a.substring((""+c).length))?b/100*c+.5|0:c));return a} function ja(){B=document.body;B[J="requestFullscreen"]||B[J="msRequestFullscreen"]||B[J="webkitRequestFullscreen"]||B[J="mozRequestFullscreen"]||(J="");ha=J&&J.replace("request","exit").replace("mozRequest","mozCancel").replace("Request","Exit");k(window,"resize",function(){na();oa()});k(B,"mousedown",function(){ia=!1},!0);k(B,"mousedown",function(){if(!ia){var a=A.length;if(a)for(--a;0<=a;a--){var b=A[a];if(b.focused){b.blur();break}}}});na()} function ka(a){W(a,"drag");W(a,"n");W(a,"s");W(a,"w");W(a,"e");W(a,"nw");W(a,"ne");W(a,"se");W(a,"sw");k(a.g.getElementsByClassName("wb-min")[0],"click",function(b){t(b);a.min?a.restore().focus():a.minimize()});k(a.g.getElementsByClassName("wb-max")[0],"click",function(b){t(b);a.max?a.restore().focus():a.maximize().focus()});J?k(a.g.getElementsByClassName("wb-full")[0],"click",function(b){t(b);a.fullscreen().focus()}):a.addClass("no-full");k(a.g.getElementsByClassName("wb-close")[0],"click",function(b){t(b); a.close()||(a=null)});k(a.g,"mousedown",function(){ia=!0},!0);k(a.body,"mousedown",function(){a.focus()},!0)}function pa(a){x.splice(x.indexOf(a),1);oa();a.removeClass("min");a.min=!1;a.g.title=""}function oa(){for(var a=x.length,b={},c={},f=0,d;fr){a.max?a.restore():a.maximize();return}}}a.min||(B.classList.add("wb-lock"),(p=g.touches)&&(p=p[0])?(g=p,k(window,"touchmove",f,m),k(window,"touchend",d,m)):(k(window,"mousemove",f,m),k(window,"mouseup",d,m)),v=g.pageX,L=g.pageY)}function f(g){t(g);p&&(g=g.touches[0]);var q=g.pageX;g=g.pageY;var r=q-v,w=g-L,C=a.width,y=a.height,z=a.x, ea=a.y,D;if("drag"===b){if(a.g.classList.contains("no-move"))return;a.x+=r;a.y+=w;var G=D=1}else{if("e"===b||"se"===b||"ne"===b){a.width+=r;var H=1}else if("w"===b||"sw"===b||"nw"===b)a.x+=r,a.width-=r,G=H=1;if("s"===b||"se"===b||"sw"===b){a.height+=w;var I=1}else if("n"===b||"ne"===b||"nw"===b)a.y+=w,a.height-=w,D=I=1}H&&(a.width=Math.max(Math.min(a.width,a.m,K-a.x-a.right),a.s),H=a.width!==C);I&&(a.height=Math.max(Math.min(a.height,a.l,P-a.y-a.bottom),a.o),I=a.height!==y);(H||I)&&a.resize();G&& (a.max&&(a.x=(qK/3*2?K-a.width-a.right:K/2-a.width/2)+r),a.x=Math.max(Math.min(a.x,a.j?K-30:K-a.width-a.right),a.j?30-a.width:a.left),G=a.x!==z);D&&(a.max&&(a.y=a.top+w),a.y=Math.max(Math.min(a.y,a.j?P-a.h:P-a.height-a.bottom),a.top),D=a.y!==ea);if(G||D)a.max&&a.restore(),a.move();if(H||G)v=q;if(I||D)L=g}function d(g){t(g);B.classList.remove("wb-lock");p?(l("touchmove",f),l("touchend",d)):(l("mousemove",f),l("mouseup",d))}var n=a.g.getElementsByClassName("wb-"+b)[0];if(n){var p,v,L, Q=0;k(n,"mousedown",c,ca);k(n,"touchstart",c,ca)}}function na(){var a=document.documentElement;K=a.clientWidth;P=a.clientHeight}e=U.prototype;e.mount=function(a){this.unmount();a.i||(a.i=a.parentNode);this.body.textContent="";this.body.appendChild(a);return this};e.unmount=function(a){var b=this.body.firstChild;if(b){var c=a||b.i;c&&c.appendChild(b);b.i=a}return this}; e.setTitle=function(a){var b=this.g.getElementsByClassName("wb-title")[0];a=this.title=a;var c=b.firstChild;c?c.nodeValue=a:b.textContent=a;return this};e.setIcon=function(a){var b=this.g.getElementsByClassName("wb-icon")[0];u(b,"background-image","url("+a+")");u(b,"display","inline-block");return this};e.setBackground=function(a){u(this.g,"background",a);return this}; e.setUrl=function(a,b){var c=this.body.firstChild;c&&"iframe"===c.tagName.toLowerCase()?c.src=a:(this.body.innerHTML='',b&&(this.body.firstChild.onload=b));return this};e.focus=function(a){if(!1===a)return this.blur();if(!this.focused){a=A.length;if(1 {}, //onDownload: (instance, index) => {}, onSettings: (instance) => { instance.showInfo(); }, }; /** * 存储所有窗口实例 */ const windows = new Map(); let windowIdCounter = 0; let topZIndex = 2147483640; /** * 获取下一个 z-index */ function getNextZIndex() { topZIndex += 10; return topZIndex; } /** * 注入自定义样式 */ function injectCustomStyles() { if (document.getElementById('video-pip-styles')) return; const css = ` .video-pip-container { width: 100%; height: 100%; background: #000; position: relative; overflow: hidden; } .video-pip-slider { width: 100%; height: 100%; position: relative; } .video-pip-slide { width: 100%; height: 100%; position: absolute; top: 0; left: 0; display: flex; align-items: center; justify-content: center; background: #000; transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94); will-change: transform; } .video-pip-slide video { width: 100%; height: 100%; object-fit: contain; background: #000; } .video-pip-action-bar { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 16px; z-index: 50; opacity: 1; transition: opacity 0.3s ease; } .video-pip-action-bar.hidden { opacity: 0; pointer-events: none; } .video-pip-action-btn { width: 36px; height: 36px; border-radius: 50%; background: rgba(0, 0, 0, 0.25); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; border: none; padding: 0; } .video-pip-action-btn:hover { background: rgba(0, 0, 0, 0.4); transform: scale(1.1); } .video-pip-action-btn:active { transform: scale(0.95); } .video-pip-action-btn svg { width: 20px; height: 20px; fill: #fff; } .video-pip-action-btn.liked svg { fill: #fe2c55; } .video-pip-info-bar { position: absolute; left: 0; right: 60px; bottom: 50px; padding: 12px 16px; pointer-events: none; z-index: 20; opacity: 1; transition: opacity 0.3s ease; } .video-pip-info-bar.hidden { opacity: 0; } .video-pip-info-title { color: #fff; font-size: 14px; font-weight: 500; line-height: 1.4; text-shadow: 0 1px 2px rgba(0,0,0,0.5); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .winbox.video-pip-window { border-radius: 8px; overflow: hidden; background: #000 !important; user-select: none !important; -webkit-user-select: none !important; pointer-events: auto !important; } .winbox.video-pip-window * { user-select: none !important; -webkit-user-select: none !important; } .winbox.video-pip-window .wb-header, .winbox.video-pip-window .wb-resize { pointer-events: auto !important; } .winbox.video-pip-window .wb-header { position: absolute; top: 0; left: 0; right: 0; background: linear-gradient(180deg, rgba(0,0,0,0.7) 0%, transparent 100%); height: 40px; z-index: 10; border-radius: 8px 8px 0 0; opacity: 1; transition: opacity 0.3s ease, visibility 0.3s ease; cursor: move !important; touch-action: none !important; } .winbox.video-pip-window .wb-header * { touch-action: none !important; } .winbox.video-pip-window .wb-drag { touch-action: none !important; } .video-pip-header-hidden { opacity: 0 !important; pointer-events: none !important; visibility: hidden !important; z-index: -1 !important; } .winbox.video-pip-window .wb-title { font-size: 12px; font-weight: 500; line-height: 40px; color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.5); } .winbox.video-pip-window .wb-control { display: flex; } .winbox.video-pip-window .wb-body { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: 0; } .winbox.video-pip-window .wb-resize { display: block !important; pointer-events: auto !important; touch-action: none !important; } .winbox.video-pip-window .wb-resize * { touch-action: none !important; } .winbox.video-pip-window .wb-resize-n, .winbox.video-pip-window .wb-resize-s, .winbox.video-pip-window .wb-resize-e, .winbox.video-pip-window .wb-resize-w, .winbox.video-pip-window .wb-resize-ne, .winbox.video-pip-window .wb-resize-nw, .winbox.video-pip-window .wb-resize-se, .winbox.video-pip-window .wb-resize-sw { touch-action: none !important; pointer-events: auto !important; } .winbox .wb-settings { background-size: 16px auto; background-position: center; background-repeat: no-repeat; opacity: 0.8; transition: opacity 0.2s ease; } .winbox .wb-settings:hover { opacity: 1; } .winbox .wb-likes { background-size: 16px auto; background-position: center; background-repeat: no-repeat; opacity: 0.8; transition: opacity 0.2s ease; } .winbox .wb-likes:hover { opacity: 1; } .video-pip-likes-slide { width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #0f0f23; overflow-y: auto; overflow-x: hidden; z-index: 100; transform: translateY(100%); transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .video-pip-likes-slide.show { transform: translateY(0); } .video-pip-likes-content { padding: 56px 8px 16px 8px; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; line-height: 1.5; } .video-pip-likes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); max-width: 100%; gap: 6px; } @media (min-width: 300px) { .video-pip-likes-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 8px; } } @media (min-width: 500px) { .video-pip-likes-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; } } @media (min-width: 700px) { .video-pip-likes-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; } } .video-pip-likes-item { position: relative; aspect-ratio: 9 / 16; border-radius: 8px; overflow: hidden; cursor: pointer; background: #1a1a2e; transition: transform 0.2s ease, box-shadow 0.2s ease; } .video-pip-likes-item:hover { transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); } .video-pip-likes-item:active { transform: scale(0.98); } .video-pip-likes-item video, .video-pip-likes-item img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; background: #000; } .video-pip-likes-item video { opacity: 0; transition: opacity 0.3s ease; } .video-pip-likes-item video.loaded { opacity: 1; } .video-pip-likes-item.playing video { object-fit: contain; } .video-pip-likes-item .video-controls { position: absolute; bottom: 0; left: 0; right: 0; height: 36px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); display: flex; align-items: flex-end; justify-content: center; padding-bottom: 4px; opacity: 0; transition: opacity 0.2s ease; } .video-pip-likes-item:hover .video-controls, .video-pip-likes-item.playing .video-controls { opacity: 1; } .video-pip-likes-item .video-action-bar { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 12px; z-index: 10; opacity: 0; transition: opacity 0.2s ease; } .video-pip-likes-item:hover .video-action-bar, .video-pip-likes-item.playing .video-action-bar { opacity: 1; } .video-pip-likes-item .video-action-btn { width: 32px; height: 32px; border-radius: 50%; background: rgba(0, 0, 0, 0.4); display: flex; align-items: center; justify-content: center; cursor: pointer; border: none; transition: all 0.2s ease; } .video-pip-likes-item .video-action-btn:hover { background: rgba(0, 0, 0, 0.6); transform: scale(1.1); } .video-pip-likes-item .video-action-btn svg { width: 18px; height: 18px; fill: #fff; } .video-pip-likes-item .video-action-btn.liked svg { fill: #ff2d55; } .video-pip-likes-overlay { position: absolute; bottom: 0; left: 0; right: 0; padding: 24px 8px 8px 8px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.8)); pointer-events: none; } .video-pip-likes-title { font-size: 12px; color: #fff; margin: 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.4; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } .video-pip-likes-empty { text-align: center; color: #999999; padding: 40px 20px; grid-column: 1 / -1; } .video-pip-likes-loading { text-align: center; color: #999999; padding: 20px; grid-column: 1 / -1; } .video-pip-likes-loading-more { text-align: center; color: #999999; padding: 16px; font-size: 12px; } .video-pip-likes-slider { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: #000; } .video-pip-likes-slider-item { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } .video-pip-likes-slider-item video { width: 100%; height: 100%; object-fit: contain; } .video-pip-likes-slider-action-bar { position: absolute; right: 12px; top: 50%; transform: translateY(-50%); display: flex; flex-direction: column; gap: 16px; z-index: 10; } .video-pip-likes-slider-btn { width: 40px; height: 40px; border-radius: 50%; background: rgba(0, 0, 0, 0.4); display: flex; align-items: center; justify-content: center; cursor: pointer; border: none; transition: all 0.2s ease; } .video-pip-likes-slider-btn:hover { background: rgba(0, 0, 0, 0.6); transform: scale(1.1); } .video-pip-likes-slider-btn svg { width: 22px; height: 22px; fill: #fff; } .video-pip-likes-slider-btn.liked svg { fill: #ff2d55; } .video-pip-likes-slider-info { position: absolute; bottom: 0; left: 0; right: 60px; padding: 40px 16px 16px 16px; background: linear-gradient(transparent, rgba(0, 0, 0, 0.7)); pointer-events: none; } .video-pip-likes-slider-title { font-size: 14px; color: #fff; margin: 0; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; line-height: 1.4; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5); } .video-pip-info-slide { width: 100%; height: 100%; position: absolute; top: 0; left: 0; background: #0f0f23; overflow-y: auto; z-index: 100; transform: translateY(100%); transition: transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94); } .video-pip-info-slide.show { transform: translateY(0); } .video-pip-info-content { padding: 56px 16px 16px 16px; color: #e0e0e0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 14px; line-height: 1.5; } .video-pip-info-section { background: #252542; border: 1px solid #4e4e6e; border-radius: 8px; padding: 12px; margin-bottom: 12px; } .video-pip-info-title { font-size: 14px; font-weight: 600; color: #e0e0e0; margin: 0 0 8px 0; display: flex; align-items: center; gap: 6px; } .video-pip-info-text { font-size: 13px; color: #999999; line-height: 1.5; margin: 4px 0; } .video-pip-info-sites { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; } .video-pip-info-link { background: #0f0f23; border: 1px solid #4e4e6e; border-radius: 12px; padding: 4px 10px; font-size: 12px; color: #999999; text-decoration: none; display: inline-block; } .video-pip-info-link:hover { border-color: #8b5cf6; color: #8b5cf6; } .video-pip-info-btn { display: inline-block; padding: 6px 14px; background: linear-gradient(135deg, #8b5cf6, #a78bfa); color: white; text-decoration: none; border-radius: 4px; font-size: 13px; border: none; cursor: pointer; margin-top: 8px; } .video-pip-info-btn:hover { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(139, 92, 246, 0.3); } .video-pip-info-status { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; } .video-pip-info-dot { width: 8px; height: 8px; border-radius: 50%; } .video-pip-info-dot.active { background: #52c41a; } .video-pip-info-dot.inactive { background: #ff4d4f; } .video-pip-info-input { width: 100%; padding: 8px 12px; box-sizing: border-box; background: #0f0f23; color: #e0e0e0; border: 1px solid #4e4e6e; border-radius: 4px; font-size: 14px; outline: none; margin-top: 8px; } .video-pip-info-input:focus { border-color: #8b5cf6; } .video-pip-info-message { font-size: 12px; margin-top: 8px; } .video-pip-info-message.success { color: #52c41a; } .video-pip-info-message.error { color: #ff4d4f; } .video-pip-info-close { position: absolute; top: 12px; right: 12px; width: 32px; height: 32px; border-radius: 50%; background: rgba(0, 0, 0, 0.5); border: none; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 10; } .video-pip-info-close svg { width: 16px; height: 16px; fill: #fff; } `; const style = document.createElement('style'); style.id = 'video-pip-styles'; style.textContent = css; document.head.appendChild(style); } /** * SVG 图标 */ const ICONS = { like: '', likeFilled: '', download: '', link: '', heart: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTEyIDIxLjM1bC0xLjQ1LTEuMzJDNS40IDE1LjM2IDIgMTIuMjggMiA4LjUgMiA1LjQyIDQuNDIgMyA3LjUgM2MxLjc0IDAgMy40MS44MSA0LjUgMi4wOUMxMy4wOSAzLjgxIDE0Ljc2IDMgMTYuNSAzIDE5LjU4IDMgMjIgNS40MiAyMiA4LjVjMCAzLjc4LTMuNCA2Ljg2LTguNTUgMTEuNTRMMTIgMjEuMzV6Ii8+PC9zdmc+', settings: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iI2ZmZiI+PHBhdGggZD0iTTE5LjE0IDEyLjAxYzAuMDQtLjMyLjA2LS42Ni4wNi0xLjAwcy0uMDItLjY4LS4wNi0xLjAxbDIuMTEtMS42NWMuMTktLjE1LjI0LS40Mi4xMi0uNjRsLTIuMDMtMy41Yy0uMTItLjIyLS4zOS0uMy0uNjEtLjIybC0yLjQ5IDFjLS41Mi0uNC0xLjA4LS43My0xLjY5LS45OGwtLjM4LTIuNjVDMTQuMDYgMi4xOCAxMy44MiAyIDEzLjU1IDJoLTQuMWMtLjI3IDAtLjUxLjE4LS41NC40NGwtLjM4IDIuNjVjLS42MS4yNS0xLjE3LjU5LTEuNjkuOThsLTIuNDktMWMtLjIzLS4wOS0uNDkgMC0uNjEuMjJsLTIuMDMgMy41Yy0uMTIuMjItLjA3LjQ5LjEyLjY0bDIuMTEgMS42NWMtLjA0LjMyLS4wNi42Ni0uMDYgMS4wMHMuMDIuNjguMDYgMS4wMWwtMi4xMSAxLjY1Yy0uMTkuMTUtLjI0LjQyLS4xMi42NGwyLjAzIDMuNWMuMTIuMjIuMzkuMy42MS4yMmwyLjQ5LTFjLjUyLjQgMS4wOC43MyAxLjY5Ljk4bC4zOCAyLjY1Yy4wMy4yNi4yNy40NC41NC40NGg0LjFjLjI3IDAgLjUxLS4xOC41NC0uNDRsLjM4LTIuNjVjLjYxLS4yNSAxLjE3LS41OSAxLjY5LS45OGwyLjQ5IDFjLjIzLjA5LjQ5IDAgLjYxLS4yMmwyLjAzLTMuNWMuMTItLjIyLjA3LS40OS0uMTItLjY0bC0yLjExLTEuNjV6TTEyIDE1LjVjLTEuOTMgMC0zLjUtMS41Ny0zLjUtMy41czEuNTctMy41IDMuNS0zLjUgMy41IDEuNTcgMy41IDMuNS0xLjU3IDMuNS0zLjUgMy41eiIvPjwvc3ZnPg==', gridView: '', sliderView: '' }; /** * 创建视频窗口 * @param {Object} options - 配置选项 * @returns {Object} 窗口实例 */ function create(options = {}) { const config = { ...DEFAULT_CONFIG, ...options }; const id = `video-pip-${++windowIdCounter}`; injectCustomStyles(); /** * 视频滑动状态 */ let videoSlides = []; let currentSlideIndex = 0; let isSliding = false; let touchStartY = 0; let touchStartX = 0; let currentVideo = null; /** * 自动隐藏状态 */ let autoHideTimer = null; let isUIVisible = true; const container = document.createElement('div'); container.className = 'video-pip-container'; container.innerHTML = `
`; const slider = container.querySelector('.video-pip-slider'); const actionBar = container.querySelector('.video-pip-action-bar'); const infoBar = container.querySelector('.video-pip-info-bar'); const infoTitle = container.querySelector('.video-pip-info-title'); let currentAspectRatio = config.aspectRatio; let isAdjustingSize = false; let adjustTimer = null; const initialZIndex = getNextZIndex(); let winbox = null; /** * 更新标题(包含索引) */ function updateTitle() { if (!winbox) return; const count = videoSlides.length; if (count > 1) { winbox.setTitle(`${config.title} (${currentSlideIndex + 1}/${count})`); } else { winbox.setTitle(config.title); } } /** * 显示UI元素 */ function showUI() { isUIVisible = true; // 没有视频时不显示操作栏 if (videoSlides.length > 0) { actionBar.classList.remove('hidden'); } // 底部信息栏仅在视频暂停时显示 if (!currentVideo || currentVideo.paused) { infoBar.classList.remove('hidden'); } const header = document.getElementById(id)?.querySelector('.wb-header'); if (header) { header.hidden = false; } resetAutoHideTimer(); } /** * 隐藏UI元素 */ function hideUI() { // 最小化时不隐藏标题栏 if (winbox && winbox.min) return; // 视频暂停时不隐藏 UI if (currentVideo && currentVideo.paused) return; // 没有视频时不隐藏标题栏 if (videoSlides.length === 0) return; if (isUIVisible) { isUIVisible = false; actionBar.classList.add('hidden'); infoBar.classList.add('hidden'); const header = document.getElementById(id)?.querySelector('.wb-header'); if (header) { header.hidden = true; } } } /** * 重置自动隐藏计时器 */ function resetAutoHideTimer() { if (autoHideTimer) { clearTimeout(autoHideTimer); } if (config.autoHideDelay > 0) { autoHideTimer = setTimeout(hideUI, config.autoHideDelay); } } /** * 信息面板状态 */ let infoSlideElement = null; let isInfoVisible = false; /** * 显示脚本信息面板 */ async function showInfo() { if (isInfoVisible) { hideInfo(); return; } // 先隐藏喜欢列表 if (isLikesVisible) { hideLikes(); } // 暂停当前播放的视频 if (currentVideo && !currentVideo.paused) { currentVideo.pause(); } if (!infoSlideElement) { infoSlideElement = document.createElement('div'); infoSlideElement.className = 'video-pip-info-slide'; container.appendChild(infoSlideElement); } const scriptConfig = typeof SbCLi !== 'undefined' ? await SbCLi.getScriptConfig() : {}; const userId = typeof SbCLi !== 'undefined' ? SbCLi.getUserId() : 'unknown'; const currentVersion = typeof GM_info !== 'undefined' ? GM_info.script.version : '未知'; const isLatest = currentVersion === scriptConfig.version; const formattedDate = scriptConfig.updated_at ? new Date(scriptConfig.updated_at).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }) : ''; infoSlideElement.innerHTML = `

📢 ${scriptConfig.name || '脚本公告'}

${formattedDate ? `

更新时间: ${formattedDate}

` : ''}

${scriptConfig.latest_notice || scriptConfig.description || '暂无公告'}

当前版本: ${currentVersion}

最新版本: ${scriptConfig.version || '未知'}

${!isLatest && scriptConfig.url ? `🔥 更新脚本` : ''}
${scriptConfig.applicable_sites && scriptConfig.applicable_sites.length > 0 ? `

🔗 最新网址

${scriptConfig.applicable_sites.map(site => { const isUrl = /^https?:\/\//i.test(site); if (isUrl) { return `${site}`; } return `${site}`; }).join('')}
` : ''}

👤 激活信息

加载中...

${scriptConfig.purchase_url ? ` ⚡️ 发电支持 ` : ''}
`; requestAnimationFrame(() => { infoSlideElement.classList.add('show'); // 自动调整窗口高度以适应内容(最小化、最大化、全屏时不调整) if (!winbox.min && !winbox.max && !winbox.full) { const contentHeight = infoSlideElement.querySelector('.video-pip-info-content').scrollHeight; const newHeight = Math.min(contentHeight + 60, config.maxHeight); winbox.resize(winbox.width, `${newHeight}px`); } }); isInfoVisible = true; // 加载激活信息 loadActivationInfoInSlide(userId, scriptConfig); } /** * 隐藏脚本信息面板 */ function hideInfo() { if (!isInfoVisible || !infoSlideElement) return; infoSlideElement.classList.remove('show'); isInfoVisible = false; } /** * 喜欢列表状态 */ let likesSlideElement = null; let isLikesVisible = false; let likesCurrentPage = 0; let likesPageSize = 10; let likesAllData = []; let likesIsLoading = false; let likesHasMore = true; let likesViewMode = 'grid'; // 'grid' | 'slider' let likesSliderIndex = 0; // 当前滑动索引 let likesCurrentVideo = null; // 当前播放的视频 /** * 显示喜欢列表面板 */ async function showLikes() { if (isLikesVisible) { hideLikes(); return; } // 先隐藏信息面板 if (isInfoVisible) { hideInfo(); } // 暂停当前播放的视频 if (currentVideo && !currentVideo.paused) { currentVideo.pause(); } if (!likesSlideElement) { likesSlideElement = document.createElement('div'); likesSlideElement.className = 'video-pip-likes-slide'; container.appendChild(likesSlideElement); } // 重置分页状态和视图模式 likesCurrentPage = 0; likesAllData = []; likesHasMore = true; likesIsLoading = false; likesViewMode = 'grid'; likesSliderIndex = 0; likesCurrentVideo = null; likesSlideElement.innerHTML = `
加载中...
`; requestAnimationFrame(() => { likesSlideElement.classList.add('show'); // 自动调整窗口高度以适应内容(最小化、最大化、全屏时不调整) if (!winbox.min && !winbox.max && !winbox.full) { const screenHeight = window.innerHeight; const maxHeight = Math.min(screenHeight - 40, config.maxHeight); winbox.resize(winbox.width, `${maxHeight}px`); } }); isLikesVisible = true; // 加载喜欢的视频列表 await loadMoreLikes(); // 绑定滚动事件 bindLikesScrollEvent(); } /** * 加载更多喜欢列表数据 */ async function loadMoreLikes() { if (likesIsLoading || !likesHasMore) return; likesIsLoading = true; const gridEl = likesSlideElement?.querySelector('.video-pip-likes-grid'); if (!gridEl) { likesIsLoading = false; return; } // 显示加载更多提示(确保在最后) let loadingEl = gridEl.querySelector('.video-pip-likes-loading-more'); if (!loadingEl) { loadingEl = document.createElement('div'); loadingEl.className = 'video-pip-likes-loading-more'; gridEl.appendChild(loadingEl); } loadingEl.textContent = '加载中...'; loadingEl.style.display = 'block'; loadingEl.style.cursor = 'default'; loadingEl.onclick = null; try { const offset = likesCurrentPage * likesPageSize; const newLikes = await SbCLi.loadHistory(likesPageSize, 'my_likes', offset); if (!newLikes || newLikes.length === 0) { likesHasMore = false; if (likesAllData.length === 0) { gridEl.innerHTML = `
暂无喜欢的视频
`; } else { loadingEl.textContent = '— 没有更多了 —'; loadingEl.style.display = 'block'; } } else { likesAllData = [...likesAllData, ...newLikes]; likesCurrentPage++; likesHasMore = newLikes.length >= likesPageSize; // 在加载提示之前插入新内容 loadingEl.insertAdjacentHTML('beforebegin', renderLikesItems(newLikes)); // 触发视频首帧加载 triggerFirstFrameLoad(gridEl); // 绑定点击事件 bindLikesItemClick(gridEl); if (!likesHasMore) { loadingEl.textContent = '— 没有更多了 —'; loadingEl.style.display = 'block'; } else { loadingEl.style.display = 'none'; } } } catch (error) { console.error('加载喜欢列表失败:', error); if (likesAllData.length === 0) { gridEl.innerHTML = `
加载失败,请稍后重试
`; } else { loadingEl.textContent = '加载失败,点击重试'; loadingEl.style.display = 'block'; loadingEl.style.cursor = 'pointer'; loadingEl.onclick = () => { loadingEl.onclick = null; likesIsLoading = false; loadMoreLikes(); }; } } finally { likesIsLoading = false; // 检查是否需要加载更多(内容不足以产生滚动条时自动加载) setTimeout(() => checkAndLoadMoreIfNeeded(), 100); } } /** * 渲染喜欢列表项HTML */ function renderLikesItems(likes) { const validLikes = (likes || []).filter(item => item && item.video_url); return validLikes.map((item, index) => { const globalIndex = likesAllData.length - validLikes.length + index; return `

${item.content || '无标题'}

`}).join(''); } /** * 绑定喜欢列表滚动事件 */ function bindLikesScrollEvent() { if (!likesSlideElement) return; const scrollHandler = () => { if (!isLikesVisible || likesIsLoading || !likesHasMore) return; const { scrollTop, scrollHeight, clientHeight } = likesSlideElement; // 距离底部150px时触发加载,增大触发距离以适应最大化场景 if (scrollHeight - scrollTop - clientHeight < 150) { loadMoreLikes(); } }; // 移除旧的滚动事件 likesSlideElement.removeEventListener('scroll', likesSlideElement._likesScrollHandler); // 保存新的滚动处理器引用 likesSlideElement._likesScrollHandler = scrollHandler; likesSlideElement.addEventListener('scroll', scrollHandler, { passive: true }); // 监听容器大小变化 if (!likesSlideElement._resizeObserver) { likesSlideElement._resizeObserver = new ResizeObserver(() => { // 容器大小变化时检查是否需要加载更多 checkAndLoadMoreIfNeeded(); }); } likesSlideElement._resizeObserver.observe(likesSlideElement); } /** * 检查是否需要加载更多(内容不足以产生滚动条时自动加载) */ function checkAndLoadMoreIfNeeded() { if (!likesSlideElement || !isLikesVisible || likesIsLoading || !likesHasMore) return; // 检查内容是否不足以产生滚动条 const { scrollHeight, clientHeight } = likesSlideElement; if (scrollHeight <= clientHeight + 50) { // 内容不足以填满容器,自动加载更多 loadMoreLikes(); } } /** * 触发视频首帧加载 */ function triggerFirstFrameLoad(gridEl) { const videos = gridEl.querySelectorAll('video[data-first-frame="true"]'); videos.forEach(video => { video.removeAttribute('data-first-frame'); // 尝试加载第一帧 video.currentTime = 0.1; }); } /** * 绑定喜欢列表项点击事件 */ function bindLikesItemClick(gridEl) { const items = gridEl.querySelectorAll('.video-pip-likes-item:not([data-bound])'); items.forEach(item => { item.setAttribute('data-bound', 'true'); // 绑定操作栏按钮事件 const likeBtn = item.querySelector('.video-like-btn'); const downloadBtn = item.querySelector('.video-download-btn'); const linkBtn = item.querySelector('.video-link-btn'); likeBtn?.addEventListener('click', (e) => { e.stopPropagation(); const videoUrl = decodeURIComponent(item.dataset.videoUrl || ''); const imageUrl = decodeURIComponent(item.dataset.imageUrl || ''); const content = decodeURIComponent(item.dataset.content || ''); const url = decodeURIComponent(item.dataset.url || ''); // 切换喜欢状态 const isCurrentlyLiked = likeBtn.classList.contains('liked'); const newIsLiked = !isCurrentlyLiked; // 更新按钮状态 if (newIsLiked) { likeBtn.classList.add('liked'); likeBtn.title = '取消喜欢'; likeBtn.innerHTML = ICONS.likeFilled; } else { likeBtn.classList.remove('liked'); likeBtn.title = '喜欢'; likeBtn.innerHTML = ICONS.like; } // 调用喜欢回调 if (config.onLike) { config.onLike({ video_url: videoUrl, image_url: imageUrl, content: content, url: url, isLiked: newIsLiked }); } }); downloadBtn?.addEventListener('click', (e) => { e.stopPropagation(); const videoUrl = decodeURIComponent(item.dataset.videoUrl || ''); const content = decodeURIComponent(item.dataset.content || ''); const url = decodeURIComponent(item.dataset.url || ''); if (videoUrl) { if (videoUrl.includes('.m3u8')) { const downurl = `https://tools.thatwind.com/tool/m3u8downloader#m3u8=${videoUrl}&referer=${url}&filename=${content}`; window.open(downurl, '_blank'); } else { const a = document.createElement('a'); a.href = videoUrl; a.download = videoUrl; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } } }); linkBtn?.addEventListener('click', (e) => { e.stopPropagation(); const url = decodeURIComponent(item.dataset.url || ''); if (url) { window.open(url, '_blank'); } }); // 点击卡片切换到滑动视图 item.addEventListener('click', handleLikesItemClick); }); } /** * 处理喜欢列表项点击事件 */ function handleLikesItemClick(e) { const item = e.target.closest('.video-pip-likes-item'); if (!item) return; e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); // 获取点击的视频索引 const clickIndex = parseInt(item.dataset.index || '0', 10); // 切换到滑动视图模式 switchToLikesSliderView(clickIndex); } /** * 切换到滑动视图模式 */ function switchToLikesSliderView(startIndex) { if (likesAllData.length === 0) return; likesViewMode = 'slider'; likesSliderIndex = startIndex; // 渲染滑动视图 renderLikesSliderView(); } /** * 切换回网格视图模式 */ function switchToLikesGridView() { // 暂停当前视频 if (likesCurrentVideo) { likesCurrentVideo.pause(); likesCurrentVideo = null; } likesViewMode = 'grid'; // 重新渲染网格视图 likesSlideElement.innerHTML = `
${renderLikesItems(likesAllData)}
— 没有更多了 —
`; // 绑定视图切换按钮事件 const toggleBtn = likesSlideElement.querySelector('.video-pip-likes-view-toggle'); toggleBtn?.addEventListener('click', () => { switchToLikesSliderView(0); }); // 重新绑定事件 const gridEl = likesSlideElement.querySelector('.video-pip-likes-grid'); if (gridEl) { bindLikesItemClick(gridEl); triggerFirstFrameLoad(gridEl); } // 绑定滚动事件 bindLikesScrollEvent(); } /** * 渲染滑动视图 */ function renderLikesSliderView() { const currentVideoInfo = likesAllData[likesSliderIndex]; if (!currentVideoInfo) return; likesSlideElement.innerHTML = `

${likesSliderIndex + 1}. ${currentVideoInfo.content || '无标题'}

`; // 获取视频元素 const videoEl = likesSlideElement.querySelector('.video-pip-likes-slider-item video'); likesCurrentVideo = videoEl; // 自动播放 if (config.autoplay) { videoEl.play().catch(() => {}); } // 绑定事件 bindLikesSliderEvents(); } /** * 绑定滑动视图事件 */ function bindLikesSliderEvents() { const slider = likesSlideElement.querySelector('.video-pip-likes-slider'); const actionBar = likesSlideElement.querySelector('.video-pip-likes-slider-action-bar'); let touchStartY = 0; let isSliding = false; // 视图切换按钮 actionBar?.querySelector('.view-toggle-btn')?.addEventListener('click', (e) => { e.stopPropagation(); switchToLikesGridView(); }); // 点击播放/暂停 slider?.addEventListener('click', (e) => { if (e.target.tagName === 'VIDEO' && likesCurrentVideo) { if (likesCurrentVideo.paused) { likesCurrentVideo.play().catch(() => {}); } else { likesCurrentVideo.pause(); } } }); // 触摸滑动 slider?.addEventListener('touchstart', (e) => { if (isSliding) return; touchStartY = e.touches[0].clientY; }, { passive: true }); slider?.addEventListener('touchmove', (e) => { if (isSliding) return; const deltaY = e.touches[0].clientY - touchStartY; slider.style.transform = `translateY(${deltaY}px)`; }, { passive: true }); slider?.addEventListener('touchend', (e) => { if (isSliding) return; const deltaY = e.changedTouches[0].clientY - touchStartY; const threshold = slider.offsetHeight * 0.2; if (deltaY < -threshold && likesSliderIndex < likesAllData.length - 1) { // 向上滑动,下一个 slideToNext(); } else if (deltaY > threshold && likesSliderIndex > 0) { // 向下滑动,上一个 slideToPrev(); } else { slider.style.transition = 'transform 0.2s ease'; slider.style.transform = 'translateY(0)'; setTimeout(() => { slider.style.transition = ''; }, 200); } }, { passive: true }); // 鼠标滚轮滑动 slider?.addEventListener('wheel', (e) => { if (isSliding) return; e.preventDefault(); if (e.deltaY > 0 && likesSliderIndex < likesAllData.length - 1) { // 向下滚动,下一个 slideToNext(); } else if (e.deltaY < 0 && likesSliderIndex > 0) { // 向上滚动,上一个 slideToPrev(); } }, { passive: false }); // 操作栏按钮 actionBar?.querySelector('.like-btn')?.addEventListener('click', (e) => { e.stopPropagation(); const currentVideoInfo = likesAllData[likesSliderIndex]; const btn = actionBar.querySelector('.like-btn'); const isCurrentlyLiked = btn.classList.contains('liked'); const newIsLiked = !isCurrentlyLiked; btn.classList.toggle('liked'); btn.innerHTML = newIsLiked ? ICONS.likeFilled : ICONS.like; if (config.onLike && currentVideoInfo) { config.onLike({ ...currentVideoInfo, isLiked: newIsLiked }); } }); actionBar?.querySelector('.download-btn')?.addEventListener('click', (e) => { e.stopPropagation(); const currentVideoInfo = likesAllData[likesSliderIndex]; if (currentVideoInfo?.video_url) { if (currentVideoInfo.video_url.includes('.m3u8')) { const downurl = `https://tools.thatwind.com/tool/m3u8downloader#m3u8=${currentVideoInfo.video_url}&referer=${currentVideoInfo.url}&filename=${currentVideoInfo.content}`; window.open(downurl, '_blank'); } else { const a = document.createElement('a'); a.href = currentVideoInfo.video_url; a.download = currentVideoInfo.video_url; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } } }); actionBar?.querySelector('.link-btn')?.addEventListener('click', (e) => { e.stopPropagation(); const currentVideoInfo = likesAllData[likesSliderIndex]; if (currentVideoInfo?.url) { window.open(currentVideoInfo.url, '_blank'); } }); // 滑动切换函数 function slideToNext() { if (likesSliderIndex >= likesAllData.length - 1) return; slideTo(likesSliderIndex + 1); } function slideToPrev() { if (likesSliderIndex <= 0) return; slideTo(likesSliderIndex - 1); } function slideTo(index) { if (isSliding) return; isSliding = true; // 暂停当前视频 if (likesCurrentVideo) { likesCurrentVideo.pause(); } const direction = index > likesSliderIndex ? 1 : -1; const slideHeight = slider.offsetHeight; slider.style.transition = 'transform 0.3s ease'; slider.style.transform = `translateY(${-direction * slideHeight}px)`; setTimeout(() => { likesSliderIndex = index; renderLikesSliderView(); isSliding = false; }, 300); } } /** * 清空视频滑动列表 */ function clearVideoSlides() { videoSlides.forEach(slide => { if (slide.video) { slide.video.pause(); slide.video.src = ''; } slide.element.remove(); }); videoSlides = []; currentSlideIndex = 0; currentVideo = null; } /** * 隐藏喜欢列表面板 */ function hideLikes() { if (!isLikesVisible || !likesSlideElement) return; // 暂停当前播放的视频(滑动视图) if (likesCurrentVideo) { likesCurrentVideo.pause(); likesCurrentVideo = null; } // 暂停所有视频卡片中的视频(网格视图) const allVideos = likesSlideElement.querySelectorAll('.video-pip-likes-item video'); allVideos.forEach(v => { if (!v.paused) { v.pause(); v.closest('.video-pip-likes-item')?.classList.remove('playing'); } }); likesSlideElement.classList.remove('show'); isLikesVisible = false; likesViewMode = 'grid'; } /** * 在 slide 中加载激活信息 */ async function loadActivationInfoInSlide(userId, scriptConfig) { const contentEl = document.getElementById('video-pip-activation-content'); if (!contentEl) return; try { const { success, message, data } = await SbCLi.getActivationInfo() || {}; const isActive = success; const activationCode = data?.activation_code || null; if (isActive && activationCode) { contentEl.innerHTML = `
已激活

激活码: ${activationCode}

有效期: ${data?.valid_for_days < 999 ? data.valid_for_days + '天' : '永久'}

${data?.activated_at ? `

激活时间: ${new Date(data.activated_at).toLocaleString()}

` : ''} `; } else { const trialCount = await SbCLi.getTrialCount() || 0; contentEl.innerHTML = `
${message || '未激活'} (今日试看: ${trialCount}次)

匿名ID: ${userId}

${scriptConfig.purchase_url ? `获取激活码` : ''}
`; const input = document.getElementById('video-pip-activation-input'); const submitBtn = document.getElementById('video-pip-activation-submit'); const messageEl = document.getElementById('video-pip-activation-message'); const handleActivation = async () => { const code = input.value.trim(); if (!code) { messageEl.textContent = '请输入激活码'; messageEl.className = 'video-pip-info-message error'; return; } submitBtn.disabled = true; submitBtn.textContent = '激活中...'; messageEl.textContent = ''; try { const result = await SbCLi.verifyActivation(code); if (result.success) { messageEl.textContent = result.message || '激活成功!'; messageEl.className = 'video-pip-info-message success'; setTimeout(() => loadActivationInfoInSlide(userId, scriptConfig), 1000); } else { messageEl.textContent = result.message || '激活失败'; messageEl.className = 'video-pip-info-message error'; submitBtn.disabled = false; submitBtn.textContent = '激活'; } } catch (error) { messageEl.textContent = error.message || '激活失败,请稍后重试'; messageEl.className = 'video-pip-info-message error'; submitBtn.disabled = false; submitBtn.textContent = '激活'; } }; submitBtn.addEventListener('click', handleActivation); input.addEventListener('keypress', (e) => { if (e.key === 'Enter') handleActivation(); }); } } catch (error) { contentEl.innerHTML = `

加载激活信息失败

匿名ID: ${userId}

`; } } /** * 创建操作栏按钮 */ function createActionBar() { actionBar.innerHTML = ''; const likeBtn = document.createElement('button'); likeBtn.className = 'video-pip-action-btn'; likeBtn.innerHTML = ICONS.like; likeBtn.title = '喜欢'; likeBtn.addEventListener('click', (e) => { e.stopPropagation(); showUI(); const currentSlide = videoSlides[currentSlideIndex]; if (currentSlide) { currentSlide.isLiked = !currentSlide.isLiked; likeBtn.innerHTML = currentSlide.isLiked ? ICONS.likeFilled : ICONS.like; likeBtn.classList.toggle('liked', currentSlide.isLiked); if (config.onLike) { currentSlide.likes = currentSlide.isLiked ? currentSlide.likes + 1 : currentSlide.likes - 1; const userId = typeof SbCLi !== 'undefined' ? SbCLi.getUserId() : 'unknown'; currentSlide.like_list = currentSlide.isLiked ? [...currentSlide.like_list, userId] : currentSlide.like_list.filter(id => id !== userId); config.onLike(currentSlide); } } }); actionBar.appendChild(likeBtn); const downloadBtn = document.createElement('button'); downloadBtn.className = 'video-pip-action-btn'; downloadBtn.innerHTML = ICONS.download; downloadBtn.title = '下载'; downloadBtn.addEventListener('click', (e) => { e.stopPropagation(); showUI(); const currentSlide = videoSlides[currentSlideIndex]; //m3u8视频下载工具 if (currentSlide.video_url.includes('.m3u8')) { const downurl = `https://tools.thatwind.com/tool/m3u8downloader#m3u8=${currentSlide.video_url}&referer=${currentSlide.url}&filename=${currentSlide.content}`; window.open(downurl, '_blank'); } else if (currentSlide) { const videoUrl = currentSlide.video_url; if (videoUrl) { const a = document.createElement('a'); a.href = videoUrl; a.download = videoUrl; a.target = '_blank'; document.body.appendChild(a); a.click(); document.body.removeChild(a); } } }); actionBar.appendChild(downloadBtn); const linkBtn = document.createElement('button'); linkBtn.className = 'video-pip-action-btn'; linkBtn.innerHTML = ICONS.link; linkBtn.title = '原文链接'; linkBtn.addEventListener('click', (e) => { e.stopPropagation(); showUI(); const currentSlide = videoSlides[currentSlideIndex]; if (currentSlide?.url) { window.open(currentSlide.url, '_blank'); } }); actionBar.appendChild(linkBtn); } createActionBar(); /** * 更新信息栏 */ function updateInfoBar() { const currentSlide = videoSlides[currentSlideIndex]; if (currentSlide && config.showInfoBar) { infoTitle.textContent = currentSlide.content || ''; } } /** * 更新喜欢按钮状态 */ function updateLikeButton() { const currentSlide = videoSlides[currentSlideIndex]; const likeBtn = actionBar.querySelector('.video-pip-action-btn'); if (likeBtn && currentSlide) { likeBtn.innerHTML = currentSlide.isLiked ? ICONS.likeFilled : ICONS.like; likeBtn.classList.toggle('liked', currentSlide.isLiked); } } /** * 滑动到指定视频 * @param {number} index - 目标索引 */ function slideTo(index) { if (index < 0 || index >= videoSlides.length || isSliding) return; if (index === currentSlideIndex) return; isSliding = true; const prevIndex = currentSlideIndex; currentSlideIndex = index; const prevSlide = videoSlides[prevIndex]; const nextSlide = videoSlides[currentSlideIndex]; if (prevSlide && prevSlide.video) { prevSlide.video.pause(); } const isNext = index > prevIndex; const direction = isNext ? 1 : -1; nextSlide.element.style.transform = `translateY(${direction * 100}%)`; nextSlide.element.style.zIndex = '2'; requestAnimationFrame(() => { prevSlide.element.style.transition = 'transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; prevSlide.element.style.transform = `translateY(${-direction * 100}%)`; nextSlide.element.style.transition = 'transform 0.35s cubic-bezier(0.25, 0.46, 0.45, 0.94)'; nextSlide.element.style.transform = 'translateY(0)'; setTimeout(() => { prevSlide.element.style.transition = ''; prevSlide.element.style.zIndex = '0'; nextSlide.element.style.transition = ''; nextSlide.element.style.zIndex = '1'; isSliding = false; currentVideo = nextSlide.video; if (nextSlide.video && config.autoplay) { nextSlide.video.play().catch(() => {}); } updateLikeButton(); updateInfoBar(); updateTitle(); showUI(); if (nextSlide.video && nextSlide.video.videoWidth && nextSlide.video.videoHeight) { currentAspectRatio = nextSlide.video.videoWidth / nextSlide.video.videoHeight; } }, 350); }); } /** * 滑动到上一个视频 */ function slidePrev() { slideTo(currentSlideIndex - 1); } /** * 滑动到下一个视频 */ function slideNext() { slideTo(currentSlideIndex + 1); } /** * 处理触摸开始 */ function handleTouchStart(e) { showUI(); // 显示信息面板或喜欢列表时不处理滑动 if (isInfoVisible || isLikesVisible) return; // 始终记录触摸起始位置 touchStartY = e.touches[0].clientY; touchStartX = e.touches[0].clientX; } /** * 处理触摸移动 */ function handleTouchMove(e) { // 显示信息面板或喜欢列表时不处理滑动 if (isInfoVisible || isLikesVisible) return; if (videoSlides.length <= 1 || isSliding) return; const deltaY = e.touches[0].clientY - touchStartY; const deltaX = e.touches[0].clientX - touchStartX; if (Math.abs(deltaY) > Math.abs(deltaX) && Math.abs(deltaY) > 10) { e.preventDefault(); e.stopPropagation(); } } /** * 处理触摸结束 */ function handleTouchEnd(e) { // 显示信息面板或喜欢列表时不处理滑动 if (isInfoVisible || isLikesVisible) return; if (isSliding) return; const deltaY = e.changedTouches[0].clientY - touchStartY; const deltaX = e.changedTouches[0].clientX - touchStartX; const absDeltaY = Math.abs(deltaY); const absDeltaX = Math.abs(deltaX); // 只有滑动距离足够大且是垂直滑动时才切换视频 if (videoSlides.length > 1 && absDeltaY > absDeltaX && absDeltaY > 50) { if (deltaY < 0) { slideNext(); } else { slidePrev(); } } // 点击暂停/播放由视频原生控制条处理,不再自定义处理 } /** * 处理鼠标滚轮 */ function handleWheel(e) { showUI(); // 显示信息面板或喜欢列表时不阻止滚动,允许正常滚动 if (isInfoVisible || isLikesVisible) return; e.preventDefault(); e.stopPropagation(); if (videoSlides.length <= 1 || isSliding) return; if (Math.abs(e.deltaY) > 30) { if (e.deltaY > 0) { slideNext(); } else { slidePrev(); } } } /** * 处理鼠标移动(显示UI) */ function handleMouseMove() { showUI(); } container.addEventListener('touchstart', handleTouchStart, { passive: false }); container.addEventListener('touchmove', handleTouchMove, { passive: false }); container.addEventListener('touchend', handleTouchEnd, { passive: false }); container.addEventListener('wheel', handleWheel, { passive: false }); container.addEventListener('mousemove', handleMouseMove); container.addEventListener('mouseover', handleMouseMove); /** * 为视频元素添加事件监听 */ function addVideoEvents(video) { video.addEventListener('mousemove', (e) => { e.stopPropagation(); showUI(); }); video.addEventListener('mouseover', () => showUI()); // touchstart 事件让容器处理,不要阻止冒泡 } /** * 添加视频到滑动容器 * @param {Object} videoOptions - 视频配置 * @returns {number} 视频索引 */ function addVideo(videoOptions = {}) { // 加载新视频时关闭信息页和收藏页 if (isInfoVisible) { hideInfo(); } if (isLikesVisible) { hideLikes(); } const slideElement = document.createElement('div'); slideElement.className = 'video-pip-slide'; slideElement.style.zIndex = videoSlides.length === 0 ? '1' : '0'; slideElement.style.transform = videoSlides.length === 0 ? 'translateY(0)' : 'translateY(100%)'; const video = document.createElement('video'); video.playsInline = true; video.controls = true; video.loop = videoOptions.loop !== undefined ? videoOptions.loop : config.loop; video.muted = videoOptions.muted !== undefined ? videoOptions.muted : config.muted; video.volume = videoOptions.volume !== undefined ? videoOptions.volume : config.volume; video.poster = videoOptions.image_url || ''; const videoUrl = videoOptions.video_url || ''; if (videoUrl.includes('.m3u8') && typeof Hls !== 'undefined') { const hls = new Hls({ maxBufferLength: 10, maxMaxBufferLength: 30 }); hls.loadSource(videoUrl); hls.attachMedia(video); hls.on(Hls.Events.MANIFEST_PARSED, () => { if (config.autoplay && videoSlides.length === 0) { video.play().catch(() => {}); } }); } else if (videoUrl) { video.src = videoUrl; } slideElement.appendChild(video); slider.appendChild(slideElement); addVideoEvents(video); const slideData = { element: slideElement, video: video, video_url: videoUrl, image_url: videoOptions.image_url || '', content: videoOptions.content || config.content, url: videoOptions.url || '', likes: videoOptions.likes || 0, like_list: videoOptions.like_list || [], user_id: videoOptions.user_id, isLiked: videoOptions.isLiked || false, config: { ...videoOptions } }; videoSlides.push(slideData); video.addEventListener('loadedmetadata', () => { // 加载首个视频时不调整容器大小 if (video.videoWidth && video.videoHeight) { currentAspectRatio = video.videoWidth / video.videoHeight; } if (config.autoplay && videoSlides.length === 1 && !videoUrl.includes('.m3u8')) { video.play().catch(() => {}); } }); video.addEventListener('play', () => { currentVideo = video; if (video.videoWidth && video.videoHeight) { currentAspectRatio = video.videoWidth / video.videoHeight; } // 播放时显示控制栏 showUI(); // 播放时隐藏信息栏 infoBar.classList.add('hidden'); }); video.addEventListener('pause', () => { // 暂停时不要将 currentVideo 设为 null,保持引用以便点击播放 // 暂停时显示信息栏 infoBar.classList.remove('hidden'); showUI(); // 暂停时停止自动隐藏计时器 if (autoHideTimer) { clearTimeout(autoHideTimer); autoHideTimer = null; } }); video.addEventListener('dblclick', (e) => { e.stopPropagation(); if (winbox.full) { winbox.fullscreen(false); } else { winbox.fullscreen(true); } }); if (videoSlides.length === 1) { currentVideo = video; updateInfoBar(); updateTitle(); } const newIndex = videoSlides.length - 1; if (videoSlides.length > 1) { slideTo(newIndex); } return newIndex; } /** * 根据视频宽高比调整窗口尺寸(带防抖) */ function adjustToAspectRatio(w, h) { if (isAdjustingSize || !currentAspectRatio || !winbox) return; // 最小化、最大化、全屏、显示信息面板时不调整尺寸 if (winbox.min || winbox.max || winbox.full || isInfoVisible || isLikesVisible) return; if (adjustTimer) { clearTimeout(adjustTimer); } adjustTimer = setTimeout(() => { if (!winbox) return; // 再次检查状态 if (winbox.min || winbox.max || winbox.full || isInfoVisible || isLikesVisible) return; const currentSlide = videoSlides[currentSlideIndex]; if (currentSlide?.video && currentSlide.video.videoWidth && currentSlide.video.videoHeight) { currentAspectRatio = currentSlide.video.videoWidth / currentSlide.video.videoHeight; } isAdjustingSize = true; const containerAspectRatio = w / h; let targetWidth = w; let targetHeight = h; // 计算视频在容器中实际显示的尺寸(object-fit: contain) if (containerAspectRatio > currentAspectRatio) { // 容器比视频"更扁",视频受高度限制,宽度有剩余 // 视频实际显示:宽度 = 高度 * 视频宽高比 targetWidth = h * currentAspectRatio; } else if (containerAspectRatio < currentAspectRatio) { // 容器比视频"更高",视频受宽度限制,高度有剩余 // 视频实际显示:高度 = 宽度 / 视频宽高比 targetHeight = w / currentAspectRatio; } // 只减小不增大 targetWidth = Math.min(targetWidth, w); targetHeight = Math.min(targetHeight, h); // 应用最小限制 targetWidth = Math.max(config.minWidth, targetWidth); targetHeight = Math.max(config.minHeight, targetHeight); if (Math.round(targetWidth) !== Math.round(w) || Math.round(targetHeight) !== Math.round(h)) { winbox.resize(`${Math.round(targetWidth)}px`, `${Math.round(targetHeight)}px`); // 确保窗口不超出屏幕边界 ensureWindowInBounds(); } setTimeout(() => { isAdjustingSize = false; }, 50); }, 2000); } /** * 根据视频尺寸调整窗口大小 */ function resizeToVideoSize() { // 最小化、最大化、全屏、显示信息面板、显示喜欢列表时不调整尺寸 if (!winbox || winbox.min || winbox.max || winbox.full || isInfoVisible || isLikesVisible) return; const currentSlide = videoSlides[currentSlideIndex]; if (currentSlide?.video && currentSlide.video.videoWidth && currentSlide.video.videoHeight) { currentAspectRatio = currentSlide.video.videoWidth / currentSlide.video.videoHeight; const currentWidth = winbox.width; const currentHeight = winbox.height; const containerAspectRatio = currentWidth / currentHeight; let targetWidth = currentWidth; let targetHeight = currentHeight; // 计算视频在容器中实际显示的尺寸(object-fit: contain) if (containerAspectRatio > currentAspectRatio) { // 容器比视频"更扁",视频受高度限制,宽度有剩余 // 视频实际显示:宽度 = 高度 * 视频宽高比 targetWidth = currentHeight * currentAspectRatio; } else if (containerAspectRatio < currentAspectRatio) { // 容器比视频"更高",视频受宽度限制,高度有剩余 // 视频实际显示:高度 = 宽度 / 视频宽高比 targetHeight = currentWidth / currentAspectRatio; } // 只减小不增大 targetWidth = Math.min(targetWidth, currentWidth); targetHeight = Math.min(targetHeight, currentHeight); // 应用最小限制 targetWidth = Math.max(config.minWidth, targetWidth); targetHeight = Math.max(config.minHeight, targetHeight); winbox.resize(`${Math.round(targetWidth)}px`, `${Math.round(targetHeight)}px`); } } /** * 确保窗口在屏幕边界内 */ function ensureWindowInBounds() { if (!winbox) return; const winboxEl = document.getElementById(id); if (!winboxEl) return; const rect = winboxEl.getBoundingClientRect(); const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; let newX = winbox.x; let newY = winbox.y; let needMove = false; // 检查右边界 if (rect.right > screenWidth) { newX = screenWidth - rect.width - 10; needMove = true; } // 检查下边界 if (rect.bottom > screenHeight) { newY = screenHeight - rect.height - 10; needMove = true; } // 检查左边界 if (rect.left < 0) { newX = 10; needMove = true; } // 检查上边界 if (rect.top < 0) { newY = 10; needMove = true; } if (needMove) { winbox.move(`${newX}px`, `${newY}px`); } } winbox = new WinBox({ id: id, title: config.title, icon: config.icon, class: 'video-pip-window', background: config.headerBackground, x: config.x, y: config.y, width: config.width, height: config.height, minwidth: config.minWidth, minheight: config.minHeight, maxwidth: config.maxWidth, maxheight: config.maxHeight, top: config.top, right: config.right, bottom: config.bottom, left: config.left, index: initialZIndex, mount: container, onresize: function(w, h) { adjustToAspectRatio(w, h); if (config.onResize) { config.onResize(w, h); } }, onmove: function(x, y) { if (config.onMove) { config.onMove(x, y); } }, onfocus: function() { if (winbox && winbox.dom) { const newZIndex = getNextZIndex(); winbox.dom.style.zIndex = newZIndex; } if (config.onFocus) { config.onFocus(); } }, onblur: function() { if (config.onBlur) { config.onBlur(); } }, onminimize: function() { // 最小化时暂停主播放器视频 if (currentVideo && !currentVideo.paused) { currentVideo.pause(); } // 最小化时暂停喜欢列表视频 if (isLikesVisible) { // 暂停滑动视图中的视频 if (likesCurrentVideo) { likesCurrentVideo.pause(); } // 暂停网格视图中的视频 if (likesSlideElement) { const allVideos = likesSlideElement.querySelectorAll('.video-pip-likes-item video'); allVideos.forEach(v => { if (!v.paused) { v.pause(); v.closest('.video-pip-likes-item')?.classList.remove('playing'); } }); } } }, onrestore: function() { // 恢复时自动播放(如果配置了自动播放) if (currentVideo && currentVideo.paused && config.autoplay) { currentVideo.play().catch(() => {}); } } }); // 阻止事件冒泡到页面,防止页面干扰 WinBox 的拖动和调整大小 const winboxEl = document.getElementById(id); if (winboxEl) { // 设置更高的 z-index 确保在最上层 winboxEl.style.zIndex = getNextZIndex(); // 阻止事件冒泡到页面 const events = ['mousedown', 'mousemove', 'mouseup', 'touchstart', 'touchmove', 'touchend', 'pointerdown', 'pointermove', 'pointerup']; events.forEach(eventType => { winboxEl.addEventListener(eventType, (e) => { e.stopPropagation(); }, { passive: true }); }); } if (!config.showMinButton) { winbox.removeControl('wb-min'); } if (!config.showMaxButton) { winbox.removeControl('wb-max'); } if (!config.showFullscreenButton) { winbox.removeControl('wb-full'); } if (!config.showCloseButton) { winbox.removeControl('wb-close'); } if (config.showLikeButton !== false) { winbox.addControl({ index: 99, class: 'wb-likes', image: ICONS.heart, click: function(event, win) { event.stopPropagation(); showUI(); showLikes(); } }); } if (config.showSettingsButton !== false) { winbox.addControl({ index: 100, class: 'wb-settings', image: ICONS.settings, click: function(event, win) { event.stopPropagation(); showUI(); if (config.onSettings) { config.onSettings(instance); } } }); } if (config.video_url) { addVideo({ video_url: config.video_url, image_url: config.image_url, content: config.content, url: config.url, isLiked: config.isLiked, loop: config.loop, muted: config.muted, volume: config.volume }); } resetAutoHideTimer(); // 初始化时自动打开设置页面 if (config.showInfoOnStart !== false) { setTimeout(() => showInfo(), 300); } const instance = { id, winbox, container, slider, actionBar, infoBar, getVideo: (index) => { const i = index !== undefined ? index : currentSlideIndex; return videoSlides[i]?.video || null; }, getCurrentVideo: () => currentVideo, getCurrentIndex: () => currentSlideIndex, getSlideCount: () => videoSlides.length, getSlides: () => [...videoSlides], getConfig: () => ({ ...config }), add: addVideo, slideTo, slidePrev, slideNext, play: () => currentVideo?.play(), pause: () => currentVideo?.pause(), setVolume: (vol) => { if (currentVideo) currentVideo.volume = vol; }, seek: (time) => { if (currentVideo) currentVideo.currentTime = time; }, resize: (w, h) => winbox.resize(w, h), move: (x, y) => winbox.move(x, y), minimize: (state) => winbox.minimize(state), maximize: (state) => winbox.maximize(state), fullscreen: (state) => winbox.fullscreen(state), close: () => winbox.close(), setTitle: (title) => winbox.setTitle(title), setBackground: (color) => winbox.setBackground(color), focus: () => winbox.focus(), resizeToVideoSize, getAspectRatio: () => currentAspectRatio, setAspectRatio: (ratio) => { currentAspectRatio = ratio; }, isLiked: () => videoSlides[currentSlideIndex]?.isLiked || false, setLiked: (liked, index) => { const i = index !== undefined ? index : currentSlideIndex; if (videoSlides[i]) { videoSlides[i].isLiked = liked; if (i === currentSlideIndex) { updateLikeButton(); } } }, showActionBar: (show) => { config.showActionBar = show; actionBar.style.display = show ? 'flex' : 'none'; }, showInfoBar: (show) => { config.showInfoBar = show; infoBar.style.display = show ? 'block' : 'none'; }, showUI, hideUI, showInfo, hideInfo, showLikes, hideLikes, update: (options) => { if (options.title !== undefined) { config.title = options.title; updateTitle(); } if (options.autoplay !== undefined) { config.autoplay = options.autoplay; } if (options.showActionBar !== undefined) { instance.showActionBar(options.showActionBar); } if (options.showInfoBar !== undefined) { instance.showInfoBar(options.showInfoBar); } if (options.autoHideDelay !== undefined) { config.autoHideDelay = options.autoHideDelay; } }, remove: (index) => { const i = index !== undefined ? index : currentSlideIndex; if (videoSlides.length <= 1 || i < 0 || i >= videoSlides.length) return false; const slide = videoSlides[i]; if (slide.video) { slide.video.pause(); slide.video.src = ''; } slide.element.remove(); videoSlides.splice(i, 1); if (i === currentSlideIndex) { if (currentSlideIndex >= videoSlides.length) { currentSlideIndex = videoSlides.length - 1; } if (videoSlides[currentSlideIndex]) { videoSlides[currentSlideIndex].element.style.transform = 'translateY(0)'; videoSlides[currentSlideIndex].element.style.zIndex = '1'; currentVideo = videoSlides[currentSlideIndex].video; if (currentVideo && config.autoplay) { currentVideo.play().catch(() => {}); } updateLikeButton(); updateInfoBar(); updateTitle(); } } else if (i < currentSlideIndex) { currentSlideIndex--; updateTitle(); } return true; }, clear: () => { videoSlides.forEach(slide => { if (slide.video) { slide.video.pause(); slide.video.src = ''; } slide.element.remove(); }); videoSlides = []; currentSlideIndex = 0; currentVideo = null; } }; windows.set(id, instance); return instance; } /** * 获取窗口实例 */ function get(id) { return windows.get(id); } /** * 获取所有窗口 */ function getAll() { return new Map(windows); } /** * 关闭所有窗口 */ function closeAll() { windows.forEach(instance => { instance.close(); }); } return { VERSION, create, get, getAll, closeAll }; })();