forked from Momo707577045/m3u8-downloader
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mitm.html
154 lines (126 loc) · 5.74 KB
/
mitm.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<!-- 中间层页面,专门用于下载,在此页面中注册 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>