+
+
+
+
+
+ Enabled
+
+
+
+
+ Dark Mode
+
+
+
+
+ Widevine Device
+
+
+
+ Remote CDM
- Dark mode
-
-
-
- Widevine Device
+
+
+
+ Widevine Device
+
Choose File
Remove
- Download
-
-
-
-
-
- Remote CDM
+ Download
+
+
+
+
+
+ Remote CDM
+
Choose remote.json
Remove
- Download
-
-
-
-
-
- Command options
-
- Use Shaka Packager
- Executable name:
-
-
- Export Logs
+ Download
+
+
+
- Keys
- Clear
+ Logs
+ Clear All Logs
+ Export Logs as JSON
+
-
+
+
\ No newline at end of file
diff --git a/panel/panel.js b/panel/panel.js
index 659b312..296ecc2 100644
--- a/panel/panel.js
+++ b/panel/panel.js
@@ -1,241 +1,298 @@
import "../protobuf.min.js";
import "../license_protocol.js";
-import {AsyncLocalStorage, base64toUint8Array, stringToUint8Array, DeviceManager, RemoteCDMManager, SettingsManager} from "../util.js";
+import {
+ AsyncLocalStorage,
+ base64toUint8Array,
+ stringToUint8Array,
+ DeviceManager,
+ RemoteCDMManager,
+ SettingsManager,
+} from "../util.js";
-const key_container = document.getElementById('key-container');
+const key_container = document.getElementById("key-container");
-// ================ Main ================
-const enabled = document.getElementById('enabled');
-enabled.addEventListener('change', async function (){
- await SettingsManager.setEnabled(enabled.checked);
+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.setDarkMode(toggle.checked);
- await SettingsManager.saveDarkMode(toggle.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");
- }
+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");
- }
+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('export');
-export_button.addEventListener('click', async function() {
- const logs = await AsyncLocalStorage.getStorage(null);
- SettingsManager.downloadFile(stringToUint8Array(JSON.stringify(logs)), "logs.json");
+const export_button = document.getElementById("export");
+export_button.addEventListener("click", async function () {
+ const logs = await AsyncLocalStorage.getStorage(null);
+ SettingsManager.downloadFile(
+ stringToUint8Array(JSON.stringify(logs)),
+ "logs.json"
+ );
});
-// ======================================
-// ================ Widevine Device ================
-document.getElementById('fileInput').addEventListener('click', () => {
- chrome.runtime.sendMessage({ type: "OPEN_PICKER_WVD" });
- window.close();
+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 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 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);
+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', () => {
- chrome.runtime.sendMessage({ type: "OPEN_PICKER_REMOTE" });
- window.close();
+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_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_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 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 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){
- console.log("input change", event);
- await SettingsManager.saveExecutableName(downloader_name.value);
+const downloader_name = document.getElementById("downloader-name");
+downloader_name.addEventListener("input", async function (event) {
+ await SettingsManager.saveExecutableName(downloader_name.value);
});
-// =================================================
-// ================ Keys ================
-const clear = document.getElementById('clear');
-clear.addEventListener('click', async function() {
- chrome.runtime.sendMessage({ type: "CLEAR" });
- key_container.innerHTML = "";
+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`;
+ 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 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 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);
- });
- }
+ logContainer.querySelector(".key-copy > a").addEventListener("click", (e) => {
+ e.preventDefault();
+ navigator.clipboard.writeText(key_string);
+ });
- 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');
- }
+ 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);
+ });
+ }
- key_container.appendChild(logContainer);
+ 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);
- }
+ 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);
- });
- }
- });
+ 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();
- await SettingsManager.setSelectedDeviceType(await SettingsManager.getSelectedDeviceType());
- await DeviceManager.loadSetAllWidevineDevices();
- await DeviceManager.selectWidevineDevice(await DeviceManager.getSelectedWidevineDevice());
- await RemoteCDMManager.loadSetAllRemoteCDMs();
- await RemoteCDMManager.selectRemoteCDM(await RemoteCDMManager.getSelectedRemoteCDM());
- checkLogs();
-});
-// ======================================
+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);
+ await SettingsManager.setSelectedDeviceType(
+ await SettingsManager.getSelectedDeviceType()
+ );
+ 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.css b/picker/remote/filePicker.css
new file mode 100644
index 0000000..89f16a2
--- /dev/null
+++ b/picker/remote/filePicker.css
@@ -0,0 +1,60 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
+ Arial, sans-serif;
+ background-color: #121212;
+ color: #e0e0e0;
+}
+
+.file-drop-zone {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 320px;
+ max-width: 90%;
+ padding: 40px;
+ border: 2px dashed #444;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
+ text-align: center;
+}
+
+.file-drop-zone:hover {
+ background-color: #1e1e1e;
+ border-color: #666;
+}
+
+.file-drop-zone__icon {
+ margin-bottom: 16px;
+ color: #888;
+}
+
+.file-drop-zone__text {
+ font-size: 1.1rem;
+ font-weight: 500;
+}
+
+.file-drop-zone__format {
+ font-size: 0.8rem;
+ color: #888;
+ margin-top: 8px;
+}
+
+#fileInput[hidden] {
+ display: none;
+}
diff --git a/picker/remote/filePicker.html b/picker/remote/filePicker.html
index 646076d..4bee189 100644
--- a/picker/remote/filePicker.html
+++ b/picker/remote/filePicker.html
@@ -1,7 +1,33 @@
+
+
+
+
+
Select Device File
+
+
+
-
+
+
+
+
+ Click to select or drop JSON a file here
+
+ Supports .wvd file
+
+
+
+
+
\ No newline at end of file
diff --git a/picker/remote/filePicker.js b/picker/remote/filePicker.js
index 832609b..308859b 100644
--- a/picker/remote/filePicker.js
+++ b/picker/remote/filePicker.js
@@ -2,9 +2,28 @@ import "../../protobuf.min.js";
import "../../license_protocol.js";
import { SettingsManager } from "../../util.js";
-document.getElementById('fileInput').addEventListener('change', async (event) => {
- const file = event.target.files[0];
- await SettingsManager.loadRemoteCDM(file).then(() => {
- window.close();
- });
-});
\ No newline at end of file
+document
+ .getElementById("fileInput")
+ .addEventListener("change", async (event) => {
+ const files = event.target.files;
+
+ if (!files || files.length === 0) {
+ console.log("No file selected.");
+ return;
+ }
+
+ const file = files[0];
+
+ try {
+ console.log(`Importing: ${file.name}`);
+ await SettingsManager.importDevice(file);
+
+ console.log("File imported successfully. Closing window.");
+ window.close();
+ } catch (error) {
+ console.error("An error occurred during file import:", error);
+ alert(
+ `Failed to import "${file.name}". Please check the console for details.`
+ );
+ }
+ });
diff --git a/picker/wvd/filePicker.css b/picker/wvd/filePicker.css
new file mode 100644
index 0000000..89f16a2
--- /dev/null
+++ b/picker/wvd/filePicker.css
@@ -0,0 +1,60 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+}
+
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
+ Arial, sans-serif;
+ background-color: #121212;
+ color: #e0e0e0;
+}
+
+.file-drop-zone {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 320px;
+ max-width: 90%;
+ padding: 40px;
+ border: 2px dashed #444;
+ border-radius: 12px;
+ cursor: pointer;
+ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
+ text-align: center;
+}
+
+.file-drop-zone:hover {
+ background-color: #1e1e1e;
+ border-color: #666;
+}
+
+.file-drop-zone__icon {
+ margin-bottom: 16px;
+ color: #888;
+}
+
+.file-drop-zone__text {
+ font-size: 1.1rem;
+ font-weight: 500;
+}
+
+.file-drop-zone__format {
+ font-size: 0.8rem;
+ color: #888;
+ margin-top: 8px;
+}
+
+#fileInput[hidden] {
+ display: none;
+}
diff --git a/picker/wvd/filePicker.html b/picker/wvd/filePicker.html
index 2b8de4a..2227ca9 100644
--- a/picker/wvd/filePicker.html
+++ b/picker/wvd/filePicker.html
@@ -1,7 +1,33 @@
+
+
+
+
+
Select Device File
+
+
+
-
+
+
+
+
+ Click to select or drop WVD a file here
+
+ Supports .wvd file
+
+
+
+
+
\ No newline at end of file
diff --git a/picker/wvd/filePicker.js b/picker/wvd/filePicker.js
index 41e7af2..308859b 100644
--- a/picker/wvd/filePicker.js
+++ b/picker/wvd/filePicker.js
@@ -2,9 +2,28 @@ import "../../protobuf.min.js";
import "../../license_protocol.js";
import { SettingsManager } from "../../util.js";
-document.getElementById('fileInput').addEventListener('change', async (event) => {
- const file = event.target.files[0];
- await SettingsManager.importDevice(file).then(() => {
- window.close();
- });
-});
\ No newline at end of file
+document
+ .getElementById("fileInput")
+ .addEventListener("change", async (event) => {
+ const files = event.target.files;
+
+ if (!files || files.length === 0) {
+ console.log("No file selected.");
+ return;
+ }
+
+ const file = files[0];
+
+ try {
+ console.log(`Importing: ${file.name}`);
+ await SettingsManager.importDevice(file);
+
+ console.log("File imported successfully. Closing window.");
+ window.close();
+ } catch (error) {
+ console.error("An error occurred during file import:", error);
+ alert(
+ `Failed to import "${file.name}". Please check the console for details.`
+ );
+ }
+ });