diff --git a/README.md b/README.md index 8ba7fe7..b16c427 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,107 @@ # WidevineProxy2 + An extension-based proxy for Widevine EME challenges and license messages. \ Modifies the challenge before it reaches the web player and retrieves the decryption keys from the response. ## Features -+ User-friendly / GUI-based -+ Bypasses one-time tokens, hashes, and license wrapping -+ JavaScript native Widevine implementation -+ Supports Widevine Device files -+ Manifest V3 compliant + +- User-friendly / GUI-based +- Bypasses one-time tokens, hashes, and license wrapping +- JavaScript native Widevine implementation +- Supports Widevine Device files +- Manifest V3 compliant ## Widevine Devices + This addon requires a Widevine Device file to work, which is not provided by this project. -+ Use an existing Remote CDM like [this one](https://github.com/user-attachments/files/21834836/remote.json) -+ Follow [this](https://forum.videohelp.com/threads/408031) guide if you want to dump your own device. -+ Ready-to-use Widevine Devices can be found on the [VideoHelp forum](https://forum.videohelp.com/forums/48). + +- Use an existing Remote CDM like [this one](https://github.com/user-attachments/files/21834836/remote.json) +- Follow [this](https://forum.videohelp.com/threads/408031) guide if you want to dump your own device. +- Ready-to-use Widevine Devices can be found on the [VideoHelp forum](https://forum.videohelp.com/forums/48). ## Compatibility -+ Compatible (tested) browsers: Firefox/Chrome on Windows/Linux. -+ Works with any service that accepts challenges from Android devices on the same endpoint. + +- Compatible (tested) browsers: Firefox/Chrome on Windows/Linux. +- Works with any service that accepts challenges from Android devices on the same endpoint. ## Installation -+ Chrome + +- Chrome 1. Download the ZIP file from the [releases section](https://github.com/DevLARLEY/WidevineProxy2/releases) 2. Navigate to `chrome://extensions/` 3. Enable `Developer mode` 4. Drag-and-drop the downloaded file into the window -+ Firefox - + Persistent installation +- Firefox + - Persistent installation 1. Download the XPI file from the [releases section](https://github.com/DevLARLEY/WidevineProxy2/releases) 2. Navigate to `about:addons` 3. Click the settings icon and choose `Install Add-on From File...` 4. Select the downloaded file - + Temporary installation + - Temporary installation 1. Download the ZIP file from the [releases section](https://github.com/DevLARLEY/WidevineProxy2/releases) 2. Navigate to `about:debugging#/runtime/this-firefox` 3. Click `Load Temporary Add-on...` and select the downloaded file ## Setup + ### Widevine Device + If you only have a `device_client_id_blob` and `device_private_key`, run this command to create a .wvd file: + ``` pywidevine create-device -k device_private_key -c device_client_id_blob -t "ANDROID" -l 3 ``` + Now, open the extension, click `Choose File` and select your Widevine Device file. ### Remote CDM + If you don't already have a `remote.json` file, open the API URL in the browser (if provided) and save the response as `remote.json`. \ Now, open the extension, click `Choose remote.json` and select the JSON file provided by your API. - -+ Select the type of device you're using in the top right-hand corner -+ The files are saved in the extension's `chrome.storage.sync` storage and will be synchronized across any browsers into which the user is signed in with their Google account. -+ The maximum number of Widevine devices is ~25 **OR** ~200 Remote CDMs -+ Check `Enabled` to activate the message interception and you're done. +- Select the type of device you're using in the top right-hand corner +- The files are saved in the extension's `chrome.storage.sync` storage and will be synchronized across any browsers into which the user is signed in with their Google account. +- The maximum number of Widevine devices is ~25 **OR** ~200 Remote CDMs +- Check `Enabled` to activate the message interception and you're done. ## Usage + All the user has to do is to play a DRM protected video and the decryption keys should appear in the `Keys` group box (if the service is not unsupported, as stated above). \ Keys are saved: -+ Temporarily until the extension is either refreshed manually (if installed temporarily) or a removal of the keys is manually initiated. -+ Permanently in the extension's `chrome.storage.local` storage until manually wiped or exported via the command line. -> [!NOTE] -> The video will not play when the interception is active, as the Widevine CDM library isn't able to decrypt the Android CDM license. -+ Click the `+` button to expand the section to reveal the PSSH and keys. +- Temporarily until the extension is either refreshed manually (if installed temporarily) or a removal of the keys is manually initiated. +- Permanently in the extension's `chrome.storage.local` storage until manually wiped or exported via the command line. + + > [!NOTE] + > The video will not play when the interception is active, as the Widevine CDM library isn't able to decrypt the Android CDM license. + +- Click the `+` button to expand the section to reveal the PSSH and keys. ## FAQ + > What if I'm unable to get the keys? This automatically means that the license server is blocking your CDM and that you either need a CDM from a physical device, a ChromeCDM, or an L1 Android CDM. Don't ask where you can get these ## Issues -+ DRM playback won't work when the extension is disabled and EME Logger is active. This is caused by my fix for dealing with EME Logger interference (solutions are welcome). + +- DRM playback won't work when the extension is disabled and EME Logger is active. This is caused by my fix for dealing with EME Logger interference (solutions are welcome). ## Demo + [Widevineproxy2.webm](https://github.com/user-attachments/assets/8f51cee3-50e2-4aa4-b244-afa2d0b2987e) ## Disclaimer -+ This program is intended solely for educational purposes. -+ Do not use this program to decrypt or access any content for which you do not have the legal rights or explicit permission. -+ Unauthorized decryption or distribution of copyrighted materials is a violation of applicable laws and intellectual property rights. -+ This tool must not be used for any illegal activities, including but not limited to piracy, circumventing digital rights management (DRM), or unauthorized access to protected content. -+ The developers, contributors, and maintainers of this program are not responsible for any misuse or illegal activities performed using this software. -+ By using this program, you agree to comply with all applicable laws and regulations governing digital rights and copyright protections. + +- This program is intended solely for educational purposes. +- Do not use this program to decrypt or access any content for which you do not have the legal rights or explicit permission. +- Unauthorized decryption or distribution of copyrighted materials is a violation of applicable laws and intellectual property rights. +- This tool must not be used for any illegal activities, including but not limited to piracy, circumventing digital rights management (DRM), or unauthorized access to protected content. +- The developers, contributors, and maintainers of this program are not responsible for any misuse or illegal activities performed using this software. +- By using this program, you agree to comply with all applicable laws and regulations governing digital rights and copyright protections. ## Credits -+ [node-widevine](https://github.com/Frooastside/node-widevine) -+ [forge](https://github.com/digitalbazaar/forge) -+ [protobuf.js](https://github.com/protobufjs/protobuf.js) + +- [node-widevine](https://github.com/Frooastside/node-widevine) +- [forge](https://github.com/digitalbazaar/forge) +- [protobuf.js](https://github.com/protobufjs/protobuf.js) diff --git a/background.js b/background.js index 8f4573e..3bc5a1a 100644 --- a/background.js +++ b/background.js @@ -1,353 +1,406 @@ import "./lib/protobuf.min.js"; import "./lib/license_protocol.min.js"; -const { LicenseType, SignedMessage, LicenseRequest, License } = protobuf.roots.default.license_protocol; +const { LicenseType, SignedMessage, LicenseRequest, License } = + protobuf.roots.default.license_protocol; import "./lib/forge.min.js"; import { Session } from "./lib/cdm.js"; -import { DeviceManager, SettingsManager, AsyncLocalStorage, RemoteCDMManager, Util } from "./lib/util.js"; +import { + DeviceManager, + SettingsManager, + AsyncLocalStorage, + RemoteCDMManager, + Util, +} from "./lib/util.js"; import { WidevineDevice } from "./lib/device.js"; import { RemoteCdm } from "./lib/remote_cdm.js"; - let manifests = new Map(); let requests = new Map(); let sessions = new Map(); let logs = []; chrome.webRequest.onBeforeSendHeaders.addListener( - function(details) { - if (details.method === "GET") { - if (!requests.has(details.url)) { - const headers = details.requestHeaders - .filter(item => !( - item.name.startsWith('sec-ch-ua') || - item.name.startsWith('Sec-Fetch') || - item.name.startsWith('Accept-') || - item.name.startsWith('Host') || - item.name === "Connection" - )).reduce((acc, item) => { - acc[item.name] = item.value; - return acc; - }, {}); - requests.set(details.url, headers); - } - } - }, - {urls: [""]}, - ['requestHeaders', chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS].filter(Boolean) + function (details) { + if (details.method === "GET") { + if (!requests.has(details.url)) { + const headers = details.requestHeaders + .filter( + (item) => + !( + item.name.startsWith("sec-ch-ua") || + item.name.startsWith("Sec-Fetch") || + item.name.startsWith("Accept-") || + item.name.startsWith("Host") || + item.name === "Connection" + ), + ) + .reduce((acc, item) => { + acc[item.name] = item.value; + return acc; + }, {}); + requests.set(details.url, headers); + } + } + }, + { urls: [""] }, + [ + "requestHeaders", + chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS, + ].filter(Boolean), ); async function parseClearKey(body, sendResponse, tab_url) { - const clearkey = JSON.parse(atob(body)); - - const formatted_keys = clearkey["keys"].map(key => ({ - ...key, - kid: Util.bytesToHex(Util.b64.decode(key.kid.replace(/-/g, "+").replace(/_/g, "/") + "==")), - k: Util.bytesToHex(Util.b64.decode(key.k.replace(/-/g, "+").replace(/_/g, "/") + "==")) - })); - const pssh_data = btoa(JSON.stringify({kids: clearkey["keys"].map(key => key.k)})); - - if (logs.filter(log => log.pssh_data === pssh_data).length > 0) { - console.log("[WidevineProxy2]", `KEYS_ALREADY_RETRIEVED: ${pssh_data}`); - sendResponse(); - return; - } - - console.log("[WidevineProxy2]", "CLEARKEY KEYS", formatted_keys, tab_url); - const log = { - type: "CLEARKEY", - pssh_data: pssh_data, - keys: formatted_keys, - url: tab_url, - timestamp: Math.floor(Date.now() / 1000), - manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [] - } - logs.push(log); - - await AsyncLocalStorage.setStorage({[pssh_data]: log}); + const clearkey = JSON.parse(atob(body)); + + const formatted_keys = clearkey["keys"].map((key) => ({ + ...key, + kid: Util.bytesToHex( + Util.b64.decode(key.kid.replace(/-/g, "+").replace(/_/g, "/") + "=="), + ), + k: Util.bytesToHex( + Util.b64.decode(key.k.replace(/-/g, "+").replace(/_/g, "/") + "=="), + ), + })); + const pssh_data = btoa( + JSON.stringify({ kids: clearkey["keys"].map((key) => key.k) }), + ); + + if (logs.filter((log) => log.pssh_data === pssh_data).length > 0) { + console.log("[WidevineProxy2]", `KEYS_ALREADY_RETRIEVED: ${pssh_data}`); sendResponse(); + return; + } + + console.log("[WidevineProxy2]", "CLEARKEY KEYS", formatted_keys, tab_url); + const log = { + type: "CLEARKEY", + pssh_data: pssh_data, + keys: formatted_keys, + url: tab_url, + timestamp: Math.floor(Date.now() / 1000), + manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [], + }; + logs.push(log); + + await AsyncLocalStorage.setStorage({ [pssh_data]: log }); + sendResponse(); } async function generateChallenge(body, sendResponse) { - const signed_message = SignedMessage.decode(Util.b64.decode(body)); - const license_request = LicenseRequest.decode(signed_message.msg); - const pssh_data = license_request.contentId.widevinePsshData.psshData[0]; - - if (!pssh_data) { - console.log("[WidevineProxy2]", "NO_PSSH_DATA_IN_CHALLENGE"); - sendResponse(body); - return; - } - - if (logs.filter(log => log.pssh_data === Session.psshDataToPsshBoxB64(pssh_data)).length > 0) { - console.log("[WidevineProxy2]", `KEYS_ALREADY_RETRIEVED: ${Util.b64.encode(pssh_data)}`); - sendResponse(body); - return; - } - - const selected_device_name = await DeviceManager.getSelectedWidevineDevice(); - if (!selected_device_name) { - sendResponse(body); - return; - } - - const device_b64 = await DeviceManager.loadWidevineDevice(selected_device_name); - const widevine_device = new WidevineDevice(Util.b64.decode(device_b64).buffer); - - const private_key = `-----BEGIN RSA PRIVATE KEY-----${Util.b64.encode(widevine_device.private_key)}-----END RSA PRIVATE KEY-----`; - const session = new Session( - { - privateKey: private_key, - identifierBlob: widevine_device.client_id_bytes - }, - pssh_data + const signed_message = SignedMessage.decode(Util.b64.decode(body)); + const license_request = LicenseRequest.decode(signed_message.msg); + const pssh_data = license_request.contentId.widevinePsshData.psshData[0]; + + if (!pssh_data) { + console.log("[WidevineProxy2]", "NO_PSSH_DATA_IN_CHALLENGE"); + sendResponse(body); + return; + } + + if ( + logs.filter( + (log) => log.pssh_data === Session.psshDataToPsshBoxB64(pssh_data), + ).length > 0 + ) { + console.log( + "[WidevineProxy2]", + `KEYS_ALREADY_RETRIEVED: ${Util.b64.encode(pssh_data)}`, ); + sendResponse(body); + return; + } + + const selected_device_name = await DeviceManager.getSelectedWidevineDevice(); + if (!selected_device_name) { + sendResponse(body); + return; + } + + const device_b64 = + await DeviceManager.loadWidevineDevice(selected_device_name); + const widevine_device = new WidevineDevice( + Util.b64.decode(device_b64).buffer, + ); + + const private_key = `-----BEGIN RSA PRIVATE KEY-----${Util.b64.encode(widevine_device.private_key)}-----END RSA PRIVATE KEY-----`; + const session = new Session( + { + privateKey: private_key, + identifierBlob: widevine_device.client_id_bytes, + }, + pssh_data, + ); - const [challenge, request_id] = session.createLicenseRequest(LicenseType.STREAMING, widevine_device.type === 2); - sessions.set(Util.b64.encode(request_id), session); + const [challenge, request_id] = session.createLicenseRequest( + LicenseType.STREAMING, + widevine_device.type === 2, + ); + sessions.set(Util.b64.encode(request_id), session); - sendResponse(Util.b64.encode(challenge)); + sendResponse(Util.b64.encode(challenge)); } async function parseLicense(body, sendResponse, tab_url) { - const license = Util.b64.decode(body); - const signed_license_message = SignedMessage.decode(license); - - if (signed_license_message.type !== SignedMessage.MessageType.LICENSE) { - sendResponse(); - return; - } + const license = Util.b64.decode(body); + const signed_license_message = SignedMessage.decode(license); - const license_obj = License.decode(signed_license_message.msg); - const loaded_request_id = Util.b64.encode(license_obj.id.requestId); + if (signed_license_message.type !== SignedMessage.MessageType.LICENSE) { + sendResponse(); + return; + } - if (!sessions.has(loaded_request_id)) { - sendResponse(); - return; - } + const license_obj = License.decode(signed_license_message.msg); + const loaded_request_id = Util.b64.encode(license_obj.id.requestId); - const loadedSession = sessions.get(loaded_request_id); - const keys = await loadedSession.parseLicense(license); - const pssh = loadedSession.getPSSH(); - - console.log("[WidevineProxy2]", "KEYS", JSON.stringify(keys), tab_url); - const log = { - type: "WIDEVINE", - pssh_data: pssh, - keys: keys, - url: tab_url, - timestamp: Math.floor(Date.now() / 1000), - manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [] - } - logs.push(log); - await AsyncLocalStorage.setStorage({[pssh]: log}); - - sessions.delete(loaded_request_id); + if (!sessions.has(loaded_request_id)) { sendResponse(); + return; + } + + const loadedSession = sessions.get(loaded_request_id); + const keys = await loadedSession.parseLicense(license); + const pssh = loadedSession.getPSSH(); + + console.log("[WidevineProxy2]", "KEYS", JSON.stringify(keys), tab_url); + const log = { + type: "WIDEVINE", + pssh_data: pssh, + keys: keys, + url: tab_url, + timestamp: Math.floor(Date.now() / 1000), + manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [], + }; + logs.push(log); + await AsyncLocalStorage.setStorage({ [pssh]: log }); + + sessions.delete(loaded_request_id); + sendResponse(); } async function generateChallengeRemote(body, sendResponse) { - const signed_message = SignedMessage.decode(Util.b64.decode(body)); - const license_request = LicenseRequest.decode(signed_message.msg); - const pssh_data = license_request.contentId.widevinePsshData.psshData[0]; - - if (!pssh_data) { - console.log("[WidevineProxy2]", "NO_PSSH_DATA_IN_CHALLENGE"); - sendResponse(body); - return; - } - - const pssh = Session.psshDataToPsshBoxB64(pssh_data); - - if (logs.filter(log => log.pssh_data === pssh).length > 0) { - console.log("[WidevineProxy2]", `KEYS_ALREADY_RETRIEVED: ${Util.b64.encode(pssh_data)}`); - sendResponse(body); - return; - } - - const selected_remote_cdm_name = await RemoteCDMManager.getSelectedRemoteCDM(); - if (!selected_remote_cdm_name) { - sendResponse(body); - return; - } - - const selected_remote_cdm = JSON.parse(await RemoteCDMManager.loadRemoteCDM(selected_remote_cdm_name)); - const remote_cdm = RemoteCdm.from_object(selected_remote_cdm); - - const session_id = await remote_cdm.open(); - const challenge_b64 = await remote_cdm.get_license_challenge(session_id, pssh, true); - - const signed_challenge_message = SignedMessage.decode(Util.b64.decode(challenge_b64)); - const challenge_message = LicenseRequest.decode(signed_challenge_message.msg); - - sessions.set(Util.b64.encode(challenge_message.contentId.widevinePsshData.requestId), { - id: session_id, - pssh: pssh - }); - sendResponse(challenge_b64); + const signed_message = SignedMessage.decode(Util.b64.decode(body)); + const license_request = LicenseRequest.decode(signed_message.msg); + const pssh_data = license_request.contentId.widevinePsshData.psshData[0]; + + if (!pssh_data) { + console.log("[WidevineProxy2]", "NO_PSSH_DATA_IN_CHALLENGE"); + sendResponse(body); + return; + } + + const pssh = Session.psshDataToPsshBoxB64(pssh_data); + + if (logs.filter((log) => log.pssh_data === pssh).length > 0) { + console.log( + "[WidevineProxy2]", + `KEYS_ALREADY_RETRIEVED: ${Util.b64.encode(pssh_data)}`, + ); + sendResponse(body); + return; + } + + const selected_remote_cdm_name = + await RemoteCDMManager.getSelectedRemoteCDM(); + if (!selected_remote_cdm_name) { + sendResponse(body); + return; + } + + const selected_remote_cdm = JSON.parse( + await RemoteCDMManager.loadRemoteCDM(selected_remote_cdm_name), + ); + const remote_cdm = RemoteCdm.from_object(selected_remote_cdm); + + const session_id = await remote_cdm.open(); + const challenge_b64 = await remote_cdm.get_license_challenge( + session_id, + pssh, + true, + ); + + const signed_challenge_message = SignedMessage.decode( + Util.b64.decode(challenge_b64), + ); + const challenge_message = LicenseRequest.decode(signed_challenge_message.msg); + + sessions.set( + Util.b64.encode(challenge_message.contentId.widevinePsshData.requestId), + { + id: session_id, + pssh: pssh, + }, + ); + sendResponse(challenge_b64); } async function parseLicenseRemote(body, sendResponse, tab_url) { - const license = Util.b64.decode(body); - const signed_license_message = SignedMessage.decode(license); - - if (signed_license_message.type !== SignedMessage.MessageType.LICENSE) { - sendResponse(); - return; - } - - const license_obj = License.decode(signed_license_message.msg); - const loaded_request_id = Util.b64.encode(license_obj.id.requestId); + const license = Util.b64.decode(body); + const signed_license_message = SignedMessage.decode(license); - if (!sessions.has(loaded_request_id)) { - sendResponse(); - return; - } - - const session_id = sessions.get(loaded_request_id); + if (signed_license_message.type !== SignedMessage.MessageType.LICENSE) { + sendResponse(); + return; + } - const selected_remote_cdm_name = await RemoteCDMManager.getSelectedRemoteCDM(); - if (!selected_remote_cdm_name) { - sendResponse(); - return; - } + const license_obj = License.decode(signed_license_message.msg); + const loaded_request_id = Util.b64.encode(license_obj.id.requestId); - const selected_remote_cdm = JSON.parse(await RemoteCDMManager.loadRemoteCDM(selected_remote_cdm_name)); - const remote_cdm = RemoteCdm.from_object(selected_remote_cdm); + if (!sessions.has(loaded_request_id)) { + sendResponse(); + return; + } - await remote_cdm.parse_license(session_id.id, body); - const returned_keys = await remote_cdm.get_keys(session_id.id, "CONTENT"); - await remote_cdm.close(session_id.id); + const session_id = sessions.get(loaded_request_id); - if (returned_keys.length === 0) { - sendResponse(); - return; - } + const selected_remote_cdm_name = + await RemoteCDMManager.getSelectedRemoteCDM(); + if (!selected_remote_cdm_name) { + sendResponse(); + return; + } - const keys = returned_keys.map(({ key, key_id }) => ({ k: key, kid: key_id })); + const selected_remote_cdm = JSON.parse( + await RemoteCDMManager.loadRemoteCDM(selected_remote_cdm_name), + ); + const remote_cdm = RemoteCdm.from_object(selected_remote_cdm); - console.log("[WidevineProxy2]", "KEYS", JSON.stringify(keys), tab_url); - const log = { - type: "WIDEVINE", - pssh_data: session_id.pssh, - keys: keys, - url: tab_url, - timestamp: Math.floor(Date.now() / 1000), - manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [] - } - logs.push(log); - await AsyncLocalStorage.setStorage({[session_id.pssh]: log}); + await remote_cdm.parse_license(session_id.id, body); + const returned_keys = await remote_cdm.get_keys(session_id.id, "CONTENT"); + await remote_cdm.close(session_id.id); - sessions.delete(loaded_request_id); + if (returned_keys.length === 0) { sendResponse(); + return; + } + + const keys = returned_keys.map(({ key, key_id }) => ({ + k: key, + kid: key_id, + })); + + console.log("[WidevineProxy2]", "KEYS", JSON.stringify(keys), tab_url); + const log = { + type: "WIDEVINE", + pssh_data: session_id.pssh, + keys: keys, + url: tab_url, + timestamp: Math.floor(Date.now() / 1000), + manifests: manifests.has(tab_url) ? manifests.get(tab_url) : [], + }; + logs.push(log); + await AsyncLocalStorage.setStorage({ [session_id.pssh]: log }); + + sessions.delete(loaded_request_id); + sendResponse(); } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { - (async () => { - const tab_url = sender.tab ? sender.tab.url : null; - - switch (message.type) { - case "REQUEST": - if (!await SettingsManager.getEnabled()) { - sendResponse(message.body); - manifests.clear(); - return; - } - - try { - JSON.parse(atob(message.body)); - sendResponse(message.body); - return; - } catch { - if (message.body) { - const device_type = await SettingsManager.getSelectedDeviceType(); - switch (device_type) { - case "WVD": - await generateChallenge(message.body, sendResponse); - break; - case "REMOTE": - await generateChallengeRemote(message.body, sendResponse); - break; - } - } - } - break; + (async () => { + const tab_url = sender.tab ? sender.tab.url : null; + + switch (message.type) { + case "REQUEST": + if (!(await SettingsManager.getEnabled())) { + sendResponse(message.body); + manifests.clear(); + return; + } - case "RESPONSE": - if (!await SettingsManager.getEnabled()) { - sendResponse(message.body); - manifests.clear(); - return; - } - - try { - await parseClearKey(message.body, sendResponse, tab_url); - return; - } catch (e) { - const device_type = await SettingsManager.getSelectedDeviceType(); - switch (device_type) { - case "WVD": - await parseLicense(message.body, sendResponse, tab_url); - break; - case "REMOTE": - await parseLicenseRemote(message.body, sendResponse, tab_url); - break; - } - return; - } - case "GET_LOGS": - sendResponse(logs); - break; - case "OPEN_PICKER_WVD": - chrome.windows.create({ - url: 'picker/wvd/filePicker.html', - type: 'popup', - width: 300, - height: 200, - }); - break; - case "OPEN_PICKER_WVD_MOBILE": - chrome.tabs.create({ - url: chrome.runtime.getURL("picker/wvd/filePicker.html") - }); - break; - case "OPEN_PICKER_REMOTE": - chrome.windows.create({ - url: 'picker/remote/filePicker.html', - type: 'popup', - width: 300, - height: 200, - }); - break; - case "OPEN_PICKER_REMOTE_MOBILE": - chrome.tabs.create({ - url: chrome.runtime.getURL("picker/remote/filePicker.html") - }); + try { + JSON.parse(atob(message.body)); + sendResponse(message.body); + return; + } catch { + if (message.body) { + const device_type = await SettingsManager.getSelectedDeviceType(); + switch (device_type) { + case "WVD": + await generateChallenge(message.body, sendResponse); break; - case "CLEAR": - logs = []; - manifests.clear(); + case "REMOTE": + await generateChallengeRemote(message.body, sendResponse); break; - case "MANIFEST": - const parsed = JSON.parse(message.body); - const element = { - type: parsed.type, - url: parsed.url, - headers: requests.has(parsed.url) ? requests.get(parsed.url) : [], - }; - - if (!manifests.has(tab_url)) { - manifests.set(tab_url, [element]); - } else { - let elements = manifests.get(tab_url); - if (!elements.some(e => e.url === parsed.url)) { - elements.push(element); - manifests.set(tab_url, elements); - } - } - sendResponse(); + } + } + } + break; + + case "RESPONSE": + if (!(await SettingsManager.getEnabled())) { + sendResponse(message.body); + manifests.clear(); + return; + } + + try { + await parseClearKey(message.body, sendResponse, tab_url); + return; + } catch (e) { + const device_type = await SettingsManager.getSelectedDeviceType(); + switch (device_type) { + case "WVD": + await parseLicense(message.body, sendResponse, tab_url); + break; + case "REMOTE": + await parseLicenseRemote(message.body, sendResponse, tab_url); + break; + } + return; + } + case "GET_LOGS": + sendResponse(logs); + break; + case "OPEN_PICKER_WVD": + chrome.windows.create({ + url: "picker/wvd/filePicker.html", + type: "popup", + width: 300, + height: 200, + }); + break; + case "OPEN_PICKER_WVD_MOBILE": + chrome.tabs.create({ + url: chrome.runtime.getURL("picker/wvd/filePicker.html"), + }); + break; + case "OPEN_PICKER_REMOTE": + chrome.windows.create({ + url: "picker/remote/filePicker.html", + type: "popup", + width: 300, + height: 200, + }); + break; + case "OPEN_PICKER_REMOTE_MOBILE": + chrome.tabs.create({ + url: chrome.runtime.getURL("picker/remote/filePicker.html"), + }); + break; + case "CLEAR": + logs = []; + manifests.clear(); + break; + case "MANIFEST": + const parsed = JSON.parse(message.body); + const element = { + type: parsed.type, + url: parsed.url, + headers: requests.has(parsed.url) ? requests.get(parsed.url) : [], + }; + + if (!manifests.has(tab_url)) { + manifests.set(tab_url, [element]); + } else { + let elements = manifests.get(tab_url); + if (!elements.some((e) => e.url === parsed.url)) { + elements.push(element); + manifests.set(tab_url, elements); + } } - })(); - return true; + sendResponse(); + } + })(); + return true; }); diff --git a/content_script.js b/content_script.js index 7c18e70..e23b2f4 100644 --- a/content_script.js +++ b/content_script.js @@ -1,193 +1,241 @@ (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('')) { - 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('')) { - return "MSS"; - } + 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("")) { + 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("") + ) { + 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); + } - 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, - } + 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, }); - document.dispatchEvent(requestEvent); - }); - } - - if (typeof EventTarget !== 'undefined') { - proxy(EventTarget.prototype, 'addEventListener', (target, thisArg, args) => { - const [type, listener] = args; - - if (thisArg == null || !(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); - } - }; + event.stopImmediatePropagation(); + event.preventDefault(); - 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.dispatchEvent(clonedEvent); + return; + } - thisArg.requestMethod = method.toUpperCase(); - thisArg.requestURL = url; + if (listener.handleEvent) { + listener.handleEvent.call(listener, event); + } else { + listener.call(this, event); + } + }; 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, - })); - } - } - }); + }, + ); + } + + if (typeof MediaKeySession !== "undefined") { + proxy( + MediaKeySession.prototype, + "update", + async (target, thisArg, args) => { + if (thisArg == null || !(thisArg instanceof MediaKeySession)) { + return target.apply(thisArg, args); + } - 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, + }), + ); + } + } }); - 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 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; - }); + return response; + }); })(); diff --git a/lib/cdm.js b/lib/cdm.js index a338a21..43e256f 100644 --- a/lib/cdm.js +++ b/lib/cdm.js @@ -1,372 +1,436 @@ -import { AES_CMAC } from "./cmac.js" -import { Util } from "./util.js" - -const { ClientIdentification, DrmCertificate, EncryptedClientIdentification, License, LicenseRequest, LicenseType, - ProtocolVersion, SignedDrmCertificate, SignedMessage, WidevinePsshData } = protobuf.roots.default.license_protocol; - +import { AES_CMAC } from "./cmac.js"; +import { Util } from "./util.js"; + +const { + ClientIdentification, + DrmCertificate, + EncryptedClientIdentification, + License, + LicenseRequest, + LicenseType, + ProtocolVersion, + SignedDrmCertificate, + SignedMessage, + WidevinePsshData, +} = protobuf.roots.default.license_protocol; export const SERVICE_CERTIFICATE_CHALLENGE = new Uint8Array([0x08, 0x04]); -const WIDEVINE_SYSTEM_ID = new Uint8Array([0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed]); -const PSSH_MAGIC = new Uint8Array([0x70, 0x73, 0x73, 0x68]) +const WIDEVINE_SYSTEM_ID = new Uint8Array([ + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, + 0x1d, 0x21, 0xed, +]); +const PSSH_MAGIC = new Uint8Array([0x70, 0x73, 0x73, 0x68]); const WIDEVINE_ROOT_PUBLIC_KEY = new Uint8Array([ - 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd, - 0xf2, 0xc3, 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6, - 0x6d, 0x35, 0x7d, 0xa6, 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98, - 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, 0xbe, 0x34, 0x23, 0x8b, 0xab, - 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, 0xd6, 0x4c, 0xdf, - 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, 0xd7, - 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50, 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd, - 0x29, 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7, - 0xb2, 0x33, 0xb9, 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11, - 0xff, 0x6b, 0x36, 0xe4, 0x74, 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33, - 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, 0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, 0x5e, 0x48, 0x77, 0x5b, - 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, 0x5e, 0x69, - 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24, - 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5, - 0x08, 0xfd, 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d, - 0xc5, 0xfe, 0x1c, 0xdc, 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f, - 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01 + 0x30, 0x82, 0x01, 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xb4, 0xfe, 0x39, 0xc3, + 0x65, 0x90, 0x03, 0xdb, 0x3c, 0x11, 0x97, 0x09, 0xe8, 0x68, 0xcd, 0xf2, 0xc3, + 0x5e, 0x9b, 0xf2, 0xe7, 0x4d, 0x23, 0xb1, 0x10, 0xdb, 0x87, 0x65, 0xdf, 0xdc, + 0xfb, 0x9f, 0x35, 0xa0, 0x57, 0x03, 0x53, 0x4c, 0xf6, 0x6d, 0x35, 0x7d, 0xa6, + 0x78, 0xdb, 0xb3, 0x36, 0xd2, 0x3f, 0x9c, 0x40, 0xa9, 0x95, 0x26, 0x72, 0x7f, + 0xb8, 0xbe, 0x66, 0xdf, 0xc5, 0x21, 0x98, 0x78, 0x15, 0x16, 0x68, 0x5d, 0x2f, + 0x46, 0x0e, 0x43, 0xcb, 0x8a, 0x84, 0x39, 0xab, 0xfb, 0xb0, 0x35, 0x80, 0x22, + 0xbe, 0x34, 0x23, 0x8b, 0xab, 0x53, 0x5b, 0x72, 0xec, 0x4b, 0xb5, 0x48, 0x69, + 0x53, 0x3e, 0x47, 0x5f, 0xfd, 0x09, 0xfd, 0xa7, 0x76, 0x13, 0x8f, 0x0f, 0x92, + 0xd6, 0x4c, 0xdf, 0xae, 0x76, 0xa9, 0xba, 0xd9, 0x22, 0x10, 0xa9, 0x9d, 0x71, + 0x45, 0xd6, 0xd7, 0xe1, 0x19, 0x25, 0x85, 0x9c, 0x53, 0x9a, 0x97, 0xeb, 0x84, + 0xd7, 0xcc, 0xa8, 0x88, 0x82, 0x20, 0x70, 0x26, 0x20, 0xfd, 0x7e, 0x40, 0x50, + 0x27, 0xe2, 0x25, 0x93, 0x6f, 0xbc, 0x3e, 0x72, 0xa0, 0xfa, 0xc1, 0xbd, 0x29, + 0xb4, 0x4d, 0x82, 0x5c, 0xc1, 0xb4, 0xcb, 0x9c, 0x72, 0x7e, 0xb0, 0xe9, 0x8a, + 0x17, 0x3e, 0x19, 0x63, 0xfc, 0xfd, 0x82, 0x48, 0x2b, 0xb7, 0xb2, 0x33, 0xb9, + 0x7d, 0xec, 0x4b, 0xba, 0x89, 0x1f, 0x27, 0xb8, 0x9b, 0x88, 0x48, 0x84, 0xaa, + 0x18, 0x92, 0x0e, 0x65, 0xf5, 0xc8, 0x6c, 0x11, 0xff, 0x6b, 0x36, 0xe4, 0x74, + 0x34, 0xca, 0x8c, 0x33, 0xb1, 0xf9, 0xb8, 0x8e, 0xb4, 0xe6, 0x12, 0xe0, 0x02, + 0x98, 0x79, 0x52, 0x5e, 0x45, 0x33, 0xff, 0x11, 0xdc, 0xeb, 0xc3, 0x53, 0xba, + 0x7c, 0x60, 0x1a, 0x11, 0x3d, 0x00, 0xfb, 0xd2, 0xb7, 0xaa, 0x30, 0xfa, 0x4f, + 0x5e, 0x48, 0x77, 0x5b, 0x17, 0xdc, 0x75, 0xef, 0x6f, 0xd2, 0x19, 0x6d, 0xdc, + 0xbe, 0x7f, 0xb0, 0x78, 0x8f, 0xdc, 0x82, 0x60, 0x4c, 0xbf, 0xe4, 0x29, 0x06, + 0x5e, 0x69, 0x8c, 0x39, 0x13, 0xad, 0x14, 0x25, 0xed, 0x19, 0xb2, 0xf2, 0x9f, + 0x01, 0x82, 0x0d, 0x56, 0x44, 0x88, 0xc8, 0x35, 0xec, 0x1f, 0x11, 0xb3, 0x24, + 0xe0, 0x59, 0x0d, 0x37, 0xe4, 0x47, 0x3c, 0xea, 0x4b, 0x7f, 0x97, 0x31, 0x1c, + 0x81, 0x7c, 0x94, 0x8a, 0x4c, 0x7d, 0x68, 0x15, 0x84, 0xff, 0xa5, 0x08, 0xfd, + 0x18, 0xe7, 0xe7, 0x2b, 0xe4, 0x47, 0x27, 0x12, 0x11, 0xb8, 0x23, 0xec, 0x58, + 0x93, 0x3c, 0xac, 0x12, 0xd2, 0x88, 0x6d, 0x41, 0x3d, 0xc5, 0xfe, 0x1c, 0xdc, + 0xb9, 0xf8, 0xd4, 0x51, 0x3e, 0x07, 0xe5, 0x03, 0x6f, 0xa7, 0x12, 0xe8, 0x12, + 0xf7, 0xb5, 0xce, 0xa6, 0x96, 0x55, 0x3f, 0x78, 0xb4, 0x64, 0x82, 0x50, 0xd2, + 0x33, 0x5f, 0x91, 0x02, 0x03, 0x01, 0x00, 0x01, ]); const STAGING_PRIVACY_CERT = new Uint8Array([ - 0x0a, 0xbf, 0x02, 0x08, 0x03, 0x12, 0x10, 0x28, 0x70, 0x34, 0x54, 0xc0, 0x08, 0xf6, 0x36, 0x18, 0xad, 0xe7, 0x44, 0x3d, 0xb6, 0xc4, 0xc8, 0x18, - 0x8b, 0xe7, 0xf9, 0x90, 0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb5, 0x21, 0x12, 0xb8, 0xd0, 0x5d, 0x02, - 0x3f, 0xcc, 0x5d, 0x95, 0xe2, 0xc2, 0x51, 0xc1, 0xc6, 0x49, 0xb4, 0x17, 0x7c, 0xd8, 0xd2, 0xbe, 0xef, 0x35, 0x5b, 0xb0, 0x67, 0x43, 0xde, 0x66, - 0x1e, 0x3d, 0x2a, 0xbc, 0x31, 0x82, 0xb7, 0x99, 0x46, 0xd5, 0x5f, 0xdc, 0x08, 0xdf, 0xe9, 0x54, 0x07, 0x81, 0x5e, 0x9a, 0x62, 0x74, 0xb3, 0x22, - 0xa2, 0xc7, 0xf5, 0xe0, 0x67, 0xbb, 0x5f, 0x0a, 0xc0, 0x7a, 0x89, 0xd4, 0x5a, 0xea, 0x94, 0xb2, 0x51, 0x6f, 0x07, 0x5b, 0x66, 0xef, 0x81, 0x1d, - 0x0d, 0x26, 0xe1, 0xb9, 0xa6, 0xb8, 0x94, 0xf2, 0xb9, 0x85, 0x79, 0x62, 0xaa, 0x17, 0x1c, 0x4f, 0x66, 0x63, 0x0d, 0x3e, 0x4c, 0x60, 0x27, 0x18, - 0x89, 0x7f, 0x5e, 0x1e, 0xf9, 0xb6, 0xaa, 0xf5, 0xad, 0x4d, 0xba, 0x2a, 0x7e, 0x14, 0x17, 0x6d, 0xf1, 0x34, 0xa1, 0xd3, 0x18, 0x5b, 0x5a, 0x21, - 0x8a, 0xc0, 0x5a, 0x4c, 0x41, 0xf0, 0x81, 0xef, 0xff, 0x80, 0xa3, 0xa0, 0x40, 0xc5, 0x0b, 0x09, 0xbb, 0xc7, 0x40, 0xee, 0xdc, 0xd8, 0xf1, 0x4d, - 0x67, 0x5a, 0x91, 0x98, 0x0f, 0x92, 0xca, 0x7d, 0xdc, 0x64, 0x6a, 0x06, 0xad, 0xad, 0x51, 0x01, 0xf7, 0x4a, 0x0e, 0x49, 0x8c, 0xc0, 0x1f, 0x00, - 0x53, 0x2b, 0xac, 0x21, 0x78, 0x50, 0xbd, 0x90, 0x5e, 0x90, 0x92, 0x36, 0x56, 0xb7, 0xdf, 0xef, 0xef, 0x42, 0x48, 0x67, 0x67, 0xf3, 0x3e, 0xf6, - 0x28, 0x3d, 0x4f, 0x42, 0x54, 0xab, 0x72, 0x58, 0x93, 0x90, 0xbe, 0xe5, 0x58, 0x08, 0xf1, 0xd6, 0x68, 0x08, 0x0d, 0x45, 0xd8, 0x93, 0xc2, 0xbc, - 0xa2, 0xf7, 0x4d, 0x60, 0xa0, 0xc0, 0xd0, 0xa0, 0x99, 0x3c, 0xef, 0x01, 0x60, 0x47, 0x03, 0x33, 0x4c, 0x36, 0x38, 0x13, 0x94, 0x86, 0xbc, 0x9d, - 0xaf, 0x24, 0xfd, 0x67, 0xa0, 0x7f, 0x9a, 0xd9, 0x43, 0x02, 0x03, 0x01, 0x00, 0x01, 0x3a, 0x12, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x98, 0x3e, 0x30, 0x35, 0x26, 0x75, 0xf4, 0x0b, 0xa7, 0x15, 0xfc, - 0x24, 0x9b, 0xda, 0xe5, 0xd4, 0xac, 0x72, 0x49, 0xa2, 0x66, 0x65, 0x21, 0xe4, 0x36, 0x55, 0x73, 0x95, 0x29, 0x72, 0x1f, 0xf8, 0x80, 0xe0, 0xaa, - 0xef, 0xc5, 0xe2, 0x7b, 0xc9, 0x80, 0xda, 0xea, 0xda, 0xbf, 0x3f, 0xc3, 0x86, 0xd0, 0x84, 0xa0, 0x2c, 0x82, 0x53, 0x78, 0x48, 0xcc, 0x75, 0x3f, - 0xf4, 0x97, 0xb0, 0x11, 0xa7, 0xda, 0x97, 0x78, 0x8a, 0x00, 0xe2, 0xaa, 0x6b, 0x84, 0xcd, 0x7d, 0x71, 0xc0, 0x7a, 0x48, 0xeb, 0xf6, 0x16, 0x02, - 0xcc, 0xa5, 0xa3, 0xf3, 0x20, 0x30, 0xa7, 0x29, 0x5c, 0x30, 0xda, 0x91, 0x5b, 0x91, 0xdc, 0x18, 0xb9, 0xbc, 0x95, 0x93, 0xb8, 0xde, 0x8b, 0xb5, - 0x0f, 0x0d, 0xed, 0xc1, 0x29, 0x38, 0xb8, 0xe9, 0xe0, 0x39, 0xcd, 0xde, 0x18, 0xfa, 0x82, 0xe8, 0x1b, 0xb0, 0x32, 0x63, 0x0f, 0xe9, 0x55, 0xd8, - 0x5a, 0x56, 0x6c, 0xe1, 0x54, 0x30, 0x0b, 0xf6, 0xd4, 0xc1, 0xbd, 0x12, 0x69, 0x66, 0x35, 0x6b, 0x28, 0x7d, 0x65, 0x7b, 0x18, 0xce, 0x63, 0xd0, - 0xef, 0xd4, 0x5f, 0xc5, 0x26, 0x9e, 0x97, 0xea, 0xb1, 0x1c, 0xb5, 0x63, 0xe5, 0x56, 0x43, 0xb2, 0x6f, 0xf4, 0x9f, 0x10, 0x9c, 0x21, 0x01, 0xaf, - 0xca, 0xf3, 0x5b, 0x83, 0x2f, 0x28, 0x8f, 0x0d, 0x9d, 0x45, 0x96, 0x0e, 0x25, 0x9e, 0x85, 0xfb, 0x5d, 0x24, 0xdb, 0xd2, 0xcf, 0x82, 0x76, 0x4c, - 0x5d, 0xd9, 0xbf, 0x72, 0x7e, 0xfb, 0xe9, 0xc8, 0x61, 0xf8, 0x69, 0x32, 0x1f, 0x6a, 0xde, 0x18, 0x90, 0x5f, 0x4d, 0x92, 0xf9, 0xa6, 0xda, 0x65, - 0x36, 0xdb, 0x84, 0x75, 0x87, 0x1d, 0x16, 0x8e, 0x87, 0x0b, 0xb2, 0x30, 0x3c, 0xf7, 0x0c, 0x6e, 0x97, 0x84, 0xc9, 0x3d, 0x2d, 0xe8, 0x45, 0xad, - 0x82, 0x62, 0xbe, 0x7e, 0x0d, 0x4e, 0x2e, 0x4a, 0x07, 0x59, 0xce, 0xf8, 0x2d, 0x10, 0x9d, 0x25, 0x92, 0xc7, 0x24, 0x29, 0xf8, 0xc0, 0x17, 0x42, - 0xba, 0xe2, 0xb3, 0xde, 0xca, 0xdb, 0xc3, 0x3c, 0x3e, 0x5f, 0x4b, 0xaf, 0x5e, 0x16, 0xec, 0xb7, 0x4e, 0xad, 0xba, 0xfc, 0xb7, 0xc6, 0x70, 0x5f, - 0x7a, 0x9e, 0x3b, 0x6f, 0x39, 0x40, 0x38, 0x3f, 0x9c, 0x51, 0x16, 0xd2, 0x02, 0xa2, 0x0c, 0x92, 0x29, 0xee, 0x96, 0x9c, 0x25, 0x19, 0x71, 0x83, - 0x03, 0xb5, 0x0d, 0x01, 0x30, 0xc3, 0x35, 0x2e, 0x06, 0xb0, 0x14, 0xd8, 0x38, 0x54, 0x0f, 0x8a, 0x0c, 0x22, 0x7c, 0x00, 0x11, 0xe0, 0xf5, 0xb3, - 0x8e, 0x4e, 0x29, 0x8e, 0xd2, 0xcb, 0x30, 0x1e, 0xb4, 0x56, 0x49, 0x65, 0xf5, 0x5c, 0x5d, 0x79, 0x75, 0x7a, 0x25, 0x0a, 0x4e, 0xb9, 0xc8, 0x4a, - 0xb3, 0xe6, 0x53, 0x9f, 0x6b, 0x6f, 0xdf, 0x56, 0x89, 0x9e, 0xa2, 0x99, 0x14 -]) - + 0x0a, 0xbf, 0x02, 0x08, 0x03, 0x12, 0x10, 0x28, 0x70, 0x34, 0x54, 0xc0, 0x08, + 0xf6, 0x36, 0x18, 0xad, 0xe7, 0x44, 0x3d, 0xb6, 0xc4, 0xc8, 0x18, 0x8b, 0xe7, + 0xf9, 0x90, 0x05, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xb5, 0x21, 0x12, 0xb8, 0xd0, 0x5d, 0x02, 0x3f, 0xcc, 0x5d, 0x95, + 0xe2, 0xc2, 0x51, 0xc1, 0xc6, 0x49, 0xb4, 0x17, 0x7c, 0xd8, 0xd2, 0xbe, 0xef, + 0x35, 0x5b, 0xb0, 0x67, 0x43, 0xde, 0x66, 0x1e, 0x3d, 0x2a, 0xbc, 0x31, 0x82, + 0xb7, 0x99, 0x46, 0xd5, 0x5f, 0xdc, 0x08, 0xdf, 0xe9, 0x54, 0x07, 0x81, 0x5e, + 0x9a, 0x62, 0x74, 0xb3, 0x22, 0xa2, 0xc7, 0xf5, 0xe0, 0x67, 0xbb, 0x5f, 0x0a, + 0xc0, 0x7a, 0x89, 0xd4, 0x5a, 0xea, 0x94, 0xb2, 0x51, 0x6f, 0x07, 0x5b, 0x66, + 0xef, 0x81, 0x1d, 0x0d, 0x26, 0xe1, 0xb9, 0xa6, 0xb8, 0x94, 0xf2, 0xb9, 0x85, + 0x79, 0x62, 0xaa, 0x17, 0x1c, 0x4f, 0x66, 0x63, 0x0d, 0x3e, 0x4c, 0x60, 0x27, + 0x18, 0x89, 0x7f, 0x5e, 0x1e, 0xf9, 0xb6, 0xaa, 0xf5, 0xad, 0x4d, 0xba, 0x2a, + 0x7e, 0x14, 0x17, 0x6d, 0xf1, 0x34, 0xa1, 0xd3, 0x18, 0x5b, 0x5a, 0x21, 0x8a, + 0xc0, 0x5a, 0x4c, 0x41, 0xf0, 0x81, 0xef, 0xff, 0x80, 0xa3, 0xa0, 0x40, 0xc5, + 0x0b, 0x09, 0xbb, 0xc7, 0x40, 0xee, 0xdc, 0xd8, 0xf1, 0x4d, 0x67, 0x5a, 0x91, + 0x98, 0x0f, 0x92, 0xca, 0x7d, 0xdc, 0x64, 0x6a, 0x06, 0xad, 0xad, 0x51, 0x01, + 0xf7, 0x4a, 0x0e, 0x49, 0x8c, 0xc0, 0x1f, 0x00, 0x53, 0x2b, 0xac, 0x21, 0x78, + 0x50, 0xbd, 0x90, 0x5e, 0x90, 0x92, 0x36, 0x56, 0xb7, 0xdf, 0xef, 0xef, 0x42, + 0x48, 0x67, 0x67, 0xf3, 0x3e, 0xf6, 0x28, 0x3d, 0x4f, 0x42, 0x54, 0xab, 0x72, + 0x58, 0x93, 0x90, 0xbe, 0xe5, 0x58, 0x08, 0xf1, 0xd6, 0x68, 0x08, 0x0d, 0x45, + 0xd8, 0x93, 0xc2, 0xbc, 0xa2, 0xf7, 0x4d, 0x60, 0xa0, 0xc0, 0xd0, 0xa0, 0x99, + 0x3c, 0xef, 0x01, 0x60, 0x47, 0x03, 0x33, 0x4c, 0x36, 0x38, 0x13, 0x94, 0x86, + 0xbc, 0x9d, 0xaf, 0x24, 0xfd, 0x67, 0xa0, 0x7f, 0x9a, 0xd9, 0x43, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x3a, 0x12, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0x67, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, + 0x98, 0x3e, 0x30, 0x35, 0x26, 0x75, 0xf4, 0x0b, 0xa7, 0x15, 0xfc, 0x24, 0x9b, + 0xda, 0xe5, 0xd4, 0xac, 0x72, 0x49, 0xa2, 0x66, 0x65, 0x21, 0xe4, 0x36, 0x55, + 0x73, 0x95, 0x29, 0x72, 0x1f, 0xf8, 0x80, 0xe0, 0xaa, 0xef, 0xc5, 0xe2, 0x7b, + 0xc9, 0x80, 0xda, 0xea, 0xda, 0xbf, 0x3f, 0xc3, 0x86, 0xd0, 0x84, 0xa0, 0x2c, + 0x82, 0x53, 0x78, 0x48, 0xcc, 0x75, 0x3f, 0xf4, 0x97, 0xb0, 0x11, 0xa7, 0xda, + 0x97, 0x78, 0x8a, 0x00, 0xe2, 0xaa, 0x6b, 0x84, 0xcd, 0x7d, 0x71, 0xc0, 0x7a, + 0x48, 0xeb, 0xf6, 0x16, 0x02, 0xcc, 0xa5, 0xa3, 0xf3, 0x20, 0x30, 0xa7, 0x29, + 0x5c, 0x30, 0xda, 0x91, 0x5b, 0x91, 0xdc, 0x18, 0xb9, 0xbc, 0x95, 0x93, 0xb8, + 0xde, 0x8b, 0xb5, 0x0f, 0x0d, 0xed, 0xc1, 0x29, 0x38, 0xb8, 0xe9, 0xe0, 0x39, + 0xcd, 0xde, 0x18, 0xfa, 0x82, 0xe8, 0x1b, 0xb0, 0x32, 0x63, 0x0f, 0xe9, 0x55, + 0xd8, 0x5a, 0x56, 0x6c, 0xe1, 0x54, 0x30, 0x0b, 0xf6, 0xd4, 0xc1, 0xbd, 0x12, + 0x69, 0x66, 0x35, 0x6b, 0x28, 0x7d, 0x65, 0x7b, 0x18, 0xce, 0x63, 0xd0, 0xef, + 0xd4, 0x5f, 0xc5, 0x26, 0x9e, 0x97, 0xea, 0xb1, 0x1c, 0xb5, 0x63, 0xe5, 0x56, + 0x43, 0xb2, 0x6f, 0xf4, 0x9f, 0x10, 0x9c, 0x21, 0x01, 0xaf, 0xca, 0xf3, 0x5b, + 0x83, 0x2f, 0x28, 0x8f, 0x0d, 0x9d, 0x45, 0x96, 0x0e, 0x25, 0x9e, 0x85, 0xfb, + 0x5d, 0x24, 0xdb, 0xd2, 0xcf, 0x82, 0x76, 0x4c, 0x5d, 0xd9, 0xbf, 0x72, 0x7e, + 0xfb, 0xe9, 0xc8, 0x61, 0xf8, 0x69, 0x32, 0x1f, 0x6a, 0xde, 0x18, 0x90, 0x5f, + 0x4d, 0x92, 0xf9, 0xa6, 0xda, 0x65, 0x36, 0xdb, 0x84, 0x75, 0x87, 0x1d, 0x16, + 0x8e, 0x87, 0x0b, 0xb2, 0x30, 0x3c, 0xf7, 0x0c, 0x6e, 0x97, 0x84, 0xc9, 0x3d, + 0x2d, 0xe8, 0x45, 0xad, 0x82, 0x62, 0xbe, 0x7e, 0x0d, 0x4e, 0x2e, 0x4a, 0x07, + 0x59, 0xce, 0xf8, 0x2d, 0x10, 0x9d, 0x25, 0x92, 0xc7, 0x24, 0x29, 0xf8, 0xc0, + 0x17, 0x42, 0xba, 0xe2, 0xb3, 0xde, 0xca, 0xdb, 0xc3, 0x3c, 0x3e, 0x5f, 0x4b, + 0xaf, 0x5e, 0x16, 0xec, 0xb7, 0x4e, 0xad, 0xba, 0xfc, 0xb7, 0xc6, 0x70, 0x5f, + 0x7a, 0x9e, 0x3b, 0x6f, 0x39, 0x40, 0x38, 0x3f, 0x9c, 0x51, 0x16, 0xd2, 0x02, + 0xa2, 0x0c, 0x92, 0x29, 0xee, 0x96, 0x9c, 0x25, 0x19, 0x71, 0x83, 0x03, 0xb5, + 0x0d, 0x01, 0x30, 0xc3, 0x35, 0x2e, 0x06, 0xb0, 0x14, 0xd8, 0x38, 0x54, 0x0f, + 0x8a, 0x0c, 0x22, 0x7c, 0x00, 0x11, 0xe0, 0xf5, 0xb3, 0x8e, 0x4e, 0x29, 0x8e, + 0xd2, 0xcb, 0x30, 0x1e, 0xb4, 0x56, 0x49, 0x65, 0xf5, 0x5c, 0x5d, 0x79, 0x75, + 0x7a, 0x25, 0x0a, 0x4e, 0xb9, 0xc8, 0x4a, 0xb3, 0xe6, 0x53, 0x9f, 0x6b, 0x6f, + 0xdf, 0x56, 0x89, 0x9e, 0xa2, 0x99, 0x14, +]); export class Session { - constructor(contentDecryptionModule, pssh) { - this._devicePrivateKey = forge.pki.privateKeyFromPem( - contentDecryptionModule.privateKey - ) - this._identifierBlob = ClientIdentification.decode( - contentDecryptionModule.identifierBlob - ) - if (typeof pssh === "string") { - pssh = Util.b64.decode(pssh) - } - this._pssh = pssh + constructor(contentDecryptionModule, pssh) { + this._devicePrivateKey = forge.pki.privateKeyFromPem( + contentDecryptionModule.privateKey, + ); + this._identifierBlob = ClientIdentification.decode( + contentDecryptionModule.identifierBlob, + ); + if (typeof pssh === "string") { + pssh = Util.b64.decode(pssh); } - - async setDefaultServiceCertificate() { - await this.setServiceCertificate(STAGING_PRIVACY_CERT) + this._pssh = pssh; + } + + async setDefaultServiceCertificate() { + await this.setServiceCertificate(STAGING_PRIVACY_CERT); + } + + async setServiceCertificateFromMessage(rawSignedMessage) { + const signedMessage = SignedMessage.decode( + new Uint8Array(rawSignedMessage), + ); + if (!signedMessage.msg) { + throw new Error( + "the service certificate message does not contain a message", + ); } - - async setServiceCertificateFromMessage(rawSignedMessage) { - const signedMessage = SignedMessage.decode(new Uint8Array(rawSignedMessage)) - if (!signedMessage.msg) { - throw new Error("the service certificate message does not contain a message") - } - await this.setServiceCertificate(signedMessage.msg) + await this.setServiceCertificate(signedMessage.msg); + } + + async setServiceCertificate(serviceCertificate) { + const signedServiceCertificate = + SignedDrmCertificate.decode(serviceCertificate); + if (!(await this._verifyServiceCertificate(signedServiceCertificate))) { + throw new Error( + "Service certificate is not signed by the Widevine root certificate", + ); } - - async setServiceCertificate(serviceCertificate) { - const signedServiceCertificate = SignedDrmCertificate.decode( - serviceCertificate - ) - if (!(await this._verifyServiceCertificate(signedServiceCertificate))) { - throw new Error("Service certificate is not signed by the Widevine root certificate") - } - this._serviceCertificate = signedServiceCertificate + this._serviceCertificate = signedServiceCertificate; + } + + createLicenseRequest(licenseType = LicenseType.STREAMING, android = false) { + if (Util.sequenceEquals(this._pssh.subarray(4, 8), PSSH_MAGIC)) { + if ( + Util.sequenceEquals(this._pssh.subarray(12, 28), WIDEVINE_SYSTEM_ID) + ) { + this._pssh = this._pssh.subarray(32); + } else { + throw new Error( + `Invalid System ID in PSSH Box: ${Util.bytesToHex(this._pssh.subarray(12, 28))}`, + ); + } } - createLicenseRequest(licenseType = LicenseType.STREAMING, android = false) { - if (Util.sequenceEquals(this._pssh.subarray(4, 8), PSSH_MAGIC)) { - if (Util.sequenceEquals(this._pssh.subarray(12, 28), WIDEVINE_SYSTEM_ID)) { - this._pssh = this._pssh.subarray(32) - } else { - throw new Error(`Invalid System ID in PSSH Box: ${Util.bytesToHex(this._pssh.subarray(12, 28))}`) - } - } - - const pssh = this._parseWidevinePsshData(this._pssh) - if (!pssh) { - throw new Error("Invalid WidevinePsshData found") - } - - const request_id = android ? this._generateAndroidIdentifier() : this._generateGenericIdentifier(); - const licenseRequest = new LicenseRequest({ - type: LicenseRequest.RequestType.NEW, - contentId: new LicenseRequest.ContentIdentification({ - widevinePsshData: new LicenseRequest.ContentIdentification.WidevinePsshData({ - psshData: [this._pssh], - licenseType: licenseType, - requestId: request_id - }) - }), - requestTime: Math.floor(Date.now() / 1000), - protocolVersion: ProtocolVersion.VERSION_2_1, - keyControlNonce: Math.floor(Math.random() * 2 ** 31) - }) - - if (this._serviceCertificate) { - licenseRequest.encryptedClientId = this._encryptClientIdentification( - this._identifierBlob, - this._serviceCertificate - ) - } else { - licenseRequest.clientId = this._identifierBlob - } - - this._rawLicenseRequest = LicenseRequest.encode(licenseRequest).finish() - - const pss = forge.pss.create({ - md: forge.md.sha1.create(), - mgf: forge.mgf.mgf1.create(forge.md.sha1.create()), - saltLength: 20 - }) - - const md = forge.md.sha1.create() - md.update(Util.utf8.decode(this._rawLicenseRequest), "raw") - - const signature = Util.utf8.encode(this._devicePrivateKey.sign(md, pss)) - const signedLicenseRequest = new SignedMessage({ - type: SignedMessage.MessageType.LICENSE_REQUEST, - msg: this._rawLicenseRequest, - signature: signature - }) - - return [SignedMessage.encode(signedLicenseRequest).finish(), request_id] + const pssh = this._parseWidevinePsshData(this._pssh); + if (!pssh) { + throw new Error("Invalid WidevinePsshData found"); } - async parseLicense(rawLicense) { - if (!this._rawLicenseRequest) { - throw new Error("please request a license first") - } - - const signedLicense = SignedMessage.decode(new Uint8Array(rawLicense)) - if (!signedLicense.sessionKey) { - throw new Error("the license does not contain a session key") - } - if (!signedLicense.msg) { - throw new Error("the license does not contain a message") - } - if (!signedLicense.signature) { - throw new Error("the license does not contain a signature") - } - - const sessionKey = this._devicePrivateKey.decrypt( - Util.utf8.decode(signedLicense.sessionKey), - "RSA-OAEP", - { - md: forge.md.sha1.create() - } - ) - - const cmac = new AES_CMAC(Util.utf8.encode(sessionKey)) - const encKeyBase = new Uint8Array([ - ...Util.utf8.encode("ENCRYPTION"), - ...new Uint8Array([0x00]), - ...this._rawLicenseRequest, - ...new Uint8Array([0x00, 0x00, 0x00, 0x80]) - ]) - const authKeyBase = new Uint8Array([ - ...Util.utf8.encode("AUTHENTICATION"), - ...new Uint8Array([0x00]), - ...this._rawLicenseRequest, - ...new Uint8Array([0x00, 0x00, 0x02, 0x00]) - ]) - - const encKey = await cmac.calculate( - new Uint8Array([ - ...new Uint8Array([0x01]), - ...encKeyBase - ]) - ) - - const server_key_1 = await cmac.calculate(new Uint8Array([ - ...new Uint8Array([0x01]), - ...authKeyBase - ])) - const server_key_2 = await cmac.calculate(new Uint8Array([ - ...new Uint8Array([0x02]), - ...authKeyBase - ])) - const serverKey = new Uint8Array([ - ...new Uint8Array(server_key_1), - ...new Uint8Array(server_key_2) - ]) - - const hmac = forge.hmac.create() - hmac.start(forge.md.sha256.create(), Util.utf8.decode(serverKey), "raw") - hmac.update(Util.utf8.decode(signedLicense.msg)) - const calculatedSignature = Util.utf8.encode(hmac.digest().data) - - if (!Util.sequenceEquals(calculatedSignature, signedLicense.signature)) { - throw new Error("signatures do not match") - } - - const license = License.decode(signedLicense.msg) - - const keyContainers = license.key.map(keyContainer => { - if (keyContainer.type && keyContainer.type === 2 && keyContainer.key && keyContainer.iv) { - const keyBuffer = forge.util.createBuffer(encKey, 'raw'); - const decipher = forge.cipher.createDecipher( - "AES-CBC", - keyBuffer - ) - - decipher.start({ - iv: Util.utf8.decode(keyContainer.iv) - }) - decipher.update(forge.util.createBuffer(keyContainer.key)) - decipher.finish() - - return { - kid: keyContainer.id.length !== 0 ? Util.bytesToHex(keyContainer.id) : "00000000000000000000000000000000", - k: Util.bytesToHex(Util.utf8.encode(decipher.output.data)) - } - } - }) - const valid_containers = keyContainers.filter(container => !!container); - if (valid_containers.length < 1) { - throw new Error("there was not a single valid key in the response") - } - return valid_containers; + const request_id = android + ? this._generateAndroidIdentifier() + : this._generateGenericIdentifier(); + const licenseRequest = new LicenseRequest({ + type: LicenseRequest.RequestType.NEW, + contentId: new LicenseRequest.ContentIdentification({ + widevinePsshData: + new LicenseRequest.ContentIdentification.WidevinePsshData({ + psshData: [this._pssh], + licenseType: licenseType, + requestId: request_id, + }), + }), + requestTime: Math.floor(Date.now() / 1000), + protocolVersion: ProtocolVersion.VERSION_2_1, + keyControlNonce: Math.floor(Math.random() * 2 ** 31), + }); + + if (this._serviceCertificate) { + licenseRequest.encryptedClientId = this._encryptClientIdentification( + this._identifierBlob, + this._serviceCertificate, + ); + } else { + licenseRequest.clientId = this._identifierBlob; } - _encryptClientIdentification(clientIdentification, signedServiceCertificate) { - if (!signedServiceCertificate.drmCertificate) { - throw new Error("the service certificate does not contain an actual certificate") - } - - const serviceCertificate = DrmCertificate.decode( - signedServiceCertificate.drmCertificate - ) - console.log("[WidevineProxy2]", "SERVICE_CERTIFICATE", serviceCertificate); - - if (!serviceCertificate.publicKey) { - throw new Error("the service certificate does not contain a public key") - } - - const key = forge.random.getBytesSync(16) - const iv = forge.random.getBytesSync(16) - - const cipher = forge.cipher.createCipher("AES-CBC", key) - cipher.start({ - iv: iv - }) - cipher.update( - forge.util.createBuffer( - ClientIdentification.encode(clientIdentification).finish() - ) - ) - cipher.finish() - const rawEncryptedClientIdentification = Util.utf8.encode(cipher.output.data) - - const publicKey = forge.pki.publicKeyFromAsn1( - forge.asn1.fromDer( - Util.utf8.decode(serviceCertificate.publicKey) - ) - ) - const encryptedKey = publicKey.encrypt(key, "RSA-OAEP", { - md: forge.md.sha1.create() - }) - - return new EncryptedClientIdentification({ - encryptedClientId: rawEncryptedClientIdentification, - encryptedClientIdIv: Util.utf8.encode(iv), - encryptedPrivacyKey: Util.utf8.encode(encryptedKey), - providerId: serviceCertificate.providerId, - serviceCertificateSerialNumber: serviceCertificate.serialNumber - }) + this._rawLicenseRequest = LicenseRequest.encode(licenseRequest).finish(); + + const pss = forge.pss.create({ + md: forge.md.sha1.create(), + mgf: forge.mgf.mgf1.create(forge.md.sha1.create()), + saltLength: 20, + }); + + const md = forge.md.sha1.create(); + md.update(Util.utf8.decode(this._rawLicenseRequest), "raw"); + + const signature = Util.utf8.encode(this._devicePrivateKey.sign(md, pss)); + const signedLicenseRequest = new SignedMessage({ + type: SignedMessage.MessageType.LICENSE_REQUEST, + msg: this._rawLicenseRequest, + signature: signature, + }); + + return [SignedMessage.encode(signedLicenseRequest).finish(), request_id]; + } + + async parseLicense(rawLicense) { + if (!this._rawLicenseRequest) { + throw new Error("please request a license first"); } - async _verifyServiceCertificate(signedServiceCertificate) { - if (!signedServiceCertificate.drmCertificate) { - throw new Error("the service certificate does not contain an actual certificate") - } - if (!signedServiceCertificate.signature) { - throw new Error("the service certificate does not contain a signature") - } - - const pss = forge.pss.create({ - md: forge.md.sha1.create(), - mgf: forge.mgf.mgf1.create(forge.md.sha1.create()), - saltLength: 20 - }) - - const sha1 = forge.md.sha1.create() - sha1.update( - Util.utf8.decode(signedServiceCertificate.drmCertificate), - "raw" - ) - - const publicKey = forge.pki.publicKeyFromAsn1( - forge.asn1.fromDer( - Util.utf8.decode(WIDEVINE_ROOT_PUBLIC_KEY) - ) - ) - - return publicKey.verify( - sha1.digest().bytes(), - Util.utf8.decode(signedServiceCertificate.signature), - pss - ) + const signedLicense = SignedMessage.decode(new Uint8Array(rawLicense)); + if (!signedLicense.sessionKey) { + throw new Error("the license does not contain a session key"); + } + if (!signedLicense.msg) { + throw new Error("the license does not contain a message"); + } + if (!signedLicense.signature) { + throw new Error("the license does not contain a signature"); } - _parseWidevinePsshData(pssh) { - try { - return WidevinePsshData.decode(pssh) - } catch { - return null - } + const sessionKey = this._devicePrivateKey.decrypt( + Util.utf8.decode(signedLicense.sessionKey), + "RSA-OAEP", + { + md: forge.md.sha1.create(), + }, + ); + + const cmac = new AES_CMAC(Util.utf8.encode(sessionKey)); + const encKeyBase = new Uint8Array([ + ...Util.utf8.encode("ENCRYPTION"), + ...new Uint8Array([0x00]), + ...this._rawLicenseRequest, + ...new Uint8Array([0x00, 0x00, 0x00, 0x80]), + ]); + const authKeyBase = new Uint8Array([ + ...Util.utf8.encode("AUTHENTICATION"), + ...new Uint8Array([0x00]), + ...this._rawLicenseRequest, + ...new Uint8Array([0x00, 0x00, 0x02, 0x00]), + ]); + + const encKey = await cmac.calculate( + new Uint8Array([...new Uint8Array([0x01]), ...encKeyBase]), + ); + + const server_key_1 = await cmac.calculate( + new Uint8Array([...new Uint8Array([0x01]), ...authKeyBase]), + ); + const server_key_2 = await cmac.calculate( + new Uint8Array([...new Uint8Array([0x02]), ...authKeyBase]), + ); + const serverKey = new Uint8Array([ + ...new Uint8Array(server_key_1), + ...new Uint8Array(server_key_2), + ]); + + const hmac = forge.hmac.create(); + hmac.start(forge.md.sha256.create(), Util.utf8.decode(serverKey), "raw"); + hmac.update(Util.utf8.decode(signedLicense.msg)); + const calculatedSignature = Util.utf8.encode(hmac.digest().data); + + if (!Util.sequenceEquals(calculatedSignature, signedLicense.signature)) { + throw new Error("signatures do not match"); } - _generateAndroidIdentifier() { - const randomBytes = Util.utf8.encode(forge.random.getBytesSync(8)); - return Util.utf8.encode(`${Util.bytesToHex(randomBytes)}0100000000000000`) + const license = License.decode(signedLicense.msg); + + const keyContainers = license.key.map((keyContainer) => { + if ( + keyContainer.type && + keyContainer.type === 2 && + keyContainer.key && + keyContainer.iv + ) { + const keyBuffer = forge.util.createBuffer(encKey, "raw"); + const decipher = forge.cipher.createDecipher("AES-CBC", keyBuffer); + + decipher.start({ + iv: Util.utf8.decode(keyContainer.iv), + }); + decipher.update(forge.util.createBuffer(keyContainer.key)); + decipher.finish(); + + return { + kid: + keyContainer.id.length !== 0 + ? Util.bytesToHex(keyContainer.id) + : "00000000000000000000000000000000", + k: Util.bytesToHex(Util.utf8.encode(decipher.output.data)), + }; + } + }); + const valid_containers = keyContainers.filter((container) => !!container); + if (valid_containers.length < 1) { + throw new Error("there was not a single valid key in the response"); + } + return valid_containers; + } + + _encryptClientIdentification(clientIdentification, signedServiceCertificate) { + if (!signedServiceCertificate.drmCertificate) { + throw new Error( + "the service certificate does not contain an actual certificate", + ); } - _generateGenericIdentifier() { - return Util.utf8.encode(forge.random.getBytesSync(16)) + const serviceCertificate = DrmCertificate.decode( + signedServiceCertificate.drmCertificate, + ); + console.log("[WidevineProxy2]", "SERVICE_CERTIFICATE", serviceCertificate); + + if (!serviceCertificate.publicKey) { + throw new Error("the service certificate does not contain a public key"); } - static psshDataToPsshBoxB64(pssh_data) { - const dataLength = pssh_data.length; - const totalLength = dataLength + 32; - const pssh = new Uint8Array([ - ...Util.u32toBytes(totalLength), - ...PSSH_MAGIC, - ...new Uint8Array(4), - ...WIDEVINE_SYSTEM_ID, - ...Util.u32toBytes(dataLength), - ...pssh_data - ]); - return Util.b64.encode(pssh); + const key = forge.random.getBytesSync(16); + const iv = forge.random.getBytesSync(16); + + const cipher = forge.cipher.createCipher("AES-CBC", key); + cipher.start({ + iv: iv, + }); + cipher.update( + forge.util.createBuffer( + ClientIdentification.encode(clientIdentification).finish(), + ), + ); + cipher.finish(); + const rawEncryptedClientIdentification = Util.utf8.encode( + cipher.output.data, + ); + + const publicKey = forge.pki.publicKeyFromAsn1( + forge.asn1.fromDer(Util.utf8.decode(serviceCertificate.publicKey)), + ); + const encryptedKey = publicKey.encrypt(key, "RSA-OAEP", { + md: forge.md.sha1.create(), + }); + + return new EncryptedClientIdentification({ + encryptedClientId: rawEncryptedClientIdentification, + encryptedClientIdIv: Util.utf8.encode(iv), + encryptedPrivacyKey: Util.utf8.encode(encryptedKey), + providerId: serviceCertificate.providerId, + serviceCertificateSerialNumber: serviceCertificate.serialNumber, + }); + } + + async _verifyServiceCertificate(signedServiceCertificate) { + if (!signedServiceCertificate.drmCertificate) { + throw new Error( + "the service certificate does not contain an actual certificate", + ); + } + if (!signedServiceCertificate.signature) { + throw new Error("the service certificate does not contain a signature"); } - getPSSH() { - return Session.psshDataToPsshBoxB64(this._pssh) + const pss = forge.pss.create({ + md: forge.md.sha1.create(), + mgf: forge.mgf.mgf1.create(forge.md.sha1.create()), + saltLength: 20, + }); + + const sha1 = forge.md.sha1.create(); + sha1.update( + Util.utf8.decode(signedServiceCertificate.drmCertificate), + "raw", + ); + + const publicKey = forge.pki.publicKeyFromAsn1( + forge.asn1.fromDer(Util.utf8.decode(WIDEVINE_ROOT_PUBLIC_KEY)), + ); + + return publicKey.verify( + sha1.digest().bytes(), + Util.utf8.decode(signedServiceCertificate.signature), + pss, + ); + } + + _parseWidevinePsshData(pssh) { + try { + return WidevinePsshData.decode(pssh); + } catch { + return null; } + } + + _generateAndroidIdentifier() { + const randomBytes = Util.utf8.encode(forge.random.getBytesSync(8)); + return Util.utf8.encode(`${Util.bytesToHex(randomBytes)}0100000000000000`); + } + + _generateGenericIdentifier() { + return Util.utf8.encode(forge.random.getBytesSync(16)); + } + + static psshDataToPsshBoxB64(pssh_data) { + const dataLength = pssh_data.length; + const totalLength = dataLength + 32; + const pssh = new Uint8Array([ + ...Util.u32toBytes(totalLength), + ...PSSH_MAGIC, + ...new Uint8Array(4), + ...WIDEVINE_SYSTEM_ID, + ...Util.u32toBytes(dataLength), + ...pssh_data, + ]); + return Util.b64.encode(pssh); + } + + getPSSH() { + return Session.psshDataToPsshBoxB64(this._pssh); + } } diff --git a/lib/cmac.js b/lib/cmac.js index 3bc7eef..f75a4eb 100644 --- a/lib/cmac.js +++ b/lib/cmac.js @@ -1,118 +1,124 @@ export class AES_CMAC { - BLOCK_SIZE = 16; - XOR_RIGHT = new Uint8Array([ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87 - ]); - EMPTY_BLOCK_SIZE_BUFFER = new Uint8Array(this.BLOCK_SIZE); - - constructor(key) { - if (![16, 24, 32].includes(key.length)) { - throw new Error("Key size must be 128, 192, or 256 bits."); - } - this._key = key; + BLOCK_SIZE = 16; + XOR_RIGHT = new Uint8Array([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x87, + ]); + EMPTY_BLOCK_SIZE_BUFFER = new Uint8Array(this.BLOCK_SIZE); + + constructor(key) { + if (![16, 24, 32].includes(key.length)) { + throw new Error("Key size must be 128, 192, or 256 bits."); } + this._key = key; + } - async calculate(message) { - this._subkeys = await this._generateSubkeys(); - const blockCount = this._getBlockCount(message); + async calculate(message) { + this._subkeys = await this._generateSubkeys(); + const blockCount = this._getBlockCount(message); - let x = this.EMPTY_BLOCK_SIZE_BUFFER; - let y; + let x = this.EMPTY_BLOCK_SIZE_BUFFER; + let y; - for (let i = 0; i < blockCount - 1; i++) { - const from = i * this.BLOCK_SIZE; - const block = message.subarray(from, from + this.BLOCK_SIZE); - y = this._xor(x, block); - x = await this._aes(y); - } - - y = this._xor(x, this._getLastBlock(message)); - x = await this._aes(y); - - return x; + for (let i = 0; i < blockCount - 1; i++) { + const from = i * this.BLOCK_SIZE; + const block = message.subarray(from, from + this.BLOCK_SIZE); + y = this._xor(x, block); + x = await this._aes(y); } - async _generateSubkeys() { - const l = await this._aes(this.EMPTY_BLOCK_SIZE_BUFFER); + y = this._xor(x, this._getLastBlock(message)); + x = await this._aes(y); - let first = this._bitShiftLeft(l); - if (l[0] & 0x80) { - first = this._xor(first, this.XOR_RIGHT); - } + return x; + } - let second = this._bitShiftLeft(first); - if (first[0] & 0x80) { - second = this._xor(second, this.XOR_RIGHT); - } + async _generateSubkeys() { + const l = await this._aes(this.EMPTY_BLOCK_SIZE_BUFFER); - return { first: first, second: second }; + let first = this._bitShiftLeft(l); + if (l[0] & 0x80) { + first = this._xor(first, this.XOR_RIGHT); } - _getBlockCount(message) { - const blockCount = Math.ceil(message.length / this.BLOCK_SIZE); - return blockCount === 0 ? 1 : blockCount; + let second = this._bitShiftLeft(first); + if (first[0] & 0x80) { + second = this._xor(second, this.XOR_RIGHT); } - async _aes(message) { - const keyBuffer = forge.util.createBuffer(this._key, 'raw'); - const cipher = forge.cipher.createCipher('AES-CBC', keyBuffer); - - const iv = forge.util.createBuffer(new Uint8Array(16), 'raw'); - cipher.start({ - iv: iv.getBytes() - }); - - cipher.update(forge.util.createBuffer(message)); - cipher.finish(); - - const outputBuffer = cipher.output; - return new Uint8Array(outputBuffer.getBytes().slice(0, 16).split('').map(c => c.charCodeAt(0))); + return { first: first, second: second }; + } + + _getBlockCount(message) { + const blockCount = Math.ceil(message.length / this.BLOCK_SIZE); + return blockCount === 0 ? 1 : blockCount; + } + + async _aes(message) { + const keyBuffer = forge.util.createBuffer(this._key, "raw"); + const cipher = forge.cipher.createCipher("AES-CBC", keyBuffer); + + const iv = forge.util.createBuffer(new Uint8Array(16), "raw"); + cipher.start({ + iv: iv.getBytes(), + }); + + cipher.update(forge.util.createBuffer(message)); + cipher.finish(); + + const outputBuffer = cipher.output; + return new Uint8Array( + outputBuffer + .getBytes() + .slice(0, 16) + .split("") + .map((c) => c.charCodeAt(0)), + ); + } + + _getLastBlock(message) { + const blockCount = this._getBlockCount(message); + const paddedBlock = this._padding(message, blockCount - 1); + + let complete = false; + if (message.length > 0) { + complete = message.length % this.BLOCK_SIZE === 0; } - _getLastBlock(message) { - const blockCount = this._getBlockCount(message); - const paddedBlock = this._padding(message, blockCount - 1); + const key = complete ? this._subkeys.first : this._subkeys.second; + return this._xor(paddedBlock, key); + } - let complete = false; - if (message.length > 0) { - complete = message.length % this.BLOCK_SIZE === 0; - } + _padding(message, blockIndex) { + const block = new Uint8Array(this.BLOCK_SIZE); - const key = complete ? this._subkeys.first : this._subkeys.second; - return this._xor(paddedBlock, key); - } + const from = blockIndex * this.BLOCK_SIZE; + const slice = message.subarray(from, from + this.BLOCK_SIZE); + block.set(slice); - _padding(message, blockIndex) { - const block = new Uint8Array(this.BLOCK_SIZE); - - const from = blockIndex * this.BLOCK_SIZE; - const slice = message.subarray(from, from + this.BLOCK_SIZE); - block.set(slice); - - if (slice.length !== this.BLOCK_SIZE) { - block[slice.length] = 0x80; - } - - return block; + if (slice.length !== this.BLOCK_SIZE) { + block[slice.length] = 0x80; } - _bitShiftLeft(input) { - const output = new Uint8Array(input.length); - let overflow = 0; - for (let i = input.length - 1; i >= 0; i--) { - output[i] = (input[i] << 1) | overflow; - overflow = input[i] & 0x80 ? 1 : 0; - } - return output; - } + return block; + } - _xor(a, b) { - const length = Math.min(a.length, b.length); - const output = new Uint8Array(length); - for (let i = 0; i < length; i++) { - output[i] = a[i] ^ b[i]; - } - return output; + _bitShiftLeft(input) { + const output = new Uint8Array(input.length); + let overflow = 0; + for (let i = input.length - 1; i >= 0; i--) { + output[i] = (input[i] << 1) | overflow; + overflow = input[i] & 0x80 ? 1 : 0; + } + return output; + } + + _xor(a, b) { + const length = Math.min(a.length, b.length); + const output = new Uint8Array(length); + for (let i = 0; i < length; i++) { + output[i] = a[i] ^ b[i]; } + return output; + } } diff --git a/lib/device.js b/lib/device.js index 3aebc2b..9a78d78 100644 --- a/lib/device.js +++ b/lib/device.js @@ -1,72 +1,74 @@ import "./protobuf.min.js"; import "./license_protocol.min.js"; -const { ClientIdentification, SignedDrmCertificate, DrmCertificate } = protobuf.roots.default.license_protocol; +const { ClientIdentification, SignedDrmCertificate, DrmCertificate } = + protobuf.roots.default.license_protocol; export class Crc32 { - constructor() { - this.crc_table = this.setupTable(); - } + constructor() { + this.crc_table = this.setupTable(); + } - setupTable() { - let c; - const crcTable = []; - for (let n = 0; n < 256; n++) { - c = n; - for (let k = 0; k < 8; k++) { - c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); - } - crcTable[n] = c; - } - return crcTable; + setupTable() { + let c; + const crcTable = []; + for (let n = 0; n < 256; n++) { + c = n; + for (let k = 0; k < 8; k++) { + c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; + } + crcTable[n] = c; } + return crcTable; + } - crc32(uint8Array) { - let crc = 0 ^ (-1); - for (let i = 0; i < uint8Array.length; i++) { - crc = (crc >>> 8) ^ this.crc_table[(crc ^ uint8Array[i]) & 0xFF]; - } - return (crc ^ (-1)) >>> 0; + crc32(uint8Array) { + let crc = 0 ^ -1; + for (let i = 0; i < uint8Array.length; i++) { + crc = (crc >>> 8) ^ this.crc_table[(crc ^ uint8Array[i]) & 0xff]; } + return (crc ^ -1) >>> 0; + } } export class WidevineDevice { - constructor(bytes) { - this._raw_bytes = new Uint8Array(bytes); - this._data_view = new DataView(bytes); + constructor(bytes) { + this._raw_bytes = new Uint8Array(bytes); + this._data_view = new DataView(bytes); - this.version = this._data_view.getUint8(3); - this.type = this._data_view.getUint8(4); - this.security_level = this._data_view.getUint8(5); - this.flags = this._data_view.getUint8(6); + this.version = this._data_view.getUint8(3); + this.type = this._data_view.getUint8(4); + this.security_level = this._data_view.getUint8(5); + this.flags = this._data_view.getUint8(6); - this.private_key_len = this._data_view.getUint16(7); - this.private_key = this._raw_bytes.subarray(9, 9 + this.private_key_len); + this.private_key_len = this._data_view.getUint16(7); + this.private_key = this._raw_bytes.subarray(9, 9 + this.private_key_len); - this.client_id_len = this._data_view.getUint16(9 + this.private_key_len); - this.client_id_bytes = this._raw_bytes.subarray(11 + this.private_key_len, 11 + this.private_key_len + this.client_id_len); - this.client_id = ClientIdentification.decode(this.client_id_bytes); - } + this.client_id_len = this._data_view.getUint16(9 + this.private_key_len); + this.client_id_bytes = this._raw_bytes.subarray( + 11 + this.private_key_len, + 11 + this.private_key_len + this.client_id_len, + ); + this.client_id = ClientIdentification.decode(this.client_id_bytes); + } - get_name() { - const client_info = Object.fromEntries(this.client_id.clientInfo.map(item => [item.name, item.value])) - const type = this.type === 1 ? "CHROME" : `L${this.security_level}` + get_name() { + const client_info = Object.fromEntries( + this.client_id.clientInfo.map((item) => [item.name, item.value]), + ); + const type = this.type === 1 ? "CHROME" : `L${this.security_level}`; - const root_signed_cert = SignedDrmCertificate.decode(this.client_id.token); - const root_cert = DrmCertificate.decode(root_signed_cert.drmCertificate); + const root_signed_cert = SignedDrmCertificate.decode(this.client_id.token); + const root_cert = DrmCertificate.decode(root_signed_cert.drmCertificate); - let name = `[${type}]`; - if (client_info["company_name"]) - name += ` ${client_info["company_name"]}`; - if (client_info["model_name"]) - name += ` ${client_info["model_name"]}`; - if (client_info["product_name"]) - name += ` ${client_info["product_name"]}`; - if (root_cert.systemId) - name += ` (${root_cert.systemId})`; + let name = `[${type}]`; + if (client_info["company_name"]) name += ` ${client_info["company_name"]}`; + if (client_info["model_name"]) name += ` ${client_info["model_name"]}`; + if (client_info["product_name"]) name += ` ${client_info["product_name"]}`; + if (root_cert.systemId) name += ` (${root_cert.systemId})`; - const crc32 = new Crc32(); - name += ` [${crc32.crc32(this._raw_bytes).toString(16)}]`; + const crc32 = new Crc32(); + name += ` [${crc32.crc32(this._raw_bytes).toString(16)}]`; - return name; - } + return name; + } } diff --git a/lib/protobuf.min.js b/lib/protobuf.min.js index 2708fe2..c268974 100644 --- a/lib/protobuf.min.js +++ b/lib/protobuf.min.js @@ -1,8 +1,8 @@ /*! - * protobuf.js v7.4.0 (c) 2016, daniel wirtz - * compiled thu, 22 aug 2024 20:30:39 utc + * protobuf.js v8.0.0 (c) 2016, daniel wirtz + * compiled tue, 16 dec 2025 22:00:06 utc * licensed under the bsd-3-clause license * see: https://github.com/dcodeio/protobuf.js for details */ -!function(d){"use strict";!function(r,u,t){var n=function t(n){var i=u[n];return i||r[n][0].call(i=u[n]={exports:{}},t,i,i.exports),i.exports}(t[0]);n.util.global.protobuf=n,"function"==typeof define&&define.amd&&define(["long"],function(t){return t&&t.isLong&&(n.util.Long=t,n.configure()),n}),"object"==typeof module&&module&&module.exports&&(module.exports=n)}({1:[function(t,n,i){n.exports=function(t,n){var i=Array(arguments.length-1),e=0,r=2,s=!0;for(;r>2],r=(3&o)<<4,h=1;break;case 1:e[s++]=f[r|o>>4],r=(15&o)<<2,h=2;break;case 2:e[s++]=f[r|o>>6],e[s++]=f[63&o],h=0}8191>4,r=h,e=2;break;case 2:n[i++]=(15&r)<<4|(60&h)>>2,r=h,e=3;break;case 3:n[i++]=(3&r)<<6|h,e=0}}if(1===e)throw Error(c);return i-u},i.test=function(t){return/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(t)}},{}],3:[function(t,n,i){function r(){this.t={}}(n.exports=r).prototype.on=function(t,n,i){return(this.t[t]||(this.t[t]=[])).push({fn:n,ctx:i||this}),this},r.prototype.off=function(t,n){if(t===d)this.t={};else if(n===d)this.t[t]=[];else for(var i=this.t[t],r=0;r>>0:n<11754943508222875e-54?(u<<31|Math.round(n/1401298464324817e-60))>>>0:(u<<31|127+(t=Math.floor(Math.log(n)/Math.LN2))<<23|8388607&Math.round(n*Math.pow(2,-t)*8388608))>>>0,i,r)}function i(t,n,i){t=t(n,i),n=2*(t>>31)+1,i=t>>>23&255,t&=8388607;return 255==i?t?NaN:1/0*n:0==i?1401298464324817e-60*n*t:n*Math.pow(2,i-150)*(8388608+t)}function r(t,n,i){h[0]=t,n[i]=o[0],n[i+1]=o[1],n[i+2]=o[2],n[i+3]=o[3]}function u(t,n,i){h[0]=t,n[i]=o[3],n[i+1]=o[2],n[i+2]=o[1],n[i+3]=o[0]}function e(t,n){return o[0]=t[n],o[1]=t[n+1],o[2]=t[n+2],o[3]=t[n+3],h[0]}function s(t,n){return o[3]=t[n],o[2]=t[n+1],o[1]=t[n+2],o[0]=t[n+3],h[0]}var h,o,f,c,a;function l(t,n,i,r,u,e){var s,h=r<0?1:0;0===(r=h?-r:r)?(t(0,u,e+n),t(0<1/r?0:2147483648,u,e+i)):isNaN(r)?(t(0,u,e+n),t(2146959360,u,e+i)):17976931348623157e292>>0,u,e+i)):r<22250738585072014e-324?(t((s=r/5e-324)>>>0,u,e+n),t((h<<31|s/4294967296)>>>0,u,e+i)):(t(4503599627370496*(s=r*Math.pow(2,-(r=1024===(r=Math.floor(Math.log(r)/Math.LN2))?1023:r)))>>>0,u,e+n),t((h<<31|r+1023<<20|1048576*s&1048575)>>>0,u,e+i))}function v(t,n,i,r,u){n=t(r,u+n),t=t(r,u+i),r=2*(t>>31)+1,u=t>>>20&2047,i=4294967296*(1048575&t)+n;return 2047==u?i?NaN:1/0*r:0==u?5e-324*r*i:r*Math.pow(2,u-1075)*(i+4503599627370496)}function w(t,n,i){f[0]=t,n[i]=c[0],n[i+1]=c[1],n[i+2]=c[2],n[i+3]=c[3],n[i+4]=c[4],n[i+5]=c[5],n[i+6]=c[6],n[i+7]=c[7]}function b(t,n,i){f[0]=t,n[i]=c[7],n[i+1]=c[6],n[i+2]=c[5],n[i+3]=c[4],n[i+4]=c[3],n[i+5]=c[2],n[i+6]=c[1],n[i+7]=c[0]}function y(t,n){return c[0]=t[n],c[1]=t[n+1],c[2]=t[n+2],c[3]=t[n+3],c[4]=t[n+4],c[5]=t[n+5],c[6]=t[n+6],c[7]=t[n+7],f[0]}function g(t,n){return c[7]=t[n],c[6]=t[n+1],c[5]=t[n+2],c[4]=t[n+3],c[3]=t[n+4],c[2]=t[n+5],c[1]=t[n+6],c[0]=t[n+7],f[0]}return"undefined"!=typeof Float32Array?(h=new Float32Array([-0]),o=new Uint8Array(h.buffer),a=128===o[3],t.writeFloatLE=a?r:u,t.writeFloatBE=a?u:r,t.readFloatLE=a?e:s,t.readFloatBE=a?s:e):(t.writeFloatLE=n.bind(null,d),t.writeFloatBE=n.bind(null,A),t.readFloatLE=i.bind(null,p),t.readFloatBE=i.bind(null,m)),"undefined"!=typeof Float64Array?(f=new Float64Array([-0]),c=new Uint8Array(f.buffer),a=128===c[7],t.writeDoubleLE=a?w:b,t.writeDoubleBE=a?b:w,t.readDoubleLE=a?y:g,t.readDoubleBE=a?g:y):(t.writeDoubleLE=l.bind(null,d,0,4),t.writeDoubleBE=l.bind(null,A,4,0),t.readDoubleLE=v.bind(null,p,0,4),t.readDoubleBE=v.bind(null,m,4,0)),t}function d(t,n,i){n[i]=255&t,n[i+1]=t>>>8&255,n[i+2]=t>>>16&255,n[i+3]=t>>>24}function A(t,n,i){n[i]=t>>>24,n[i+1]=t>>>16&255,n[i+2]=t>>>8&255,n[i+3]=255&t}function p(t,n){return(t[n]|t[n+1]<<8|t[n+2]<<16|t[n+3]<<24)>>>0}function m(t,n){return(t[n]<<24|t[n+1]<<16|t[n+2]<<8|t[n+3])>>>0}n.exports=r(r)},{}],5:[function(t,n,i){function r(t){try{var n=void 0;if(n&&(n.length||Object.keys(n).length))return n}catch(t){}return null}n.exports=r},{}],6:[function(t,n,i){n.exports=function(n,i,t){var r=t||8192,u=r>>>1,e=null,s=r;return function(t){if(t<1||u>10),e[s++]=56320+(1023&r)):e[s++]=(15&r)<<12|(63&t[n++])<<6|63&t[n++],8191>6|192:(55296==(64512&r)&&56320==(64512&(u=t.charCodeAt(s+1)))?(++s,n[i++]=(r=65536+((1023&r)<<10)+(1023&u))>>18|240,n[i++]=r>>12&63|128):n[i++]=r>>12|224,n[i++]=r>>6&63|128),n[i++]=63&r|128);return i-e}},{}],8:[function(t,n,i){var r=i;function u(){r.util.n(),r.Writer.n(r.BufferWriter),r.Reader.n(r.BufferReader)}r.build="minimal",r.Writer=t(16),r.BufferWriter=t(17),r.Reader=t(9),r.BufferReader=t(10),r.util=t(15),r.rpc=t(12),r.roots=t(11),r.configure=u,u()},{10:10,11:11,12:12,15:15,16:16,17:17,9:9}],9:[function(t,n,i){n.exports=o;var r,u=t(15),e=u.LongBits,s=u.utf8;function h(t,n){return RangeError("index out of range: "+t.pos+" + "+(n||1)+" > "+t.len)}function o(t){this.buf=t,this.pos=0,this.len=t.length}function f(){return u.Buffer?function(t){return(o.create=function(t){return u.Buffer.isBuffer(t)?new r(t):a(t)})(t)}:a}var c,a="undefined"!=typeof Uint8Array?function(t){if(t instanceof Uint8Array||Array.isArray(t))return new o(t);throw Error("illegal buffer")}:function(t){if(Array.isArray(t))return new o(t);throw Error("illegal buffer")};function l(){var t=new e(0,0),n=0;if(!(4=this.len)throw h(this);if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*n)>>>0,this.buf[this.pos++]<128)return t}return t.lo=(t.lo|(127&this.buf[this.pos++])<<7*n)>>>0,t}for(;n<4;++n)if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*n)>>>0,this.buf[this.pos++]<128)return t;if(t.lo=(t.lo|(127&this.buf[this.pos])<<28)>>>0,t.hi=(t.hi|(127&this.buf[this.pos])>>4)>>>0,this.buf[this.pos++]<128)return t;if(n=0,4>>0,this.buf[this.pos++]<128)return t}else for(;n<5;++n){if(this.pos>=this.len)throw h(this);if(t.hi=(t.hi|(127&this.buf[this.pos])<<7*n+3)>>>0,this.buf[this.pos++]<128)return t}throw Error("invalid varint encoding")}function v(t,n){return(t[n-4]|t[n-3]<<8|t[n-2]<<16|t[n-1]<<24)>>>0}function w(){if(this.pos+8>this.len)throw h(this,8);return new e(v(this.buf,this.pos+=4),v(this.buf,this.pos+=4))}o.create=f(),o.prototype.i=u.Array.prototype.subarray||u.Array.prototype.slice,o.prototype.uint32=(c=4294967295,function(){if(c=(127&this.buf[this.pos])>>>0,this.buf[this.pos++]<128||(c=(c|(127&this.buf[this.pos])<<7)>>>0,this.buf[this.pos++]<128||(c=(c|(127&this.buf[this.pos])<<14)>>>0,this.buf[this.pos++]<128||(c=(c|(127&this.buf[this.pos])<<21)>>>0,this.buf[this.pos++]<128||(c=(c|(15&this.buf[this.pos])<<28)>>>0,this.buf[this.pos++]<128||!((this.pos+=5)>this.len))))))return c;throw this.pos=this.len,h(this,10)}),o.prototype.int32=function(){return 0|this.uint32()},o.prototype.sint32=function(){var t=this.uint32();return t>>>1^-(1&t)|0},o.prototype.bool=function(){return 0!==this.uint32()},o.prototype.fixed32=function(){if(this.pos+4>this.len)throw h(this,4);return v(this.buf,this.pos+=4)},o.prototype.sfixed32=function(){if(this.pos+4>this.len)throw h(this,4);return 0|v(this.buf,this.pos+=4)},o.prototype.float=function(){if(this.pos+4>this.len)throw h(this,4);var t=u.float.readFloatLE(this.buf,this.pos);return this.pos+=4,t},o.prototype.double=function(){if(this.pos+8>this.len)throw h(this,4);var t=u.float.readDoubleLE(this.buf,this.pos);return this.pos+=8,t},o.prototype.bytes=function(){var t=this.uint32(),n=this.pos,i=this.pos+t;if(i>this.len)throw h(this,t);return this.pos+=t,Array.isArray(this.buf)?this.buf.slice(n,i):n===i?(t=u.Buffer)?t.alloc(0):new this.buf.constructor(0):this.i.call(this.buf,n,i)},o.prototype.string=function(){var t=this.bytes();return s.read(t,0,t.length)},o.prototype.skip=function(t){if("number"==typeof t){if(this.pos+t>this.len)throw h(this,t);this.pos+=t}else do{if(this.pos>=this.len)throw h(this)}while(128&this.buf[this.pos++]);return this},o.prototype.skipType=function(t){switch(t){case 0:this.skip();break;case 1:this.skip(8);break;case 2:this.skip(this.uint32());break;case 3:for(;4!=(t=7&this.uint32());)this.skipType(t);break;case 5:this.skip(4);break;default:throw Error("invalid wire type "+t+" at offset "+this.pos)}return this},o.n=function(t){r=t,o.create=f(),r.n();var n=u.Long?"toLong":"toNumber";u.merge(o.prototype,{int64:function(){return l.call(this)[n](!1)},uint64:function(){return l.call(this)[n](!0)},sint64:function(){return l.call(this).zzDecode()[n](!1)},fixed64:function(){return w.call(this)[n](!0)},sfixed64:function(){return w.call(this)[n](!1)}})}},{15:15}],10:[function(t,n,i){n.exports=e;var r=t(9),u=((e.prototype=Object.create(r.prototype)).constructor=e,t(15));function e(t){r.call(this,t)}e.n=function(){u.Buffer&&(e.prototype.i=u.Buffer.prototype.slice)},e.prototype.string=function(){var t=this.uint32();return this.buf.utf8Slice?this.buf.utf8Slice(this.pos,this.pos=Math.min(this.pos+t,this.len)):this.buf.toString("utf-8",this.pos,this.pos=Math.min(this.pos+t,this.len))},e.n()},{15:15,9:9}],11:[function(t,n,i){n.exports={}},{}],12:[function(t,n,i){i.Service=t(13)},{13:13}],13:[function(t,n,i){n.exports=r;var h=t(15);function r(t,n,i){if("function"!=typeof t)throw TypeError("rpcImpl must be a function");h.EventEmitter.call(this),this.rpcImpl=t,this.requestDelimited=!!n,this.responseDelimited=!!i}((r.prototype=Object.create(h.EventEmitter.prototype)).constructor=r).prototype.rpcCall=function t(i,n,r,u,e){if(!u)throw TypeError("request must be specified");var s=this;if(!e)return h.asPromise(t,s,i,n,r,u);if(!s.rpcImpl)return setTimeout(function(){e(Error("already ended"))},0),d;try{return s.rpcImpl(i,n[s.requestDelimited?"encodeDelimited":"encode"](u).finish(),function(t,n){if(t)return s.emit("error",t,i),e(t);if(null===n)return s.end(!0),d;if(!(n instanceof r))try{n=r[s.responseDelimited?"decodeDelimited":"decode"](n)}catch(t){return s.emit("error",t,i),e(t)}return s.emit("data",n,i),e(null,n)})}catch(t){return s.emit("error",t,i),setTimeout(function(){e(t)},0),d}},r.prototype.end=function(t){return this.rpcImpl&&(t||this.rpcImpl(null,null,null),this.rpcImpl=null,this.emit("end").off()),this}},{15:15}],14:[function(t,n,i){n.exports=u;var r=t(15);function u(t,n){this.lo=t>>>0,this.hi=n>>>0}var e=u.zero=new u(0,0),s=(e.toNumber=function(){return 0},e.zzEncode=e.zzDecode=function(){return this},e.length=function(){return 1},u.zeroHash="\0\0\0\0\0\0\0\0",u.fromNumber=function(t){var n,i;return 0===t?e:(i=(t=(n=t<0)?-t:t)>>>0,t=(t-i)/4294967296>>>0,n&&(t=~t>>>0,i=~i>>>0,4294967295<++i&&(i=0,4294967295<++t&&(t=0))),new u(i,t))},u.from=function(t){if("number"==typeof t)return u.fromNumber(t);if(r.isString(t)){if(!r.Long)return u.fromNumber(parseInt(t,10));t=r.Long.fromString(t)}return t.low||t.high?new u(t.low>>>0,t.high>>>0):e},u.prototype.toNumber=function(t){var n;return!t&&this.hi>>>31?(t=1+~this.lo>>>0,n=~this.hi>>>0,-(t+4294967296*(n=t?n:n+1>>>0))):this.lo+4294967296*this.hi},u.prototype.toLong=function(t){return r.Long?new r.Long(0|this.lo,0|this.hi,!!t):{low:0|this.lo,high:0|this.hi,unsigned:!!t}},String.prototype.charCodeAt);u.fromHash=function(t){return"\0\0\0\0\0\0\0\0"===t?e:new u((s.call(t,0)|s.call(t,1)<<8|s.call(t,2)<<16|s.call(t,3)<<24)>>>0,(s.call(t,4)|s.call(t,5)<<8|s.call(t,6)<<16|s.call(t,7)<<24)>>>0)},u.prototype.toHash=function(){return String.fromCharCode(255&this.lo,this.lo>>>8&255,this.lo>>>16&255,this.lo>>>24,255&this.hi,this.hi>>>8&255,this.hi>>>16&255,this.hi>>>24)},u.prototype.zzEncode=function(){var t=this.hi>>31;return this.hi=((this.hi<<1|this.lo>>>31)^t)>>>0,this.lo=(this.lo<<1^t)>>>0,this},u.prototype.zzDecode=function(){var t=-(1&this.lo);return this.lo=((this.lo>>>1|this.hi<<31)^t)>>>0,this.hi=(this.hi>>>1^t)>>>0,this},u.prototype.length=function(){var t=this.lo,n=(this.lo>>>28|this.hi<<4)>>>0,i=this.hi>>>24;return 0==i?0==n?t<16384?t<128?1:2:t<2097152?3:4:n<16384?n<128?5:6:n<2097152?7:8:i<128?9:10}},{15:15}],15:[function(t,n,i){var r=i;function u(t,n,i){for(var r=Object.keys(n),u=0;u>>7|t.hi<<25)>>>0,t.hi>>>=7;for(;127>>7;n[i++]=t.lo}function y(t,n,i){n[i]=255&t,n[i+1]=t>>>8&255,n[i+2]=t>>>16&255,n[i+3]=t>>>24}a.create=l(),a.alloc=function(t){return new u.Array(t)},u.Array!==Array&&(a.alloc=u.pool(a.alloc,u.Array.prototype.subarray)),a.prototype.e=function(t,n,i){return this.tail=this.tail.next=new o(t,n,i),this.len+=n,this},(w.prototype=Object.create(o.prototype)).fn=function(t,n,i){for(;127>>=7;n[i]=t},a.prototype.uint32=function(t){return this.len+=(this.tail=this.tail.next=new w((t>>>=0)<128?1:t<16384?2:t<2097152?3:t<268435456?4:5,t)).len,this},a.prototype.int32=function(t){return t<0?this.e(b,10,e.fromNumber(t)):this.uint32(t)},a.prototype.sint32=function(t){return this.uint32((t<<1^t>>31)>>>0)},a.prototype.int64=a.prototype.uint64=function(t){t=e.from(t);return this.e(b,t.length(),t)},a.prototype.sint64=function(t){t=e.from(t).zzEncode();return this.e(b,t.length(),t)},a.prototype.bool=function(t){return this.e(v,1,t?1:0)},a.prototype.sfixed32=a.prototype.fixed32=function(t){return this.e(y,4,t>>>0)},a.prototype.sfixed64=a.prototype.fixed64=function(t){t=e.from(t);return this.e(y,4,t.lo).e(y,4,t.hi)},a.prototype.float=function(t){return this.e(u.float.writeFloatLE,4,t)},a.prototype.double=function(t){return this.e(u.float.writeDoubleLE,8,t)};var g=u.Array.prototype.set?function(t,n,i){n.set(t,i)}:function(t,n,i){for(var r=0;r>>0;return i?(u.isString(t)&&(n=a.alloc(i=s.length(t)),s.decode(t,n,0),t=n),this.uint32(i).e(g,i,t)):this.e(v,1,0)},a.prototype.string=function(t){var n=h.length(t);return n?this.uint32(n).e(h.write,n,t):this.e(v,1,0)},a.prototype.fork=function(){return this.states=new c(this),this.head=this.tail=new o(f,0,0),this.len=0,this},a.prototype.reset=function(){return this.states?(this.head=this.states.head,this.tail=this.states.tail,this.len=this.states.len,this.states=this.states.next):(this.head=this.tail=new o(f,0,0),this.len=0),this},a.prototype.ldelim=function(){var t=this.head,n=this.tail,i=this.len;return this.reset().uint32(i),i&&(this.tail.next=t.next,this.tail=n,this.len+=i),this},a.prototype.finish=function(){for(var t=this.head.next,n=this.constructor.alloc(this.len),i=0;t;)t.fn(t.val,n,i),i+=t.len,t=t.next;return n},a.n=function(t){r=t,a.create=l(),r.n()}},{15:15}],17:[function(t,n,i){n.exports=e;var r=t(16),u=((e.prototype=Object.create(r.prototype)).constructor=e,t(15));function e(){r.call(this)}function s(t,n,i){t.length<40?u.utf8.write(t,n,i):n.utf8Write?n.utf8Write(t,i):n.write(t,i)}e.n=function(){e.alloc=u.u,e.writeBytesBuffer=u.Buffer&&u.Buffer.prototype instanceof Uint8Array&&"set"===u.Buffer.prototype.set.name?function(t,n,i){n.set(t,i)}:function(t,n,i){if(t.copy)t.copy(n,i,0,t.length);else for(var r=0;r>>0;return this.uint32(n),n&&this.e(e.writeBytesBuffer,n,t),this},e.prototype.string=function(t){var n=u.Buffer.byteLength(t);return this.uint32(n),n&&this.e(s,n,t),this},e.n()},{15:15,16:16}]},{},[8])}(); -//# sourceMappingURL=protobuf.min.js.map +!function(it){"use strict";!function(r,e,t){var i=function t(i){var n=e[i];return n||r[i][0].call(n=e[i]={exports:{}},t,n,n.exports),n.exports}(t[0]);i.util.global.protobuf=i,"function"==typeof define&&define.amd&&define(["long"],function(t){return t&&t.isLong&&(i.util.Long=t,i.configure()),i}),"object"==typeof module&&module&&module.exports&&(module.exports=i)}({1:[function(t,i,n){i.exports=function(t,i){var n=Array(arguments.length-1),s=0,r=2,o=!0;for(;r>2],r=(3&f)<<4,u=1;break;case 1:s[o++]=h[r|f>>4],r=(15&f)<<2,u=2;break;case 2:s[o++]=h[r|f>>6],s[o++]=h[63&f],u=0}8191>4,r=u,s=2;break;case 2:i[n++]=(15&r)<<4|(60&u)>>2,r=u,s=3;break;case 3:i[n++]=(3&r)<<6|u,s=0}}if(1===s)throw Error(a);return n-e},n.test=function(t){return/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(t)}},{}],3:[function(t,i,n){function c(i,n){"string"==typeof i&&(n=i,i=it);var f=[];function h(t){if("string"!=typeof t){var i=a();if(c.verbose&&console.log("codegen: "+i),i="return "+i,t){for(var n=Object.keys(t),r=Array(n.length+1),e=Array(n.length),s=0;s>>0:i<11754943508222875e-54?(e<<31|Math.round(i/1401298464324817e-60))>>>0:(e<<31|127+(t=Math.floor(Math.log(i)/Math.LN2))<<23|8388607&Math.round(i*Math.pow(2,-t)*8388608))>>>0,n,r)}function n(t,i,n){t=t(i,n),i=2*(t>>31)+1,n=t>>>23&255,t&=8388607;return 255==n?t?NaN:1/0*i:0==n?1401298464324817e-60*i*t:i*Math.pow(2,n-150)*(8388608+t)}function r(t,i,n){u[0]=t,i[n]=f[0],i[n+1]=f[1],i[n+2]=f[2],i[n+3]=f[3]}function e(t,i,n){u[0]=t,i[n]=f[3],i[n+1]=f[2],i[n+2]=f[1],i[n+3]=f[0]}function s(t,i){return f[0]=t[i],f[1]=t[i+1],f[2]=t[i+2],f[3]=t[i+3],u[0]}function o(t,i){return f[3]=t[i],f[2]=t[i+1],f[1]=t[i+2],f[0]=t[i+3],u[0]}var u,f,h,a,c;function l(t,i,n,r,e,s){var o,u=r<0?1:0;0===(r=u?-r:r)?(t(0,e,s+i),t(0<1/r?0:2147483648,e,s+n)):isNaN(r)?(t(0,e,s+i),t(2146959360,e,s+n)):17976931348623157e292>>0,e,s+n)):r<22250738585072014e-324?(t((o=r/5e-324)>>>0,e,s+i),t((u<<31|o/4294967296)>>>0,e,s+n)):(t(4503599627370496*(o=r*Math.pow(2,-(r=1024===(r=Math.floor(Math.log(r)/Math.LN2))?1023:r)))>>>0,e,s+i),t((u<<31|r+1023<<20|1048576*o&1048575)>>>0,e,s+n))}function d(t,i,n,r,e){i=t(r,e+i),t=t(r,e+n),r=2*(t>>31)+1,e=t>>>20&2047,n=4294967296*(1048575&t)+i;return 2047==e?n?NaN:1/0*r:0==e?5e-324*r*n:r*Math.pow(2,e-1075)*(n+4503599627370496)}function p(t,i,n){h[0]=t,i[n]=a[0],i[n+1]=a[1],i[n+2]=a[2],i[n+3]=a[3],i[n+4]=a[4],i[n+5]=a[5],i[n+6]=a[6],i[n+7]=a[7]}function v(t,i,n){h[0]=t,i[n]=a[7],i[n+1]=a[6],i[n+2]=a[5],i[n+3]=a[4],i[n+4]=a[3],i[n+5]=a[2],i[n+6]=a[1],i[n+7]=a[0]}function b(t,i){return a[0]=t[i],a[1]=t[i+1],a[2]=t[i+2],a[3]=t[i+3],a[4]=t[i+4],a[5]=t[i+5],a[6]=t[i+6],a[7]=t[i+7],h[0]}function w(t,i){return a[7]=t[i],a[6]=t[i+1],a[5]=t[i+2],a[4]=t[i+3],a[3]=t[i+4],a[2]=t[i+5],a[1]=t[i+6],a[0]=t[i+7],h[0]}return"undefined"!=typeof Float32Array?(u=new Float32Array([-0]),f=new Uint8Array(u.buffer),c=128===f[3],t.writeFloatLE=c?r:e,t.writeFloatBE=c?e:r,t.readFloatLE=c?s:o,t.readFloatBE=c?o:s):(t.writeFloatLE=i.bind(null,y),t.writeFloatBE=i.bind(null,m),t.readFloatLE=n.bind(null,g),t.readFloatBE=n.bind(null,j)),"undefined"!=typeof Float64Array?(h=new Float64Array([-0]),a=new Uint8Array(h.buffer),c=128===a[7],t.writeDoubleLE=c?p:v,t.writeDoubleBE=c?v:p,t.readDoubleLE=c?b:w,t.readDoubleBE=c?w:b):(t.writeDoubleLE=l.bind(null,y,0,4),t.writeDoubleBE=l.bind(null,m,4,0),t.readDoubleLE=d.bind(null,g,0,4),t.readDoubleBE=d.bind(null,j,4,0)),t}function y(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}function m(t,i,n){i[n]=t>>>24,i[n+1]=t>>>16&255,i[n+2]=t>>>8&255,i[n+3]=255&t}function g(t,i){return(t[i]|t[i+1]<<8|t[i+2]<<16|t[i+3]<<24)>>>0}function j(t,i){return(t[i]<<24|t[i+1]<<16|t[i+2]<<8|t[i+3])>>>0}i.exports=r(r)},{}],7:[function(t,i,n){function r(t){try{var i=eval("require")(t);if(i&&(i.length||Object.keys(i).length))return i}catch(t){}return null}i.exports=r},{}],8:[function(t,i,n){var e=n.isAbsolute=function(t){return/^(?:\/|\w+:)/.test(t)},r=n.normalize=function(t){var i=(t=t.replace(/\\/g,"/").replace(/\/{2,}/g,"/")).split("/"),n=e(t),t="";n&&(t=i.shift()+"/");for(var r=0;r>>1,s=null,o=r;return function(t){if(t<1||e>10),s[o++]=56320+(1023&r)):s[o++]=(15&r)<<12|(63&t[i++])<<6|63&t[i++],8191>6|192:(55296==(64512&r)&&56320==(64512&(e=t.charCodeAt(o+1)))?(++o,i[n++]=(r=65536+((1023&r)<<10)+(1023&e))>>18|240,i[n++]=r>>12&63|128):i[n++]=r>>12|224,i[n++]=r>>6&63|128),i[n++]=63&r|128);return n-s}},{}],11:[function(t,i,n){i.exports=e;var r=/\/|\./;function e(t,i){r.test(t)||(t="google/protobuf/"+t+".proto",i={nested:{google:{nested:{protobuf:{nested:i}}}}}),e[t]=i}e("any",{Any:{fields:{type_url:{type:"string",id:1},value:{type:"bytes",id:2}}}}),e("duration",{Duration:i={fields:{seconds:{type:"int64",id:1},nanos:{type:"int32",id:2}}}}),e("timestamp",{Timestamp:i}),e("empty",{Empty:{fields:{}}}),e("struct",{Struct:{fields:{fields:{keyType:"string",type:"Value",id:1}}},Value:{oneofs:{kind:{oneof:["nullValue","numberValue","stringValue","boolValue","structValue","listValue"]}},fields:{nullValue:{type:"NullValue",id:1},numberValue:{type:"double",id:2},stringValue:{type:"string",id:3},boolValue:{type:"bool",id:4},structValue:{type:"Struct",id:5},listValue:{type:"ListValue",id:6}}},NullValue:{values:{NULL_VALUE:0}},ListValue:{fields:{values:{rule:"repeated",type:"Value",id:1}}}}),e("wrappers",{DoubleValue:{fields:{value:{type:"double",id:1}}},FloatValue:{fields:{value:{type:"float",id:1}}},Int64Value:{fields:{value:{type:"int64",id:1}}},UInt64Value:{fields:{value:{type:"uint64",id:1}}},Int32Value:{fields:{value:{type:"int32",id:1}}},UInt32Value:{fields:{value:{type:"uint32",id:1}}},BoolValue:{fields:{value:{type:"bool",id:1}}},StringValue:{fields:{value:{type:"string",id:1}}},BytesValue:{fields:{value:{type:"bytes",id:1}}}}),e("field_mask",{FieldMask:{fields:{paths:{rule:"repeated",type:"string",id:1}}}}),e.get=function(t){return e[t]||null}},{}],12:[function(t,i,n){var l=t(15),d=t(37);function o(t,i,n,r){var e=!1;if(i.resolvedType)if(i.resolvedType instanceof l){t("switch(d%s){",r);for(var s=i.resolvedType.values,o=Object.keys(s),u=0;u>>0",r,r);break;case"int32":case"sint32":case"sfixed32":t("m%s=d%s|0",r,r);break;case"uint64":f=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t("if(util.Long)")("(m%s=util.Long.fromValue(d%s)).unsigned=%j",r,r,f)('else if(typeof d%s==="string")',r)("m%s=parseInt(d%s,10)",r,r)('else if(typeof d%s==="number")',r)("m%s=d%s",r,r)('else if(typeof d%s==="object")',r)("m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)",r,r,r,f?"true":"");break;case"bytes":t('if(typeof d%s==="string")',r)("util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)",r,r,r)("else if(d%s.length >= 0)",r)("m%s=d%s",r,r);break;case"string":t("m%s=String(d%s)",r,r);break;case"bool":t("m%s=Boolean(d%s)",r,r)}}return t}function p(t,i,n,r){if(i.resolvedType)i.resolvedType instanceof l?t("d%s=o.enums===String?(types[%i].values[m%s]===undefined?m%s:types[%i].values[m%s]):m%s",r,n,r,r,n,r,r):t("d%s=types[%i].toObject(m%s,o)",r,n,r);else{var e=!1;switch(i.type){case"double":case"float":t("d%s=o.json&&!isFinite(m%s)?String(m%s):m%s",r,r,r,r);break;case"uint64":e=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t('if(typeof m%s==="number")',r)("d%s=o.longs===String?String(m%s):m%s",r,r,r)("else")("d%s=o.longs===String?util.Long.prototype.toString.call(m%s):o.longs===Number?new util.LongBits(m%s.low>>>0,m%s.high>>>0).toNumber(%s):m%s",r,r,r,r,e?"true":"",r);break;case"bytes":t("d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s",r,r,r,r,r);break;default:t("d%s=m%s",r,r)}}return t}n.fromObject=function(t){var i=t.fieldsArray,n=d.codegen(["d"],t.name+"$fromObject")("if(d instanceof this.ctor)")("return d");if(!i.length)return n("return new this.ctor");n("var m=new this.ctor");for(var r=0;r>>3){"),n=0;n>>3){")("case 1: k=r.%s(); break",r.keyType)("case 2:"),f.basic[e]===it?i("value=types[%i].decode(r,r.uint32())",n):i("value=r.%s()",e),i("break")("default:")("r.skipType(tag2&7)")("break")("}")("}"),f.long[r.keyType]!==it?i('%s[typeof k==="object"?util.longToHash(k):k]=value',s):i("%s[k]=value",s)):r.repeated?(i("if(!(%s&&%s.length))",s,s)("%s=[]",s),f.packed[e]!==it&&i("if((t&7)===2){")("var c2=r.uint32()+r.pos")("while(r.pos>>0,8|a.mapKey[s.keyType],s.keyType),f===it?n("types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()",o,i):n(".uint32(%i).%s(%s[ks[i]]).ldelim()",16|f,u,i),n("}")("}")):s.repeated?(n("if(%s!=null&&%s.length){",i,i),s.packed&&a.packed[u]!==it?n("w.uint32(%i).fork()",(s.id<<3|2)>>>0)("for(var i=0;i<%s.length;++i)",i)("w.%s(%s[i])",u,i)("w.ldelim()"):(n("for(var i=0;i<%s.length;++i)",i),f===it?l(n,s,o,i+"[i]"):n("w.uint32(%i).%s(%s[i])",(s.id<<3|f)>>>0,u,i)),n("}")):(s.optional&&n("if(%s!=null&&Object.hasOwnProperty.call(m,%j))",i,s.name),f===it?l(n,s,o,i):n("w.uint32(%i).%s(%s)",(s.id<<3|f)>>>0,u,i))}return n("return w")};var h=t(15),a=t(36),c=t(37);function l(t,i,n,r){i.delimited?t("types[%i].encode(%s,w.uint32(%i)).uint32(%i)",n,r,(i.id<<3|3)>>>0,(i.id<<3|4)>>>0):t("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()",n,r,(i.id<<3|2)>>>0)}},{15:15,36:36,37:37}],15:[function(t,i,n){i.exports=s;var f=t(24),r=(((s.prototype=Object.create(f.prototype)).constructor=s).className="Enum",t(23)),e=t(37);function s(t,i,n,r,e,s){if(f.call(this,t,n),i&&"object"!=typeof i)throw TypeError("values must be an object");if(this.valuesById={},this.values=Object.create(this.valuesById),this.comment=r,this.comments=e||{},this.valuesOptions=s,this.o={},this.reserved=it,i)for(var o=Object.keys(i),u=0;u{var i=Object.assign({},this.h);this.o[t]=Object.assign(i,this.valuesOptions&&this.valuesOptions[t]&&this.valuesOptions[t].features)}),this},s.fromJSON=function(t,i){t=new s(t,i.values,i.options,i.comment,i.comments);return t.reserved=i.reserved,i.edition&&(t.f=i.edition),t.a="proto3",t},s.prototype.toJSON=function(t){t=!!t&&!!t.keepComments;return e.toObject(["edition",this.c(),"options",this.options,"valuesOptions",this.valuesOptions,"values",this.values,"reserved",this.reserved&&this.reserved.length?this.reserved:it,"comment",t?this.comment:it,"comments",t?this.comments:it])},s.prototype.add=function(t,i,n,r){if(!e.isString(t))throw TypeError("name must be a string");if(!e.isInteger(i))throw TypeError("id must be an integer");if(this.values[t]!==it)throw Error("duplicate name '"+t+"' in "+this);if(this.isReservedId(i))throw Error("id "+i+" is reserved in "+this);if(this.isReservedName(t))throw Error("name '"+t+"' is reserved in "+this);if(this.valuesById[i]!==it){if(!this.options||!this.options.allow_alias)throw Error("duplicate id "+i+" in "+this);this.values[t]=i}else this.valuesById[this.values[t]=i]=t;return r&&(this.valuesOptions===it&&(this.valuesOptions={}),this.valuesOptions[t]=r||null),this.comments[t]=n||null,this},s.prototype.remove=function(t){if(!e.isString(t))throw TypeError("name must be a string");var i=this.values[t];if(null==i)throw Error("name '"+t+"' does not exist in "+this);return delete this.valuesById[i],delete this.values[t],delete this.comments[t],this.valuesOptions&&delete this.valuesOptions[t],this},s.prototype.isReservedId=function(t){return r.isReservedId(this.reserved,t)},s.prototype.isReservedName=function(t){return r.isReservedName(this.reserved,t)}},{23:23,24:24,37:37}],16:[function(t,i,n){i.exports=o;var r,u=t(24),e=(((o.prototype=Object.create(u.prototype)).constructor=o).className="Field",t(15)),f=t(36),h=t(37),a=/^required|optional|repeated$/;function o(t,i,n,r,e,s,o){if(h.isObject(r)?(o=e,s=r,r=e=it):h.isObject(e)&&(o=s,s=e,e=it),u.call(this,t,s),!h.isInteger(i)||i<0)throw TypeError("id must be a non-negative integer");if(!h.isString(n))throw TypeError("type must be a string");if(r!==it&&!a.test(r=r.toString().toLowerCase()))throw TypeError("rule must be a string rule");if(e!==it&&!h.isString(e))throw TypeError("extend must be a string");this.rule=(r="proto3_optional"===r?"optional":r)&&"optional"!==r?r:it,this.type=n,this.id=i,this.extend=e||it,this.repeated="repeated"===r,this.map=!1,this.message=null,this.partOf=null,this.typeDefault=null,this.defaultValue=null,this.long=!!h.Long&&f.long[n]!==it,this.bytes="bytes"===n,this.resolvedType=null,this.extensionField=null,this.declaringField=null,this.comment=o}o.fromJSON=function(t,i){t=new o(t,i.id,i.type,i.rule,i.extend,i.options,i.comment);return i.edition&&(t.f=i.edition),t.a="proto3",t},Object.defineProperty(o.prototype,"required",{get:function(){return"LEGACY_REQUIRED"===this.h.field_presence}}),Object.defineProperty(o.prototype,"optional",{get:function(){return!this.required}}),Object.defineProperty(o.prototype,"delimited",{get:function(){return this.resolvedType instanceof r&&"DELIMITED"===this.h.message_encoding}}),Object.defineProperty(o.prototype,"packed",{get:function(){return"PACKED"===this.h.repeated_field_encoding}}),Object.defineProperty(o.prototype,"hasPresence",{get:function(){return!this.repeated&&!this.map&&(this.partOf||this.declaringField||this.extensionField||"IMPLICIT"!==this.h.field_presence)}}),o.prototype.setOption=function(t,i,n){return u.prototype.setOption.call(this,t,i,n)},o.prototype.toJSON=function(t){t=!!t&&!!t.keepComments;return h.toObject(["edition",this.c(),"rule","optional"!==this.rule&&this.rule||it,"type",this.type,"id",this.id,"extend",this.extend,"options",this.options,"comment",t?this.comment:it])},o.prototype.resolve=function(){var t;return this.resolved?this:((this.typeDefault=f.defaults[this.type])===it?(this.resolvedType=(this.declaringField||this).parent.lookupTypeOrEnum(this.type),this.resolvedType instanceof r?this.typeDefault=null:this.typeDefault=this.resolvedType.values[Object.keys(this.resolvedType.values)[0]]):this.options&&this.options.proto3_optional&&(this.typeDefault=null),this.options&&null!=this.options.default&&(this.typeDefault=this.options.default,this.resolvedType instanceof e&&"string"==typeof this.typeDefault&&(this.typeDefault=this.resolvedType.values[this.typeDefault])),this.options&&(this.options.packed===it||!this.resolvedType||this.resolvedType instanceof e||delete this.options.packed,Object.keys(this.options).length||(this.options=it)),this.long?(this.typeDefault=h.Long.fromNumber(this.typeDefault,"u"==(this.type[0]||"")),Object.freeze&&Object.freeze(this.typeDefault)):this.bytes&&"string"==typeof this.typeDefault&&(h.base64.test(this.typeDefault)?h.base64.decode(this.typeDefault,t=h.newBuffer(h.base64.length(this.typeDefault)),0):h.utf8.write(this.typeDefault,t=h.newBuffer(h.utf8.length(this.typeDefault)),0),this.typeDefault=t),this.map?this.defaultValue=h.emptyObject:this.repeated?this.defaultValue=h.emptyArray:this.defaultValue=this.typeDefault,this.parent instanceof r&&(this.parent.ctor.prototype[this.name]=this.defaultValue),u.prototype.resolve.call(this))},o.prototype.l=function(t){var i;return"proto2"!==t&&"proto3"!==t?{}:(t={},"required"===this.rule&&(t.field_presence="LEGACY_REQUIRED"),this.parent&&f.defaults[this.type]===it&&(i=this.parent.get(this.type.split(".").pop()))&&i instanceof r&&i.group&&(t.message_encoding="DELIMITED"),!0===this.getOption("packed")?t.repeated_field_encoding="PACKED":!1===this.getOption("packed")&&(t.repeated_field_encoding="EXPANDED"),t)},o.prototype.u=function(t){return u.prototype.u.call(this,this.f||t)},o.d=function(n,r,e,s){return"function"==typeof r?r=h.decorateType(r).name:r&&"object"==typeof r&&(r=h.decorateEnum(r).name),function(t,i){h.decorateType(t.constructor).add(new o(i,n,r,e,{default:s}))}},o.p=function(t){r=t}},{15:15,24:24,36:36,37:37}],17:[function(t,i,n){var r=i.exports=t(18);r.build="light",r.load=function(t,i,n){return(i="function"==typeof i?(n=i,new r.Root):i||new r.Root).load(t,n)},r.loadSync=function(t,i){return(i=i||new r.Root).loadSync(t)},r.encoder=t(14),r.decoder=t(13),r.verifier=t(40),r.converter=t(12),r.ReflectionObject=t(24),r.Namespace=t(23),r.Root=t(29),r.Enum=t(15),r.Type=t(35),r.Field=t(16),r.OneOf=t(25),r.MapField=t(20),r.Service=t(33),r.Method=t(22),r.Message=t(21),r.wrappers=t(41),r.types=t(36),r.util=t(37),r.ReflectionObject.p(r.Root),r.Namespace.p(r.Type,r.Service,r.Enum),r.Root.p(r.Type),r.Field.p(r.Type)},{12:12,13:13,14:14,15:15,16:16,18:18,20:20,21:21,22:22,23:23,24:24,25:25,29:29,33:33,35:35,36:36,37:37,40:40,41:41}],18:[function(t,i,n){var r=n;function e(){r.util.p(),r.Writer.p(r.BufferWriter),r.Reader.p(r.BufferReader)}r.build="minimal",r.Writer=t(42),r.BufferWriter=t(43),r.Reader=t(27),r.BufferReader=t(28),r.util=t(39),r.rpc=t(31),r.roots=t(30),r.configure=e,e()},{27:27,28:28,30:30,31:31,39:39,42:42,43:43}],19:[function(t,i,n){i=i.exports=t(17);i.build="full",i.tokenize=t(34),i.parse=t(26),i.common=t(11),i.Root.p(i.Type,i.parse,i.common)},{11:11,17:17,26:26,34:34}],20:[function(t,i,n){i.exports=s;var o=t(16),r=(((s.prototype=Object.create(o.prototype)).constructor=s).className="MapField",t(36)),u=t(37);function s(t,i,n,r,e,s){if(o.call(this,t,i,r,it,it,e,s),!u.isString(n))throw TypeError("keyType must be a string");this.keyType=n,this.resolvedKeyType=null,this.map=!0}s.fromJSON=function(t,i){return new s(t,i.id,i.keyType,i.type,i.options,i.comment)},s.prototype.toJSON=function(t){t=!!t&&!!t.keepComments;return u.toObject(["keyType",this.keyType,"type",this.type,"id",this.id,"extend",this.extend,"options",this.options,"comment",t?this.comment:it])},s.prototype.resolve=function(){if(this.resolved)return this;if(r.mapKey[this.keyType]===it)throw Error("invalid key type: "+this.keyType);return o.prototype.resolve.call(this)},s.d=function(n,r,e){return"function"==typeof e?e=u.decorateType(e).name:e&&"object"==typeof e&&(e=u.decorateEnum(e).name),function(t,i){u.decorateType(t.constructor).add(new s(i,n,r,e))}}},{16:16,36:36,37:37}],21:[function(t,i,n){i.exports=e;var r=t(39);function e(t){if(t)for(var i=Object.keys(t),n=0;ni)return!0;return!1},c.isReservedName=function(t,i){if(t)for(var n=0;n{t.g(i)})),this},c.prototype.lookup=function(t,i,n){if("boolean"==typeof i?(n=i,i=it):i&&!Array.isArray(i)&&(i=[i]),h.isString(t)&&t.length){if("."===t)return this.root;t=t.split(".")}else if(!t.length)return this;var r=t.join(".");if(""===t[0])return this.root.lookup(t.slice(1),i);var e=this.root.j&&this.root.j["."+r];if(e&&(!i||~i.indexOf(e.constructor)))return e;if((e=this.k(t,r))&&(!i||~i.indexOf(e.constructor)))return e;if(!n)for(var s=this;s.parent;){if((e=s.parent.k(t,r))&&(!i||~i.indexOf(e.constructor)))return e;s=s.parent}return null},c.prototype.k=function(t,i){if(Object.prototype.hasOwnProperty.call(this.b,i))return this.b[i];var n=this.get(t[0]),r=null;if(n)1===t.length?r=n:n instanceof c&&(t=t.slice(1),r=n.k(t,t.join(".")));else for(var e=0;e");var e=c();if(!J.test(e))throw g(e,"name");d("=");var s=new F(m(e),O(c()),n,r);A(s,function(t){if("option"!==t)throw g(t);S(s,t),d(";")},function(){N(s)}),i.add(s);break;case"required":if("proto2"!==v)throw g(t);case"repeated":x(u,t);break;case"optional":if("proto3"===v)x(u,"proto3_optional");else{if("proto2"!==v)throw g(t);x(u,"optional")}break;case"oneof":e=u,n=t;if(!J.test(n=c()))throw g(n,"name");var o=new $(m(n));A(o,function(t){"option"===t?(S(o,t),d(";")):(f(t),x(o,"optional"))}),e.add(o);break;case"extensions":k(u.extensions||(u.extensions=[]));break;case"reserved":k(u.reserved||(u.reserved=[]),!0);break;default:if("proto2"===v||!Q.test(t))throw g(t);f(t),x(u,"optional")}}),t.add(u),t===b&&w.push(u)}function x(t,i,n){var r=c();if("group"===r){var e=t,s=i;if(2023<=v)throw g("group");var o,u,f=c();if(J.test(f))return u=Y.lcFirst(f),f===u&&(f=Y.ucFirst(f)),d("="),h=O(c()),(o=new M(f)).group=!0,(u=new R(u,h,f,s)).filename=tt.filename,A(o,function(t){switch(t){case"option":S(o,t),d(";");break;case"required":case"repeated":x(o,t);break;case"optional":x(o,"proto3"===v?"proto3_optional":"optional");break;case"message":T(o);break;case"enum":L(o);break;case"reserved":k(o.reserved||(o.reserved=[]),!0);break;case"export":case"local":if(v<"2024")throw g(t);switch(t=c()){case"message":case"enum":T(o);break;default:throw g(t)}break;default:throw g(t)}}),void e.add(o).add(u);throw g(f,"name")}for(;r.endsWith(".")||l().startsWith(".");)r+=c();if(!Q.test(r))throw g(r,"type");var h=c();if(!J.test(h))throw g(h,"name");h=m(h),d("=");var a=new R(h,O(c()),r,i,n);A(a,function(t){if("option"!==t)throw g(t);S(a,t),d(";")},function(){N(a)}),"proto3_optional"===i?(s=new $("_"+h),a.setOption("proto3_optional",!0),s.add(a),t.add(s)):t.add(a),t===b&&w.push(a)}function L(t,i){if(!J.test(i=c()))throw g(i,"name");var s=new D(i);A(s,function(t){switch(t){case"option":S(s,t),d(";");break;case"reserved":k(s.reserved||(s.reserved=[]),!0),s.reserved===it&&(s.reserved=[]);break;default:var i=s,n=t;if(!J.test(n))throw g(n,"name");d("=");var r=O(c(),!0),e={options:it,getOption:function(t){return this.options[t]},setOption:function(t,i){q.prototype.setOption.call(e,t,i)},setParsedOption:function(){return it}};return A(e,function(t){if("option"!==t)throw g(t);S(e,t),d(";")},function(){N(e)}),void i.add(n,r,e.comment,e.parsedOptions||e.options)}}),t.add(s),t===b&&w.push(s)}function S(t,i){var n=!0;for("option"===i&&(i=c());"="!==i;){if("("===i&&(r=c(),d(")"),i="("+r+")"),n){if(n=!1,i.includes(".")&&!i.includes("(")){var r=i.split("."),e=r[0]+".";i=r[1];continue}e=i}else f=f?f+i:i;i=c()}var s,o,u=f?e.concat(f):e,u=function t(i,n){if(d("{",!0)){for(var r={};!d("}",!0);){if(!J.test(h=c()))throw g(h,"name");if(null===h)throw g(h,"end of input");var e,s,o=h;if(d(":",!0),"{"===l())e=t(i,n+"."+h);else if("["===l()){if(e=[],d("[",!0)){for(;s=E(!0),e.push(s),d(",",!0););d("]"),void 0!==s&&I(i,n+"."+h,s)}}else e=E(!0),I(i,n+"."+h,e);var u=r[o];u&&(e=[].concat(u).concat(e)),r[o]=e,d(",",!0),d(";",!0)}return r}var f=E(!0);I(i,n,f);return f}(t,u),f=f&&"."===f[0]?f.slice(1):f;e=e&&"."===e[e.length-1]?e.slice(0,-1):e,s=e,u=u,o=f,(t=t).setParsedOption&&t.setParsedOption(s,u,o)}function I(t,i,n){b===t&&/^features\./.test(i)?y[i]=n:t.setOption&&t.setOption(i,n)}function N(t){if(d("[",!0)){for(;S(t,"option"),d(",",!0););d("]")}}for(;null!==(h=c());)switch(h){case"package":if(!p)throw g(h);if(r!==it)throw g("package");if(r=c(),!Q.test(r))throw g(r,"name");b=b.define(r),d(";");break;case"import":if(!p)throw g(h);!function(){var t,i;switch(l()){case"option":if(v<"2024")throw g("option");return c(),j(),d(";");case"weak":i=s=s||[],c();break;case"public":c();default:i=e=e||[]}t=j(),d(";"),i.push(t)}();break;case"syntax":if(!p)throw g(h);if(d("="),(v=j())<2023)throw g(v,"syntax");d(";");break;case"edition":if(!p)throw g(h);if(d("="),v=j(),!["2023","2024"].includes(v))throw g(v,"edition");d(";");break;case"option":S(b,h),d(";",!0);break;default:if(_(b,h)){p=!1;continue}throw g(h)}return w.forEach(i=>{i.f=v,Object.keys(y).forEach(t=>{i.getOption(t)===it&&i.setOption(t,y[t],!0)})}),tt.filename=null,{package:r,imports:e,weakImports:s,root:i}}},{15:15,16:16,20:20,22:22,24:24,25:25,29:29,33:33,34:34,35:35,36:36,37:37}],27:[function(t,i,n){i.exports=f;var r,e=t(39),s=e.LongBits,o=e.utf8;function u(t,i){return RangeError("index out of range: "+t.pos+" + "+(i||1)+" > "+t.len)}function f(t){this.buf=t,this.pos=0,this.len=t.length}function h(){return e.Buffer?function(t){return(f.create=function(t){return e.Buffer.isBuffer(t)?new r(t):c(t)})(t)}:c}var a,c="undefined"!=typeof Uint8Array?function(t){if(t instanceof Uint8Array||Array.isArray(t))return new f(t);throw Error("illegal buffer")}:function(t){if(Array.isArray(t))return new f(t);throw Error("illegal buffer")};function l(){var t=new s(0,0),i=0;if(!(4=this.len)throw u(this);if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t}return t.lo=(t.lo|(127&this.buf[this.pos++])<<7*i)>>>0,t}for(;i<4;++i)if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t;if(t.lo=(t.lo|(127&this.buf[this.pos])<<28)>>>0,t.hi=(t.hi|(127&this.buf[this.pos])>>4)>>>0,this.buf[this.pos++]<128)return t;if(i=0,4>>0,this.buf[this.pos++]<128)return t}else for(;i<5;++i){if(this.pos>=this.len)throw u(this);if(t.hi=(t.hi|(127&this.buf[this.pos])<<7*i+3)>>>0,this.buf[this.pos++]<128)return t}throw Error("invalid varint encoding")}function d(t,i){return(t[i-4]|t[i-3]<<8|t[i-2]<<16|t[i-1]<<24)>>>0}function p(){if(this.pos+8>this.len)throw u(this,8);return new s(d(this.buf,this.pos+=4),d(this.buf,this.pos+=4))}f.create=h(),f.prototype.T=e.Array.prototype.subarray||e.Array.prototype.slice,f.prototype.uint32=(a=4294967295,function(){if(a=(127&this.buf[this.pos])>>>0,this.buf[this.pos++]<128||(a=(a|(127&this.buf[this.pos])<<7)>>>0,this.buf[this.pos++]<128||(a=(a|(127&this.buf[this.pos])<<14)>>>0,this.buf[this.pos++]<128||(a=(a|(127&this.buf[this.pos])<<21)>>>0,this.buf[this.pos++]<128||(a=(a|(15&this.buf[this.pos])<<28)>>>0,this.buf[this.pos++]<128||!((this.pos+=5)>this.len))))))return a;throw this.pos=this.len,u(this,10)}),f.prototype.int32=function(){return 0|this.uint32()},f.prototype.sint32=function(){var t=this.uint32();return t>>>1^-(1&t)|0},f.prototype.bool=function(){return 0!==this.uint32()},f.prototype.fixed32=function(){if(this.pos+4>this.len)throw u(this,4);return d(this.buf,this.pos+=4)},f.prototype.sfixed32=function(){if(this.pos+4>this.len)throw u(this,4);return 0|d(this.buf,this.pos+=4)},f.prototype.float=function(){if(this.pos+4>this.len)throw u(this,4);var t=e.float.readFloatLE(this.buf,this.pos);return this.pos+=4,t},f.prototype.double=function(){if(this.pos+8>this.len)throw u(this,4);var t=e.float.readDoubleLE(this.buf,this.pos);return this.pos+=8,t},f.prototype.bytes=function(){var t=this.uint32(),i=this.pos,n=this.pos+t;if(n>this.len)throw u(this,t);return this.pos+=t,Array.isArray(this.buf)?this.buf.slice(i,n):i===n?(t=e.Buffer)?t.alloc(0):new this.buf.constructor(0):this.T.call(this.buf,i,n)},f.prototype.string=function(){var t=this.bytes();return o.read(t,0,t.length)},f.prototype.skip=function(t){if("number"==typeof t){if(this.pos+t>this.len)throw u(this,t);this.pos+=t}else do{if(this.pos>=this.len)throw u(this)}while(128&this.buf[this.pos++]);return this},f.prototype.skipType=function(t){switch(t){case 0:this.skip();break;case 1:this.skip(8);break;case 2:this.skip(this.uint32());break;case 3:for(;4!=(t=7&this.uint32());)this.skipType(t);break;case 5:this.skip(4);break;default:throw Error("invalid wire type "+t+" at offset "+this.pos)}return this},f.p=function(t){r=t,f.create=h(),r.p();var i=e.Long?"toLong":"toNumber";e.merge(f.prototype,{int64:function(){return l.call(this)[i](!1)},uint64:function(){return l.call(this)[i](!0)},sint64:function(){return l.call(this).zzDecode()[i](!1)},fixed64:function(){return p.call(this)[i](!0)},sfixed64:function(){return p.call(this)[i](!1)}})}},{39:39}],28:[function(t,i,n){i.exports=s;var r=t(27),e=((s.prototype=Object.create(r.prototype)).constructor=s,t(39));function s(t){r.call(this,t)}s.p=function(){e.Buffer&&(s.prototype.T=e.Buffer.prototype.slice)},s.prototype.string=function(){var t=this.uint32();return this.buf.utf8Slice?this.buf.utf8Slice(this.pos,this.pos=Math.min(this.pos+t,this.len)):this.buf.toString("utf-8",this.pos,this.pos=Math.min(this.pos+t,this.len))},s.p()},{27:27,39:39}],29:[function(t,i,n){i.exports=f;var r,d,p,e=t(23),s=(((f.prototype=Object.create(e.prototype)).constructor=f).className="Root",t(16)),o=t(15),u=t(25),v=t(37);function f(t){e.call(this,"",t),this.deferred=[],this.files=[],this.f="proto2",this.j={}}function b(){}f.fromJSON=function(t,i){return i=i||new f,t.options&&i.setOptions(t.options),i.addJSON(t.nested).resolveAll()},f.prototype.resolvePath=v.path.resolve,f.prototype.fetch=v.fetch,f.prototype.load=function t(i,s,e){"function"==typeof s&&(e=s,s=it);var o=this;if(!e)return v.asPromise(t,o,i,s);var u=e===b;function f(t,i){if(e){if(u)throw t;i&&i.resolveAll();var n=e;e=null,n(t,i)}}function h(t){var i=t.lastIndexOf("google/protobuf/");if(-1{t.g(i)})),this},o.prototype.add=function(t){if(this.get(t.name))throw Error("duplicate name '"+t.name+"' in "+this);return t instanceof s?e((this.methods[t.name]=t).parent=this):r.prototype.add.call(this,t)},o.prototype.remove=function(t){if(t instanceof s){if(this.methods[t.name]!==t)throw Error(t+" is not a member of "+this);return delete this.methods[t.name],t.parent=null,e(this)}return r.prototype.remove.call(this,t)},o.prototype.create=function(t,i,n){for(var r,e=new f.Service(t,i,n),s=0;s]/g,O=/(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g,_=/(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g,A=/^ *[*/]+ */,T=/^\s*\*?\/*/,x=/\n/g,L=/\s/,r=/\\(.?)/g,e={0:"\0",r:"\r",n:"\n",t:"\t"};function S(t){return t.replace(r,function(t,i){switch(i){case"\\":case"":return i;default:return e[i]||""}})}function s(h,a){h=h.toString();var c=0,l=h.length,d=1,f=0,p={},v=[],b=null;function w(t){return Error("illegal "+t+" (line "+d+")")}function y(t){return h[0|t]||""}function m(t,i,n){var r,e={type:h[0|t++]||"",lineEmpty:!1,leading:n},n=a?2:3,s=t-n;do{if(--s<0||"\n"==(r=h[0|s]||"")){e.lineEmpty=!0;break}}while(" "===r||"\t"===r);for(var o=h.substring(t,i).split(x),u=0;u{t.u(i)}),this.fieldsArray.forEach(t=>{t.u(i)})),this},m.prototype.get=function(t){return this.fields[t]||this.oneofs&&this.oneofs[t]||this.nested&&this.nested[t]||null},m.prototype.add=function(t){if(this.get(t.name))throw Error("duplicate name '"+t.name+"' in "+this);if(t instanceof h&&t.extend===it){if((this.L||this.fieldsById)[t.id])throw Error("duplicate id "+t.id+" in "+this);if(this.isReservedId(t.id))throw Error("id "+t.id+" is reserved in "+this);if(this.isReservedName(t.name))throw Error("name '"+t.name+"' is reserved in "+this);return t.parent&&t.parent.remove(t),(this.fields[t.name]=t).message=this,t.onAdd(this),r(this)}return t instanceof f?(this.oneofs||(this.oneofs={}),(this.oneofs[t.name]=t).onAdd(this),r(this)):o.prototype.add.call(this,t)},m.prototype.remove=function(t){if(t instanceof h&&t.extend===it){if(this.fields&&this.fields[t.name]===t)return delete this.fields[t.name],t.parent=null,t.onRemove(this),r(this);throw Error(t+" is not a member of "+this)}if(t instanceof f){if(this.oneofs&&this.oneofs[t.name]===t)return delete this.oneofs[t.name],t.parent=null,t.onRemove(this),r(this);throw Error(t+" is not a member of "+this)}return o.prototype.remove.call(this,t)},m.prototype.isReservedId=function(t){return o.isReservedId(this.reserved,t)},m.prototype.isReservedName=function(t){return o.isReservedName(this.reserved,t)},m.prototype.create=function(t){return new this.ctor(t)},m.prototype.setup=function(){for(var t=this.fullName,i=[],n=0;n>>0,this.hi=i>>>0}var s=e.zero=new e(0,0),o=(s.toNumber=function(){return 0},s.zzEncode=s.zzDecode=function(){return this},s.length=function(){return 1},e.zeroHash="\0\0\0\0\0\0\0\0",e.fromNumber=function(t){var i,n;return 0===t?s:(n=(t=(i=t<0)?-t:t)>>>0,t=(t-n)/4294967296>>>0,i&&(t=~t>>>0,n=~n>>>0,4294967295<++n&&(n=0,4294967295<++t&&(t=0))),new e(n,t))},e.from=function(t){if("number"==typeof t)return e.fromNumber(t);if(r.isString(t)){if(!r.Long)return e.fromNumber(parseInt(t,10));t=r.Long.fromString(t)}return t.low||t.high?new e(t.low>>>0,t.high>>>0):s},e.prototype.toNumber=function(t){var i;return!t&&this.hi>>>31?(t=1+~this.lo>>>0,i=~this.hi>>>0,-(t+4294967296*(i=t?i:i+1>>>0))):this.lo+4294967296*this.hi},e.prototype.toLong=function(t){return r.Long?new r.Long(0|this.lo,0|this.hi,!!t):{low:0|this.lo,high:0|this.hi,unsigned:!!t}},String.prototype.charCodeAt);e.fromHash=function(t){return"\0\0\0\0\0\0\0\0"===t?s:new e((o.call(t,0)|o.call(t,1)<<8|o.call(t,2)<<16|o.call(t,3)<<24)>>>0,(o.call(t,4)|o.call(t,5)<<8|o.call(t,6)<<16|o.call(t,7)<<24)>>>0)},e.prototype.toHash=function(){return String.fromCharCode(255&this.lo,this.lo>>>8&255,this.lo>>>16&255,this.lo>>>24,255&this.hi,this.hi>>>8&255,this.hi>>>16&255,this.hi>>>24)},e.prototype.zzEncode=function(){var t=this.hi>>31;return this.hi=((this.hi<<1|this.lo>>>31)^t)>>>0,this.lo=(this.lo<<1^t)>>>0,this},e.prototype.zzDecode=function(){var t=-(1&this.lo);return this.lo=((this.lo>>>1|this.hi<<31)^t)>>>0,this.hi=(this.hi>>>1^t)>>>0,this},e.prototype.length=function(){var t=this.lo,i=(this.lo>>>28|this.hi<<4)>>>0,n=this.hi>>>24;return 0==n?0==i?t<16384?t<128?1:2:t<2097152?3:4:i<16384?i<128?5:6:i<2097152?7:8:n<128?9:10}},{39:39}],39:[function(t,i,n){var r=n;function e(t,i,n){for(var r=Object.keys(i),e=0;e>>7|t.hi<<25)>>>0,t.hi>>>=7;for(;127>>7;i[n++]=t.lo}function b(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}c.create=l(),c.alloc=function(t){return new e.Array(t)},e.Array!==Array&&(c.alloc=e.pool(c.alloc,e.Array.prototype.subarray)),c.prototype.V=function(t,i,n){return this.tail=this.tail.next=new f(t,i,n),this.len+=i,this},(p.prototype=Object.create(f.prototype)).fn=function(t,i,n){for(;127>>=7;i[n]=t},c.prototype.uint32=function(t){return this.len+=(this.tail=this.tail.next=new p((t>>>=0)<128?1:t<16384?2:t<2097152?3:t<268435456?4:5,t)).len,this},c.prototype.int32=function(t){return t<0?this.V(v,10,s.fromNumber(t)):this.uint32(t)},c.prototype.sint32=function(t){return this.uint32((t<<1^t>>31)>>>0)},c.prototype.int64=c.prototype.uint64=function(t){t=s.from(t);return this.V(v,t.length(),t)},c.prototype.sint64=function(t){t=s.from(t).zzEncode();return this.V(v,t.length(),t)},c.prototype.bool=function(t){return this.V(d,1,t?1:0)},c.prototype.sfixed32=c.prototype.fixed32=function(t){return this.V(b,4,t>>>0)},c.prototype.sfixed64=c.prototype.fixed64=function(t){t=s.from(t);return this.V(b,4,t.lo).V(b,4,t.hi)},c.prototype.float=function(t){return this.V(e.float.writeFloatLE,4,t)},c.prototype.double=function(t){return this.V(e.float.writeDoubleLE,8,t)};var w=e.Array.prototype.set?function(t,i,n){i.set(t,n)}:function(t,i,n){for(var r=0;r>>0;return n?(e.isString(t)&&(i=c.alloc(n=o.length(t)),o.decode(t,i,0),t=i),this.uint32(n).V(w,n,t)):this.V(d,1,0)},c.prototype.string=function(t){var i=u.length(t);return i?this.uint32(i).V(u.write,i,t):this.V(d,1,0)},c.prototype.fork=function(){return this.states=new a(this),this.head=this.tail=new f(h,0,0),this.len=0,this},c.prototype.reset=function(){return this.states?(this.head=this.states.head,this.tail=this.states.tail,this.len=this.states.len,this.states=this.states.next):(this.head=this.tail=new f(h,0,0),this.len=0),this},c.prototype.ldelim=function(){var t=this.head,i=this.tail,n=this.len;return this.reset().uint32(n),n&&(this.tail.next=t.next,this.tail=i,this.len+=n),this},c.prototype.finish=function(){for(var t=this.head.next,i=this.constructor.alloc(this.len),n=0;t;)t.fn(t.val,i,n),n+=t.len,t=t.next;return i},c.p=function(t){r=t,c.create=l(),r.p()}},{39:39}],43:[function(t,i,n){i.exports=s;var r=t(42),e=((s.prototype=Object.create(r.prototype)).constructor=s,t(39));function s(){r.call(this)}function o(t,i,n){t.length<40?e.utf8.write(t,i,n):i.utf8Write?i.utf8Write(t,n):i.write(t,n)}s.p=function(){s.alloc=e.P,s.writeBytesBuffer=e.Buffer&&e.Buffer.prototype instanceof Uint8Array&&"set"===e.Buffer.prototype.set.name?function(t,i,n){i.set(t,n)}:function(t,i,n){if(t.copy)t.copy(i,n,0,t.length);else for(var r=0;r>>0;return this.uint32(i),i&&this.V(s.writeBytesBuffer,i,t),this},s.prototype.string=function(t){var i=e.Buffer.byteLength(t);return this.uint32(i),i&&this.V(o,i,t),this},s.p()},{39:39,42:42}]},{},[19])}(); +//# sourceMappingURL=protobuf.min.js.map \ No newline at end of file diff --git a/lib/remote_cdm.js b/lib/remote_cdm.js index 8457670..c4f4ba7 100644 --- a/lib/remote_cdm.js +++ b/lib/remote_cdm.js @@ -1,115 +1,140 @@ export class RemoteCdm { - constructor(device_type, system_id, security_level, host, secret, device_name) { - this.device_type = device_type; - this.system_id = system_id; - this.security_level = security_level; - this.host = host; - this.secret = secret; - this.device_name = device_name; - } + constructor( + device_type, + system_id, + security_level, + host, + secret, + device_name, + ) { + this.device_type = device_type; + this.system_id = system_id; + this.security_level = security_level; + this.host = host; + this.secret = secret; + this.device_name = device_name; + } - static from_object(obj) { - return new RemoteCdm( - obj.device_type, - obj.system_id, - obj.security_level, - obj.host, - obj.secret, - obj.device_name ?? obj.name, - ); - } + static from_object(obj) { + return new RemoteCdm( + obj.device_type, + obj.system_id, + obj.security_level, + obj.host, + obj.secret, + obj.device_name ?? obj.name, + ); + } - get_name() { - const type = this.device_type === "CHROME" ? "CHROME" : `L${this.security_level}` - return `[${type}] ${this.host}/${this.device_name} (${this.system_id})`; - } + get_name() { + const type = + this.device_type === "CHROME" ? "CHROME" : `L${this.security_level}`; + return `[${type}] ${this.host}/${this.device_name} (${this.system_id})`; + } - async open() { - const open_request = await fetch( - `${this.host}/${this.device_name}/open`, - { - method: 'GET', - headers: { - "X-Secret-Key": this.secret - } - } - ); - console.log("[WidevineProxy2]", "REMOTE_CDM", "OPEN", open_request.status); - const open_json = await open_request.json(); + async open() { + const open_request = await fetch(`${this.host}/${this.device_name}/open`, { + method: "GET", + headers: { + "X-Secret-Key": this.secret, + }, + }); + console.log("[WidevineProxy2]", "REMOTE_CDM", "OPEN", open_request.status); + const open_json = await open_request.json(); - return open_json.data.session_id; - } + return open_json.data.session_id; + } - async close(session_id) { - const close_request = await fetch( - `${this.host}/${this.device_name}/close/${session_id}`, - { - method: 'GET', - headers: { - "X-Secret-Key": this.secret - } - } - ); - console.log("[WidevineProxy2]", "REMOTE_CDM", "CLOSE", close_request.status); - } + async close(session_id) { + const close_request = await fetch( + `${this.host}/${this.device_name}/close/${session_id}`, + { + method: "GET", + headers: { + "X-Secret-Key": this.secret, + }, + }, + ); + console.log( + "[WidevineProxy2]", + "REMOTE_CDM", + "CLOSE", + close_request.status, + ); + } - async get_license_challenge(session_id, pssh, privacy_mode) { - const license_request = await fetch( - `${this.host}/${this.device_name}/get_license_challenge/STREAMING`, - { - method: "POST", - headers: { - "content-type": "application/json", - "X-Secret-Key": this.secret - }, - body: JSON.stringify({ - session_id: session_id, - init_data: pssh, - privacy_mode: privacy_mode - }) - } - ) - console.log("[WidevineProxy2]", "REMOTE_CDM", "GET_LICENSE_CHALLENGE", license_request.status); - const license_request_json = await license_request.json(); + async get_license_challenge(session_id, pssh, privacy_mode) { + const license_request = await fetch( + `${this.host}/${this.device_name}/get_license_challenge/STREAMING`, + { + method: "POST", + headers: { + "content-type": "application/json", + "X-Secret-Key": this.secret, + }, + body: JSON.stringify({ + session_id: session_id, + init_data: pssh, + privacy_mode: privacy_mode, + }), + }, + ); + console.log( + "[WidevineProxy2]", + "REMOTE_CDM", + "GET_LICENSE_CHALLENGE", + license_request.status, + ); + const license_request_json = await license_request.json(); - return license_request_json.data.challenge_b64; - } + return license_request_json.data.challenge_b64; + } - async parse_license(session_id, license_b64) { - const license = await fetch( - `${this.host}/${this.device_name}/parse_license`, - { - method: "POST", - headers: { - "content-type": "application/json", - "X-Secret-Key": this.secret - }, - body: JSON.stringify({ - session_id: session_id, - license_message: license_b64 - }) - } - ) - console.log("[WidevineProxy2]", "REMOTE_CDM", "PARSE_LICENSE", license.status); - } + async parse_license(session_id, license_b64) { + const license = await fetch( + `${this.host}/${this.device_name}/parse_license`, + { + method: "POST", + headers: { + "content-type": "application/json", + "X-Secret-Key": this.secret, + }, + body: JSON.stringify({ + session_id: session_id, + license_message: license_b64, + }), + }, + ); + console.log( + "[WidevineProxy2]", + "REMOTE_CDM", + "PARSE_LICENSE", + license.status, + ); + } - async get_keys(session_id, type) { - const key_request = await fetch( - `${this.host}/${this.device_name}/get_keys/${type}`, - { - method: "POST", - headers: { - "content-type": "application/json", - "X-Secret-Key": this.secret - }, - body: JSON.stringify({ - session_id: session_id - }) - } - ) - console.log("[WidevineProxy2]", "REMOTE_CDM", "GET_KEYS", key_request.status); - const key_request_json = await key_request.json(); + async get_keys(session_id, type) { + const key_request = await fetch( + `${this.host}/${this.device_name}/get_keys/${type}`, + { + method: "POST", + headers: { + "content-type": "application/json", + "X-Secret-Key": this.secret, + }, + body: JSON.stringify({ + session_id: session_id, + }), + }, + ); + console.log( + "[WidevineProxy2]", + "REMOTE_CDM", + "GET_KEYS", + key_request.status, + ); + const key_request_json = await key_request.json(); - return key_request_json.data.keys; - } -} \ No newline at end of file + return key_request_json.data.keys; + } +} diff --git a/lib/util.js b/lib/util.js index 2b802f2..b3ca096 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2,342 +2,347 @@ import { WidevineDevice } from "./device.js"; import { RemoteCdm } from "./remote_cdm.js"; export class AsyncSyncStorage { - static async setStorage(items) { - return await chrome.storage.sync.set(items); - } + static async setStorage(items) { + return await chrome.storage.sync.set(items); + } - static async getStorage(keys) { - return await chrome.storage.sync.get(keys); - } + static async getStorage(keys) { + return await chrome.storage.sync.get(keys); + } - static async removeStorage(keys) { - await chrome.storage.sync.remove(keys); - } + static async removeStorage(keys) { + await chrome.storage.sync.remove(keys); + } } export class AsyncLocalStorage { - static async setStorage(items) { - return await chrome.storage.local.set(items); - } + static async setStorage(items) { + return await chrome.storage.local.set(items); + } - static async getStorage(keys) { - return await chrome.storage.local.get(keys); - } + static async getStorage(keys) { + return await chrome.storage.local.get(keys); + } - static clearStorage() { - chrome.storage.local.clear(); - } + static clearStorage() { + chrome.storage.local.clear(); + } } export class DeviceManager { - static async saveWidevineDevice(name, value) { - const result = await AsyncSyncStorage.getStorage(['devices']); - const array = result.devices === undefined ? [] : result.devices; - array.push(name); - await AsyncSyncStorage.setStorage({ devices: array }); - await AsyncSyncStorage.setStorage({ [name]: value }); - } - - static async loadWidevineDevice(name) { - const result = await AsyncSyncStorage.getStorage([name]); - return result[name] || ""; - } - - static setWidevineDevice(name, value){ - const wvd_combobox = document.getElementById('wvd-combobox'); - const wvd_element = document.createElement('option'); - - wvd_element.text = name; - wvd_element.value = value; - - wvd_combobox.appendChild(wvd_element); - } - - static async loadSetAllWidevineDevices() { - const result = await AsyncSyncStorage.getStorage(['devices']); - const array = result.devices || []; - for (const item of array) { - this.setWidevineDevice(item, await this.loadWidevineDevice(item)); - } - } - - static async saveSelectedWidevineDevice(name) { - await AsyncSyncStorage.setStorage({ selected: name }); - } - - static async getSelectedWidevineDevice() { - const result = await AsyncSyncStorage.getStorage(["selected"]); - return result["selected"] || ""; - } - - static async selectWidevineDevice(name) { - document.getElementById('wvd-combobox').value = await this.loadWidevineDevice(name); - } - - static async removeSelectedWidevineDevice() { - const selected_device_name = await DeviceManager.getSelectedWidevineDevice(); - - const result = await AsyncSyncStorage.getStorage(['devices']); - const array = result.devices === undefined ? [] : result.devices; - - const index = array.indexOf(selected_device_name); - if (index > -1) { - array.splice(index, 1); - } - - await AsyncSyncStorage.setStorage({ devices: array }); - await AsyncSyncStorage.removeStorage([selected_device_name]); - } - - static async removeSelectedWidevineDeviceKey() { - await AsyncSyncStorage.removeStorage(["selected"]); - } + static async saveWidevineDevice(name, value) { + const result = await AsyncSyncStorage.getStorage(["devices"]); + const array = result.devices === undefined ? [] : result.devices; + array.push(name); + await AsyncSyncStorage.setStorage({ devices: array }); + await AsyncSyncStorage.setStorage({ [name]: value }); + } + + static async loadWidevineDevice(name) { + const result = await AsyncSyncStorage.getStorage([name]); + return result[name] || ""; + } + + static setWidevineDevice(name, value) { + const wvd_combobox = document.getElementById("wvd-combobox"); + const wvd_element = document.createElement("option"); + + wvd_element.text = name; + wvd_element.value = value; + + wvd_combobox.appendChild(wvd_element); + } + + static async loadSetAllWidevineDevices() { + const result = await AsyncSyncStorage.getStorage(["devices"]); + const array = result.devices || []; + for (const item of array) { + this.setWidevineDevice(item, await this.loadWidevineDevice(item)); + } + } + + static async saveSelectedWidevineDevice(name) { + await AsyncSyncStorage.setStorage({ selected: name }); + } + + static async getSelectedWidevineDevice() { + const result = await AsyncSyncStorage.getStorage(["selected"]); + return result["selected"] || ""; + } + + static async selectWidevineDevice(name) { + document.getElementById("wvd-combobox").value = + await this.loadWidevineDevice(name); + } + + static async removeSelectedWidevineDevice() { + const selected_device_name = + await DeviceManager.getSelectedWidevineDevice(); + + const result = await AsyncSyncStorage.getStorage(["devices"]); + const array = result.devices === undefined ? [] : result.devices; + + const index = array.indexOf(selected_device_name); + if (index > -1) { + array.splice(index, 1); + } + + await AsyncSyncStorage.setStorage({ devices: array }); + await AsyncSyncStorage.removeStorage([selected_device_name]); + } + + static async removeSelectedWidevineDeviceKey() { + await AsyncSyncStorage.removeStorage(["selected"]); + } } export class RemoteCDMManager { - static async saveRemoteCDM(name, obj) { - const result = await AsyncSyncStorage.getStorage(['remote_cdms']); - const array = result.remote_cdms === undefined ? [] : result.remote_cdms; - array.push(name); - await AsyncSyncStorage.setStorage({ remote_cdms: array }); - await AsyncSyncStorage.setStorage({ [name]: obj }); - } - - static async loadRemoteCDM(name) { - const result = await AsyncSyncStorage.getStorage([name]); - return JSON.stringify(result[name] || {}); - } - - static setRemoteCDM(name, value){ - const remote_combobox = document.getElementById('remote-combobox'); - const remote_element = document.createElement('option'); - - remote_element.text = name; - remote_element.value = value; - - remote_combobox.appendChild(remote_element); - } - - static async loadSetAllRemoteCDMs() { - const result = await AsyncSyncStorage.getStorage(['remote_cdms']); - const array = result.remote_cdms || []; - for (const item of array) { - this.setRemoteCDM(item, await this.loadRemoteCDM(item)); - } - } - - static async saveSelectedRemoteCDM(name) { - await AsyncSyncStorage.setStorage({ selected_remote_cdm: name }); - } - - static async getSelectedRemoteCDM() { - const result = await AsyncSyncStorage.getStorage(["selected_remote_cdm"]); - return result["selected_remote_cdm"] || ""; - } - - static async selectRemoteCDM(name) { - document.getElementById('remote-combobox').value = await this.loadRemoteCDM(name); - } - - static async removeSelectedRemoteCDM() { - const selected_remote_cdm_name = await RemoteCDMManager.getSelectedRemoteCDM(); - - const result = await AsyncSyncStorage.getStorage(['remote_cdms']); - const array = result.remote_cdms === undefined ? [] : result.remote_cdms; - - const index = array.indexOf(selected_remote_cdm_name); - if (index > -1) { - array.splice(index, 1); - } - - await AsyncSyncStorage.setStorage({ remote_cdms: array }); - await AsyncSyncStorage.removeStorage([selected_remote_cdm_name]); - } - - static async removeSelectedRemoteCDMKey() { - await AsyncSyncStorage.removeStorage(["selected_remote_cdm"]); - } + static async saveRemoteCDM(name, obj) { + const result = await AsyncSyncStorage.getStorage(["remote_cdms"]); + const array = result.remote_cdms === undefined ? [] : result.remote_cdms; + array.push(name); + await AsyncSyncStorage.setStorage({ remote_cdms: array }); + await AsyncSyncStorage.setStorage({ [name]: obj }); + } + + static async loadRemoteCDM(name) { + const result = await AsyncSyncStorage.getStorage([name]); + return JSON.stringify(result[name] || {}); + } + + static setRemoteCDM(name, value) { + const remote_combobox = document.getElementById("remote-combobox"); + const remote_element = document.createElement("option"); + + remote_element.text = name; + remote_element.value = value; + + remote_combobox.appendChild(remote_element); + } + + static async loadSetAllRemoteCDMs() { + const result = await AsyncSyncStorage.getStorage(["remote_cdms"]); + const array = result.remote_cdms || []; + for (const item of array) { + this.setRemoteCDM(item, await this.loadRemoteCDM(item)); + } + } + + static async saveSelectedRemoteCDM(name) { + await AsyncSyncStorage.setStorage({ selected_remote_cdm: name }); + } + + static async getSelectedRemoteCDM() { + const result = await AsyncSyncStorage.getStorage(["selected_remote_cdm"]); + return result["selected_remote_cdm"] || ""; + } + + static async selectRemoteCDM(name) { + document.getElementById("remote-combobox").value = + await this.loadRemoteCDM(name); + } + + static async removeSelectedRemoteCDM() { + const selected_remote_cdm_name = + await RemoteCDMManager.getSelectedRemoteCDM(); + + const result = await AsyncSyncStorage.getStorage(["remote_cdms"]); + const array = result.remote_cdms === undefined ? [] : result.remote_cdms; + + const index = array.indexOf(selected_remote_cdm_name); + if (index > -1) { + array.splice(index, 1); + } + + await AsyncSyncStorage.setStorage({ remote_cdms: array }); + await AsyncSyncStorage.removeStorage([selected_remote_cdm_name]); + } + + static async removeSelectedRemoteCDMKey() { + await AsyncSyncStorage.removeStorage(["selected_remote_cdm"]); + } } export class SettingsManager { - static async setEnabled(enabled) { - await AsyncSyncStorage.setStorage({ enabled: enabled }); - } - - static async getEnabled() { - const result = await AsyncSyncStorage.getStorage(["enabled"]); - return result["enabled"] === undefined ? false : result["enabled"]; - } - - static downloadFile(content, filename) { - const blob = new Blob([content], { type: 'application/octet-stream' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = filename; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - } - - static async importDevice(file) { - return new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = async function (loaded) { - const result = loaded.target.result; - - const widevine_device = new WidevineDevice(result); - const b64_device = Util.b64.encode(new Uint8Array(result)); - const device_name = widevine_device.get_name(); - - if (!await DeviceManager.loadWidevineDevice(device_name)) { - await DeviceManager.saveWidevineDevice(device_name, b64_device); - } - - await DeviceManager.saveSelectedWidevineDevice(device_name); - resolve(); - }; - reader.readAsArrayBuffer(file); - }); - } - - static async saveDarkMode(dark_mode) { - await AsyncSyncStorage.setStorage({ dark_mode: dark_mode }); - } - - static async getDarkMode() { - const result = await AsyncSyncStorage.getStorage(["dark_mode"]); - return result["dark_mode"] || false; - } - - static setDarkMode(dark_mode) { - const textImage = document.getElementById("textImage"); - const toggle = document.getElementById('darkModeToggle'); - toggle.checked = dark_mode; - document.body.classList.toggle('dark-mode', dark_mode); - textImage.src = dark_mode ? "../images/proxy_text_dark.png" : "../images/proxy_text.png"; - } - - static async loadRemoteCDM(file) { - return new Promise((resolve) => { - const reader = new FileReader(); - reader.onload = async function (loaded) { - const result = loaded.target.result; - - let json_file = void 0; - try { - json_file = JSON.parse(result); - } catch { - resolve(); - return; - } - - console.log("LOADED DEVICE:", json_file); - const remote_cdm = new RemoteCdm( - json_file.device_type, - json_file.system_id, - json_file.security_level, - json_file.host, - json_file.secret, - json_file.device_name ?? json_file.name, - ); - const device_name = remote_cdm.get_name(); - console.log("NAME:", device_name); - - if (await RemoteCDMManager.loadRemoteCDM(device_name) === "{}") { - await RemoteCDMManager.saveRemoteCDM(device_name, json_file); - } - - await RemoteCDMManager.saveSelectedRemoteCDM(device_name); - resolve(); - }; - reader.readAsText(file); - }); - } - - static async saveSelectedDeviceType(selected_type) { - await AsyncSyncStorage.setStorage({ device_type: selected_type }); - } - - static async getSelectedDeviceType() { - const result = await AsyncSyncStorage.getStorage(["device_type"]); - return result["device_type"] || "WVD"; - } - - static setSelectedDeviceType(device_type) { - switch (device_type) { - case "WVD": - const wvd_select = document.getElementById('wvd_select'); - wvd_select.checked = true; - break; - case "REMOTE": - const remote_select = document.getElementById('remote_select'); - remote_select.checked = true; - break; + static async setEnabled(enabled) { + await AsyncSyncStorage.setStorage({ enabled: enabled }); + } + + static async getEnabled() { + const result = await AsyncSyncStorage.getStorage(["enabled"]); + return result["enabled"] === undefined ? false : result["enabled"]; + } + + static downloadFile(content, filename) { + const blob = new Blob([content], { type: "application/octet-stream" }); + const url = URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } + + static async importDevice(file) { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = async function (loaded) { + const result = loaded.target.result; + + const widevine_device = new WidevineDevice(result); + const b64_device = Util.b64.encode(new Uint8Array(result)); + const device_name = widevine_device.get_name(); + + if (!(await DeviceManager.loadWidevineDevice(device_name))) { + await DeviceManager.saveWidevineDevice(device_name, b64_device); } - } - - static async saveUseShakaPackager(use_shaka) { - await AsyncSyncStorage.setStorage({ use_shaka: use_shaka }); - } - static async getUseShakaPackager() { - const result = await AsyncSyncStorage.getStorage(["use_shaka"]); - return result["use_shaka"] ?? true; - } - - static async saveExecutableName(exe_name) { - await AsyncSyncStorage.setStorage({ exe_name: exe_name }); - } - - static async getExecutableName() { - const result = await AsyncSyncStorage.getStorage(["exe_name"]); - return result["exe_name"] ?? "N_m3u8DL-RE"; - } + await DeviceManager.saveSelectedWidevineDevice(device_name); + resolve(); + }; + reader.readAsArrayBuffer(file); + }); + } + + static async saveDarkMode(dark_mode) { + await AsyncSyncStorage.setStorage({ dark_mode: dark_mode }); + } + + static async getDarkMode() { + const result = await AsyncSyncStorage.getStorage(["dark_mode"]); + return result["dark_mode"] || false; + } + + static setDarkMode(dark_mode) { + const textImage = document.getElementById("textImage"); + const toggle = document.getElementById("darkModeToggle"); + toggle.checked = dark_mode; + document.body.classList.toggle("dark-mode", dark_mode); + textImage.src = dark_mode + ? "../images/proxy_text_dark.png" + : "../images/proxy_text.png"; + } + + static async loadRemoteCDM(file) { + return new Promise((resolve) => { + const reader = new FileReader(); + reader.onload = async function (loaded) { + const result = loaded.target.result; + + let json_file = void 0; + try { + json_file = JSON.parse(result); + } catch { + resolve(); + return; + } - static async saveAdditionalArguments(additional_args) { - await AsyncSyncStorage.setStorage({ additional_args: additional_args }); - } + console.log("LOADED DEVICE:", json_file); + const remote_cdm = new RemoteCdm( + json_file.device_type, + json_file.system_id, + json_file.security_level, + json_file.host, + json_file.secret, + json_file.device_name ?? json_file.name, + ); + const device_name = remote_cdm.get_name(); + console.log("NAME:", device_name); + + if ((await RemoteCDMManager.loadRemoteCDM(device_name)) === "{}") { + await RemoteCDMManager.saveRemoteCDM(device_name, json_file); + } - static async getAdditionalArguments() { - const result = await AsyncSyncStorage.getStorage(["additional_args"]); - return result["additional_args"] ?? "-M format=mkv"; - } + await RemoteCDMManager.saveSelectedRemoteCDM(device_name); + resolve(); + }; + reader.readAsText(file); + }); + } + + static async saveSelectedDeviceType(selected_type) { + await AsyncSyncStorage.setStorage({ device_type: selected_type }); + } + + static async getSelectedDeviceType() { + const result = await AsyncSyncStorage.getStorage(["device_type"]); + return result["device_type"] || "WVD"; + } + + static setSelectedDeviceType(device_type) { + switch (device_type) { + case "WVD": + const wvd_select = document.getElementById("wvd_select"); + wvd_select.checked = true; + break; + case "REMOTE": + const remote_select = document.getElementById("remote_select"); + remote_select.checked = true; + break; + } + } + + static async saveUseShakaPackager(use_shaka) { + await AsyncSyncStorage.setStorage({ use_shaka: use_shaka }); + } + + static async getUseShakaPackager() { + const result = await AsyncSyncStorage.getStorage(["use_shaka"]); + return result["use_shaka"] ?? true; + } + + static async saveExecutableName(exe_name) { + await AsyncSyncStorage.setStorage({ exe_name: exe_name }); + } + + static async getExecutableName() { + const result = await AsyncSyncStorage.getStorage(["exe_name"]); + return result["exe_name"] ?? "N_m3u8DL-RE"; + } + + static async saveAdditionalArguments(additional_args) { + await AsyncSyncStorage.setStorage({ additional_args: additional_args }); + } + + static async getAdditionalArguments() { + const result = await AsyncSyncStorage.getStorage(["additional_args"]); + return result["additional_args"] ?? "-M format=mkv"; + } } export class Util { - static utf8 = { - /* Uint8Array -> String */ - decode: b => String.fromCharCode.apply(null, b), - /* String -> Uint8Array */ - encode: s => Uint8Array.from(s.split("").map(x => x.charCodeAt(0))) - } - - static b64 = { - /* b64 String -> Uint8Array */ - decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)), - /* Uint8Array -> b64 String */ - encode: b => btoa(String.fromCharCode(...new Uint8Array(b))) - }; - - static u32toBytes(num) { - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - view.setUint32(0, num, false); - return new Uint8Array(buffer); - } - - static sequenceEquals(arr1, arr2) { - if (arr1.length !== arr2.length) - return false; - return Array.from(arr1).every((value, index) => value === arr2[index]); - } - - static bytesToHex(u8) { - if (typeof Uint8Array.prototype.toHex === "function") { - return u8.toHex(); - } - return Array.from(u8, b => b.toString(16).padStart(2, '0')).join(''); - } + static utf8 = { + /* Uint8Array -> String */ + decode: (b) => String.fromCharCode.apply(null, b), + /* String -> Uint8Array */ + encode: (s) => Uint8Array.from(s.split("").map((x) => x.charCodeAt(0))), + }; + + static b64 = { + /* b64 String -> Uint8Array */ + decode: (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0)), + /* Uint8Array -> b64 String */ + encode: (b) => btoa(String.fromCharCode(...new Uint8Array(b))), + }; + + static u32toBytes(num) { + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + view.setUint32(0, num, false); + return new Uint8Array(buffer); + } + + static sequenceEquals(arr1, arr2) { + if (arr1.length !== arr2.length) return false; + return Array.from(arr1).every((value, index) => value === arr2[index]); + } + + static bytesToHex(u8) { + if (typeof Uint8Array.prototype.toHex === "function") { + return u8.toHex(); + } + return Array.from(u8, (b) => b.toString(16).padStart(2, "0")).join(""); + } } diff --git a/manifest.json b/manifest.json index 77b98d3..931f0a0 100644 --- a/manifest.json +++ b/manifest.json @@ -23,21 +23,31 @@ "128": "images/icon-128.png" }, "background": { - "scripts": ["background.js"], + "scripts": [ + "background.js" + ], "service_worker": "background.js", "type": "module" }, "content_scripts": [ { - "matches": [""], - "js": ["message_proxy.js"], + "matches": [ + "" + ], + "js": [ + "message_proxy.js" + ], "run_at": "document_start", "world": "ISOLATED", "all_frames": true }, { - "matches": [""], - "js": ["content_script.js"], + "matches": [ + "" + ], + "js": [ + "content_script.js" + ], "run_at": "document_start", "world": "MAIN", "all_frames": true @@ -49,4 +59,4 @@ "strict_min_version": "58.0" } } -} +} \ No newline at end of file diff --git a/message_proxy.js b/message_proxy.js index 699bb5f..18f6f77 100644 --- a/message_proxy.js +++ b/message_proxy.js @@ -1,22 +1,25 @@ async function processMessage(detail) { - return new Promise((resolve, reject) => { - chrome.runtime.sendMessage({ - type: detail.type, - body: detail.body, - }, (response) => { - if (chrome.runtime.lastError) { - return reject(chrome.runtime.lastError); - } - resolve(response); - }); - }) + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage( + { + type: detail.type, + body: detail.body, + }, + (response) => { + if (chrome.runtime.lastError) { + return reject(chrome.runtime.lastError); + } + resolve(response); + }, + ); + }); } -document.addEventListener('response', async (event) => { - const { detail } = event; - const responseData = await processMessage(detail); - const responseEvent = new CustomEvent('responseReceived', { - detail: detail.requestId.concat(responseData) - }); - document.dispatchEvent(responseEvent); +document.addEventListener("response", async (event) => { + const { detail } = event; + const responseData = await processMessage(detail); + const responseEvent = new CustomEvent("responseReceived", { + detail: detail.requestId.concat(responseData), + }); + document.dispatchEvent(responseEvent); }); diff --git a/panel/panel.css b/panel/panel.css index ef1f017..c3ea42f 100644 --- a/panel/panel.css +++ b/panel/panel.css @@ -1,144 +1,221 @@ -body { - font-family: Arial, sans-serif; - background-color: #ffffff; - color: black; - width: 400px; -} -.toggleButton { - width: 24px; -} -select { - max-width: 330px; -} -.text-box { - outline: none; - width: 80%; -} -#downloader-name { - width: 160px; -} -#export { - margin-top: 5px; -} - - -/* Dark mode */ -.dark-mode { - background-color: #1b1b1b; - color: #D5D5D5; -} -.dark-mode fieldset { - border-color: #535353; -} -.dark-mode button, .dark-mode select { - background-color: #222; - color: #D5D5D5; -} -.dark-mode button:hover, .dark-mode select:hover { - background-color: #323232; -} -.dark-mode .expandableDiv { - background-color: #1b2027; -} -.dark-mode .text-box { - background-color: #323232; - color: #D5D5D5; -} - -/* Switch */ -.switch { - position: relative; - display: inline-block; - width: 40px; - height: 20px; - margin-left: 10px; -} -.switch input { - opacity: 0; - width: 0; - height: 0; -} - -/* Slider */ -.slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - transition: 0.4s; - border-radius: 20px; -} -.slider:before { - position: absolute; - content: ""; - height: 14px; - width: 14px; - left: 3px; - bottom: 3px; - background-color: white; - border-radius: 50%; -} -input:checked + .slider { - background-color: #2196F3; -} -input:checked + .slider:before { - transform: translateX(20px); -} - -#clear { - width: 100%; - margin-bottom: 3px; -} - -#type-select { - margin-left: auto; -} - -#settings { - display: flex; -} - -.header { - display: flex; - justify-content: center; - align-items: center; -} -.header > * { - margin: 5px; -} - -#wvd > *, #remote > * { - margin: 5px; -} - -.log-container { - display: flex; - justify-content: center; -} -.right-bound { - text-align: right; -} -.expandableDiv { - width: 100%; - overflow: hidden; - background-color: lightblue; - padding: 0; -} -.expandableDiv.expanded { - padding: 5px; -} -.expandableDiv.collapsed { - padding: 0; -} -.always-visible { - display: block; -} -.expanded-only { - display: none; -} -.expandableDiv.expanded .expanded-only { - display: block; -} \ No newline at end of file +:root { + --font-family-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-weight-normal: 400; + --font-weight-bold: 600; + --border-radius: 0.375rem; + --shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + + --color-bg: #f8f9fa; + --color-bg-inset: #ffffff; + --color-text: #212529; + --color-text-muted: #6c757d; + --color-border: #dee2e6; + --color-accent: #0d6efd; + --color-input-bg: #ffffff; + --color-input-focus-border: #86b7fe; + --color-danger-bg: #dc3545; + --color-danger-border: #dc3545; + --color-danger-hover-bg: #bb2d3b; + --color-danger-hover-border: #b02a37; +} + +.dark-mode { + --color-bg: #212529; + --color-bg-inset: #2c3136; + --color-text: #e9ecef; + --color-text-muted: #adb5bd; + --color-border: #495057; + --color-accent: #3b82f6; + --color-input-bg: #343a40; + --color-input-focus-border: #528BFF; + --color-danger-bg: #842029; + --color-danger-border: #842029; + --color-danger-hover-bg: #972e3a; + --color-danger-hover-border: #8f2c38; +} + +body { + font-family: var(--font-family-sans-serif); + background-color: var(--color-bg); + color: var(--color-text); + width: 400px; + padding: 0.75rem; + box-sizing: border-box; +} + + +.header { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +fieldset { + background-color: var(--color-bg-inset); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + padding: 0.75rem 1rem; + margin-bottom: 0.75rem; + box-shadow: var(--shadow-sm); +} + +fieldset legend { + font-weight: var(--font-weight-bold); + padding: 0 0.5em; + color: var(--color-text-muted); + font-size: 0.875rem; +} + +#wvd, #remote { + display: none; +} + +button, +select, +input[type="text"] { + display: inline-block; + font-family: inherit; + font-size: 0.875rem; + color: var(--color-text); + background-color: var(--color-input-bg); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + padding: 0.375rem 0.75rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; + box-sizing: border-box; +} + +button { + cursor: pointer; + background-color: var(--color-bg); +} + +button:hover { + border-color: var(--color-accent); +} + +select, +input[type="text"] { + width: 100%; +} + +select:focus, +input[type="text"]:focus { + outline: 0; + border-color: var(--color-input-focus-border); + box-shadow: 0 0 0 0.25rem rgba(var(--color-accent), 0.25); +} + +.button-group { + display: flex; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +#clear { + width: 100%; + margin-bottom: 0.5rem; + background-color: var(--color-danger-bg); + border-color: var(--color-danger-border); + color: white; +} +#clear:hover{ + background-color: var(--color-danger-hover-bg); + border-color: var(--color-danger-hover-border); +} + +#settings-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 0.5rem 1rem; + align-items: center; +} +#settings-grid > label, #settings-grid > div { + display: flex; + align-items: center; + gap: 0.5rem; +} +#settings-grid > div { + flex-direction: column; + align-items: flex-start; +} +label { + user-select: none; + font-size: 0.875rem; +} +input[type="checkbox"], input[type="radio"] { + margin: 0; +} + +.switch { + position: relative; + display: inline-block; + width: 34px; + height: 20px; + flex-shrink: 0; +} +.switch input { opacity: 0; width: 0; height: 0; } +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--color-border); + transition: 0.4s; + border-radius: 20px; +} +.slider:before { + position: absolute; + content: ""; + height: 14px; + width: 14px; + left: 3px; + bottom: 3px; + background-color: white; + border-radius: 50%; + transition: 0.4s; +} +input:checked + .slider { background-color: var(--color-accent); } +input:checked + .slider:before { transform: translateX(14px); } + + +#key-container { display: flex; flex-direction: column; gap: 0.5rem; } +.log-container { + display: flex; + align-items: flex-start; + gap: 0.5rem; + background-color: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--border-radius); + padding: 0.5rem; +} +.toggleButton { + flex-shrink: 0; + width: 24px; + height: 24px; + padding: 0; + font-family: monospace; + font-size: 1rem; +} +.expandableDiv { width: 100%; overflow: hidden; display: flex; flex-direction: column; gap: 0.5rem; } +.expandableDiv label { + display: grid; + grid-template-columns: 60px 1fr; + align-items: center; + gap: 0.5rem; + width: 100%; +} +.expandableDiv label > a { + color: var(--color-accent); + text-decoration: none; + cursor: pointer; + font-weight: var(--font-weight-bold); + text-align: right; + justify-self: end; +} +.expandableDiv label a:hover { text-decoration: underline; } +.expandableDiv.collapsed .expanded-only { display: none; } diff --git a/panel/panel.html b/panel/panel.html index 06cf6fe..2979670 100644 --- a/panel/panel.html +++ b/panel/panel.html @@ -1,74 +1,86 @@ - - - - - - - - -
- WidevineProxy Icon - ProxyText -
-
- Settings -
- -
- - -
- -
- - -
- -
- Device Type:
- -
- -
-
-
-
- Widevine Device - - -
- -
-
- Remote CDM - - -
- -
-
- Command options - -
- -
- - -
- - -
-
- Keys - -
-
- - + + + + + WidevineProxy + + + + + + + +
+ WidevineProxy Icon + ProxyText +
+ +
+ Settings +
+ + +
+ + +
+
+ + +
+
+
+ +
+ Widevine Device +
+ + + +
+ +
+ +
+ Remote CDM +
+ + + +
+ +
+ +
+ Logs + +
+ +
+ + + + diff --git a/panel/panel.js b/panel/panel.js index 77631bf..fd831f5 100644 --- a/panel/panel.js +++ b/panel/panel.js @@ -1,261 +1,304 @@ -import { AsyncLocalStorage, DeviceManager, RemoteCDMManager, SettingsManager, Util } from "../lib/util.js"; - -const key_container = document.getElementById('key-container'); - -// ================ Main ================ -const enabled = document.getElementById('enabled'); -enabled.addEventListener('change', async function (){ - await SettingsManager.setEnabled(enabled.checked); -}); - -const toggle = document.getElementById('darkModeToggle'); -toggle.addEventListener('change', async () => { - SettingsManager.setDarkMode(toggle.checked); - await SettingsManager.saveDarkMode(toggle.checked); -}); - -const wvd_select = document.getElementById('wvd_select'); -wvd_select.addEventListener('change', async function (){ - if (wvd_select.checked) { - await SettingsManager.saveSelectedDeviceType("WVD"); - } -}); - -const remote_select = document.getElementById('remote_select'); -remote_select.addEventListener('change', async function (){ - if (remote_select.checked) { - await SettingsManager.saveSelectedDeviceType("REMOTE"); - } -}); - -const export_button = document.getElementById('exportLogs'); -export_button.addEventListener('click', async function() { - const logs = await AsyncLocalStorage.getStorage(null); - SettingsManager.downloadFile(new Blob([JSON.stringify(logs)], { type: "application/json;charset=utf-8" }), "logs.json"); -}); - -const clear_logs = document.getElementById('clearLogs'); -clear_logs.addEventListener('click', function() { - AsyncLocalStorage.clearStorage(); -}); -// ====================================== - -// ================ Widevine Device ================ -const fileInput = document.getElementById('fileInput'); -fileInput.addEventListener('click', () => { - if ("ontouchstart" in window || navigator.maxTouchPoints > 0) { - chrome.runtime.sendMessage({ type: "OPEN_PICKER_WVD_MOBILE" }); - } else { - chrome.runtime.sendMessage({ type: "OPEN_PICKER_WVD" }); - } - window.close(); -}); - -const remove = document.getElementById('remove'); -remove.addEventListener('click', async function() { - await DeviceManager.removeSelectedWidevineDevice(); - wvd_combobox.innerHTML = ''; - await DeviceManager.loadSetAllWidevineDevices(); - const selected_option = wvd_combobox.options[wvd_combobox.selectedIndex]; - if (selected_option) { - await DeviceManager.saveSelectedWidevineDevice(selected_option.text); - } else { - await DeviceManager.removeSelectedWidevineDeviceKey(); - } -}); - -const download = document.getElementById('download'); -download.addEventListener('click', async function() { - const widevine_device = await DeviceManager.getSelectedWidevineDevice(); - SettingsManager.downloadFile( - Util.b64.decode(await DeviceManager.loadWidevineDevice(widevine_device)), - widevine_device + ".wvd" - ) -}); - -const wvd_combobox = document.getElementById('wvd-combobox'); -wvd_combobox.addEventListener('change', async function() { - await DeviceManager.saveSelectedWidevineDevice(wvd_combobox.options[wvd_combobox.selectedIndex].text); -}); -// ================================================= - -// ================ Remote CDM ================ -document.getElementById('remoteInput').addEventListener('click', () => { - if ("ontouchstart" in window || navigator.maxTouchPoints > 0) { - chrome.runtime.sendMessage({ type: "OPEN_PICKER_REMOTE_MOBILE" }); - } else { - chrome.runtime.sendMessage({ type: "OPEN_PICKER_REMOTE" }); - } - window.close(); -}); - -const remote_remove = document.getElementById('remoteRemove'); -remote_remove.addEventListener('click', async function() { - await RemoteCDMManager.removeSelectedRemoteCDM(); - remote_combobox.innerHTML = ''; - await RemoteCDMManager.loadSetAllRemoteCDMs(); - const selected_option = remote_combobox.options[remote_combobox.selectedIndex]; - if (selected_option) { - await RemoteCDMManager.saveSelectedRemoteCDM(selected_option.text); - } else { - await RemoteCDMManager.removeSelectedRemoteCDMKey(); - } -}); - -const remote_download = document.getElementById('remoteDownload'); -remote_download.addEventListener('click', async function() { - const remote_cdm = await RemoteCDMManager.getSelectedRemoteCDM(); - SettingsManager.downloadFile( - await RemoteCDMManager.loadRemoteCDM(remote_cdm), - remote_cdm + ".json" - ) -}); - -const remote_combobox = document.getElementById('remote-combobox'); -remote_combobox.addEventListener('change', async function() { - await RemoteCDMManager.saveSelectedRemoteCDM(remote_combobox.options[remote_combobox.selectedIndex].text); -}); -// ============================================ - -// ================ Command Options ================ -const use_shaka = document.getElementById('use-shaka'); -use_shaka.addEventListener('change', async function (){ - await SettingsManager.saveUseShakaPackager(use_shaka.checked); -}); - -const downloader_name = document.getElementById('downloader-name'); -downloader_name.addEventListener('input', async function (){ - await SettingsManager.saveExecutableName(downloader_name.value); -}); - -const downloader_args = document.getElementById('downloader-args'); -downloader_args.addEventListener('input', async function (){ - await SettingsManager.saveAdditionalArguments(downloader_args.value); -}); -// ================================================= - -// ================ Keys ================ -const clear = document.getElementById('clear'); -clear.addEventListener('click', async function() { - chrome.runtime.sendMessage({ type: "CLEAR" }); - key_container.innerHTML = ""; -}); - -async function createCommand(json, key_string) { - const metadata = JSON.parse(json); - const headerString = Object.entries(metadata.headers).map(([key, value]) => `-H "${key}: ${value.replace(/"/g, "'")}"`).join(' '); - const executableName = await SettingsManager.getExecutableName(); - const useShaka = await SettingsManager.getUseShakaPackager(); - const additionalArgs = await SettingsManager.getAdditionalArguments(); - return `${executableName} "${metadata.url}" ${headerString} ${key_string} ${useShaka ? "--use-shaka-packager " : ""}${additionalArgs}`; -} - -async function appendLog(result) { - const key_string = result.keys.map(key => `--key ${key.kid}:${key.k}`).join(' '); - const date = new Date(result.timestamp * 1000); - const date_string = date.toLocaleString(); - - const logContainer = document.createElement('div'); - logContainer.classList.add('log-container'); - logContainer.innerHTML = ` - - `; - - const keysInput = logContainer.querySelector('.key-copy'); - keysInput.addEventListener('click', () => { - navigator.clipboard.writeText(key_string); - }); - - if (result.manifests.length > 0) { - const command = logContainer.querySelector('#command'); - - const select = logContainer.querySelector("#manifest"); - select.addEventListener('change', async () => { - command.value = await createCommand(select.value, key_string); - }); - result.manifests.forEach((manifest) => { - const option = new Option(`[${manifest.type}] ${manifest.url}`, JSON.stringify(manifest)); - select.add(option); - }); - command.value = await createCommand(select.value, key_string); - - const manifest_copy = logContainer.querySelector('.manifest-copy'); - manifest_copy.addEventListener('click', () => { - navigator.clipboard.writeText(JSON.parse(select.value).url); - }); - - const command_copy = logContainer.querySelector('.command-copy'); - command_copy.addEventListener('click', () => { - navigator.clipboard.writeText(command.value); - }); - } - - const toggleButtons = logContainer.querySelector('.toggleButton'); - toggleButtons.addEventListener('click', function () { - const expandableDiv = this.nextElementSibling; - if (expandableDiv.classList.contains('collapsed')) { - toggleButtons.innerHTML = "-"; - expandableDiv.classList.remove('collapsed'); - expandableDiv.classList.add('expanded'); - } else { - toggleButtons.innerHTML = "+"; - expandableDiv.classList.remove('expanded'); - expandableDiv.classList.add('collapsed'); - } - }); - - key_container.appendChild(logContainer); -} - -chrome.storage.onChanged.addListener(async (changes, areaName) => { - if (areaName === 'local') { - for (const [key, values] of Object.entries(changes)) { - await appendLog(values.newValue); - } - } -}); - -function checkLogs() { - chrome.runtime.sendMessage({ type: "GET_LOGS" }, (response) => { - if (response) { - response.forEach(async (result) => { - await appendLog(result); - }); - } - }); -} - -document.addEventListener('DOMContentLoaded', async function () { - enabled.checked = await SettingsManager.getEnabled(); - SettingsManager.setDarkMode(await SettingsManager.getDarkMode()); - use_shaka.checked = await SettingsManager.getUseShakaPackager(); - downloader_name.value = await SettingsManager.getExecutableName(); - downloader_args.value = await SettingsManager.getAdditionalArguments(); - SettingsManager.setSelectedDeviceType(await SettingsManager.getSelectedDeviceType()); - await DeviceManager.loadSetAllWidevineDevices(); - await DeviceManager.selectWidevineDevice(await DeviceManager.getSelectedWidevineDevice()); - await RemoteCDMManager.loadSetAllRemoteCDMs(); - await RemoteCDMManager.selectRemoteCDM(await RemoteCDMManager.getSelectedRemoteCDM()); - checkLogs(); -}); -// ====================================== +import "../lib/protobuf.min.js"; +import "../lib/license_protocol.js"; +import { + AsyncLocalStorage, + Util, + DeviceManager, + RemoteCDMManager, + SettingsManager, +} from "../lib/util.js"; + +const base64toUint8Array = (b64) => Util.b64.decode(b64); +const stringToUint8Array = (str) => Util.utf8.encode(str); + +const key_container = document.getElementById("key-container"); + +function updateThemeVisuals(isDarkMode) { + document.body.classList.toggle("dark-mode", isDarkMode); + const textImage = document.getElementById("textImage"); + if (textImage) { + textImage.src = isDarkMode + ? "../images/proxy_text_dark.png" + : "../images/proxy_text.png"; + } +} + +function updateDeviceFieldsetVisibility() { + const wvd_select = document.getElementById("wvd_select"); + document.getElementById("wvd").style.display = wvd_select.checked + ? "block" + : "none"; + document.getElementById("remote").style.display = wvd_select.checked + ? "none" + : "block"; +} + +const enabled = document.getElementById("enabled"); +enabled.addEventListener("change", async function () { + await SettingsManager.setEnabled(enabled.checked); +}); + +const toggle = document.getElementById("darkModeToggle"); +toggle.addEventListener("change", async () => { + await SettingsManager.saveDarkMode(toggle.checked); + updateThemeVisuals(toggle.checked); +}); + +const wvd_select = document.getElementById("wvd_select"); +wvd_select.addEventListener("change", async function () { + if (wvd_select.checked) { + await SettingsManager.saveSelectedDeviceType("WVD"); + updateDeviceFieldsetVisibility(); + } +}); + +const remote_select = document.getElementById("remote_select"); +remote_select.addEventListener("change", async function () { + if (remote_select.checked) { + await SettingsManager.saveSelectedDeviceType("REMOTE"); + updateDeviceFieldsetVisibility(); + } +}); + +const export_button = document.getElementById("exportLogs"); +export_button.addEventListener("click", async function () { + const logs = await AsyncLocalStorage.getStorage(null); + SettingsManager.downloadFile( + stringToUint8Array(JSON.stringify(logs)), + "logs.json" + ); +}); + +document.getElementById("fileInput").addEventListener("click", () => { + chrome.runtime.sendMessage({ type: "OPEN_PICKER_WVD" }); + window.close(); +}); + +const remove = document.getElementById("remove"); +remove.addEventListener("click", async function () { + await DeviceManager.removeSelectedWidevineDevice(); + wvd_combobox.innerHTML = ""; + await DeviceManager.loadSetAllWidevineDevices(); + const selected_option = wvd_combobox.options[wvd_combobox.selectedIndex]; + if (selected_option) { + await DeviceManager.saveSelectedWidevineDevice(selected_option.text); + } else { + await DeviceManager.removeSelectedWidevineDeviceKey(); + } +}); + +const download = document.getElementById("download"); +download.addEventListener("click", async function () { + const widevine_device = await DeviceManager.getSelectedWidevineDevice(); + SettingsManager.downloadFile( + base64toUint8Array(await DeviceManager.loadWidevineDevice(widevine_device)), + widevine_device + ".wvd" + ); +}); + +const wvd_combobox = document.getElementById("wvd-combobox"); +wvd_combobox.addEventListener("change", async function () { + await DeviceManager.saveSelectedWidevineDevice( + wvd_combobox.options[wvd_combobox.selectedIndex].text + ); +}); + +document.getElementById("remoteInput").addEventListener("click", () => { + chrome.runtime.sendMessage({ type: "OPEN_PICKER_REMOTE" }); + window.close(); +}); + +const remote_remove = document.getElementById("remoteRemove"); +remote_remove.addEventListener("click", async function () { + await RemoteCDMManager.removeSelectedRemoteCDM(); + remote_combobox.innerHTML = ""; + await RemoteCDMManager.loadSetAllRemoteCDMs(); + const selected_option = + remote_combobox.options[remote_combobox.selectedIndex]; + if (selected_option) { + await RemoteCDMManager.saveSelectedRemoteCDM(selected_option.text); + } else { + await RemoteCDMManager.removeSelectedRemoteCDMKey(); + } +}); + +const remote_download = document.getElementById("remoteDownload"); +remote_download.addEventListener("click", async function () { + const remote_cdm = await RemoteCDMManager.getSelectedRemoteCDM(); + SettingsManager.downloadFile( + await RemoteCDMManager.loadRemoteCDM(remote_cdm), + remote_cdm + ".json" + ); +}); + +const remote_combobox = document.getElementById("remote-combobox"); +remote_combobox.addEventListener("change", async function () { + await RemoteCDMManager.saveSelectedRemoteCDM( + remote_combobox.options[remote_combobox.selectedIndex].text + ); +}); + +const use_shaka = document.getElementById("use-shaka"); +use_shaka.addEventListener("change", async function () { + await SettingsManager.saveUseShakaPackager(use_shaka.checked); +}); + +const downloader_name = document.getElementById("downloader-name"); +downloader_name.addEventListener("input", async function (event) { + await SettingsManager.saveExecutableName(downloader_name.value); +}); + +const clear = document.getElementById("clear"); +clear.addEventListener("click", async function () { + chrome.runtime.sendMessage({ type: "CLEAR" }); + key_container.innerHTML = ""; +}); + +async function createCommand(json, key_string) { + const metadata = JSON.parse(json); + const header_string = Object.entries(metadata.headers) + .map(([key, value]) => `-H "${key}: ${value.replace(/"/g, "'")}"`) + .join(" "); + return `${await SettingsManager.getExecutableName()} "${ + metadata.url + }" ${header_string} ${key_string} ${ + (await SettingsManager.getUseShakaPackager()) ? "--use-shaka-packager " : "" + }-M format=mkv`; +} + +async function appendLog(result) { + const key_string = result.keys + .map((key) => `--key ${key.kid}:${key.k}`) + .join(" "); + const date = new Date(result.timestamp * 1000); + const date_string = date.toLocaleString(); + + const logContainer = document.createElement("div"); + logContainer.classList.add("log-container"); + logContainer.innerHTML = ` + + `; + + logContainer.querySelector(".key-copy > a").addEventListener("click", (e) => { + e.preventDefault(); + navigator.clipboard.writeText(key_string); + }); + + if (result.manifests.length > 0) { + const commandInput = logContainer.querySelector(".command-copy input"); + const select = logContainer.querySelector(".manifest-copy select"); + const updateCommand = async () => { + commandInput.value = await createCommand(select.value, key_string); + }; + select.addEventListener("change", updateCommand); + result.manifests.forEach((manifest) => { + const option = new Option( + `[${manifest.type}] ${manifest.url}`, + JSON.stringify(manifest) + ); + select.add(option); + }); + updateCommand(); + logContainer + .querySelector(".manifest-copy > a") + .addEventListener("click", (e) => { + e.preventDefault(); + navigator.clipboard.writeText(JSON.parse(select.value).url); + }); + logContainer + .querySelector(".command-copy > a") + .addEventListener("click", (e) => { + e.preventDefault(); + navigator.clipboard.writeText(commandInput.value); + }); + } + + const toggleButtons = logContainer.querySelector(".toggleButton"); + toggleButtons.addEventListener("click", function () { + const expandableDiv = this.nextElementSibling; + if (expandableDiv.classList.contains("collapsed")) { + this.innerHTML = "-"; + expandableDiv.classList.remove("collapsed"); + } else { + this.innerHTML = "+"; + expandableDiv.classList.add("collapsed"); + } + }); + key_container.appendChild(logContainer); +} + +chrome.storage.onChanged.addListener(async (changes, areaName) => { + if (areaName === "local") { + for (const [key, values] of Object.entries(changes)) { + await appendLog(values.newValue); + } + } +}); + +function checkLogs() { + chrome.runtime.sendMessage({ type: "GET_LOGS" }, (response) => { + if (response) { + response.forEach(async (result) => { + await appendLog(result); + }); + } + }); +} + +document.addEventListener("DOMContentLoaded", async function () { + enabled.checked = await SettingsManager.getEnabled(); + const isDarkMode = await SettingsManager.getDarkMode(); + toggle.checked = isDarkMode; + use_shaka.checked = await SettingsManager.getUseShakaPackager(); + downloader_name.value = await SettingsManager.getExecutableName(); + + updateThemeVisuals(isDarkMode); + + const deviceType = await SettingsManager.getSelectedDeviceType(); + if (deviceType === "WVD") { + wvd_select.checked = true; + } else { + remote_select.checked = true; + } + updateDeviceFieldsetVisibility(); + + await DeviceManager.loadSetAllWidevineDevices(); + await DeviceManager.selectWidevineDevice( + await DeviceManager.getSelectedWidevineDevice() + ); + await RemoteCDMManager.loadSetAllRemoteCDMs(); + await RemoteCDMManager.selectRemoteCDM( + await RemoteCDMManager.getSelectedRemoteCDM() + ); + + checkLogs(); +}); diff --git a/picker/remote/filePicker.html b/picker/remote/filePicker.html index 646076d..ba8e775 100644 --- a/picker/remote/filePicker.html +++ b/picker/remote/filePicker.html @@ -1,7 +1,7 @@ - + - - + + - - \ No newline at end of file + + diff --git a/picker/remote/filePicker.js b/picker/remote/filePicker.js index 6314abc..68f40f1 100644 --- a/picker/remote/filePicker.js +++ b/picker/remote/filePicker.js @@ -1,8 +1,10 @@ import { SettingsManager } from "../../lib/util.js"; -document.getElementById('fileInput').addEventListener('change', async (event) => { +document + .getElementById("fileInput") + .addEventListener("change", async (event) => { const file = event.target.files[0]; await SettingsManager.loadRemoteCDM(file).then(() => { - window.close(); + window.close(); }); -}); \ No newline at end of file + }); diff --git a/picker/wvd/filePicker.html b/picker/wvd/filePicker.html index 2b8de4a..7b28b3c 100644 --- a/picker/wvd/filePicker.html +++ b/picker/wvd/filePicker.html @@ -1,7 +1,7 @@ - + - - + + - - \ No newline at end of file + + diff --git a/picker/wvd/filePicker.js b/picker/wvd/filePicker.js index 033ab74..9b251f0 100644 --- a/picker/wvd/filePicker.js +++ b/picker/wvd/filePicker.js @@ -1,8 +1,10 @@ import { SettingsManager } from "../../lib/util.js"; -document.getElementById('fileInput').addEventListener('change', async (event) => { +document + .getElementById("fileInput") + .addEventListener("change", async (event) => { const file = event.target.files[0]; await SettingsManager.importDevice(file).then(() => { - window.close(); + window.close(); }); -}); \ No newline at end of file + });