Files
WidevineProxy2/lib/util.js
2026-03-15 08:05:58 +01:00

371 lines
12 KiB
JavaScript

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 getStorage(keys) {
return await chrome.storage.sync.get(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 getStorage(keys) {
return await chrome.storage.local.get(keys);
}
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"]);
}
}
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"]);
}
}
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 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 saveUseSingleQuotes(use_single_quotes) {
await AsyncSyncStorage.setStorage({ use_single_quotes, });
}
static async getUseSingleQuotes() {
const result = await AsyncSyncStorage.getStorage(["use_single_quotes"]);
return result["use_single_quotes"] ?? false;
}
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 IconManager {
static setDefaultIcon() {
chrome.action.setIcon({
path: {
128: "images/icon-128.png"
}
});
}
static setNotificationIcon() {
chrome.action.setIcon({
path: {
128: "images/icon-128-notification.png"
}
});
}
}
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('');
}
}