mirror of
https://github.com/DevLARLEY/WidevineProxy2.git
synced 2026-04-02 02:28:34 +00:00
194 lines
7.2 KiB
JavaScript
194 lines
7.2 KiB
JavaScript
(async () => {
|
|
const proxy = (object, method, handler) => {
|
|
const original = object[method];
|
|
if (typeof original !== "function")
|
|
return;
|
|
|
|
Object.defineProperty(object, method, {
|
|
value: new Proxy(original, { apply: handler }),
|
|
configurable: true,
|
|
writable: true
|
|
});
|
|
};
|
|
|
|
const b64 = {
|
|
decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)),
|
|
encode: b => btoa(String.fromCharCode(...new Uint8Array(b)))
|
|
};
|
|
|
|
const getManifestType = (text) => {
|
|
const lower = text.toLowerCase();
|
|
if (lower.includes('<mpd') && lower.includes('</mpd>')) {
|
|
return "DASH";
|
|
} else if (lower.includes('#extm3u')) {
|
|
if (lower.includes('#ext-x-stream-inf')) {
|
|
return "HLS_MASTER";
|
|
} else {
|
|
return "HLS_PLAYLIST";
|
|
}
|
|
} else if (lower.includes('<smoothstreamingmedia') && lower.includes('</smoothstreamingmedia>')) {
|
|
return "MSS";
|
|
}
|
|
}
|
|
|
|
function emitAndWaitForResponse(type, data) {
|
|
return new Promise((resolve) => {
|
|
const requestId = Math.random().toString(16).substring(2, 9);
|
|
const responseHandler = (event) => {
|
|
const { detail } = event;
|
|
if (detail.substring(0, 7) === requestId) {
|
|
document.removeEventListener('responseReceived', responseHandler);
|
|
resolve(detail.substring(7));
|
|
}
|
|
};
|
|
document.addEventListener('responseReceived', responseHandler);
|
|
const requestEvent = new CustomEvent('response', {
|
|
detail: {
|
|
type: type,
|
|
body: data,
|
|
requestId: requestId,
|
|
}
|
|
});
|
|
document.dispatchEvent(requestEvent);
|
|
});
|
|
}
|
|
|
|
if (typeof EventTarget !== 'undefined') {
|
|
proxy(EventTarget.prototype, 'addEventListener', (target, thisArg, args) => {
|
|
const [type, listener] = args;
|
|
|
|
if (thisArg == null || typeof MediaKeySession === 'undefined' || !(thisArg instanceof MediaKeySession) || typeof MediaKeyMessageEvent === 'undefined' || type !== "message" || !listener) {
|
|
return target.apply(thisArg, args);
|
|
}
|
|
|
|
args[1] = async function(event) {
|
|
if (event instanceof MediaKeyMessageEvent && event.isTrusted && event.message.byteLength > 2) {
|
|
const oldChallenge = b64.encode(event.message);
|
|
const newChallenge = await emitAndWaitForResponse("REQUEST", oldChallenge);
|
|
|
|
const clonedEvent = new MediaKeyMessageEvent("message", {
|
|
messageType: event.messageType,
|
|
message: b64.decode(newChallenge).buffer
|
|
});
|
|
|
|
event.stopImmediatePropagation();
|
|
event.preventDefault();
|
|
|
|
thisArg.dispatchEvent(clonedEvent);
|
|
return;
|
|
}
|
|
|
|
if (listener.handleEvent) {
|
|
listener.handleEvent.call(listener, event);
|
|
} else {
|
|
listener.call(this, event);
|
|
}
|
|
};
|
|
|
|
return target.apply(thisArg, args);
|
|
});
|
|
}
|
|
|
|
if (typeof MediaKeySession !== 'undefined') {
|
|
proxy(MediaKeySession.prototype, 'update', async (target, thisArg, args) => {
|
|
if (thisArg == null || !(thisArg instanceof MediaKeySession)) {
|
|
return target.apply(thisArg, args);
|
|
}
|
|
|
|
await emitAndWaitForResponse("RESPONSE", b64.encode(args[0]))
|
|
|
|
return await target.apply(thisArg, args);
|
|
});
|
|
}
|
|
|
|
proxy(XMLHttpRequest.prototype, "open", (target, thisArg, args) => {
|
|
const [method, url] = args;
|
|
|
|
thisArg.requestMethod = method.toUpperCase();
|
|
thisArg.requestURL = url;
|
|
|
|
return target.apply(thisArg, args);
|
|
});
|
|
|
|
proxy(XMLHttpRequest.prototype, "send", (target, thisArg, args) => {
|
|
thisArg.addEventListener("readystatechange", async () => {
|
|
if (thisArg.requestMethod !== "GET" || thisArg.readyState !== 4) {
|
|
return;
|
|
}
|
|
|
|
let body = null;
|
|
switch (thisArg.responseType) {
|
|
case "":
|
|
case "text":
|
|
body = thisArg.responseText ?? thisArg.response;
|
|
break;
|
|
|
|
case "json":
|
|
body = typeof thisArg.response === 'string' ? thisArg.response : JSON.stringify(thisArg.response);
|
|
break;
|
|
|
|
case "arraybuffer":
|
|
if (thisArg.response && thisArg.response.byteLength > 0 && thisArg.response.byteLength < 1_000_000) {
|
|
const arr = new Uint8Array(thisArg.response);
|
|
const decoder = new TextDecoder('utf-8', { fatal: false });
|
|
body = arr.length <= 2000
|
|
? decoder.decode(arr)
|
|
: decoder.decode(arr.slice(0, 1000)) + decoder.decode(arr.slice(-1000));
|
|
}
|
|
break;
|
|
|
|
case "blob":
|
|
if (thisArg.response.type.startsWith('text/') || thisArg.response.type.includes('xml') || thisArg.response.type.includes('json') || thisArg.response.size < 100_000) {
|
|
body = await thisArg.response.text();
|
|
}
|
|
break;
|
|
|
|
case "document":
|
|
if (thisArg.response?.documentElement) {
|
|
body = new XMLSerializer().serializeToString(thisArg.response);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (body) {
|
|
const manifest_type = getManifestType(body);
|
|
if (manifest_type) {
|
|
console.log("WVP2 FOUND MANIFEST", manifest_type, thisArg.responseURL);
|
|
await emitAndWaitForResponse("MANIFEST", JSON.stringify({
|
|
url: thisArg.responseURL,
|
|
type: manifest_type,
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
|
|
return target.apply(thisArg, args);
|
|
});
|
|
|
|
proxy(window, "fetch", async (target, thisArg, args) => {
|
|
const response = await target.apply(thisArg, args);
|
|
|
|
try {
|
|
if (response) {
|
|
const text = await response.clone().text();
|
|
const manifest_type = getManifestType(text);
|
|
|
|
if (manifest_type) {
|
|
const url = typeof args[0] === "string" ? args[0] : args[0]?.url;
|
|
|
|
if (url) {
|
|
await emitAndWaitForResponse("MANIFEST", JSON.stringify({
|
|
url,
|
|
type: manifest_type
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.debug("Manifest intercept failed:", err);
|
|
}
|
|
|
|
return response;
|
|
});
|
|
})();
|