Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ chrome.webRequest.onBeforeSendHeaders.addListener(
['requestHeaders', chrome.webRequest.OnSendHeadersOptions.EXTRA_HEADERS].filter(Boolean)
);

async function parseClearKey(body, sendResponse, tab_url) {
async function parseClearKey(body, sendResponse, tab_title, tab_url) {
const clearkey = JSON.parse(atob(body));

const formatted_keys = clearkey["keys"].map(key => ({
Expand All @@ -66,6 +66,7 @@ async function parseClearKey(body, sendResponse, tab_url) {
type: "CLEARKEY",
pssh_data: pssh_data,
keys: formatted_keys,
title: tab_title,
url: tab_url,
timestamp: Math.floor(Date.now() / 1000),
manifests: manifests.has(tab_url) ? manifests.get(tab_url) : []
Expand Down Expand Up @@ -117,7 +118,7 @@ async function generateChallenge(body, sendResponse) {
sendResponse(uint8ArrayToBase64(challenge));
}

async function parseLicense(body, sendResponse, tab_url) {
async function parseLicense(body, sendResponse, tab_title, tab_url) {
const license = base64toUint8Array(body);
const signed_license_message = SignedMessage.decode(license);

Expand All @@ -144,6 +145,7 @@ async function parseLicense(body, sendResponse, tab_url) {
type: "WIDEVINE",
pssh_data: pssh,
keys: keys,
title: tab_title,
url: tab_url,
timestamp: Math.floor(Date.now() / 1000),
manifests: manifests.has(tab_url) ? manifests.get(tab_url) : []
Expand Down Expand Up @@ -196,7 +198,7 @@ async function generateChallengeRemote(body, sendResponse) {
sendResponse(challenge_b64);
}

async function parseLicenseRemote(body, sendResponse, tab_url) {
async function parseLicenseRemote(body, sendResponse, tab_title, tab_url) {
const license = base64toUint8Array(body);
const signed_license_message = SignedMessage.decode(license);

Expand Down Expand Up @@ -241,6 +243,7 @@ async function parseLicenseRemote(body, sendResponse, tab_url) {
type: "WIDEVINE",
pssh_data: session_id.pssh,
keys: keys,
title: tab_title,
url: tab_url,
timestamp: Math.floor(Date.now() / 1000),
manifests: manifests.has(tab_url) ? manifests.get(tab_url) : []
Expand All @@ -254,6 +257,7 @@ async function parseLicenseRemote(body, sendResponse, tab_url) {

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
(async () => {
const tab_title = sender.tab ? sender.tab.title : '';
const tab_url = sender.tab ? sender.tab.url : null;

switch (message.type) {
Expand Down Expand Up @@ -291,16 +295,16 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
}

try {
await parseClearKey(message.body, sendResponse, tab_url);
await parseClearKey(message.body, sendResponse, tab_title, tab_url);
return;
} catch (e) {
const device_type = await SettingsManager.getSelectedDeviceType();
switch (device_type) {
case "WVD":
await parseLicense(message.body, sendResponse, tab_url);
await parseLicense(message.body, sendResponse, tab_title, tab_url);
break;
case "REMOTE":
await parseLicenseRemote(message.body, sendResponse, tab_url);
await parseLicenseRemote(message.body, sendResponse, tab_title, tab_url);
break;
}
return;
Expand Down
1 change: 1 addition & 0 deletions panel/panel.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<input type="text" class="text-box" id="downloader-name">
</fieldset>
<button type="button" id="export">Export Logs</button>
<button type="button" id="export_kodi_m3u8">Export Kodi M3U8</button>
</fieldset>
<fieldset>
<legend>Keys</legend>
Expand Down
59 changes: 56 additions & 3 deletions panel/panel.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "../protobuf.min.js";
import "../license_protocol.js";
import {AsyncLocalStorage, base64toUint8Array, stringToUint8Array, DeviceManager, RemoteCDMManager, SettingsManager} from "../util.js";
import {AsyncLocalStorage, base64toUint8Array, DeviceManager, RemoteCDMManager, SettingsManager} from "../util.js";

const key_container = document.getElementById('key-container');

Expand Down Expand Up @@ -30,10 +30,60 @@ remote_select.addEventListener('change', async function (){
}
});

async function getLogs() {
return await AsyncLocalStorage.getStorage(null);
}

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");
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code was replaced as a title can contain unicode characters and those need to be exported correctly

const logs = await getLogs();
SettingsManager.downloadFile(new Blob([JSON.stringify(logs)]), "logs.json");
});

const export_kodi_m3u8_button = document.getElementById('export_kodi_m3u8');
export_kodi_m3u8_button.addEventListener('click', async function () {
const logs = await getLogs();

const getKodiDrmLegacyProp = log => "#KODIPROP:inputstream.adaptive.drm_legacy=org.w3.clearkey|" +
log.keys.map(({kid, k}) => `${kid}:${k}`).join(',');

const getKodiDrmProp = log => {
const drm = {};
drm["org.w3.clearkey"] = {};
drm["org.w3.clearkey"]["license"] = {};
drm["org.w3.clearkey"]["license"]["keyids"] = {};
log.keys.forEach(({kid, k}) => {
drm["org.w3.clearkey"]["license"]["keyids"][`${kid}`] = `${k}`
});
return `#KODIPROP:inputstream.adaptive.drm=${JSON.stringify(drm)}`;
};

const m3u8 = "#EXTM3U\n" + Object.values(logs)
.sort((a, b) => a.title.localeCompare(b.title))
.map(log => {
try {
const manifest = log.manifests.find(manifest => manifest.url);
if (manifest) {
const legacy_drm = true;
return [
`#EXTINF:-1,${log.title}`,
'#KODIPROP:inputstream=inputstream.adaptive',
'#KODIPROP:inputstream.adaptive.common_headers=' +
Object.entries(manifest.headers).map(([key, value]) => `${key}=${value}`).join('&'),
legacy_drm ? getKodiDrmLegacyProp(log) : getKodiDrmProp(log),
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inputstream.adaptive.drm_legacy is stable and thus made the default
inputstream.adaptive.drm is still under development and could break compatability

manifest.url
].join('\n');
} else {
console.warn('Skipping ' + JSON.stringify(log) + ' as it has no url');
}
} catch (e) {
console.error('Skipping ' + JSON.stringify(log) + ' due to ' + e);
}
return undefined;
})
.filter(entry => entry)
.join('\n');
SettingsManager.downloadFile(new Blob([m3u8]), 'kodi.m3u8');
});
// ======================================

Expand Down Expand Up @@ -145,6 +195,9 @@ async function appendLog(result) {
URL:<input type="text" class="text-box" value="${result.url}">
</label>
<label class="expanded-only right-bound">
<label class="expanded-only right-bound">
Title:<input type="text" class="text-box" value="${result.title}">
</label>
<label class="expanded-only right-bound">
PSSH:<input type="text" class="text-box" value="${result.pssh_data}">
</label>
Expand Down