Files
Docker/uniapp/Google Chrome/Resources/media_sniffer.js
T
2026-11-03 09:19:50 +08:00

145 lines
4.3 KiB
JavaScript
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.
// 资源嗅探脚本:在安装页面播放器脚本之前接管 fetch / XMLHttpRequest / createObjectURL,
// 把真正被下载的媒体地址(mp4 / m3u8 等)回报给宿主程序,用于"下载视频"按钮。
(function () {
if (window.__mediaSniffInstalled) return;
window.__mediaSniffInstalled = true;
window.__mediaUrls = [];
window.__m3u8Blobs = [];
var MEDIA_EXT =
/\.(mp4|flv|m3u8|mpd|mkv|mov|m4v|webm|f4v|3gp|mpg|mpeg|ts)(\?|#|$)/i;
var PLAIN_EXT =
/\.(jpg|jpeg|png|gif|webp|ico|svg|bmp|css|js|json|woff2?|ttf|map|txt)(\?|#|$)/i;
function isMediaUrl(u) {
if (!u) return false;
u = String(u);
if (u.indexOf("blob:") === 0 || u.indexOf("data:") === 0) return false;
if (PLAIN_EXT.test(u)) return false;
return MEDIA_EXT.test(u) || /\/(video|vod|playback|media)[\/-]/i.test(u);
}
function absUrl(u) {
try {
return new URL(String(u), location.href).href;
} catch (e) {
return String(u);
}
}
function post(msg) {
try {
if (
window.chrome &&
window.chrome.webview &&
window.chrome.webview.postMessage
) {
window.chrome.webview.postMessage(JSON.stringify(msg));
}
} catch (e) {}
}
function remember(text) {
if (!text || typeof text !== "string") return;
if (text.indexOf("#EXTM3U") < 0) return;
if (window.__m3u8Blobs.indexOf(text) >= 0) return;
window.__m3u8Blobs.push(text);
post({ type: "hlsText", text: text });
}
function report(u) {
try {
var full = absUrl(u);
if (!isMediaUrl(full)) return;
if (window.__mediaUrls.indexOf(full) >= 0) return;
window.__mediaUrls.push(full);
// base 是发出这个请求的文档地址:播放器常在跨域 iframe 里,
// 宿主必须用它当 Referer,用顶层页面地址会被防盗链判为非法来源
post({ type: "media", url: full, base: location.href });
} catch (e) {}
}
window.__reportMedia = report;
window.__rememberHlsText = remember;
// 抖音等站点会用 atob(base64) 把播放地址还原出来,这里顺手抓一份解码结果
try {
var origAtob = window.atob;
if (origAtob) {
window.atob = function (s) {
var r = origAtob.apply(this, arguments);
try {
if (r && typeof r === "string") {
if (r.indexOf("#EXTM3U") >= 0) remember(r);
else if (isMediaUrl(r)) report(r);
}
} catch (e) {}
return r;
};
}
} catch (e) {}
try {
var origFetch = window.fetch;
if (origFetch) {
window.fetch = function (input, init) {
try {
var u =
typeof input === "string" || input instanceof URL
? input
: input && input.url;
if (u) report(u);
} catch (e) {}
return origFetch.apply(this, arguments);
};
}
} catch (e) {}
try {
var xhrOpen = XMLHttpRequest.prototype.open;
var xhrSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, u) {
try {
if (u) report(u);
} catch (e) {}
return xhrOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function () {
var xhr = this;
try {
xhr.addEventListener("load", function () {
try {
if (xhr.responseType === "" || xhr.responseType === "text") {
remember(xhr.responseText);
} else if (
xhr.responseType === "blob" &&
xhr.response &&
xhr.response.text
) {
xhr.response.text().then(remember);
}
} catch (e) {}
});
} catch (e) {}
return xhrSend.apply(this, arguments);
};
} catch (e) {}
// MSE 播放时 video.src 只能是 blob:,抓不到 http 地址,所以靠上面的分片嗅探兜底;
// 这里额外记录 createObjectURL 的调用,方便识别"确实在用 MSE"。
try {
var ocu = window.URL && window.URL.createObjectURL;
if (ocu) {
window.URL.createObjectURL = function (obj) {
var u = ocu.apply(this, arguments);
try {
window.__usesMse = true;
if (obj && typeof obj.text === "function") obj.text().then(remember);
} catch (e) {}
return u;
};
}
} catch (e) {}
})();