Some fixes

This commit is contained in:
Joris Bertomeu
2025-08-26 09:21:46 +02:00
parent ac0dc8286b
commit 1851ca9241
2 changed files with 389 additions and 383 deletions

View File

@@ -142,7 +142,7 @@
<i class="fas fa-server"></i> CrawlFlix Server
</label>
<input type="text" id="crawlFlixUrl" class="form-control"
placeholder="http://localhost:3000" value="http://localhost:3000">
placeholder="http://localhost:4200" value="http://localhost:4200">
</div>
</div>
<div class="col-md-6">
@@ -237,7 +237,7 @@
<div class="col-12">
<label for="crawlFlixUrlCK" class="form-label">CrawlFlix Server</label>
<input type="text" id="crawlFlixUrlCK" class="form-control form-control-sm"
placeholder="http://localhost:3000" value="http://localhost:3000">
placeholder="http://localhost:4200" value="http://localhost:4200">
</div>
</div>

View File

@@ -4,109 +4,113 @@ let requests = chrome.extension.getBackgroundPage().requests;
let pageURL = chrome.extension.getBackgroundPage().pageURL;
let targetIds = chrome.extension.getBackgroundPage().targetIds;
let clearkey = chrome.extension.getBackgroundPage().clearkey;
let userInputs = {};
let userInputs = {}; // IMPORTANT: Cette variable était manquante!
// === WIDEVINE KEY EXTRACTION ===
class WidevineExtractor {
static async extractKeys() {
const guessButton = document.getElementById("guess");
const resultTextarea = document.getElementById('result');
static async extractKeys() {
const guessButton = document.getElementById("guess");
const resultTextarea = document.getElementById('result');
try {
// UI feedback
UIHelpers.setLoadingState(guessButton, true);
document.body.style.cursor = "wait";
try {
// Vérifier que userInputs.license est défini
if (userInputs.license === undefined || !requests[userInputs.license]) {
throw new Error('License not selected. Please wait for auto-selection or select manually.');
}
// Initialize Pyodide
const pyodide = await loadPyodide();
await pyodide.loadPackage([
"certifi-2024.2.2-py3-none-any.whl",
"charset_normalizer-3.3.2-py3-none-any.whl",
"construct-2.8.8-py2.py3-none-any.whl",
"idna-3.6-py3-none-any.whl",
"packaging-23.2-py3-none-any.whl",
"protobuf-4.24.4-cp312-cp312-emscripten_3_1_52_wasm32.whl",
"pycryptodome-3.20.0-cp35-abi3-emscripten_3_1_52_wasm32.whl",
"pymp4-1.4.0-py3-none-any.whl",
"pyodide_http-0.2.1-py3-none-any.whl",
"pywidevine-1.8.0-py3-none-any.whl",
"requests-2.31.0-py3-none-any.whl",
"urllib3-2.2.1-py3-none-any.whl"
].map(e => "/libs/wheels/" + e));
// UI feedback
UIHelpers.setLoadingState(guessButton, true);
document.body.style.cursor = "wait";
// Configure Guesser
pyodide.globals.set("pssh", document.getElementById('pssh').value);
pyodide.globals.set("licUrl", requests[userInputs['license']]['url']);
pyodide.globals.set("licHeaders", requests[userInputs['license']]['headers']);
pyodide.globals.set("licBody", requests[userInputs['license']]['body']);
// Initialize Pyodide
const pyodide = await loadPyodide();
await pyodide.loadPackage([
"certifi-2024.2.2-py3-none-any.whl",
"charset_normalizer-3.3.2-py3-none-any.whl",
"construct-2.8.8-py2.py3-none-any.whl",
"idna-3.6-py3-none-any.whl",
"packaging-23.2-py3-none-any.whl",
"protobuf-4.24.4-cp312-cp312-emscripten_3_1_52_wasm32.whl",
"pycryptodome-3.20.0-cp35-abi3-emscripten_3_1_52_wasm32.whl",
"pymp4-1.4.0-py3-none-any.whl",
"pyodide_http-0.2.1-py3-none-any.whl",
"pywidevine-1.8.0-py3-none-any.whl",
"requests-2.31.0-py3-none-any.whl",
"urllib3-2.2.1-py3-none-any.whl"
].map(e => "/libs/wheels/" + e));
// Load Python scripts
const [pre, after, scheme] = await Promise.all([
fetch('/python/pre.py').then(res => res.text()),
fetch('/python/after.py').then(res => res.text()),
Promise.resolve(document.getElementById("schemeCode").value)
]);
// Configure Guesser
pyodide.globals.set("pssh", document.getElementById('pssh').value);
pyodide.globals.set("licUrl", requests[userInputs['license']]['url']);
pyodide.globals.set("licHeaders", requests[userInputs['license']]['headers']);
pyodide.globals.set("licBody", requests[userInputs['license']]['body']);
// Execute Python script
const result = await pyodide.runPythonAsync([pre, scheme, after].join("\n"));
resultTextarea.value = result;
// Load Python scripts
const [pre, after, scheme] = await Promise.all([
fetch('/python/pre.py').then(res => res.text()),
fetch('/python/after.py').then(res => res.text()),
Promise.resolve(document.getElementById("schemeCode").value)
]);
// Save to history
this.saveToHistory(result);
// Execute Python script
const result = await pyodide.runPythonAsync([pre, scheme, after].join("\n"));
resultTextarea.value = result;
// Auto-update CrawlFlix integration
CrawlFlixIntegration.updateAfterKeyExtraction();
// Save to history
this.saveToHistory(result);
StatusManager.show('Keys extracted successfully!', 'success');
// Auto-update CrawlFlix integration
CrawlFlixIntegration.updateAfterKeyExtraction();
} catch (error) {
console.error('Key extraction failed:', error);
StatusManager.show(`Key extraction failed: ${error.message}`, 'error');
} finally {
// Reset UI
UIHelpers.setLoadingState(guessButton, false);
document.body.style.cursor = "auto";
}
}
StatusManager.show('Keys extracted successfully!', 'success');
static saveToHistory(result) {
const historyData = {
PSSH: document.getElementById('pssh').value,
KEYS: result.split("\n").slice(0, -1)
};
chrome.storage.local.set({[pageURL]: historyData}, null);
}
} catch (error) {
console.error('Key extraction failed:', error);
StatusManager.show(`Key extraction failed: ${error.message}`, 'error');
} finally {
// Reset UI
UIHelpers.setLoadingState(guessButton, false);
document.body.style.cursor = "auto";
}
}
static async autoSelect() {
userInputs["license"] = 0;
document.getElementById("license").value = requests[0]['url'];
document.getElementById('pssh').value = psshs[0];
static saveToHistory(result) {
const historyData = {
PSSH: document.getElementById('pssh').value,
KEYS: result.split("\n").slice(0, -1)
};
chrome.storage.local.set({[pageURL]: historyData}, null);
}
try {
const selectRules = await fetch("/selectRules.conf").then(r => r.text());
const rules = selectRules
.replace(/\n^\s*$|\s*\/\/.*|\s*$/gm, "")
.split("\n")
.map(row => row.split("$$"));
static async autoSelect() {
userInputs["license"] = 0;
document.getElementById("license").value = requests[0]['url'];
document.getElementById('pssh').value = psshs[0];
for (const item of rules) {
const search = requests.map(r => r['url']).findIndex(e => e.includes(item[0]));
if (search >= 0) {
if (item[1]) document.getElementById("schemeSelect").value = item[1];
userInputs["license"] = search;
document.getElementById("license").value = requests[search]['url'];
break;
}
}
try {
const selectRules = await fetch("/selectRules.conf").then(r => r.text());
const rules = selectRules
.replace(/\n^\s*$|\s*\/\/.*|\s*$/gm, "")
.split("\n")
.map(row => row.split("$$"));
document.getElementById("schemeSelect").dispatchEvent(new Event("input"));
} catch (error) {
console.error('Auto-select failed:', error);
}
}
for (const item of rules) {
const search = requests.map(r => r['url']).findIndex(e => e.includes(item[0]));
if (search >= 0) {
if (item[1]) document.getElementById("schemeSelect").value = item[1];
userInputs["license"] = search;
document.getElementById("license").value = requests[search]['url'];
break;
}
}
document.getElementById("schemeSelect").dispatchEvent(new Event("input"));
} catch (error) {
console.error('Auto-select failed:', error);
}
}
}
// === SMART MPD SELECTOR ===
class SmartMPDSelector {
static scoreAndRankMPDs(mpdUrls) {
const scoredMPDs = mpdUrls.map(url => ({
@@ -359,343 +363,345 @@ class MPDDetector {
// === CRAWLFLIX INTEGRATION ===
class CrawlFlixIntegration {
static async sendToCrawlFlix(isClearchey = false) {
const suffix = isClearchey ? 'CK' : '';
const crawlFlixUrl = document.getElementById(`crawlFlixUrl${suffix}`).value || 'http://localhost:3000';
const mpdUrl = document.getElementById(`mpdUrl${suffix}`).value;
const resultTextarea = document.getElementById(isClearchey ? 'ckResult' : 'result');
static async sendToCrawlFlix(isClearchey = false) {
const suffix = isClearchey ? 'CK' : '';
const crawlFlixUrl = document.getElementById(`crawlFlixUrl${suffix}`).value || 'http://localhost:4200';
const mpdUrl = document.getElementById(`mpdUrl${suffix}`).value;
const resultTextarea = document.getElementById(isClearchey ? 'ckResult' : 'result');
// Validation
if (!mpdUrl.trim()) {
StatusManager.show('Please enter or select an MPD URL', 'error', suffix);
return;
}
// Validation
if (!mpdUrl.trim()) {
StatusManager.show('Please enter or select an MPD URL', 'error', suffix);
return;
}
if (!resultTextarea.value.trim()) {
StatusManager.show('No keys available to send', 'error', suffix);
return;
}
if (!resultTextarea.value.trim()) {
StatusManager.show('No keys available to send', 'error', suffix);
return;
}
try {
StatusManager.show('Processing MPD and sending to CrawlFlix...', 'info', suffix);
try {
StatusManager.show('Opening CrawlFlix with preloaded data...', 'info', suffix);
// Parse and validate keys
const keys = this.parseKeys(resultTextarea.value);
if (keys.length === 0) {
StatusManager.show('No valid keys found in the format key:value', 'error', suffix);
return;
}
// Parse et valide les clés pour information
const keys = this.parseKeys(resultTextarea.value);
if (keys.length === 0) {
StatusManager.show('No valid keys found in the format key:value', 'error', suffix);
return;
}
// Test CrawlFlix connection and process MPD
const mpdData = await this.processMPD(crawlFlixUrl, mpdUrl);
// Ouvrir CrawlFlix directement avec les paramètres
this.openCrawlFlixTab(crawlFlixUrl, mpdUrl, resultTextarea.value);
// Create success message
const message = `✓ Successfully sent to CrawlFlix!
${keys.length} key(s) • ${mpdData.videoTracks.length} video track(s) • ${mpdData.audioTracks.length} audio track(s) • ${mpdData.subtitles.length} subtitle(s)`;
// Message de succès
const contentType = isClearchey ? 'ClearKey' : 'Widevine';
StatusManager.show(`${contentType} data sent to CrawlFlix! (${keys.length} keys, MPD URL)`, 'success', suffix);
StatusManager.show(message, 'success', suffix);
} catch (error) {
console.error('CrawlFlix send error:', error);
StatusManager.show(`Failed to send: ${error.message}`, 'error', suffix);
}
}
// Open CrawlFlix with pre-filled data
this.openCrawlFlixTab(crawlFlixUrl, mpdUrl, resultTextarea.value);
static parseKeys(keysText) {
return keysText
.split('\n')
.map(line => line.trim())
.filter(line => line && line.includes(':'))
.map(line => {
const [key, value] = line.split(':');
return {
key: key?.trim(),
value: value?.trim()
};
})
.filter(k => k.key && k.value && k.key.length > 0 && k.value.length > 0);
}
} catch (error) {
console.error('CrawlFlix send error:', error);
StatusManager.show(`Failed to send: ${error.message}`, 'error', suffix);
}
}
static async processMPD(crawlFlixUrl, mpdUrl) {
const response = await fetch(`${crawlFlixUrl}/processMPD`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mpdUrl })
});
static parseKeys(keysText) {
return keysText
.split('\n')
.map(line => line.trim())
.filter(line => line && line.includes(':'))
.map(line => {
const [key, value] = line.split(':');
return {
key: key?.trim(),
value: value?.trim()
};
})
.filter(k => k.key && k.value && k.key.length > 0 && k.value.length > 0);
}
if (!response.ok) {
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(`MPD processing failed (${response.status}): ${errorText}`);
}
static async processMPD(crawlFlixUrl, mpdUrl) {
const response = await fetch(`${crawlFlixUrl}/processMPD`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mpdUrl })
});
return await response.json();
}
if (!response.ok) {
const errorText = await response.text().catch(() => 'Unknown error');
throw new Error(`MPD processing failed (${response.status}): ${errorText}`);
}
static openCrawlFlixTab(crawlFlixUrl, mpdUrl, keysText) {
// Encoder les données pour l'URL
const params = new URLSearchParams({
mpdUrl: mpdUrl,
keys: keysText,
autoLoad: 'true'
});
return await response.json();
}
// Ouvrir CrawlFlix avec les paramètres pré-remplis
const crawlFlixTab = `${crawlFlixUrl}?${params.toString()}`;
chrome.tabs.create({ url: crawlFlixTab });
static openCrawlFlixTab(crawlFlixUrl, mpdUrl, keysText) {
const params = new URLSearchParams({
mpdUrl: mpdUrl,
keys: keysText,
source: 'widevine-plugin'
});
console.log('Opening CrawlFlix with preloaded data:', crawlFlixTab);
}
const crawlFlixTab = `${crawlFlixUrl}?${params.toString()}`;
chrome.tabs.create({ url: crawlFlixTab });
}
static copyKeys(resultTextareaId) {
const textarea = document.getElementById(resultTextareaId);
if (!textarea || !textarea.value.trim()) {
StatusManager.show('No keys to copy', 'error');
return;
}
static copyKeys(resultTextareaId) {
const textarea = document.getElementById(resultTextareaId);
if (!textarea || !textarea.value.trim()) {
StatusManager.show('No keys to copy', 'error');
return;
}
navigator.clipboard.writeText(textarea.value).then(() => {
StatusManager.show('Keys copied to clipboard!', 'success');
}).catch(err => {
console.error('Copy failed:', err);
StatusManager.show('Failed to copy keys', 'error');
});
}
navigator.clipboard.writeText(textarea.value).then(() => {
StatusManager.show('Keys copied to clipboard!', 'success');
}).catch(err => {
console.error('Copy failed:', err);
StatusManager.show('Failed to copy keys', 'error');
});
}
static updateAfterKeyExtraction() {
// Auto-refresh MPD detection after key extraction
setTimeout(() => {
MPDDetector.updateDropdowns();
}, 1000);
}
static updateAfterKeyExtraction() {
// Auto-refresh MPD detection after key extraction
setTimeout(() => {
MPDDetector.updateDropdowns();
}, 1000);
}
static loadSavedSettings() {
chrome.storage.local.get(['crawlFlixUrl'], (result) => {
if (result.crawlFlixUrl) {
const inputs = ['crawlFlixUrl', 'crawlFlixUrlCK'];
inputs.forEach(id => {
const input = document.getElementById(id);
if (input && !input.value) {
input.value = result.crawlFlixUrl;
}
});
}
});
}
static loadSavedSettings() {
chrome.storage.local.get(['crawlFlixUrl'], (result) => {
if (result.crawlFlixUrl) {
const inputs = ['crawlFlixUrl', 'crawlFlixUrlCK'];
inputs.forEach(id => {
const input = document.getElementById(id);
if (input && !input.value) {
input.value = result.crawlFlixUrl;
}
});
}
});
}
static saveSettings() {
const inputs = ['crawlFlixUrl', 'crawlFlixUrlCK'];
inputs.forEach(id => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('change', (e) => {
chrome.storage.local.set({ crawlFlixUrl: e.target.value });
});
}
});
}
static saveSettings() {
const inputs = ['crawlFlixUrl', 'crawlFlixUrlCK'];
inputs.forEach(id => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('change', (e) => {
chrome.storage.local.set({ crawlFlixUrl: e.target.value });
});
}
});
}
}
// === STATUS MANAGER ===
class StatusManager {
static show(message, type, suffix = '') {
const statusDiv = document.getElementById(`crawlFlixStatus${suffix}`);
if (!statusDiv) return;
static show(message, type, suffix = '') {
const statusDiv = document.getElementById(`crawlFlixStatus${suffix}`);
if (!statusDiv) return;
// Clear any existing content
statusDiv.innerHTML = '';
// Clear any existing content
statusDiv.innerHTML = '';
// Create alert element
const alert = document.createElement('div');
alert.className = `alert alert-${this.getBootstrapClass(type)} alert-dismissible fade show`;
alert.innerHTML = `
<i class="fas ${this.getIcon(type)} me-2"></i>
${message.replace(/\n/g, '<br>')}
<button type="button" class="btn-close btn-close-sm" data-bs-dismiss="alert"></button>
`;
// Create alert element
const alert = document.createElement('div');
alert.className = `alert alert-${this.getBootstrapClass(type)} alert-dismissible fade show`;
alert.innerHTML = `
<i class="fas ${this.getIcon(type)} me-2"></i>
${message.replace(/\n/g, '<br>')}
<button type="button" class="btn-close btn-close-sm" data-bs-dismiss="alert"></button>
`;
statusDiv.appendChild(alert);
statusDiv.appendChild(alert);
// Auto-dismiss after delay
if (type === 'success' || type === 'info') {
setTimeout(() => {
if (alert.parentNode) {
alert.classList.remove('show');
setTimeout(() => alert.remove(), 300);
}
}, 5000);
}
}
// Auto-dismiss after delay
if (type === 'success' || type === 'info') {
setTimeout(() => {
if (alert.parentNode) {
alert.classList.remove('show');
setTimeout(() => alert.remove(), 300);
}
}, 5000);
}
}
static getBootstrapClass(type) {
const mapping = {
success: 'success',
error: 'danger',
info: 'info',
warning: 'warning'
};
return mapping[type] || 'secondary';
}
static getBootstrapClass(type) {
const mapping = {
success: 'success',
error: 'danger',
info: 'info',
warning: 'warning'
};
return mapping[type] || 'secondary';
}
static getIcon(type) {
const mapping = {
success: 'fa-check-circle',
error: 'fa-exclamation-triangle',
info: 'fa-info-circle',
warning: 'fa-exclamation-triangle'
};
return mapping[type] || 'fa-info';
}
static getIcon(type) {
const mapping = {
success: 'fa-check-circle',
error: 'fa-exclamation-triangle',
info: 'fa-info-circle',
warning: 'fa-exclamation-triangle'
};
return mapping[type] || 'fa-info';
}
}
// === UI HELPERS ===
class UIHelpers {
static setLoadingState(button, isLoading) {
if (!button) return;
static setLoadingState(button, isLoading) {
if (!button) return;
if (isLoading) {
button.disabled = true;
const originalText = button.innerHTML;
button.dataset.originalText = originalText;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Extracting Keys...';
} else {
button.disabled = false;
button.innerHTML = button.dataset.originalText || button.innerHTML;
}
}
if (isLoading) {
button.disabled = true;
const originalText = button.innerHTML;
button.dataset.originalText = originalText;
button.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Extracting Keys...';
} else {
button.disabled = false;
button.innerHTML = '<i class="fas fa-magic me-2"></i> Extract Widevine Keys';
}
}
static copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
static copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
}
// === EVENT LISTENERS ===
class EventManager {
static init() {
this.setupWidevineListeners();
this.setupCrawlFlixListeners();
this.setupUIListeners();
}
static init() {
this.setupWidevineListeners();
this.setupCrawlFlixListeners();
this.setupUIListeners();
}
static setupWidevineListeners() {
const guessButton = document.getElementById('guess');
const resultTextarea = document.getElementById('result');
static setupWidevineListeners() {
const guessButton = document.getElementById('guess');
const resultTextarea = document.getElementById('result');
if (guessButton) {
guessButton.addEventListener('click', () => WidevineExtractor.extractKeys());
}
if (guessButton) {
guessButton.addEventListener('click', () => WidevineExtractor.extractKeys());
}
if (resultTextarea) {
resultTextarea.addEventListener('click', function() {
this.select();
navigator.clipboard.writeText(this.value);
});
}
}
if (resultTextarea) {
resultTextarea.addEventListener('click', function() {
this.select();
navigator.clipboard.writeText(this.value);
});
}
}
static setupCrawlFlixListeners() {
// Dropdown selections
['mpdSelect', 'mpdSelectCK'].forEach(selectId => {
const select = document.getElementById(selectId);
const urlInput = document.getElementById(selectId.replace('Select', 'Url'));
if (select && urlInput) {
select.addEventListener('change', (e) => {
if (e.target.value) {
urlInput.value = e.target.value;
}
});
}
});
static setupCrawlFlixListeners() {
// Dropdown selections
['mpdSelect', 'mpdSelectCK'].forEach(selectId => {
const select = document.getElementById(selectId);
const urlInput = document.getElementById(selectId.replace('Select', 'Url'));
if (select && urlInput) {
select.addEventListener('change', (e) => {
if (e.target.value) {
urlInput.value = e.target.value;
}
});
}
});
// Refresh buttons
['refreshMPDs', 'refreshMPDsCK'].forEach(buttonId => {
const button = document.getElementById(buttonId);
if (button) {
button.addEventListener('click', () => MPDDetector.updateDropdowns());
}
});
// Refresh buttons
['refreshMPDs', 'refreshMPDsCK'].forEach(buttonId => {
const button = document.getElementById(buttonId);
if (button) {
button.addEventListener('click', () => MPDDetector.updateDropdowns());
}
});
// Send buttons
const sendButton = document.getElementById('sendToCrawlFlix');
if (sendButton) {
sendButton.addEventListener('click', () => CrawlFlixIntegration.sendToCrawlFlix(false));
}
// Send buttons
const sendButton = document.getElementById('sendToCrawlFlix');
if (sendButton) {
sendButton.addEventListener('click', () => CrawlFlixIntegration.sendToCrawlFlix(false));
}
const sendButtonCK = document.getElementById('sendToCrawlFlixCK');
if (sendButtonCK) {
sendButtonCK.addEventListener('click', () => CrawlFlixIntegration.sendToCrawlFlix(true));
}
const sendButtonCK = document.getElementById('sendToCrawlFlixCK');
if (sendButtonCK) {
sendButtonCK.addEventListener('click', () => CrawlFlixIntegration.sendToCrawlFlix(true));
}
// Copy buttons
const copyButton = document.getElementById('copyKeys');
if (copyButton) {
copyButton.addEventListener('click', () => CrawlFlixIntegration.copyKeys('result'));
}
// Copy buttons
const copyButton = document.getElementById('copyKeys');
if (copyButton) {
copyButton.addEventListener('click', () => CrawlFlixIntegration.copyKeys('result'));
}
const copyButtonCK = document.getElementById('copyKeysCK');
if (copyButtonCK) {
copyButtonCK.addEventListener('click', () => CrawlFlixIntegration.copyKeys('ckResult'));
}
}
const copyButtonCK = document.getElementById('copyKeysCK');
if (copyButtonCK) {
copyButtonCK.addEventListener('click', () => CrawlFlixIntegration.copyKeys('ckResult'));
}
}
static setupUIListeners() {
// ClearKey result click handler
const ckResult = document.getElementById('ckResult');
if (ckResult) {
ckResult.addEventListener('click', function() {
this.select();
navigator.clipboard.writeText(this.value);
});
}
}
static setupUIListeners() {
// ClearKey result click handler
const ckResult = document.getElementById('ckResult');
if (ckResult) {
ckResult.addEventListener('click', function() {
this.select();
navigator.clipboard.writeText(this.value);
});
}
}
}
// === CORS FETCH HELPER ===
window.corsFetch = (u, m, h, b) => {
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage(targetIds[0], {
type: "FETCH",
u: u,
m: m,
h: h,
b: b
}, {frameId: targetIds[1]}, res => {
resolve(res);
});
});
return new Promise((resolve, reject) => {
chrome.tabs.sendMessage(targetIds[0], {
type: "FETCH",
u: u,
m: m,
h: h,
b: b
}, {frameId: targetIds[1]}, res => {
resolve(res);
});
});
};
// === INITIALIZATION ===
document.addEventListener('DOMContentLoaded', () => {
EventManager.init();
CrawlFlixIntegration.loadSavedSettings();
CrawlFlixIntegration.saveSettings();
EventManager.init();
CrawlFlixIntegration.loadSavedSettings();
CrawlFlixIntegration.saveSettings();
});
// Main initialization logic
if (clearkey) {
// ClearKey detected
document.getElementById('noEME').style.display = 'none';
document.getElementById('ckHome').style.display = 'block';
document.getElementById('ckResult').value = clearkey;
// ClearKey detected
document.getElementById('noEME').style.display = 'none';
document.getElementById('ckHome').style.display = 'block';
document.getElementById('ckResult').value = clearkey;
MPDDetector.updateDropdowns();
StatusManager.show('ClearKey content detected', 'success');
MPDDetector.updateDropdowns();
StatusManager.show('ClearKey content detected', 'success');
MPDDetector.updateBadgeFromPopup();
} else if (psshs.length) {
// Widevine detected
document.getElementById('noEME').style.display = 'none';
document.getElementById('home').style.display = 'block';
// Widevine detected
document.getElementById('noEME').style.display = 'none';
document.getElementById('home').style.display = 'block';
WidevineExtractor.autoSelect();
MPDDetector.updateDropdowns();
StatusManager.show('Widevine content detected', 'success');
MPDDetector.updateBadgeFromPopup();
// Auto-refresh MPD detection periodically
setInterval(() => {
MPDDetector.updateDropdowns();
}, 3000);
// IMPORTANT: Faire autoSelect avant d'initialiser les event listeners
WidevineExtractor.autoSelect().then(() => {
EventManager.init();
MPDDetector.updateDropdowns();
StatusManager.show('Widevine content detected and configured', 'success');
});
// Auto-refresh MPD detection periodically
setInterval(() => {
MPDDetector.updateDropdowns();
}, 3000);
}