@lzwme/m3u8-dl 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/index.html CHANGED
@@ -12,8 +12,8 @@
12
12
  crossorigin="anonymous" referrerpolicy="no-referrer" />
13
13
  <script src="https://s4.zstatic.net/ajax/libs/blueimp-md5/2.19.0/js/md5.min.js" crossorigin="anonymous"
14
14
  referrerpolicy="no-referrer"></script>
15
- <script type="module" crossorigin src="/assets/main-BSWj1VKy.js"></script>
16
- <link rel="stylesheet" crossorigin href="/assets/main-T6xR17Gh.css">
15
+ <script type="module" crossorigin src="/assets/main-BWzfTVAm.js"></script>
16
+ <link rel="stylesheet" crossorigin href="/assets/main-BC3ZZLoF.css">
17
17
  </head>
18
18
  <body>
19
19
  <div id="app"></div>
@@ -4,7 +4,7 @@
4
4
  // @homepage https://m3u8-player.lzw.me/download.html
5
5
  // @supportURL https://github.com/lzwme/m3u8-dl/issues
6
6
  // @icon https://gh-proxy.org/raw.githubusercontent.com/lzwme/m3u8-dl/refs/heads/main/packages/frontend/public/logo.png
7
- // @version 1.0.1
7
+ // @version 1.0.4
8
8
  // @description 自动抓取网页中的多种媒体链接(m3u8、mp4、mkv、avi、mov、音频等),支持可配置的媒体类型,支持跳转到 m3u8-dl webui 下载
9
9
  // @author lzw
10
10
  // @updateURL https://gh-proxy.org/raw.githubusercontent.com/lzwme/m3u8-dl/refs/heads/main/client/m3u8-capture.user.js
@@ -22,9 +22,10 @@
22
22
  // @run-at document-idle
23
23
  // ==/UserScript==
24
24
 
25
- (function(){"use strict";const K="m3u8_capture_webui_url",J="m3u8_capture_exclude_urls",Q="m3u8_capture_panel_pos",Z="m3u8_capture_panel_visible",tt="m3u8_capture_media_ext_list",et="m3u8_capture_auto_start",nt="m3u8_capture_title_replace_rules",ot="m3u8_capture_auto_close_webui",bt=["m3u8","mp4","mkv","avi","mov","wmv","flv","webm","m4v","m3u","m4a","aac","flac","ape","mp3","wav","ogg","wma"],yt=["localhost:6600","/example.com/","lzw.me","doubleclick.net"];function D(){return GM_getValue(K,"http://localhost:6600").replace(/\/$/,"")}function rt(){return GM_getValue(J,"")}function vt(t){GM_setValue(J,t)}function B(){const t=GM_getValue(tt,[]);return t&&Array.isArray(t)&&t.length>0?t:[...bt]}function Et(t){if(Array.isArray(t)&&t.length>0){const e=t.map(r=>r.trim().toLowerCase()).filter(r=>r&&/^[a-z0-9]+$/i.test(r));return GM_setValue(tt,e),e}return null}function St(){return GM_getValue(Q,null)}function Ct(t){GM_setValue(Q,t)}function _t(){return GM_getValue(Z,!1)}function at(t){GM_setValue(Z,t)}function it(){return GM_getValue(et,!1)}function Mt(t){GM_setValue(et,t)}function st(){return GM_getValue(nt,"")}function Lt(t){GM_setValue(nt,t)}function lt(){return GM_getValue(ot,!1)}function Tt(t){GM_setValue(ot,t)}function q(t,e=document.head,r="css"){const o=t.length<50?GM_getResourceText(t):t;return Promise.resolve(GM_addElement(e,r==="css"?"style":"script",{type:r==="css"?"text/css":"text/javascript",textContent:o}))}function Ut(){const t=B();return new RegExp(`\\.(${t.join("|")})(\\?|$|#)`,"i")}function $(t){const e=t||window.location.href;if(e.startsWith(D()))return!0;const r=[...yt],o=rt();o.trim()&&r.push(...o.split(`
26
- `).map(n=>n.trim()).filter(n=>n));for(const n of r)try{if(e.includes(n)||n.startsWith("/")&&n.endsWith("/")&&new RegExp(n.slice(1,-1)).test(e))return!0}catch(i){console.warn("[M3U8 Capture] 排除规则格式错误:",n,i)}return!1}function ct(t){return new Promise((e,r)=>{navigator.clipboard?.writeText?navigator.clipboard.writeText(t).then(()=>e(!0)).catch(()=>{ut(t)?e(!0):r(new Error("复制失败"))}):ut(t)?e(!0):r(new Error("复制失败"))})}function ut(t){try{const e=document.createElement("textarea");e.value=t,e.style.position="fixed",e.style.opacity="0",e.style.left="-9999px",document.body.appendChild(e),e.select(),e.setSelectionRange(0,t.length);const r=document.execCommand("copy");return document.body.removeChild(e),r}catch(e){return console.error("[M3U8 Capture] fallbackCopy failed:",e),!1}}function Rt(t){try{const e=new URL(t);return`${e.protocol}//${e.host}${e.pathname}`}catch{return t}}function z(t){return"touches"in t&&t.touches.length>0?{x:t.touches[0].clientX,y:t.touches[0].clientY}:"changedTouches"in t&&t.changedTouches.length>0?{x:t.changedTouches[0].clientX,y:t.changedTouches[0].clientY}:{x:t.clientX,y:t.clientY}}function $t(t){if(!t)return t;const e=st();if(!e.trim())return t;try{const r=e.split(/,|\n/).map(n=>n.trim()).filter(n=>n);let o=t;for(const n of r)if(n)try{if(n.startsWith("/")&&n.endsWith("/")&&n.length>2){const i=n.lastIndexOf("/"),c=n.slice(1,i),l=n.slice(i+1),s=new RegExp(c,l);o=o.replace(s,"")}else o=o.replaceAll(n,"")}catch(i){console.warn("[M3U8 Capture] 标题替换规则格式错误:",n,i)}return o=o.replace(/\s+/g," ").trim(),o}catch(r){return console.warn("[M3U8 Capture] 应用标题替换规则失败:",r),t}}function C(t){if(!t||typeof t!="string"||!t.startsWith("http"))return!1;const e=t.toLowerCase();return!!(Ut().test(e)||e.includes(".m3u8"))}function At(t){if(!t||typeof t!="string")return"media";const e=t.toLowerCase(),r=B();for(const o of r)if(new RegExp(`\\.${o}(\\?|$|#)`,"i").test(e))return o;return/m3u8/i.test(e)?"m3u8":"media"}function dt(t=document){let e="";const r=["h1.title","h2.title","h1","h2"];for(const o of r){const n=t.querySelector(o);if(n?.textContent){e=n.textContent.trim();break}}if(e||(e=(t.title||"").split(/ [-|_] /)[0].trim()),!e&&window.top!==window.self)try{e=dt(window.top?.document)}catch{}return e}function kt(t){if(!t||typeof t!="string")return null;try{const e=new URL(t);for(const[r,o]of e.searchParams.entries()){if(!o)continue;const n=decodeURIComponent(o);if(C(n))return n}}catch{}return null}function It(){const t=XMLHttpRequest.prototype.open;XMLHttpRequest.prototype.open=function(r,o,n,i,c){return C(o.toString())&&this.addEventListener("load",function(){this.status>=200&&this.status<300&&v(o.toString())}),t.call(this,r,o,n??!0,i,c)};const e=window.fetch;window.fetch=function(r,o){const n=typeof r=="string"?r:r&&typeof r=="object"&&"url"in r?r.url:"";return C(n)?e.call(this,r,o).then(i=>(i.ok&&v(n),i)):e.call(this,r,o)}}function Bt(){if(typeof PerformanceObserver>"u")return;const t=new PerformanceObserver(e=>{for(const r of e.getEntries())r.name&&C(r.name)&&v(r.name)});try{t.observe({entryTypes:["resource"]})}catch{console.log("[M3U8 Capture] PerformanceObserver not supported")}}function W(){if(document.querySelectorAll("video").forEach(t=>{t.src&&C(t.src)&&v(t.src,t.getAttribute("title")||""),t.querySelectorAll("source").forEach(e=>{e.src&&C(e.src)&&v(e.src,t.getAttribute("title")||"")})}),document.querySelectorAll("a[href]").forEach(t=>{const e=t.getAttribute("href");if(e&&C(e))try{const r=new URL(e,window.location.href).href;v(r,t.textContent?.trim()||"")}catch{}}),document.body){const t=document.body.innerText||"",r=B().join("|"),o=new RegExp(`https?:\\/\\/[^\\s"'<>]+\\.(${r})(\\?[^\\s"'<>]*)?`,"gi");let n;for(;(n=o.exec(t))!==null;)v(n[0])}}async function Wt(){let t=document.body;const e=o=>{t=o},r=()=>t;unsafeWindow.SetSwalTarget=e,window.SetSwalTarget=e,unsafeWindow.GetSwalTarget=r,window.GetSwalTarget=r;try{const o=GM_getResourceText("SwalJS").replace(/document\.body/g,"GetSwalTarget()"),n=()=>q(o,document.head||document.documentElement,"script").then(()=>{const i=window.Swal||window.Sweetalert2||unsafeWindow.Swal||unsafeWindow.Sweetalert2;i&&(window.Swal=i,unsafeWindow.Swal=i)}).catch(i=>{console.error("[M3U8 Capture] Failed to add SweetAlert2 script:",i)});document.head||document.documentElement?await n():document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>n()):setTimeout(n,50)}catch(o){console.error("[M3U8 Capture] Failed to load SweetAlert2:",o)}}function Pt(t,e){q(GM_getResourceText("SwalCSS").replace(/:root *{/,`#${e.id} {`).replace(/body/g,""),t,"css"),setTimeout(()=>{const r=window.Swal;typeof window.SetSwalTarget=="function"&&r&&(window.SetSwalTarget(e),window.Swal=r.mixin({target:e}))},500)}const w=window.top&&window.top!==window.self;let M=null,b=null,u=null,a=null,x=null,A=_t(),P=!1;const G={x:0,y:0};let L=!1;const V={x:0,y:0};let F={x:0,y:0},T=!1,U=null;const y={x:0,y:0};let h;function Gt(t){h=t}function pt(){if(M)return b;M=document.createElement("div"),M.id="m3u8-capture-shadow-host",M.style.cssText="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 9999;",document.body.appendChild(M),b=M.attachShadow({mode:"open"}),q("TailwindCSS",b,"css");const t=document.createElement("div");t.id="m3u8-capture-swal-container",b.appendChild(t);const e=document.createElement("style");return e.textContent=[":host { all: initial; font-family: system-ui, -apple-system, sans-serif; }","* { box-sizing: border-box; }",".hidden { display: none !important; }",`#${t.id},`,`#${t.id} * { pointer-events: auto !important; }`].join(`
27
- `),b.appendChild(e),Wt().then(()=>{Pt(b,t)}),b}function ft(){if(a||w)return;const t=pt();if(!t)return;a=document.createElement("div"),a.id="m3u8-capture-toggle-btn",a.style.cssText="position: fixed; bottom: 30vh; right: 20px; width: 50px; height: 50px; pointer-events: auto; z-index: 99998; will-change: transform;",a.className=`fixed bottom-10 right-5 w-[50px] h-[50px] bg-blue-500 rounded-full flex items-center justify-center cursor-move shadow-lg text-2xl transition-all duration-200 hover:scale-110 hover:shadow-xl select-none touch-none ${A?"hidden":"flex"}`;const e=document.createElement("span");e.textContent="🎬",a.appendChild(e),x=document.createElement("span"),x.id="m3u8-capture-toggle-badge",x.style.cssText="position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; background: #ef4444; color: white; border-radius: 9px; font-size: 11px; font-weight: bold; display: flex; align-items: center; justify-content: center; padding: 0 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); line-height: 1;",x.textContent="0",x.classList.add("hidden"),a.appendChild(x);const r=o=>{if(!a)return;L=!0,T=!1;const n=z(o);F={x:n.x,y:n.y};const i=a.getBoundingClientRect();V.x=n.x-i.left,V.y=n.y-i.top,y.x=i.left,y.y=i.top;const c=window.getComputedStyle(a);c.transform&&c.transform!=="none"&&(a.style.transform="none",a.style.left=`${i.left}px`,a.style.top=`${i.top}px`,a.style.right="auto",a.style.bottom="auto"),a.style.cursor="move",a.style.transition="none",o.preventDefault(),o.stopPropagation()};a.addEventListener("mousedown",r),a.addEventListener("touchstart",r,{passive:!1}),a.addEventListener("click",()=>{T||mt()}),a.addEventListener("touchend",o=>{L&&!T&&(o.preventDefault(),o.stopPropagation(),mt())},{passive:!1}),t.appendChild(a),Y()}function Y(){if(!x||w||!h)return;const t=h.size;t>0?(x.textContent=t>99?"99+":t.toString(),x.classList.remove("hidden")):x.classList.add("hidden")}function mt(){w||!gt()||(A=!0,at(!0),u&&(u.style.display="flex"),a&&a.classList.add("hidden"))}function H(){w||(A=!1,at(!1),u&&(u.style.display="none"),a?a.classList.remove("hidden"):ft(),Y())}function xt(){const t=window.Swal;t&&t.fire({title:"确认清空",text:"确定要清空所有媒体链接吗?",icon:"warning",showCancelButton:!0,confirmButtonText:"确定",cancelButtonText:"取消",confirmButtonColor:"#3b82f6"}).then(e=>{e.isConfirmed&&h&&(h.clear(),X())})}function gt(){if(!document.body||w)return null;if(u)return u;const t=pt();if(!t)return null;const e=document.createElement("div");e.id="m3u8-capture-panel";const r=St(),o=r&&window.innerWidth>768?{left:`${Math.max(0,Math.min(r.x,window.innerWidth-450))}px`,top:`${Math.max(0,Math.min(r.y,window.innerHeight-350))}px`,right:"auto"}:{right:"20px",top:"20px"};e.className="fixed w-[420px] max-w-[90vw] max-h-[85vh] bg-white border-2 border-blue-500 rounded-xl shadow-2xl font-sans flex flex-col",e.style.cssText=`
25
+ (function(){"use strict";const X="m3u8_capture_webui_url",N="m3u8_capture_exclude_urls",K="m3u8_capture_panel_pos",J="m3u8_capture_panel_visible",Q="m3u8_capture_media_ext_list",Z="m3u8_capture_auto_start",ee="m3u8_capture_title_replace_rules",te="m3u8_capture_auto_close_webui",ne="m3u8_capture_capture_headers",_e=["m3u8","mp4","mkv","avi","mov","wmv","flv","webm","m4v","m3u","m4a","aac","flac","ape","mp3","wav","ogg","wma"],Me=["localhost:6600","/example.com/","lzw.me","doubleclick.net"];function H(){return GM_getValue(X,"http://localhost:6600").replace(/\/$/,"")}function oe(){return GM_getValue(N,"")}function Le(e){GM_setValue(N,e)}function B(){const e=GM_getValue(Q,[]);return e&&Array.isArray(e)&&e.length>0?e:[..._e]}function Te(e){if(Array.isArray(e)&&e.length>0){const t=e.map(r=>r.trim().toLowerCase()).filter(r=>r&&/^[a-z0-9]+$/i.test(r));return GM_setValue(Q,t),t}return null}function Re(){return GM_getValue(K,null)}function Ue(e){GM_setValue(K,e)}function $e(){return GM_getValue(J,!1)}function re(e){GM_setValue(J,e)}function ae(){return GM_getValue(Z,!1)}function Ae(e){GM_setValue(Z,e)}function se(){return GM_getValue(ee,"")}function ke(e){GM_setValue(ee,e)}function ie(){return GM_getValue(te,!1)}function Ie(e){GM_setValue(te,e)}function le(){return GM_getValue(ne,!1)}function Be(e){GM_setValue(ne,e)}const w=window.top&&window.top!==window.self;function V(e,t=document.head,r="css"){const o=e.length<50?GM_getResourceText(e):e;return Promise.resolve(GM_addElement(t,r==="css"?"style":"script",{type:r==="css"?"text/css":"text/javascript",textContent:o}))}function We(){const e=B();return new RegExp(`\\.(${e.join("|")})(\\?|$|#)`,"i")}function $(e){const t=e||window.location.href;if(t.startsWith(H()))return!0;const r=[...Me],o=oe();o.trim()&&r.push(...o.split(`
26
+ `).map(n=>n.trim()).filter(n=>n));for(const n of r)try{if(t.includes(n)||n.startsWith("/")&&n.endsWith("/")&&new RegExp(n.slice(1,-1)).test(t))return!0}catch(a){console.warn("[M3U8 Capture] 排除规则格式错误:",n,a)}return!1}function ce(e){return new Promise((t,r)=>{navigator.clipboard?.writeText?navigator.clipboard.writeText(e).then(()=>t(!0)).catch(()=>{ue(e)?t(!0):r(new Error("复制失败"))}):ue(e)?t(!0):r(new Error("复制失败"))})}function ue(e){try{const t=document.createElement("textarea");t.value=e,t.style.position="fixed",t.style.opacity="0",t.style.left="-9999px",document.body.appendChild(t),t.select(),t.setSelectionRange(0,e.length);const r=document.execCommand("copy");return document.body.removeChild(t),r}catch(t){return console.error("[M3U8 Capture] fallbackCopy failed:",t),!1}}function Pe(e){try{const t=new URL(e);return`${t.protocol}//${t.host}${t.pathname}`}catch{return e}}function D(e){return"touches"in e&&e.touches.length>0?{x:e.touches[0].clientX,y:e.touches[0].clientY}:"changedTouches"in e&&e.changedTouches.length>0?{x:e.changedTouches[0].clientX,y:e.changedTouches[0].clientY}:{x:e.clientX,y:e.clientY}}function de(e){if(!e||typeof e!="string"||e==="NaN"||e==="undefined")return"";const t=se();if(!t.trim())return e;try{const r=t.split(/,|\n/).map(n=>n.trim()).filter(n=>n);let o=e;for(const n of r)if(n)try{if(n.startsWith("/")&&n.endsWith("/")&&n.length>2){const a=n.lastIndexOf("/"),l=n.slice(1,a),c=n.slice(a+1),i=new RegExp(l,c);o=o.replace(i,"")}else o=o.replaceAll(n,"")}catch(a){console.warn("[M3U8 Capture] 标题替换规则格式错误:",n,a)}return o=o.replace(/\s+/g," ").trim(),o}catch(r){return console.warn("[M3U8 Capture] 应用标题替换规则失败:",r),e}}function Ge(e){return e==null?!0:Array.isArray(e)?e.length===0:Object.keys(e).length===0}function fe(e,t=!1){if(!e||Ge(e))return"";const r=Object.entries(e).map(([o,n])=>`${o}: ${n}`).join(`
27
+ `);return t?encodeURIComponent(r):r}async function Oe(){let e=document.body;const t=o=>{e=o},r=()=>e;unsafeWindow.SetSwalTarget=t,window.SetSwalTarget=t,unsafeWindow.GetSwalTarget=r,window.GetSwalTarget=r;try{const o=GM_getResourceText("SwalJS").replace(/document\.body/g,"GetSwalTarget()"),n=()=>V(o,document.head||document.documentElement,"script").then(()=>{const a=window.Swal||window.Sweetalert2||unsafeWindow.Swal||unsafeWindow.Sweetalert2;a&&(window.Swal=a,unsafeWindow.Swal=a)}).catch(a=>{console.error("[M3U8 Capture] Failed to add SweetAlert2 script:",a)});document.head||document.documentElement?await n():document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>n()):setTimeout(n,50)}catch(o){console.error("[M3U8 Capture] Failed to load SweetAlert2:",o)}}function He(e,t){V(GM_getResourceText("SwalCSS").replace(/(\d+)rem/g,"$1em").replace(/:root *{/,`#${t.id} {`).replace(/body/g,""),e,"css"),setTimeout(()=>{const r=window.Swal;typeof window.SetSwalTarget=="function"&&r&&(window.SetSwalTarget(t),window.Swal=r.mixin({target:t}))},500)}function Ve(e){V(GM_getResourceText("TailwindCSS").replace(/(\d+)rem/g,"$1em"),e,"css")}let _=null,v=null,f=null,s=null,b=null,A=$e(),W=!1;const P={x:0,y:0};let M=!1;const G={x:0,y:0};let q={x:0,y:0},L=!1,T=null;const E={x:0,y:0};function pe(){if(_)return v;_=document.createElement("div"),_.id="m3u8-capture-shadow-host",_.style.cssText="position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 9999999;",document.body.appendChild(_),v=_.attachShadow({mode:"open"});const e=document.createElement("div");e.id="m3u8-capture-swal-container",v.appendChild(e);const t=document.createElement("style");return t.textContent=[":host { all: initial; font-family: system-ui, -apple-system, sans-serif; font-size: 14px }","* { box-sizing: border-box; }",".hidden { display: none !important; }",`#${e.id},`,`#${e.id} * { pointer-events: auto !important; }`].join(`
28
+ `),v.appendChild(t),He(v,e),Ve(v),Oe(),v}function me(){if(s||w)return;const e=pe();if(!e)return;s=document.createElement("div"),s.id="m3u8-capture-toggle-btn",s.style.cssText="position: fixed; bottom: 30vh; right: 20px; width: 50px; height: 50px; pointer-events: auto; z-index: 99998; will-change: transform;",s.className=`fixed bottom-10 right-5 w-[50px] h-[50px] bg-blue-500 rounded-full flex items-center justify-center cursor-move shadow-lg text-2xl transition-all duration-200 hover:scale-110 hover:shadow-xl select-none touch-none ${A?"hidden":"flex"}`;const t=document.createElement("span");t.textContent="🎬",s.appendChild(t),b=document.createElement("span"),b.id="m3u8-capture-toggle-badge",b.style.cssText="position: absolute; top: -4px; right: -4px; min-width: 18px; height: 18px; background: #ef4444; color: white; border-radius: 9px; font-size: 11px; font-weight: bold; display: flex; align-items: center; justify-content: center; padding: 0 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.2); line-height: 1;",b.textContent="0",b.classList.add("hidden"),s.appendChild(b);const r=o=>{if(!s)return;M=!0,L=!1;const n=D(o);q={x:n.x,y:n.y};const a=s.getBoundingClientRect();G.x=n.x-a.left,G.y=n.y-a.top,E.x=a.left,E.y=a.top;const l=window.getComputedStyle(s);l.transform&&l.transform!=="none"&&(s.style.transform="none",s.style.left=`${a.left}px`,s.style.top=`${a.top}px`,s.style.right="auto",s.style.bottom="auto"),s.style.cursor="move",s.style.transition="none",o.preventDefault(),o.stopPropagation()};s.addEventListener("mousedown",r),s.addEventListener("touchstart",r,{passive:!1}),s.addEventListener("click",()=>{L||he()}),s.addEventListener("touchend",o=>{M&&!L&&(o.preventDefault(),o.stopPropagation(),he())},{passive:!1}),e.appendChild(s),z()}function z(){if(!b||w||!g)return;const e=g.size;e>0?(b.textContent=e>99?"99+":e.toString(),b.classList.remove("hidden")):b.classList.add("hidden")}function he(){w||!xe()||(A=!0,re(!0),f&&(f.style.display="flex"),s&&s.classList.add("hidden"))}function j(){w||(A=!1,re(!1),f&&(f.style.display="none"),s?s.classList.remove("hidden"):me(),z())}function ge(){const e=window.Swal;e&&e.fire({title:"确认清空",text:"确定要清空所有媒体链接吗?",icon:"warning",showCancelButton:!0,confirmButtonText:"确定",cancelButtonText:"取消",confirmButtonColor:"#3b82f6"}).then(t=>{t.isConfirmed&&g&&(g.clear(),Y())})}function xe(){if(!document.body||w)return null;if(f)return f;const e=pe();if(!e)return null;const t=document.createElement("div");t.id="m3u8-capture-panel";const r=Re(),o=r&&window.innerWidth>768?{left:`${Math.max(0,Math.min(r.x,window.innerWidth-450))}px`,top:`${Math.max(0,Math.min(r.y,window.innerHeight-350))}px`,right:"auto"}:{right:"20px",top:"30vh"};t.className="fixed w-[420px] max-w-[90vw] max-h-[85vh] bg-white border-2 border-blue-500 rounded-xl shadow-2xl font-sans flex flex-col",t.style.cssText=`
28
29
  position: fixed;
29
30
  width: 420px;
30
31
  max-width: 90vw;
@@ -35,7 +36,7 @@
35
36
  ${o.top?`top: ${o.top};`:""}
36
37
  ${`right: ${o.right};`}
37
38
  display: ${A?"flex":"none"};
38
- `,e.innerHTML=`
39
+ `,t.innerHTML=`
39
40
  <div id="m3u8-capture-header" class="bg-gradient-to-br from-blue-500 to-blue-600 text-white px-4 py-3.5 rounded-t-lg flex justify-between items-center cursor-move select-none touch-none">
40
41
  <div class="font-semibold text-[15px] flex items-center gap-2">
41
42
  <span>🎬</span>
@@ -56,10 +57,10 @@
56
57
  <div class="text-xs text-gray-300 mt-2">浏览网页时会自动抓取</div>
57
58
  </div>
58
59
  </div>
59
- `,t.appendChild(e),u=e;const n=e.querySelector("#m3u8-capture-header");if(!n)return u;const i=d=>{const p=d.target;if(p.tagName==="BUTTON"||p.closest("button"))return;P=!0;const E=z(d),S=e.getBoundingClientRect();G.x=E.x-S.left,G.y=E.y-S.top,e.style.cursor="move",d.preventDefault(),d.stopPropagation()};n.addEventListener("mousedown",i),n.addEventListener("touchstart",i,{passive:!1});const c=d=>{const p=z(d);if(P&&u){d.preventDefault();const E=p.x-G.x,S=p.y-G.y,k=window.innerWidth-e.offsetWidth,I=window.innerHeight-e.offsetHeight,R=Math.max(0,Math.min(E,k)),_=Math.max(0,Math.min(S,I));e.style.left=`${R}px`,e.style.top=`${_}px`,e.style.right="auto",Ct({x:R,y:_})}if(L&&a){d.preventDefault();const E=p.x-V.x,S=p.y-V.y,k=window.innerWidth-a.offsetWidth,I=window.innerHeight-a.offsetHeight,R=Math.max(0,Math.min(E,k)),_=Math.max(0,Math.min(S,I));y.x=R,y.y=_,U||(U=requestAnimationFrame(()=>{a&&L&&(a.style.transform=`translate(${y.x}px, ${y.y}px)`,a.style.left="0",a.style.top="0",a.style.right="auto",a.style.bottom="auto"),U=null})),Math.sqrt((p.x-F.x)**2+(p.y-F.y)**2)>5&&(T=!0)}},l=d=>{P&&(P=!1,u&&(u.style.cursor="default"),d.preventDefault()),L&&(L=!1,U&&(cancelAnimationFrame(U),U=null),a&&(a.style.cursor="move",a.style.transition="",T&&(a.style.transform=`translate(${y.x}px, ${y.y}px)`,a.style.left="0",a.style.top="0",a.style.right="auto",a.style.bottom="auto")),T&&d.preventDefault())};document.addEventListener("mousemove",c),document.addEventListener("mouseup",l),document.addEventListener("touchmove",c,{passive:!1}),document.addEventListener("touchend",l,{passive:!1}),document.addEventListener("touchcancel",l,{passive:!1});const s=d=>p=>{p.preventDefault(),p.stopPropagation(),d()},m=e.querySelector("#m3u8-capture-toggle");m&&(m.addEventListener("click",s(()=>H())),m.addEventListener("touchend",s(()=>H()),{passive:!1}));const f=e.querySelector("#m3u8-capture-clear");f&&(f.addEventListener("click",s(()=>xt())),f.addEventListener("touchend",s(()=>xt()),{passive:!1}));const g=e.querySelector("#m3u8-capture-settings");return g&&(g.addEventListener("click",s(()=>wt())),g.addEventListener("touchend",s(()=>wt()),{passive:!1})),!A&&!a&&ft(),u}function wt(t=b){const e=window.Swal;if(!e)return;const r=rt(),o=B(),n=it(),i=st(),c=lt();e.fire({title:"设置",html:`
60
+ `,e.appendChild(t),f=t;const n=t.querySelector("#m3u8-capture-header");if(!n)return f;const a=d=>{const m=d.target;if(m.tagName==="BUTTON"||m.closest("button"))return;W=!0;const x=D(d),y=t.getBoundingClientRect();P.x=x.x-y.left,P.y=x.y-y.top,t.style.cursor="move",d.preventDefault(),d.stopPropagation()};n.addEventListener("mousedown",a),n.addEventListener("touchstart",a,{passive:!1});const l=d=>{const m=D(d);if(W&&f){d.preventDefault();const x=m.x-P.x,y=m.y-P.y,k=window.innerWidth-t.offsetWidth,I=window.innerHeight-t.offsetHeight,R=Math.max(0,Math.min(x,k)),U=Math.max(0,Math.min(y,I));t.style.left=`${R}px`,t.style.top=`${U}px`,t.style.right="auto",Ue({x:R,y:U})}if(M&&s){d.preventDefault();const x=m.x-G.x,y=m.y-G.y,k=window.innerWidth-s.offsetWidth,I=window.innerHeight-s.offsetHeight,R=Math.max(0,Math.min(x,k)),U=Math.max(0,Math.min(y,I));E.x=R,E.y=U,T||(T=requestAnimationFrame(()=>{s&&M&&(s.style.transform=`translate(${E.x}px, ${E.y}px)`,s.style.left="0",s.style.top="0",s.style.right="auto",s.style.bottom="auto"),T=null})),Math.sqrt((m.x-q.x)**2+(m.y-q.y)**2)>5&&(L=!0)}},c=d=>{W&&(W=!1,f&&(f.style.cursor="default"),d.preventDefault()),M&&(M=!1,T&&(cancelAnimationFrame(T),T=null),s&&(s.style.cursor="move",s.style.transition="",L&&(s.style.transform=`translate(${E.x}px, ${E.y}px)`,s.style.left="0",s.style.top="0",s.style.right="auto",s.style.bottom="auto")),L&&d.preventDefault())};document.addEventListener("mousemove",l),document.addEventListener("mouseup",c),document.addEventListener("touchmove",l,{passive:!1}),document.addEventListener("touchend",c,{passive:!1}),document.addEventListener("touchcancel",c,{passive:!1});const i=d=>m=>{m.preventDefault(),m.stopPropagation(),d()},u=t.querySelector("#m3u8-capture-toggle");u&&(u.addEventListener("click",i(()=>j())),u.addEventListener("touchend",i(()=>j()),{passive:!1}));const p=t.querySelector("#m3u8-capture-clear");p&&(p.addEventListener("click",i(()=>ge())),p.addEventListener("touchend",i(()=>ge()),{passive:!1}));const h=t.querySelector("#m3u8-capture-settings");return h&&(h.addEventListener("click",i(()=>we())),h.addEventListener("touchend",i(()=>we()),{passive:!1})),!A&&!s&&me(),f}function we(e=v){const t=window.Swal;if(!t)return;const r=oe(),o=B(),n=ae(),a=se(),l=ie(),c=le();t.fire({title:"设置",html:`
60
61
  <div class="text-left">
61
62
  <label class="block text-sm font-medium text-gray-700 mb-1">WebUI 地址</label>
62
- <input id="swal-webui-url" type="text" value="${D()}"
63
+ <input id="swal-webui-url" type="text" value="${H()}"
63
64
  class="w-full p-2.5 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
64
65
  placeholder="http://localhost:6600">
65
66
  <label class="block text-sm font-medium text-gray-700 mb-1">媒体扩展名(每行一个,用逗号或换行分隔)</label>
@@ -75,7 +76,7 @@
75
76
  <label class="block text-sm font-medium text-gray-700 mb-1">标题内容替换规则(支持正则,多个规则以逗号分割)</label>
76
77
  <textarea id="swal-title-replace-rules" rows="2"
77
78
  class="w-full p-2.5 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
78
- placeholder="例如:/\\[.*?\\]//g, /第.*?话//g">${i}</textarea>
79
+ placeholder="例如:/\\[.*?\\]//g, /第.*?话//g">${a}</textarea>
79
80
  <p class="text-xs text-gray-500 mb-4">在获取标题后,将根据这些规则进行替换。支持正则表达式,格式:/pattern/flags 或 plain-text</p>
80
81
  <div class="flex items-center mb-4">
81
82
  <input id="swal-auto-start" type="checkbox" ${n?"checked":""}
@@ -84,24 +85,33 @@
84
85
  </div>
85
86
  <p class="text-xs text-gray-500 mb-4">启用后,跳转到下载页面时将自动触发开始下载(延迟1秒)</p>
86
87
  <div class="flex items-center mb-4">
87
- <input id="swal-auto-close-webui" type="checkbox" ${c?"checked":""}
88
+ <input id="swal-auto-close-webui" type="checkbox" ${l?"checked":""}
88
89
  class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
89
90
  <label for="swal-auto-close-webui" class="ml-2 text-sm font-medium text-gray-700">开始下载后自动关闭WebUI页面</label>
90
91
  </div>
91
- <p class="text-xs text-gray-500">仅当"自动开始下载"开启时有效,开始下载后将自动关闭打开的WebUI页面</p>
92
+ <p class="text-xs text-gray-500 mb-4">仅当"自动开始下载"开启时有效,开始下载后将自动关闭打开的WebUI页面</p>
93
+ <div class="flex items-center mb-4">
94
+ <input id="swal-capture-headers" type="checkbox" ${c?"checked":""}
95
+ class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
96
+ <label for="swal-capture-headers" class="ml-2 text-sm font-medium text-gray-700">携带请求 header</label>
97
+ </div>
98
+ <p class="text-xs text-gray-500">启用后,跳转下载时会携带请求 header。在需要登录、防盗链等场景下,可以提高成功率</p>
92
99
  </div>
93
- `,showCancelButton:!0,confirmButtonText:"保存",cancelButtonText:"取消",confirmButtonColor:"#3b82f6",width:"600px",preConfirm:()=>{if(!t)return!1;const l=t.getElementById("swal-webui-url"),s=t.getElementById("swal-exclude-urls"),m=t.getElementById("swal-media-ext-list"),f=t.getElementById("swal-title-replace-rules"),g=t.getElementById("swal-auto-start"),d=t.getElementById("swal-auto-close-webui"),p=l?l.value.trim():"",E=s?s.value.trim():"",S=m?m.value.trim():"",k=f?f.value.trim():"",I=g?g.checked:!1,R=d?d.checked:!1,_=window.Swal;if(!p)return _.showValidationMessage("WebUI 地址不能为空"),!1;const N=S.split(/[,\n\s]+/).map(j=>j.trim()).filter(j=>j);return N.length===0?(_.showValidationMessage("媒体扩展名列表不能为空"),!1):{url:p,excludeUrls:E,mediaExtList:N,titleReplaceRules:k,autoStart:I,autoCloseWebui:R}}}).then(l=>{if(l.isConfirmed&&l.value){const s=l.value;GM_setValue(K,s.url),vt(s.excludeUrls),Mt(s.autoStart),Tt(s.autoCloseWebui),Lt(s.titleReplaceRules);const m=Et(s.mediaExtList),f=window.Swal;if(m&&f){let g=`已保存 ${m.length} 个媒体扩展名类型`;if(s.titleReplaceRules){const d=s.titleReplaceRules.split(",").filter(p=>p.trim()).length;g+=`<br>已设置 ${d} 个标题替换规则`}s.autoCloseWebui&&(g+="<br>已启用自动关闭WebUI页面"),f.fire({icon:"success",title:"设置已保存",html:g,timer:2500,showConfirmButton:!1})}else f&&f.fire({icon:"error",title:"保存失败",text:"媒体扩展名列表格式错误",timer:2e3,showConfirmButton:!1})}})}function ht(t){const e=window.top&&window.top!==window;try{const o=window.open(t,"_blank");if(o&&!o.closed)return!0}catch(o){console.log("[M3U8 Capture] window.open failed:",o)}if(e&&window.top){try{const o=window.top.open(t,"_blank");if(o&&!o.closed)return!0}catch(o){console.log("[M3U8 Capture] window.top.open failed:",o)}try{return window.top.location.href=t,!0}catch(o){console.log("[M3U8 Capture] window.top.location.href failed:",o)}}const r=()=>{const o=window.Swal;o&&o.fire({icon:"info",title:"链接已复制",html:`由于 iframe 限制,链接已复制到剪贴板<br><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${t}</code><br><br>请手动打开`,timer:4e3,showConfirmButton:!0,confirmButtonText:"确定"})};return ct(t).then(()=>r()).catch(()=>{const o=window.Swal;o&&o.fire({icon:"warning",title:"无法复制链接",html:`由于 iframe 限制,请手动复制并打开:<br><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${t}</code>`,confirmButtonText:"确定"})}),!1}function X(){if(w||!h||!gt())return;Y();const t=u?.querySelector("#m3u8-capture-list"),e=u?.querySelector("#m3u8-capture-empty"),r=u?.querySelector("#m3u8-capture-count");if(!t||!e||!r)return;if(r.textContent=h.size.toString(),h.size===0){t.classList.add("hidden"),e.classList.remove("hidden");return}t.classList.remove("hidden"),e.classList.add("hidden"),t.innerHTML="",Array.from(h.values()).sort((n,i)=>i.timestamp-n.timestamp).forEach(n=>{const i=document.createElement("div");i.className="border border-gray-200 rounded-lg p-3 bg-white transition-all duration-200 shadow-sm hover:bg-gray-50 hover:shadow-md";const c=n.title||"",l=n.type.toUpperCase();let s="bg-gray-500";l==="M3U8"||l==="M3U"?s="bg-blue-500":["MP4","MKV","AVI","MOV","WMV","FLV","WEBM","M4V","TS"].includes(l)?s="bg-green-500":["MP3","M4A","AAC","FLAC","APE","WAV","OGG","WMA"].includes(l)&&(s="bg-purple-500"),i.innerHTML=`
100
+ `,showCancelButton:!0,confirmButtonText:"保存",cancelButtonText:"取消",confirmButtonColor:"#3b82f6",width:"600px",preConfirm:()=>{if(!e)return!1;const i=e.getElementById("swal-webui-url"),u=e.getElementById("swal-exclude-urls"),p=e.getElementById("swal-media-ext-list"),h=e.getElementById("swal-title-replace-rules"),d=e.getElementById("swal-auto-start"),m=e.getElementById("swal-auto-close-webui"),x=e.getElementById("swal-capture-headers"),y=i?i.value.trim():"",k=u?u.value.trim():"",I=p?p.value.trim():"",R=h?h.value.trim():"",U=d?d.checked:!1,Ee=m?m.checked:!1,Fe=x?x.checked:!1,Se=window.Swal;if(!y)return Se.showValidationMessage("WebUI 地址不能为空"),!1;const Ce=I.split(/[,\n\s]+/).map(F=>F.trim()).filter(F=>F);return Ce.length===0?(Se.showValidationMessage("媒体扩展名列表不能为空"),!1):{url:y,excludeUrls:k,mediaExtList:Ce,titleReplaceRules:R,autoStart:U,autoCloseWebui:Ee,captureHeaders:Fe}}}).then(i=>{if(i.isConfirmed&&i.value){const u=i.value;GM_setValue(X,u.url),Le(u.excludeUrls),Ae(u.autoStart),Ie(u.autoCloseWebui),ke(u.titleReplaceRules),Be(u.captureHeaders);const p=Te(u.mediaExtList),h=window.Swal;if(p&&h){let d=`已保存 ${p.length} 个媒体扩展名类型`;if(u.titleReplaceRules){const m=u.titleReplaceRules.split(",").filter(x=>x.trim()).length;d+=`<br>已设置 ${m} 个标题替换规则`}u.autoCloseWebui&&(d+="<br>已启用自动关闭WebUI页面"),h.fire({icon:"success",title:"设置已保存",html:d,timer:2500,showConfirmButton:!1})}else h&&h.fire({icon:"error",title:"保存失败",text:"媒体扩展名列表格式错误",timer:2e3,showConfirmButton:!1})}})}function be(e){const t=window.top&&window.top!==window;try{const o=window.open(e,"_blank");if(o&&!o.closed)return!0}catch(o){console.warn("[M3U8 Capture] window.open failed:",o)}if(t&&window.top){try{const o=window.top.open(e,"_blank");if(o&&!o.closed)return!0}catch(o){console.warn("[M3U8 Capture] window.top.open failed:",o)}try{return window.top.location.href=e,!0}catch(o){console.warn("[M3U8 Capture] window.top.location.href failed:",o)}}const r=()=>{const o=window.Swal;o&&o.fire({icon:"info",title:"链接已复制",html:`由于 iframe 限制,链接已复制到剪贴板<br><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${e}</code><br><br>请手动打开`,timer:4e3,showConfirmButton:!0,confirmButtonText:"确定"})};return ce(e).then(()=>r()).catch(()=>{const o=window.Swal;o&&o.fire({icon:"warning",title:"无法复制链接",html:`由于 iframe 限制,请手动复制并打开:<br><br><code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${e}</code>`,confirmButtonText:"确定"})}),!1}function Y(){if(w||!g||!xe()||!f)return;z();const e=f.querySelector("#m3u8-capture-list"),t=f.querySelector("#m3u8-capture-empty"),r=f.querySelector("#m3u8-capture-count");if(!e||!t||!r)return;if(r.textContent=g.size.toString(),g.size===0){e.classList.add("hidden"),t.classList.remove("hidden");return}e.classList.remove("hidden"),t.classList.add("hidden"),e.innerHTML="",Array.from(g.values()).sort((n,a)=>a.timestamp-n.timestamp).forEach(n=>{const a=document.createElement("div");a.className="border border-gray-200 rounded-lg p-3 bg-white transition-all duration-200 shadow-sm hover:bg-gray-50 hover:shadow-md";const l=n.title||"",c=n.type.toUpperCase();let i="bg-gray-500";c==="M3U8"||c==="M3U"?i="bg-blue-500":["MP4","MKV","AVI","MOV","WMV","FLV","WEBM","M4V","TS"].includes(c)?i="bg-green-500":["MP3","M4A","AAC","FLAC","APE","WAV","OGG","WMA"].includes(c)&&(i="bg-purple-500"),a.innerHTML=`
94
101
  <div class="flex justify-between items-start gap-2 mb-2">
95
102
  <div class="flex-1 min-w-0">
96
103
  <div class="flex items-center gap-1.5 mb-1.5">
97
- <span class="font-semibold text-[13px] text-gray-900 overflow-hidden text-ellipsis whitespace-nowrap" title="${c}">${c||"未命名媒体"}</span>
98
- <span class="${s} text-white px-2 py-0.5 rounded-xl text-[10px] font-bold">${l}</span>
104
+ <span class="font-semibold text-[13px] text-gray-900 overflow-hidden text-ellipsis whitespace-nowrap" title="${l}">${l||"未命名媒体"}</span>
105
+ <span class="${i} text-white px-2 py-0.5 rounded-xl text-[10px] font-bold">${c}</span>
99
106
  </div>
100
107
  <div class="text-[11px] text-gray-500 overflow-hidden text-ellipsis whitespace-nowrap leading-snug max-w-[320px]" title="${n.url}">${n.url}</div>
101
108
  </div>
102
109
  </div>
103
110
  <div class="flex gap-2">
104
- <button class="m3u8-capture-download-btn flex-1 bg-blue-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs font-medium transition-all duration-200 hover:bg-blue-600 hover:-translate-y-0.5" data-url="${encodeURIComponent(n.url)}" data-title="${encodeURIComponent(c)}">
111
+ <button class="m3u8-capture-download-btn flex-1 bg-blue-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs font-medium transition-all duration-200 hover:bg-blue-600 hover:-translate-y-0.5"
112
+ data-url="${encodeURIComponent(n.url)}"
113
+ data-title="${encodeURIComponent(l)}"
114
+ data-headers="${fe(n.headers,!0)}">
105
115
  跳转下载
106
116
  </button>
107
117
  <button class="m3u8-capture-preview-btn bg-green-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs font-medium transition-all duration-200 hover:bg-green-600" data-url="${encodeURIComponent(n.url)}">
@@ -111,4 +121,4 @@
111
121
  复制
112
122
  </button>
113
123
  </div>
114
- `,t.appendChild(i)}),u?.querySelectorAll(".m3u8-capture-download-btn").forEach(n=>{n.addEventListener("click",i=>{i.stopPropagation();const c=decodeURIComponent(n.getAttribute("data-url")||""),l=decodeURIComponent(n.getAttribute("data-title")||""),s=it()?"&autoStart=1":"",m=s&&lt()?"&autoClose=1":"",f=`${D()}/page/download?from=capture&action=new${s}${m}&url=${encodeURIComponent(c+(l?`|${l}`:""))}`;ht(f)})}),u?.querySelectorAll(".m3u8-capture-preview-btn").forEach(n=>{n.addEventListener("click",i=>{i.stopPropagation();const c=decodeURIComponent(n.getAttribute("data-url")||""),l=`https://m3u8-player.lzw.me?from=capture&url=${encodeURIComponent(c)}`;ht(l)})}),u?.querySelectorAll(".m3u8-capture-copy-btn").forEach(n=>{n.addEventListener("click",async i=>{i.stopPropagation();const c=n.getAttribute("data-url")||"",l=n.textContent,s=n.className;try{await ct(c),n.textContent="已复制",n.className="m3u8-capture-copy-btn bg-green-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs transition-all duration-200",setTimeout(()=>{n.textContent=l,n.className=s},2e3)}catch{const f=window.Swal;if(!f)return;f.fire({icon:"error",title:"复制失败",text:"请手动复制链接",html:`<code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${c}</code>`,confirmButtonText:"确定"})}})})}const O=new Map;Gt(O);function v(t,e=""){if(t=kt(t)||t,!t||$(t))return;const r=e||dt(),o={url:t,title:$t(r),type:At(t),pageUrl:window.location.href,timestamp:Date.now()};if(w){try{window.top?.postMessage({type:"m3u8-capture-link",data:o},"*")}catch(c){console.warn("[M3U8 Capture] Failed to send link to top window:",c)}return}const n=Rt(t);O.get(n)?.title||(O.set(n,o),X())}function Vt(){if($())return;w||window.addEventListener("message",r=>{if(r.data?.type==="m3u8-capture-link"&&r.data.data){const o=r.data.data;v(o.url,o.title)}}),It(),Bt();const t=()=>{if(!$()){if(w)return W();document.body&&(W(),O.size>0&&X())}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",t):setTimeout(t,100);let e=location.href;new MutationObserver(()=>{const r=location.href;if(r!==e){if(e=r,$()){H();return}setTimeout(()=>W(),1e3)}}).observe(document,{subtree:!0,childList:!0}),setInterval(()=>{document.body&&!$()&&W()},5e3)}Vt()})();
124
+ `,e.appendChild(a)}),f.querySelectorAll(".m3u8-capture-download-btn").forEach(n=>{n.addEventListener("click",a=>{a.stopPropagation();const l=decodeURIComponent(n.getAttribute("data-url")||""),c=decodeURIComponent(n.getAttribute("data-title")||""),i=ae()?"&autoStart=1":"",u=i&&ie()?"&autoClose=1":"";let p=`${H()}/page/download?from=capture&action=new${i}${u}&url=${encodeURIComponent(l+(c?`|${c}`:""))}`;if(le()){let h=n.getAttribute("data-headers");h||(h=fe({referer:window.location.href,cookie:document.cookie},!0)),h&&(p+=`#headers=${h}`)}be(p)})}),f.querySelectorAll(".m3u8-capture-preview-btn").forEach(n=>{n.addEventListener("click",a=>{a.stopPropagation();const l=decodeURIComponent(n.getAttribute("data-url")||""),c=`https://m3u8-player.lzw.me?from=capture&url=${encodeURIComponent(l)}`;be(c)})}),f.querySelectorAll(".m3u8-capture-copy-btn").forEach(n=>{n.addEventListener("click",async a=>{a.stopPropagation();const l=n.getAttribute("data-url")||"",c=n.textContent,i=n.className;try{await ce(l),n.textContent="已复制",n.className="m3u8-capture-copy-btn bg-green-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs transition-all duration-200",setTimeout(()=>{n.textContent=c,n.className=i},2e3)}catch{const p=window.Swal;if(!p)return;p.fire({icon:"error",title:"复制失败",text:"请手动复制链接",html:`<code style="word-break: break-all; background: #f3f4f6; padding: 8px; border-radius: 4px; display: block; font-size: 12px;">${l}</code>`,confirmButtonText:"确定"})}})})}const g=new Map;function S(e,t="",r={}){if(e=qe(e)||e,!e||$(e))return;const o=Pe(e),n=g.get(o)||{},a=t||n.title||ye()||"",l={timestamp:Date.now(),url:e,pageUrl:window.location.href,title:de(a).trim(),type:De(e)||n.type||"",headers:Object.assign(r,n.headers||{})};if(w){try{window.top.postMessage({type:"m3u8-capture-link",data:l},"*")}catch(c){console.warn("[M3U8 Capture] Failed to send link to top window:",c)}return}g.set(o,l),Y()}function C(e){if(!e||typeof e!="string"||!e.startsWith("http"))return!1;const t=e.toLowerCase();return!!(We().test(t)||t.includes(".m3u8"))}function De(e){if(!e||typeof e!="string")return"media";const t=e.toLowerCase(),r=B();for(const o of r)if(new RegExp(`\\.${o}(\\?|$|#)`,"i").test(t))return o;return/m3u8/i.test(t)?"m3u8":"media"}function ye(e=document){let t="";const r=["h2.title","h1.title","div.sub-title","div.title","h2","h1"];for(const o of r){const n=e.querySelector(o);if(n&&(t=de(n.textContent),t))break}if(!t)try{w?t=ye(window.top.document):t=(e.title||"").split(/ [-|_] /)[0].trim()}catch{}return t}function qe(e){if(!e||typeof e!="string")return null;try{const t=new URL(e);for(const[r,o]of t.searchParams.entries()){if(!o)continue;const n=decodeURIComponent(o);if(C(n))return n}}catch{}return null}function ve(e){const t={};e instanceof Headers?e.forEach((o,n)=>{t[n.toLowerCase()]=o}):Object.keys(e).forEach(o=>{t[o.toLowerCase()]=e[o]});const r=["accept","accept-encoding","accept-language","connection","content-length","upgrade-insecure-requests"];return Object.keys(t).forEach(o=>{(r.includes(o)||o.startsWith("sec-")||t[o]==null)&&delete t[o]}),t}function ze(){const e=XMLHttpRequest.prototype.open,t=XMLHttpRequest.prototype.setRequestHeader,r=new WeakMap;XMLHttpRequest.prototype.open=function(n,a,l,c,i){return C(a.toString())&&(r.set(this,{}),this.addEventListener("load",function(){if(this.status>=200&&this.status<300){const u=r.get(this)||{},p=ve(u);S(a.toString(),"",p)}})),e.call(this,n,a,l??!0,c,i)},XMLHttpRequest.prototype.setRequestHeader=function(n,a){const l=r.get(this)||{};return l[n]=a,r.set(this,l),t.call(this,n,a)};const o=window.fetch;window.fetch=function(n,a){const l=typeof n=="string"?n:n&&typeof n=="object"&&"url"in n?n.url:"";if(C(l)){let c={};return a?.headers&&(a.headers instanceof Headers?a.headers.forEach((i,u)=>{c[u]=i}):Array.isArray(a.headers)?a.headers.forEach(([i,u])=>{c[i]=u}):c=a.headers),n instanceof Request&&n.headers.forEach((i,u)=>{c[u]=i}),o.call(this,n,a).then(i=>(i.ok&&S(l,"",ve(c)),i))}return o.call(this,n,a)}}function je(){if(typeof PerformanceObserver>"u")return;const e=new PerformanceObserver(t=>{for(const r of t.getEntries())r.name&&C(r.name)&&S(r.name)});try{e.observe({entryTypes:["resource"]})}catch{console.warn("[M3U8 Capture] PerformanceObserver not supported")}}function O(){if(document.querySelectorAll("video").forEach(e=>{e.src&&C(e.src)&&S(e.src,e.getAttribute("title")||""),e.querySelectorAll("source").forEach(t=>{t.src&&C(t.src)&&S(t.src,e.getAttribute("title")||"")})}),document.querySelectorAll("a[href]").forEach(e=>{const t=e.getAttribute("href");if(t&&C(t))try{const r=new URL(t,window.location.href).href;S(r,e.textContent)}catch{}}),document.body){const e=document.body.innerText||"",r=B().join("|"),o=new RegExp(`https?:\\/\\/[^\\s"'<>]+\\.(${r})(\\?[^\\s"'<>]*)?`,"gi");let n;for(;(n=o.exec(e))!==null;)S(n[0])}}function Ye(){if($())return;w||window.addEventListener("message",r=>{if(r.data&&r.data.type==="m3u8-capture-link"&&r.data.data){const o=r.data.data;S(o.url,o.title,o.headers)}}),ze(),je();const e=()=>{if(!$()){if(w)return O();document.body&&(O(),g.size>0&&Y())}};document.readyState==="loading"?document.addEventListener("DOMContentLoaded",e):setTimeout(e,100);let t=location.href;new MutationObserver(()=>{const r=location.href;if(r!==t){if(t=r,$()){j();return}setTimeout(()=>O(),1e3)}}).observe(document,{subtree:!0,childList:!0}),setInterval(()=>{document.body&&!$()&&O()},5e3)}Ye()})();
package/client/play.html CHANGED
@@ -5,8 +5,7 @@
5
5
  <title>M3U8 Player</title>
6
6
  <meta charset="utf-8">
7
7
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
8
- <meta content="width=device-width, initial-scale=1.0, viewport-fit=cover"
9
- name="viewport" />
8
+ <meta content="width=device-width, initial-scale=1.0, viewport-fit=cover" name="viewport" />
10
9
  <link rel="shortcut icon" href="logo.png">
11
10
  <style>
12
11
  body {
@@ -37,7 +36,8 @@
37
36
  dplayer: 'https://s4.zstatic.net/ajax/libs/dplayer/1.26.0/DPlayer.min.js',
38
37
  artplayer: [
39
38
  'https://s4.zstatic.net/ajax/libs/artplayer/5.3.0/artplayer.min.js',
40
- // 'https://fastly.jsdelivr.net/npm/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.min.js',
39
+ 'https://fastly.jsdelivr.net/npm/artplayer-plugin-hls-control/dist/artplayer-plugin-hls-control.min.js',
40
+ 'https://fastly.jsdelivr.net/npm/artplayer-plugin-auto-thumbnail/dist/artplayer-plugin-auto-thumbnail.min.js',
41
41
  ],
42
42
  }
43
43
  const T = {
@@ -60,22 +60,19 @@
60
60
  },
61
61
  async loadJS(url) {
62
62
  if (Array.isArray(url)) {
63
- for (const u of url) {
64
- await T.loadJS(u);
65
- }
66
- return;
67
- }
68
- if (document.querySelector(`script[src="${url}"]`)) {
63
+ for (const u of url) await T.loadJS(u);
69
64
  return;
70
65
  }
71
66
 
67
+ if (document.querySelector(`script[src="${url}"]`)) return;
68
+
72
69
  return new Promise((resolve, reject) => {
73
70
  const script = document.createElement('script');
74
71
  script.src = url;
75
72
  script.onload = resolve;
76
73
  script.onerror = reject;
77
74
  document.body.appendChild(script);
78
- });
75
+ }).catch(e => console.error(e));
79
76
  },
80
77
  sleep(ms) {
81
78
  return new Promise(resolve => setTimeout(resolve, ms));
@@ -122,20 +119,22 @@
122
119
  /** 使用 artplayer 播放 */
123
120
  async artplayer(videoUrl) {
124
121
  let art = T.data.artInc;
122
+ // 从 videoUrl 提取文件类型
123
+ let type = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.m3u8', '.ogg', '.flv', '.webm'].find(d => videoUrl.includes(d)) || 'm3u8';
124
+ if (type.startsWith('.')) type = type.slice(1);
125
125
 
126
126
  if (art) {
127
127
  art.url = videoUrl;
128
+ art.type = type;
128
129
  art.playbackRate = +art.storage.get('playbackRate') || 1;
129
130
  return art;
130
- } else {
131
- await T.loadJS(CDN_CONFIG.artplayer);
132
131
  }
133
132
 
134
- Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4, 8];
135
- Artplayer.SEEK_STEP = 10;
133
+ await T.loadJS(CDN_CONFIG.artplayer);
136
134
 
137
- // videoUrl 提取文件类型
138
- const type = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm', '.m4v', '.m3u8', '.ogg', '.flv', '.webm'].find(d => videoUrl.includes(d)) || 'm3u8';
135
+ Artplayer.PLAYBACK_RATE = [0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4, 8, 16];
136
+ Artplayer.SEEK_STEP = 10; // 快进步长,单位秒
137
+ Artplayer.FAST_FORWARD_VALUE = 3; // 快进倍速
139
138
 
140
139
  art = new Artplayer({
141
140
  container: document.getElementById('dplayer'),
@@ -155,10 +154,44 @@
155
154
  miniProgressBar: true,
156
155
  pip: true, // 是否在底部控制栏里显示 画中画 的开关按钮
157
156
  playbackRate: true, // 是否显示视频播放速度功能
157
+ playsInline: true, // 在移动端是否使用 playsInline 模式
158
158
  screenshot: true,
159
159
  setting: true,
160
160
  theme: '#39f',
161
161
  type,
162
+ plugins: [
163
+ window.artplayerPluginHlsControl &&
164
+ artplayerPluginHlsControl({
165
+ quality: {
166
+ // Show qualitys in control
167
+ control: document.body.clientWidth > 768,
168
+ // Show qualitys in setting
169
+ setting: true,
170
+ // Get the quality name from level
171
+ getName: level => level.height + 'P',
172
+ // I18n
173
+ title: 'Quality',
174
+ auto: 'Auto',
175
+ },
176
+ audio: {
177
+ // Show audios in control
178
+ control: false,
179
+ // Show audios in setting
180
+ setting: true,
181
+ // Get the audio name from track
182
+ getName: track => track.name,
183
+ // I18n
184
+ title: 'Audio',
185
+ auto: 'Auto',
186
+ },
187
+ }),
188
+ window.artplayerPluginAutoThumbnail &&
189
+ artplayerPluginAutoThumbnail({
190
+ width: 160,
191
+ number: 100,
192
+ scale: 1,
193
+ }),
194
+ ].filter(Boolean),
162
195
  customType: {
163
196
  m3u8: function playM3u8(video, url, art) {
164
197
  if (Hls.isSupported()) {
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@lzwme/m3u8-dl",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "A free, open-source, and powerful m3u8 video batch downloader with multi-threaded downloading, play-while-downloading, WebUI management, video parsing, and more.",
5
5
  "main": "cjs/index.js",
6
6
  "types": "cjs/index.d.ts",
7
7
  "license": "MIT",
8
+ "homepage": "https://m3u8-downloader.lzw.me/portal",
8
9
  "repository": {
9
10
  "type": "git",
10
11
  "url": "git+https://github.com/lzwme/m3u8-dl.git"
@@ -59,34 +60,36 @@
59
60
  "registry": "https://registry.npmjs.com"
60
61
  },
61
62
  "devDependencies": {
62
- "@biomejs/biome": "^2.3.5",
63
- "@eslint/js": "^9.39.1",
63
+ "@biomejs/biome": "2.3.11",
64
+ "@eslint/js": "^9.39.2",
64
65
  "@lzwme/fed-lint-helper": "^2.6.6",
65
- "@types/express": "^5.0.5",
66
- "@types/m3u8-parser": "^7.2.5",
67
- "@types/node": "^24.10.1",
66
+ "@types/express": "^5.0.6",
67
+ "@types/global-agent": "^3.0.0",
68
+ "@types/m3u8-parser": "^7.2.6",
69
+ "@types/node": "^25.0.8",
68
70
  "@types/ws": "^8.18.1",
69
- "@typescript-eslint/eslint-plugin": "^8.46.4",
70
- "@typescript-eslint/parser": "^8.46.4",
71
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
72
+ "@typescript-eslint/parser": "^8.53.0",
71
73
  "concurrently": "^9.2.1",
72
- "eslint": "^9.39.1",
74
+ "eslint": "^9.39.2",
73
75
  "eslint-config-prettier": "^10.1.8",
74
- "eslint-plugin-prettier": "^5.5.4",
75
- "express": "^5.1.0",
76
+ "eslint-plugin-prettier": "^5.5.5",
77
+ "express": "^5.2.1",
76
78
  "husky": "^9.1.7",
77
79
  "nodemon": "^3.1.11",
78
- "prettier": "^3.6.2",
80
+ "prettier": "^3.8.0",
79
81
  "standard-version": "^9.5.0",
80
- "typedoc": "^0.28.14",
82
+ "typedoc": "^0.28.16",
81
83
  "typescript": "^5.9.3",
82
- "typescript-eslint": "^8.46.4",
83
- "ws": "^8.18.3"
84
+ "typescript-eslint": "^8.53.0",
85
+ "ws": "^8.19.0"
84
86
  },
85
87
  "dependencies": {
86
88
  "@lzwme/fe-utils": "^1.9.2",
87
89
  "commander": "^14.0.2",
88
90
  "console-log-colors": "^0.5.0",
89
91
  "enquirer": "^2.4.1",
92
+ "global-agent": "^3.0.0",
90
93
  "m3u8-parser": "^7.2.0"
91
94
  },
92
95
  "files": [