Advanced_DRM_Player/dazn_handler.js

372 lines
16 KiB
JavaScript

const DAZN_API_PLAYBACK = 'https://api.playback.indazn.com/v5/Playback';
const DAZN_API_REFRESH_TOKEN = 'https://ott-authz-bff-prod.ar.indazn.com/v5/RefreshAccessToken';
const DAZN_API_RAIL = 'https://rail-router.discovery.indazn.com/eu/v7/Rail';
const DAZN_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';
let daznTokenPromiseResolver = null;
function decodeJwtPayload(token) {
if (!token || typeof token !== 'string') return null;
try {
const parts = token.split('.');
if (parts.length !== 3) return null;
const payloadBase64 = parts[1];
if (!payloadBase64) return null;
const decoded = atob(payloadBase64.replace(/-/g, '+').replace(/_/g, '/'));
return JSON.parse(decoded);
} catch (e) {
return null;
}
}
function showDaznTokenModal() {
return new Promise((resolve) => {
daznTokenPromiseResolver = resolve;
try {
const daznModalEl = document.getElementById('daznTokenModal');
if (!daznModalEl) {
resolve({ token: null, remember: false });
daznTokenPromiseResolver = null;
return;
}
const daznModal = bootstrap.Modal.getOrCreateInstance(daznModalEl, { backdrop: 'static', keyboard: false });
$('#daznTokenModalInput').val('');
$('#daznRememberTokenCheck').prop('checked', true);
daznModal.show();
} catch (e) {
resolve({ token: null, remember: false });
daznTokenPromiseResolver = null;
}
});
}
$(document).on('click', '#submitDaznTokenBtn', async () => {
const tokenInput = $('#daznTokenModalInput').val();
const rememberToken = $('#daznRememberTokenCheck').is(':checked');
const currentResolver = daznTokenPromiseResolver;
daznTokenPromiseResolver = null;
if (currentResolver) {
currentResolver({ token: tokenInput, remember: rememberToken });
}
const daznModalEl = document.getElementById('daznTokenModal');
if (daznModalEl) {
const daznModalInstance = bootstrap.Modal.getInstance(daznModalEl);
if (daznModalInstance) {
daznModalInstance.hide();
}
}
});
$(document).on('click', '#cancelDaznTokenBtn', () => {
const currentResolver = daznTokenPromiseResolver;
daznTokenPromiseResolver = null;
if (currentResolver) {
currentResolver({ token: null, remember: false });
}
const daznModalEl = document.getElementById('daznTokenModal');
if (daznModalEl) {
const daznModalInstance = bootstrap.Modal.getInstance(daznModalEl);
if (daznModalInstance) {
daznModalInstance.hide();
}
}
});
async function getDaznTokenFromUserInputOrSettings() {
let token = daznAuthTokenState;
if (!token) {
if (typeof showLoading === 'function') showLoading(true, 'Esperando token DAZN...');
const modalResult = await showDaznTokenModal();
if (typeof showLoading === 'function') showLoading(false);
const userInputToken = modalResult.token;
const remember = modalResult.remember;
if (userInputToken && userInputToken.trim() !== '') {
daznAuthTokenState = userInputToken.trim();
if (remember && typeof saveAppConfigValue === 'function' && typeof DAZN_TOKEN_DB_KEY !== 'undefined') {
try {
await saveAppConfigValue(DAZN_TOKEN_DB_KEY, daznAuthTokenState);
if (typeof showNotification === 'function') showNotification('DAZN: Token guardado.', 'success');
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
} catch (e) {
if(typeof showNotification === 'function') showNotification('DAZN: Error al guardar token en BD.', 'error');
}
} else if (!remember && typeof deleteAppConfigValue === 'function' && typeof DAZN_TOKEN_DB_KEY !== 'undefined') {
try {
const existingToken = await getAppConfigValue(DAZN_TOKEN_DB_KEY);
if (existingToken) {
await deleteAppConfigValue(DAZN_TOKEN_DB_KEY);
}
} catch (e) { console.warn("DAZN: Error revisando/borrando token de BD al no recordar", e); }
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
}
return daznAuthTokenState;
}
if (typeof showNotification === 'function' && !userInputToken) showNotification('DAZN: Operación de token cancelada o token vacío.', 'info');
return null;
}
return token;
}
async function refreshDaznToken(currentToken, specificUserAgent = null) {
if (!currentToken) return null;
if (typeof showLoading === 'function') showLoading(true, 'Refrescando token DAZN...');
const userAgentToUse = specificUserAgent || DAZN_USER_AGENT;
try {
const decoded = decodeJwtPayload(currentToken);
if (!decoded || !decoded.deviceId) {
if (typeof showNotification === 'function') showNotification('DAZN: Token inválido o no se pudo decodificar deviceId para refrescar.', 'error');
daznAuthTokenState = null;
if (typeof deleteAppConfigValue === 'function' && typeof DAZN_TOKEN_DB_KEY !== 'undefined') {
try { await deleteAppConfigValue(DAZN_TOKEN_DB_KEY); } catch (e) { }
}
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
return null;
}
const device_id_suffix = decoded.deviceId.split('-').pop();
if (!device_id_suffix) {
if (typeof showNotification === 'function') showNotification('DAZN: DeviceId con formato inesperado.', 'error');
return null;
}
const response = await fetch(DAZN_API_REFRESH_TOKEN, {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Authorization': `Bearer ${currentToken}`,
'Content-Type': 'application/json',
'User-Agent': userAgentToUse
},
body: JSON.stringify({ DeviceId: device_id_suffix })
});
if (!response.ok) {
const errorText = await response.text().catch(() => `HTTP ${response.status}`);
if (response.status === 401 || response.status === 403) {
daznAuthTokenState = null;
if (typeof deleteAppConfigValue === 'function' && typeof DAZN_TOKEN_DB_KEY !== 'undefined') {
try { await deleteAppConfigValue(DAZN_TOKEN_DB_KEY); } catch (e) { }
}
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
if (typeof showNotification === 'function') showNotification('DAZN: Token inválido/expirado. Se solicitará uno nuevo al intentar la operación.', 'warning');
return null;
}
throw new Error(`Error ${response.status} al refrescar token: ${errorText.substring(0,100)}`);
}
const data = await response.json();
const newToken = data?.AuthToken?.Token;
if (newToken) {
daznAuthTokenState = newToken;
const storedTokenShouldBeRemembered = await getAppConfigValue(DAZN_TOKEN_DB_KEY);
if (storedTokenShouldBeRemembered && typeof saveAppConfigValue === 'function' && typeof DAZN_TOKEN_DB_KEY !== 'undefined') {
try {
await saveAppConfigValue(DAZN_TOKEN_DB_KEY, daznAuthTokenState);
if (typeof showNotification === 'function') showNotification('DAZN: Token refrescado y guardado exitosamente.', 'success');
} catch (e) {
if (typeof showNotification === 'function') showNotification('DAZN: Token refrescado, pero error al guardar en BD.', 'warning');
}
} else if (storedTokenShouldBeRemembered === null) {
if (typeof showNotification === 'function') showNotification('DAZN: Token refrescado (sesión actual).', 'info');
}
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
return newToken;
} else {
throw new Error('Respuesta de refresco de token no contenía un nuevo token.');
}
} catch (error) {
if (typeof showNotification === 'function') showNotification(`DAZN: Error refrescando token: ${error.message}`, 'error');
return null;
} finally {
if (typeof showLoading === 'function') showLoading(false);
}
}
async function getActiveDaznToken(forceUserInputIfInvalid = false, specificUserAgent = null) {
let currentToken = daznAuthTokenState;
if (!currentToken) {
currentToken = await getDaznTokenFromUserInputOrSettings();
if (!currentToken) {
return null;
}
return currentToken;
}
const refreshedToken = await refreshDaznToken(currentToken, specificUserAgent);
if (refreshedToken) {
return refreshedToken;
} else {
if (forceUserInputIfInvalid) {
if (typeof showNotification === 'function') showNotification('DAZN: El token actual no pudo ser refrescado. Se solicitará uno nuevo.', 'warning');
daznAuthTokenState = null;
currentToken = await getDaznTokenFromUserInputOrSettings();
if (!currentToken) {
return null;
}
return currentToken;
} else {
if (typeof showNotification === 'function') showNotification('DAZN: El token actual no pudo ser refrescado y no se forzó la entrada de uno nuevo. Usando token anterior si existe.', 'warning');
return currentToken;
}
}
}
async function fetchSingleDaznAssetDetails(authToken, assetId, specificUserAgent = null) {
const decodedToken = decodeJwtPayload(authToken);
if (!decodedToken || !decodedToken.deviceId) {
if (typeof showNotification === 'function') showNotification('DAZN: No se pudo decodificar deviceId del token activo.', 'error');
return null;
}
const userAgentToUse = specificUserAgent || DAZN_USER_AGENT;
const params = new URLSearchParams({
'AppVersion': '0.60.0',
'DrmType': 'WIDEVINE',
'Format': 'MPEG-DASH',
'PlayerId': '@dazn/peng-html5-core/web/web',
'Platform': 'web',
'LanguageCode': 'es',
'Model': 'unknown',
'Secure': 'true',
'Manufacturer': 'microsoft',
'PlayReadyInitiator': 'false',
'Capabilities': 'mta',
'AssetId': assetId
});
try {
const response = await fetch(`${DAZN_API_PLAYBACK}?${params.toString()}`, {
headers: {
'Authorization': `Bearer ${authToken}`,
'User-Agent': userAgentToUse,
'x-dazn-device': decodedToken.deviceId
}
});
if (!response.ok) {
const errorText = await response.text().catch(() => `HTTP ${response.status}`);
throw new Error(`HTTP ${response.status} para AssetId ${assetId}`);
}
const data = await response.json();
const playbackDetails = data?.PlaybackDetails?.[0];
if (!playbackDetails || !playbackDetails.ManifestUrl || !playbackDetails.CdnToken) {
return null;
}
const manifestUrl = playbackDetails.ManifestUrl;
const cdnToken = playbackDetails.CdnToken;
const linearIdMatch = manifestUrl.match(/dazn-linear-(\d+)/);
const daznLinearId = linearIdMatch ? linearIdMatch[1] : null;
return {
assetId: assetId,
title: null,
baseUrl: manifestUrl,
cdnTokenName: cdnToken.Name,
cdnTokenValue: cdnToken.Value,
daznLinearId: daznLinearId,
streamUserAgent: userAgentToUse
};
} catch (error) {
return null;
}
}
async function fetchDaznRailDataAndChannelDetails(authToken, specificUserAgent = null) {
if (typeof showLoading === 'function') showLoading(true, 'Obteniendo canales DAZN...');
const userAgentToUse = specificUserAgent || DAZN_USER_AGENT;
const params = new URLSearchParams({
'id': 'LiveAndNextNew',
'params': 'PageType:Home;ContentType:None',
'languageCode': 'es',
'country': 'es',
'platform': 'web'
});
try {
const response = await fetch(`${DAZN_API_RAIL}?${params.toString()}`, {
headers: { 'User-Agent': userAgentToUse }
});
if (!response.ok) {
const errorText = await response.text().catch(() => `HTTP ${response.status}`);
throw new Error(`HTTP ${response.status} al obtener Rail data`);
}
const railData = await response.json();
const daznChannelsInfo = [];
const assetPromises = [];
if (railData && railData.Tiles) {
for (const tile of railData.Tiles) {
if (tile.Type === 'Live' && tile.Label === 'Canal en directo' && tile.AssetId) {
assetPromises.push(
fetchSingleDaznAssetDetails(authToken, tile.AssetId, userAgentToUse)
.then(details => {
if (details) {
details.title = tile.Title;
return details;
}
return null;
})
);
}
}
}
const resolvedAssets = await Promise.all(assetPromises);
resolvedAssets.forEach(asset => {
if (asset) {
daznChannelsInfo.push(asset);
}
});
if (daznChannelsInfo.length === 0 && typeof showNotification === 'function') {
showNotification('DAZN: No se encontraron canales en directo desde la API.', 'warning');
}
return daznChannelsInfo;
} catch (error) {
if (typeof showNotification === 'function') showNotification(`DAZN: Error obteniendo lista de canales: ${error.message}`, 'error');
return [];
} finally {
if (typeof showLoading === 'function') showLoading(false);
}
}
async function orchestrateDaznUpdate(specificUserAgent = null) {
if (typeof showLoading === 'function') showLoading(true, 'Iniciando actualización DAZN...');
try {
const authToken = await getActiveDaznToken(true, specificUserAgent);
if (!authToken) {
if (typeof populateUserSettingsForm === 'function') populateUserSettingsForm();
return;
}
const daznChannelDetailsList = await fetchDaznRailDataAndChannelDetails(authToken, specificUserAgent);
if (daznChannelDetailsList && daznChannelDetailsList.length > 0) {
if (typeof window.updateM3UWithDaznData === 'function') {
window.updateM3UWithDaznData(daznChannelDetailsList);
} else {
if (typeof showNotification === 'function') showNotification('DAZN: Error interno, no se pudo actualizar M3U.', 'error');
}
} else if (daznChannelDetailsList) {
if (typeof showNotification === 'function') showNotification('DAZN: No se obtuvieron detalles de canales para actualizar.', 'info');
}
} catch (error) {
if (typeof showNotification === 'function') showNotification(`DAZN: Error general durante la actualización: ${error.message}`, 'error');
} finally {
if (typeof showLoading === 'function') showLoading(false);
}
}