@lzwme/m3u8-dl 1.6.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/README.MD +8 -8
- package/README.zh-CN.md +8 -8
- package/cjs/i18n/locales/en.d.ts +9 -0
- package/cjs/i18n/locales/en.js +9 -0
- package/cjs/i18n/locales/zh-CN.d.ts +9 -0
- package/cjs/i18n/locales/zh-CN.js +9 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +1 -0
- package/cjs/lib/file-download.js +2 -2
- package/cjs/lib/format-options.d.ts +2 -2
- package/cjs/lib/format-options.js +13 -1
- package/cjs/lib/init-proxy.d.ts +5 -0
- package/cjs/lib/init-proxy.js +93 -0
- package/cjs/lib/local-play.d.ts +1 -1
- package/cjs/lib/local-play.js +1 -1
- package/cjs/lib/m3u8-convert.js +2 -8
- package/cjs/lib/m3u8-download.js +4 -3
- package/cjs/lib/parseM3u8.js +1 -1
- package/cjs/lib/utils.d.ts +4 -2
- package/cjs/lib/utils.js +38 -2
- package/cjs/lib/worker_pool.d.ts +1 -1
- package/cjs/m3u8-batch-download.js +2 -3
- package/cjs/server/download-server.d.ts +11 -4
- package/cjs/server/download-server.js +251 -79
- package/cjs/types/m3u8.d.ts +6 -0
- package/cjs/video-parser/index.d.ts +3 -3
- package/cjs/video-parser/index.js +5 -5
- package/client/assets/main-BC3ZZLoF.css +1 -0
- package/client/assets/main-BWzfTVAm.js +35 -0
- package/client/index.html +2 -2
- package/client/m3u8-capture.user.js +51 -21
- package/client/play.html +81 -31
- package/package.json +23 -16
- package/client/assets/main-ChJ1yjNN.css +0 -1
- package/client/assets/main-DZTEqg-V.js +0 -29
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-
|
|
16
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
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.
|
|
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
|
|
26
|
-
`).map(
|
|
27
|
-
`)
|
|
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;
|
|
@@ -34,8 +35,8 @@
|
|
|
34
35
|
${o.left?`left: ${o.left};`:""}
|
|
35
36
|
${o.top?`top: ${o.top};`:""}
|
|
36
37
|
${`right: ${o.right};`}
|
|
37
|
-
display: ${
|
|
38
|
-
`,
|
|
38
|
+
display: ${A?"flex":"none"};
|
|
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,39 +57,68 @@
|
|
|
56
57
|
<div class="text-xs text-gray-300 mt-2">浏览网页时会自动抓取</div>
|
|
57
58
|
</div>
|
|
58
59
|
</div>
|
|
59
|
-
`,
|
|
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="${
|
|
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>
|
|
66
|
-
<textarea id="swal-media-ext-list" rows="
|
|
67
|
+
<textarea id="swal-media-ext-list" rows="2"
|
|
67
68
|
class="w-full p-2.5 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
68
|
-
placeholder="例如:m3u8, mp4, mkv, avi, mov, wmv, flv, webm, m4v, ts, m3u, m4a, aac, flac, ape, mp3, wav, ogg, wma">${
|
|
69
|
+
placeholder="例如:m3u8, mp4, mkv, avi, mov, wmv, flv, webm, m4v, ts, m3u, m4a, aac, flac, ape, mp3, wav, ogg, wma">${o.join(", ")}</textarea>
|
|
69
70
|
<p class="text-xs text-gray-500 mb-4">支持的媒体文件扩展名,将用于识别和抓取媒体链接</p>
|
|
70
71
|
<label class="block text-sm font-medium text-gray-700 mb-1">排除网址规则(每行一个,支持正则表达式,以 / 开头和结尾)</label>
|
|
71
|
-
<textarea id="swal-exclude-urls" rows="
|
|
72
|
-
class="w-full p-2.5 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
73
|
-
placeholder="例如: localhost:6600 /example.com/ 127.0.0.1">${
|
|
74
|
-
<p class="text-xs text-gray-500
|
|
72
|
+
<textarea id="swal-exclude-urls" rows="2"
|
|
73
|
+
class="w-full p-2.5 border border-gray-300 rounded-md mb-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
74
|
+
placeholder="例如: localhost:6600 /example.com/ 127.0.0.1">${r}</textarea>
|
|
75
|
+
<p class="text-xs text-gray-500 mb-4">匹配的网址将不展示面板且不抓取媒体链接</p>
|
|
76
|
+
<label class="block text-sm font-medium text-gray-700 mb-1">标题内容替换规则(支持正则,多个规则以逗号分割)</label>
|
|
77
|
+
<textarea id="swal-title-replace-rules" rows="2"
|
|
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"
|
|
79
|
+
placeholder="例如:/\\[.*?\\]//g, /第.*?话//g">${a}</textarea>
|
|
80
|
+
<p class="text-xs text-gray-500 mb-4">在获取标题后,将根据这些规则进行替换。支持正则表达式,格式:/pattern/flags 或 plain-text</p>
|
|
81
|
+
<div class="flex items-center mb-4">
|
|
82
|
+
<input id="swal-auto-start" type="checkbox" ${n?"checked":""}
|
|
83
|
+
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
|
|
84
|
+
<label for="swal-auto-start" class="ml-2 text-sm font-medium text-gray-700">自动开始下载</label>
|
|
85
|
+
</div>
|
|
86
|
+
<p class="text-xs text-gray-500 mb-4">启用后,跳转到下载页面时将自动触发开始下载(延迟1秒)</p>
|
|
87
|
+
<div class="flex items-center mb-4">
|
|
88
|
+
<input id="swal-auto-close-webui" type="checkbox" ${l?"checked":""}
|
|
89
|
+
class="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500">
|
|
90
|
+
<label for="swal-auto-close-webui" class="ml-2 text-sm font-medium text-gray-700">开始下载后自动关闭WebUI页面</label>
|
|
91
|
+
</div>
|
|
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>
|
|
75
99
|
</div>
|
|
76
|
-
`,showCancelButton:!0,confirmButtonText:"保存",cancelButtonText:"取消",confirmButtonColor:"#3b82f6",width:"600px",preConfirm:()=>{const
|
|
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=`
|
|
77
101
|
<div class="flex justify-between items-start gap-2 mb-2">
|
|
78
102
|
<div class="flex-1 min-w-0">
|
|
79
103
|
<div class="flex items-center gap-1.5 mb-1.5">
|
|
80
|
-
<span class="font-semibold text-[13px] text-gray-900 overflow-hidden text-ellipsis whitespace-nowrap" title="${
|
|
81
|
-
<span class="${
|
|
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>
|
|
82
106
|
</div>
|
|
83
|
-
<div class="text-[11px] text-gray-500 overflow-hidden text-ellipsis whitespace-nowrap leading-snug max-w-[320px]" title="${
|
|
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>
|
|
84
108
|
</div>
|
|
85
109
|
</div>
|
|
86
110
|
<div class="flex gap-2">
|
|
87
|
-
<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"
|
|
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)}">
|
|
88
115
|
跳转下载
|
|
89
116
|
</button>
|
|
90
|
-
<button class="m3u8-capture-
|
|
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)}">
|
|
118
|
+
预览
|
|
119
|
+
</button>
|
|
120
|
+
<button class="m3u8-capture-copy-btn bg-gray-500 text-white border-none px-3.5 py-2 rounded-md cursor-pointer text-xs transition-all duration-200 hover:bg-gray-600" data-url="${n.url}">
|
|
91
121
|
复制
|
|
92
122
|
</button>
|
|
93
123
|
</div>
|
|
94
|
-
`,
|
|
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,15 +36,16 @@
|
|
|
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
|
-
|
|
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 = {
|
|
44
44
|
data: {
|
|
45
45
|
playType: playUrl.includes('dplayer') ? 'dplayer' : 'artplayer',
|
|
46
46
|
videoUrl: decodeURIComponent(playUrl),
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
dpInc: null,
|
|
48
|
+
artInc: null,
|
|
49
49
|
},
|
|
50
50
|
play(videoUrl, playType) {
|
|
51
51
|
if (videoUrl && videoUrl !== T.data.videoUrl) T.data.videoUrl = videoUrl;
|
|
@@ -60,30 +60,27 @@
|
|
|
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));
|
|
82
79
|
},
|
|
83
80
|
async dplayer(videoUrl) {
|
|
84
|
-
if (T.data.
|
|
85
|
-
T.data.
|
|
86
|
-
T.data.
|
|
81
|
+
if (T.data.dpInc) {
|
|
82
|
+
T.data.dpInc.destroy();
|
|
83
|
+
T.data.dpInc = null;
|
|
87
84
|
await T.sleep(100);
|
|
88
85
|
} else {
|
|
89
86
|
await T.loadJS(CDN_CONFIG.dplayer);
|
|
@@ -116,28 +113,38 @@
|
|
|
116
113
|
console.log('[dplayer]播放完毕', videoUrl);
|
|
117
114
|
T.next_video();
|
|
118
115
|
});
|
|
119
|
-
T.data.
|
|
116
|
+
T.data.dpInc = dp;
|
|
120
117
|
return dp;
|
|
121
118
|
},
|
|
122
119
|
/** 使用 artplayer 播放 */
|
|
123
120
|
async artplayer(videoUrl) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
+
|
|
126
|
+
if (art) {
|
|
127
|
+
art.url = videoUrl;
|
|
128
|
+
art.type = type;
|
|
129
|
+
art.playbackRate = +art.storage.get('playbackRate') || 1;
|
|
130
|
+
return art;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
+
await T.loadJS(CDN_CONFIG.artplayer);
|
|
134
|
+
|
|
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; // 快进倍速
|
|
138
|
+
|
|
139
|
+
art = new Artplayer({
|
|
133
140
|
container: document.getElementById('dplayer'),
|
|
134
141
|
url: videoUrl, // 'https://playertest.longtailvideo.com/adaptive/elephants_dream_v4/index.m3u8',
|
|
135
|
-
// airplay: true,
|
|
142
|
+
// airplay: true, // 是否显示AirPlay功能。效果并不好
|
|
136
143
|
aspectRatio: true, // 是否显示视频长宽比功能
|
|
137
144
|
autoplay: true,
|
|
138
145
|
autoOrientation: true,
|
|
139
146
|
// autoMini: true, // 当播放器滚动到浏览器视口以外时,自动进入 迷你播放 模式
|
|
140
|
-
autoPlayback: true,
|
|
147
|
+
autoPlayback: true, // 是否使用自动 回放功能
|
|
141
148
|
// autoSize: true, // 自动调整播放器尺寸
|
|
142
149
|
fastForward: true,
|
|
143
150
|
flip: true, // 是否显示视频翻转功能
|
|
@@ -147,10 +154,44 @@
|
|
|
147
154
|
miniProgressBar: true,
|
|
148
155
|
pip: true, // 是否在底部控制栏里显示 画中画 的开关按钮
|
|
149
156
|
playbackRate: true, // 是否显示视频播放速度功能
|
|
157
|
+
playsInline: true, // 在移动端是否使用 playsInline 模式
|
|
150
158
|
screenshot: true,
|
|
151
159
|
setting: true,
|
|
152
160
|
theme: '#39f',
|
|
153
|
-
type
|
|
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),
|
|
154
195
|
customType: {
|
|
155
196
|
m3u8: function playM3u8(video, url, art) {
|
|
156
197
|
if (Hls.isSupported()) {
|
|
@@ -200,8 +241,8 @@
|
|
|
200
241
|
},
|
|
201
242
|
}],
|
|
202
243
|
contextmenu: [
|
|
203
|
-
{ html: '在线播放器', click: () => window.open(
|
|
204
|
-
{ html: '在线下载器', click: () => window.open(
|
|
244
|
+
// { index: 80, html: 'M3U8在线播放器', click: () => window.open(`https://m3u8-player.lzw.me`, '_blank') },
|
|
245
|
+
{ index: 99, html: 'M3U8在线下载器', click: () => window.open(`https://m3u8-downloader.lzw.me`, '_blank') },
|
|
205
246
|
],
|
|
206
247
|
});
|
|
207
248
|
|
|
@@ -209,8 +250,17 @@
|
|
|
209
250
|
console.log('[artplayer]播放完毕', videoUrl);
|
|
210
251
|
T.next_video();
|
|
211
252
|
});
|
|
253
|
+
art.on('video:ratechange', () => {
|
|
254
|
+
// console.log('[artplayer]播放进度', art.playbackRate);
|
|
255
|
+
art.storage.set('playbackRate', art.playbackRate);
|
|
256
|
+
});
|
|
257
|
+
art.on('ready', () => {
|
|
258
|
+
art.playbackRate = +art.storage.get('playbackRate') || 1;
|
|
259
|
+
art.contextmenu.remove('version');
|
|
260
|
+
});
|
|
261
|
+
|
|
212
262
|
// console.log('[artplayer]播放器初始化完成', art);
|
|
213
|
-
T.data.
|
|
263
|
+
T.data.artInc = art;
|
|
214
264
|
return art;
|
|
215
265
|
},
|
|
216
266
|
previous_video() {
|
|
@@ -231,9 +281,9 @@
|
|
|
231
281
|
// 监听来自父窗口的消息
|
|
232
282
|
window.addEventListener('message', (event) => {
|
|
233
283
|
const data = event.data;
|
|
234
|
-
if (!data) return;
|
|
284
|
+
if (!data || typeof data !== 'object') return;
|
|
235
285
|
|
|
236
|
-
if (
|
|
286
|
+
if (data.url) {
|
|
237
287
|
T.data.videoUrl = data.url;
|
|
238
288
|
if (data.playType) T.data.playType = data.playType;
|
|
239
289
|
T.play();
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lzwme/m3u8-dl",
|
|
3
|
-
"version": "1.
|
|
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"
|
|
@@ -26,17 +27,20 @@
|
|
|
26
27
|
"lint:all": "biome lint && pnpm -F @lzwme/m3u8-capture lint && pnpm -F @lzwme/m3u8-dl-frontend lint",
|
|
27
28
|
"format": "biome format --write",
|
|
28
29
|
"fix": "biome check --fix",
|
|
30
|
+
"fix:all": "biome check --fix && pnpm -F @lzwme/m3u8-capture fix && pnpm -F @lzwme/m3u8-dl-frontend fix && pnpm -F @lzwme/m3u8-dl-portal fix",
|
|
29
31
|
"build": "npm run clean && npm run build:cjs && npm run build:frontend && npm run build:capture",
|
|
30
32
|
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
31
33
|
"build:frontend": "pnpm -F @lzwme/m3u8-dl-frontend build",
|
|
32
34
|
"build:capture": "pnpm -F @lzwme/m3u8-capture build",
|
|
33
35
|
"download-cdn": "node scripts/download-cdn-resources.js",
|
|
34
|
-
"doc": "
|
|
36
|
+
"doc": "pnpm doc:api && pnpm doc:portal",
|
|
37
|
+
"doc:api": "typedoc src --tsconfig tsconfig.module.json",
|
|
38
|
+
"doc:portal": "pnpm -F @lzwme/m3u8-dl-portal build",
|
|
35
39
|
"version": "standard-version",
|
|
36
40
|
"dist": "npm run build",
|
|
37
41
|
"release": "npm run dist && npm run version",
|
|
38
42
|
"clean": "flh rm -f ./cjs ./esm ./docs ./client/assets",
|
|
39
|
-
"test": "npm run lint"
|
|
43
|
+
"test": "npm run lint:all"
|
|
40
44
|
},
|
|
41
45
|
"bin": {
|
|
42
46
|
"m3u8dl": "bin/m3u8dl.js"
|
|
@@ -56,33 +60,36 @@
|
|
|
56
60
|
"registry": "https://registry.npmjs.com"
|
|
57
61
|
},
|
|
58
62
|
"devDependencies": {
|
|
59
|
-
"@biomejs/biome": "
|
|
60
|
-
"@eslint/js": "^9.39.
|
|
63
|
+
"@biomejs/biome": "2.3.11",
|
|
64
|
+
"@eslint/js": "^9.39.2",
|
|
61
65
|
"@lzwme/fed-lint-helper": "^2.6.6",
|
|
62
|
-
"@types/express": "^5.0.
|
|
63
|
-
"@types/
|
|
64
|
-
"@types/
|
|
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",
|
|
65
70
|
"@types/ws": "^8.18.1",
|
|
66
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
67
|
-
"@typescript-eslint/parser": "^8.
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
|
72
|
+
"@typescript-eslint/parser": "^8.53.0",
|
|
68
73
|
"concurrently": "^9.2.1",
|
|
69
|
-
"eslint": "^9.39.
|
|
74
|
+
"eslint": "^9.39.2",
|
|
70
75
|
"eslint-config-prettier": "^10.1.8",
|
|
71
|
-
"eslint-plugin-prettier": "^5.5.
|
|
72
|
-
"express": "^5.1
|
|
76
|
+
"eslint-plugin-prettier": "^5.5.5",
|
|
77
|
+
"express": "^5.2.1",
|
|
73
78
|
"husky": "^9.1.7",
|
|
74
79
|
"nodemon": "^3.1.11",
|
|
75
|
-
"prettier": "^3.
|
|
80
|
+
"prettier": "^3.8.0",
|
|
76
81
|
"standard-version": "^9.5.0",
|
|
82
|
+
"typedoc": "^0.28.16",
|
|
77
83
|
"typescript": "^5.9.3",
|
|
78
|
-
"typescript-eslint": "^8.
|
|
79
|
-
"ws": "^8.
|
|
84
|
+
"typescript-eslint": "^8.53.0",
|
|
85
|
+
"ws": "^8.19.0"
|
|
80
86
|
},
|
|
81
87
|
"dependencies": {
|
|
82
88
|
"@lzwme/fe-utils": "^1.9.2",
|
|
83
89
|
"commander": "^14.0.2",
|
|
84
90
|
"console-log-colors": "^0.5.0",
|
|
85
91
|
"enquirer": "^2.4.1",
|
|
92
|
+
"global-agent": "^3.0.0",
|
|
86
93
|
"m3u8-parser": "^7.2.0"
|
|
87
94
|
},
|
|
88
95
|
"files": [
|