Files
fanmingming.live/m3u8/mitm.html
2024-12-02 09:57:49 +00:00

154 lines
5.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!-- 中间层页面,专门用于下载,在此页面中注册 serviceWorker并实现拦截 -->
<!-- 本页面存在的意义,在于隔离 web 主进程的请求,让 serviceWorker 仅拦截本页面的请求 -->
<!-- serviceWorker 完全异步,全程 API 均使用 Promise 完成异步操作 -->
<script>
// 保活,每 10s 执行一次 ping避免 serviceWorker 被删除
let keepAlive = () => {
keepAlive = () => { }
var ping = location.href.substr(0, location.href.lastIndexOf('/')) + '/ping'
var interval = setInterval(() => {
if (serviceWorker) {
serviceWorker.postMessage('ping')
} else {
fetch(ping).then(res => res.text(!res.ok && clearInterval(interval)))
}
}, 10000)
}
let scope = '' // 当前 serviceWorker 所在域,是其唯一标识符
let serviceWorker = null // serviceWorker 实例
let tempMessageStore = [] // 在处理函数未准备好之前,临时存储的 message
// 进入页面马上进行消息监听,再后续处理函数 ready 后,再触发这些 message 的处理
window.onmessage = evt => tempMessageStore.push(evt)
// 注册 serviceWorker检测是否有旧的实例进行复用。
function registerWorker() {
// 获取 ./ 域下已经注册过的 serviceWorkergetRegistration 返回单个getRegistrations 返回所有
return navigator.serviceWorker.getRegistration('./')
.then(serviceWorkerRegistration => {
// 如果已经存在注册过的 serviceWorkerRegistration则直接返回否则产生新的一个
return serviceWorkerRegistration || navigator.serviceWorker.register('serviceWorker.js', { scope: './' })
}).then(serviceWorkerRegistration => {
scope = serviceWorkerRegistration.scope // 保存所在域
// 如果注册已就绪,则直接赋值并返回
if (serviceWorkerRegistration.active) {
serviceWorker = serviceWorkerRegistration.active
return
}
// 如果处于注册中,返回 promise并监听其状态变更等待其就绪状态
const swRegTmp = serviceWorkerRegistration.installing || serviceWorkerRegistration.waiting
return new Promise(resolve => {
const onStatechange = () => {
if (swRegTmp.state === 'activated') {
swRegTmp.removeEventListener('statechange', onStatechange)
serviceWorker = serviceWorkerRegistration.active
resolve()
}
}
swRegTmp.addEventListener('statechange', onStatechange)
})
})
}
// 消息监听,监听 web 主进程发送过来的消息,并进行数据中转,及数据的处理与转译
function onMessage(event) {
let {
data, // 数据
ports, // channel 所在渠道
origin // 消息作用域
} = event
console.log('onMessage', event)
// 检测消息通道
if (!ports || !ports.length) {
throw new TypeError("[StreamSaver] You didn't send a messageChannel")
}
// 检测接受的数据实体
if (typeof data !== 'object') {
throw new TypeError("[StreamSaver] You didn't send a object")
}
// 检查 readableStream
if (data.readableStream) {
console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm")
}
// 检查 pathname
if (!data.pathname) {
console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)")
data.pathname = Math.random().toString().slice(-6) + '/' + data.filename
}
/** @since v2.0.0 */
if (!data.headers) {
console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts")
} else {
// test if it's correct
// should throw a typeError if not
new Headers(data.headers)
}
// the default public service worker for StreamSaver is shared among others.
// so all download links needs to be prefixed to avoid any other conflict
data.origin = origin
// if we ever (in some feature versoin of streamsaver) would like to
// redirect back to the page of who initiated a http request
data.referrer = data.referrer || document.referrer || origin
// 删除所有前导斜杠
data.pathname = data.pathname.replace(/^\/+/g, '')
// remove protocol
// 去除协议
let org = origin.replace(/(^\w+:|^)\/\//, '')
// 设置绝对路径,以用于下载
data.url = new URL(`${scope + org}/${data.pathname}`).toString()
// 检查路径是否合法
if (!data.url.startsWith(`${scope + org}/`)) {
throw new TypeError('[StreamSaver] bad `data.pathname`')
}
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker. The service worker can
// then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
const transferable = data.readableStream
? [ports[0], data.readableStream]
: [ports[0]]
if (!(data.readableStream || data.transferringReadable)) {
keepAlive()
}
// 将从 web 主进程接收到的数据,传输给 serviceWorker 接收
return serviceWorker.postMessage(data, transferable)
}
// 消息回调,告知主进程,本页面已准备就绪
if (window.opener) {
window.opener.postMessage('StreamSaver::loadedPopup', '*')
}
// 注册完成,并进行消息处理
if (navigator.serviceWorker) {
registerWorker().then(() => {
window.onmessage = onMessage
tempMessageStore.forEach(window.onmessage) // 将之前临时存储的 message 放入处理函数中执行
})
} else {
keepAlive()
}
</script>