* Omit {secret} header if it's not provided in JSON

* Allow SuperGeneric devices to be imported without having a base URL set ('host' key)
* Abort remote requests if expected key is not found
* Prevent duplicate log entries if a entry was added while the panel was open
This commit is contained in:
Ingan121
2025-08-27 20:56:51 +09:00
parent 9e429479d1
commit 6a41be227d
4 changed files with 25 additions and 5 deletions

View File

@@ -24,6 +24,7 @@
+ Works with any service that accepts challenges from Android devices on the same endpoint.
+ Incompatible services:
+ Netflix (unless a browser/PC device is provided, which is rare)
+ VdoCipher (ditto)
+ Some services may detect your browser and interfere with PlayReady playback. Try using a user-agent changer extension, or use a Chromium-based browser for PlayReady playback.
+ Firefox-based browsers may fail to play some PlayReady-protected video, with an internal error saying `ChromiumCDMParent::RecvDecodeFailed with status decode error`. This is a problem with the browser's ClearKey handler, and Vineless can do nothing about it. Please use a Chromium-based browser if this error occurs.
+ Incompatible extensions:

View File

@@ -114,7 +114,11 @@ export class RemoteCdm {
this.secret = dataObj.secret;
for (const [key, value] of Object.entries(this.baseHeaders)) {
if (value === "{secret}") {
this.baseHeaders[key] = this.secret;
if (this.secret) {
this.baseHeaders[key] = this.secret;
} else {
delete this.baseHeaders[key];
}
}
}
}
@@ -180,6 +184,9 @@ export class RemoteCdm {
}
if (request.sessionIdResKeyName) {
this.sessionId = getNestedProperty(jsonData, request.sessionIdResKeyName);
if (!this.sessionId) {
throw new Error("Server did not return a session ID");
}
}
if (request.challengeKeyName) {
this.challenge = getNestedProperty(jsonData, request.challengeKeyName);
@@ -259,6 +266,9 @@ export class RemoteCdm {
}
if (request.sessionIdResKeyName) {
this.sessionId = getNestedProperty(jsonData, request.sessionIdResKeyName);
if (!this.sessionId) {
throw new Error("Server did not return a session ID");
}
}
if (request.contentKeysKeyName) {
this.contentKeys = getNestedProperty(jsonData, request.contentKeysKeyName);

View File

@@ -299,6 +299,8 @@ async function appendLog(result) {
const logContainer = document.createElement('div');
logContainer.classList.add('log-container');
const pssh = result.pssh_data || result.wrm_header;
logContainer.innerHTML = `
<button class="toggleButton">+</button>
<div class="expandableDiv collapsed">
@@ -312,8 +314,7 @@ async function appendLog(result) {
Type:<input type="text" class="text-box" value="${getFriendlyType(result.type)}">
</label>
<label class="expanded-only right-bound">
<label class="expanded-only right-bound">
${result.type === "PLAYREADY" ? "WRM" : "PSSH"}:<input type="text" class="text-box" value='${escapeHTML(result.pssh_data || result.wrm_header)}'>
${result.type === "PLAYREADY" ? "WRM" : "PSSH"}:<input type="text" class="text-box pssh-box" value='${escapeHTML(pssh)}'>
</label>
<label class="expanded-only right-bound key-copy">
<a href="#" title="Click to copy">Keys:</a><input type="text" class="text-box" value="${key_string}">
@@ -372,6 +373,14 @@ async function appendLog(result) {
}
});
// Remote duplicate existing entry
const psshBoxes = key_container.querySelectorAll('.log-container .pssh-box');
psshBoxes.forEach(box => {
if (box.value === pssh) {
box.parentElement.parentElement.parentElement.remove();
}
});
key_container.appendChild(logContainer);
updateIcon();

View File

@@ -471,8 +471,8 @@ export class SettingsManager {
try {
const json_file = JSON.parse(result);
if (!json_file.host) {
throw new Error("Invalid remote CDM file: missing host");
if (!json_file.host && !json_file.sg_api_conf) {
throw new Error("Invalid remote CDM file: missing host on non-SuperGeneric device");
}
console.log("LOADED DEVICE:", json_file);