mirror of
https://github.com/fanmingming/live
synced 2026-04-03 12:17:24 +08:00
154 lines
5.7 KiB
HTML
154 lines
5.7 KiB
HTML
<!-- 中间层页面,专门用于下载,在此页面中注册 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() {
|
||
// 获取 ./ 域下已经注册过的 serviceWorker,getRegistration 返回单个,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> |