2025-06-01 12:40:24 -04:00
|
|
|
import React, { useEffect, useState } from "react";
|
|
|
|
|
|
|
|
function Results() {
|
2025-07-20 12:18:15 +07:00
|
|
|
const [drmType, setDrmType] = useState("");
|
|
|
|
const [pssh, setPssh] = useState("");
|
|
|
|
const [licenseUrl, setLicenseUrl] = useState("");
|
|
|
|
const [keys, setKeys] = useState([]);
|
2025-07-20 16:00:59 +07:00
|
|
|
const [manifestUrl, setManifestUrl] = useState("");
|
2025-07-20 20:10:47 +07:00
|
|
|
const [currentTabUrl, setCurrentTabUrl] = useState("");
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
useEffect(() => {
|
|
|
|
chrome.storage.local.get(
|
2025-07-20 16:00:59 +07:00
|
|
|
[
|
|
|
|
"drmType",
|
|
|
|
"latestPSSH",
|
|
|
|
"latestLicenseRequest",
|
|
|
|
"latestKeys",
|
|
|
|
"licenseURL",
|
|
|
|
"manifestURL",
|
|
|
|
],
|
2025-07-20 12:18:15 +07:00
|
|
|
(result) => {
|
2025-07-20 16:56:34 +07:00
|
|
|
if (result.drmType) setDrmType(result.drmType || "");
|
|
|
|
if (result.latestPSSH) setPssh(result.latestPSSH || "");
|
|
|
|
if (result.licenseURL) setLicenseUrl(result.licenseURL || "");
|
|
|
|
if (result.manifestURL) setManifestUrl(result.manifestURL || "");
|
2025-07-20 12:18:15 +07:00
|
|
|
if (result.latestKeys) {
|
|
|
|
try {
|
|
|
|
const parsed = Array.isArray(result.latestKeys)
|
|
|
|
? result.latestKeys
|
|
|
|
: JSON.parse(result.latestKeys);
|
|
|
|
setKeys(parsed);
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Failed to parse keys:", e);
|
|
|
|
setKeys([]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 20:10:47 +07:00
|
|
|
// Get current tab URL when component mounts
|
|
|
|
chrome.windows.getAll({ populate: true, windowTypes: ["normal"] }, (windows) => {
|
|
|
|
if (windows && windows.length > 0) {
|
|
|
|
const lastFocusedWindow = windows.find((w) => w.focused) || windows[0];
|
|
|
|
if (lastFocusedWindow) {
|
|
|
|
const activeTab = lastFocusedWindow.tabs.find(
|
|
|
|
(tab) => tab.active && tab.url && /^https?:\/\//.test(tab.url)
|
|
|
|
);
|
|
|
|
if (activeTab?.url) {
|
|
|
|
setCurrentTabUrl(activeTab.url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
const handleChange = (changes, area) => {
|
|
|
|
if (area === "local") {
|
|
|
|
if (changes.drmType) {
|
2025-07-20 16:56:34 +07:00
|
|
|
setDrmType(changes.drmType.newValue || "");
|
2025-07-20 12:18:15 +07:00
|
|
|
}
|
|
|
|
if (changes.latestPSSH) {
|
2025-07-20 16:56:34 +07:00
|
|
|
setPssh(changes.latestPSSH.newValue || "");
|
2025-07-20 12:18:15 +07:00
|
|
|
}
|
|
|
|
if (changes.licenseURL) {
|
2025-07-20 16:56:34 +07:00
|
|
|
setLicenseUrl(changes.licenseURL.newValue || "");
|
2025-07-20 12:18:15 +07:00
|
|
|
}
|
2025-07-20 16:00:59 +07:00
|
|
|
if (changes.manifestURL) {
|
2025-07-20 16:56:34 +07:00
|
|
|
setManifestUrl(changes.manifestURL.newValue || "");
|
2025-07-20 16:00:59 +07:00
|
|
|
}
|
2025-07-20 12:18:15 +07:00
|
|
|
if (changes.latestKeys) {
|
2025-07-20 16:56:34 +07:00
|
|
|
setKeys(changes.latestKeys.newValue || []);
|
2025-07-20 12:18:15 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
chrome.storage.onChanged.addListener(handleChange);
|
|
|
|
return () => chrome.storage.onChanged.removeListener(handleChange);
|
|
|
|
}, []);
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
const handleCapture = () => {
|
|
|
|
// Reset stored values
|
|
|
|
chrome.storage.local.set({
|
2025-07-20 16:56:34 +07:00
|
|
|
drmType: "",
|
|
|
|
latestPSSH: "",
|
|
|
|
licenseURL: "",
|
|
|
|
manifestURL: "",
|
2025-07-20 12:18:15 +07:00
|
|
|
latestKeys: [],
|
|
|
|
});
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
// Get all normal windows to exclude your popup
|
|
|
|
chrome.windows.getAll({ populate: true, windowTypes: ["normal"] }, (windows) => {
|
|
|
|
if (!windows || windows.length === 0) {
|
|
|
|
console.warn("No normal Chrome windows found");
|
|
|
|
return;
|
|
|
|
}
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
// Find the last focused normal window
|
|
|
|
const lastFocusedWindow = windows.find((w) => w.focused) || windows[0];
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
if (!lastFocusedWindow) {
|
|
|
|
console.warn("No focused normal window found");
|
|
|
|
return;
|
|
|
|
}
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
// Find the active tab in that window (that is a regular webpage)
|
|
|
|
const activeTab = lastFocusedWindow.tabs.find(
|
|
|
|
(tab) => tab.active && tab.url && /^https?:\/\//.test(tab.url)
|
|
|
|
);
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
if (activeTab?.id) {
|
|
|
|
chrome.tabs.reload(activeTab.id, () => {
|
|
|
|
if (chrome.runtime.lastError) {
|
|
|
|
console.error("Failed to reload tab:", chrome.runtime.lastError);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
console.warn("No active tab found in the last focused normal window");
|
2025-06-01 12:40:24 -04:00
|
|
|
}
|
2025-07-20 12:18:15 +07:00
|
|
|
});
|
|
|
|
};
|
2025-06-01 12:40:24 -04:00
|
|
|
|
2025-07-20 20:10:47 +07:00
|
|
|
// Check if current tab is YouTube
|
|
|
|
const isYouTube = () => {
|
|
|
|
return currentTabUrl.includes("youtube.com") || currentTabUrl.includes("youtu.be");
|
|
|
|
};
|
2025-07-20 16:56:34 +07:00
|
|
|
|
2025-07-20 20:10:47 +07:00
|
|
|
// Get manifest URL display value
|
|
|
|
const getManifestDisplayValue = () => {
|
|
|
|
if (manifestUrl) {
|
|
|
|
return manifestUrl;
|
|
|
|
}
|
|
|
|
if (isYouTube()) {
|
|
|
|
return "[Use yt-dlp to download video]";
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
|
|
|
|
// Get manifest URL placeholder
|
|
|
|
const getManifestPlaceholder = () => {
|
|
|
|
if (isYouTube() && !manifestUrl) {
|
|
|
|
return "[Use yt-dlp to download video]";
|
|
|
|
}
|
|
|
|
return "[Not available]";
|
|
|
|
};
|
|
|
|
|
|
|
|
// Export to JSON file
|
2025-07-20 16:56:34 +07:00
|
|
|
const hasData = () => {
|
2025-07-21 23:29:40 +07:00
|
|
|
return Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0;
|
2025-07-20 16:56:34 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
const handleExportJSON = () => {
|
|
|
|
const exportData = {
|
|
|
|
drmType: drmType || null,
|
|
|
|
manifestUrl: manifestUrl || null,
|
|
|
|
pssh: pssh || null,
|
|
|
|
licenseUrl: licenseUrl || null,
|
|
|
|
keys:
|
|
|
|
Array.isArray(keys) && keys.length > 0
|
|
|
|
? keys
|
|
|
|
.filter((k) => k.type !== "SIGNING")
|
|
|
|
.map((k) => `${k.key_id || k.keyId}:${k.key}`)
|
|
|
|
: null,
|
|
|
|
exportedAt: new Date().toISOString(),
|
|
|
|
};
|
|
|
|
|
|
|
|
const blob = new Blob([JSON.stringify(exportData, null, 2)], {
|
|
|
|
type: "application/json",
|
|
|
|
});
|
|
|
|
|
|
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
const a = document.createElement("a");
|
|
|
|
a.href = url;
|
|
|
|
a.download = `drm-data-${new Date().toISOString().slice(0, 19).replace(/:/g, "-")}.json`;
|
|
|
|
document.body.appendChild(a);
|
|
|
|
a.click();
|
|
|
|
document.body.removeChild(a);
|
|
|
|
URL.revokeObjectURL(url);
|
|
|
|
};
|
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
return (
|
|
|
|
<div className="w-full grow flex h-full overflow-y-auto overflow-x-auto flex-col text-white p-4">
|
|
|
|
<button
|
|
|
|
onClick={handleCapture}
|
2025-07-20 16:56:34 +07:00
|
|
|
className="w-full h-10 bg-sky-500 rounded-md p-2 mt-2 text-white cursor-pointer font-bold hover:bg-sky-600"
|
2025-07-20 12:18:15 +07:00
|
|
|
>
|
|
|
|
Capture current tab
|
|
|
|
</button>
|
2025-07-20 16:00:59 +07:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
<p className="text-2xl mt-5">DRM Type</p>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
value={drmType}
|
2025-07-20 16:00:59 +07:00
|
|
|
className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
|
|
|
|
placeholder="[Not available]"
|
|
|
|
disabled
|
|
|
|
/>
|
|
|
|
|
|
|
|
<p className="text-2xl mt-5">Manifest URL</p>
|
|
|
|
<input
|
|
|
|
type="text"
|
2025-07-20 20:10:47 +07:00
|
|
|
value={getManifestDisplayValue()}
|
|
|
|
className={`w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 font-mono ${
|
|
|
|
isYouTube() && !manifestUrl ? "text-yellow-400" : "text-white"
|
|
|
|
}`}
|
|
|
|
placeholder={getManifestPlaceholder()}
|
2025-07-20 12:18:15 +07:00
|
|
|
disabled
|
|
|
|
/>
|
2025-07-20 16:00:59 +07:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
<p className="text-2xl mt-5">PSSH</p>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
value={pssh}
|
2025-07-20 16:00:59 +07:00
|
|
|
className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
|
|
|
|
placeholder="[Not available]"
|
2025-07-20 12:18:15 +07:00
|
|
|
disabled
|
|
|
|
/>
|
2025-07-20 16:00:59 +07:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
<p className="text-2xl mt-5">License URL</p>
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
value={licenseUrl}
|
2025-07-20 16:00:59 +07:00
|
|
|
className="w-full h-10 bg-slate-800/50 rounded-md p-2 mt-2 text-white font-mono"
|
|
|
|
placeholder="[Not available]"
|
2025-07-20 12:18:15 +07:00
|
|
|
disabled
|
|
|
|
/>
|
2025-07-20 16:00:59 +07:00
|
|
|
|
2025-07-20 12:18:15 +07:00
|
|
|
<p className="text-2xl mt-5">Keys</p>
|
2025-07-20 16:00:59 +07:00
|
|
|
<div className="w-full min-h-64 h-64 flex items-center justify-center text-center overflow-y-auto bg-slate-800/50 rounded-md p-2 mt-2 text-white whitespace-pre-line font-mono">
|
2025-07-20 12:18:15 +07:00
|
|
|
{Array.isArray(keys) && keys.filter((k) => k.type !== "SIGNING").length > 0 ? (
|
|
|
|
keys
|
|
|
|
.filter((k) => k.type !== "SIGNING")
|
|
|
|
.map((k) => `${k.key_id || k.keyId}:${k.key}`)
|
|
|
|
.join("\n")
|
|
|
|
) : (
|
2025-07-20 16:00:59 +07:00
|
|
|
<span className="text-gray-400">[Not available]</span>
|
2025-07-20 12:18:15 +07:00
|
|
|
)}
|
|
|
|
</div>
|
2025-07-20 16:56:34 +07:00
|
|
|
|
|
|
|
{hasData() && (
|
|
|
|
<button
|
|
|
|
onClick={handleExportJSON}
|
|
|
|
className="w-full h-10 bg-green-500 rounded-md p-2 mt-5 text-white cursor-pointer font-bold hover:bg-green-600"
|
|
|
|
>
|
|
|
|
Export as JSON
|
|
|
|
</button>
|
|
|
|
)}
|
2025-07-20 12:18:15 +07:00
|
|
|
</div>
|
|
|
|
);
|
2025-06-01 12:40:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export default Results;
|