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 <i class="fas fa-server"></i> CrawlFlix Server
</label> </label>
<input type="text" id="crawlFlixUrl" class="form-control" <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> </div>
<div class="col-md-6"> <div class="col-md-6">
@@ -237,7 +237,7 @@
<div class="col-12"> <div class="col-12">
<label for="crawlFlixUrlCK" class="form-label">CrawlFlix Server</label> <label for="crawlFlixUrlCK" class="form-label">CrawlFlix Server</label>
<input type="text" id="crawlFlixUrlCK" class="form-control form-control-sm" <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>
</div> </div>

View File

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