Advanced_DRM_Player/m3u_operations.js

319 lines
14 KiB
JavaScript

async function loadUrl(url, sourceOrigin = null) {
showLoading(true, 'Cargando lista desde URL...');
if (typeof hideXtreamInfoBar === 'function') hideXtreamInfoBar();
if (!sourceOrigin) {
channels = [];
currentGroupOrder = [];
currentM3UName = null;
}
try {
const response = await fetch(url);
if (!response.ok) {
const errorBody = await response.text().catch(() => '');
throw new Error(`HTTP ${response.status} - ${response.statusText}${errorBody ? ': ' + errorBody.substring(0,100)+'...' : ''}`);
}
const content = await response.text();
if (!content || content.trim() === '') throw new Error('Lista vacía o inaccesible.');
const effectiveSourceName = sourceOrigin || url;
processM3UContent(content, effectiveSourceName, !sourceOrigin);
if(userSettings.autoSaveM3U && !sourceOrigin) {
await saveAppConfigValue('lastM3UUrl', url);
await deleteAppConfigValue('lastM3UFileContent');
await deleteAppConfigValue('lastM3UFileName');
await deleteAppConfigValue('currentXtreamServerInfo');
}
showNotification(`Lista cargada desde URL (${channels.length} canales).`, 'success');
} catch (err) {
showNotification(`Error cargando URL: ${err.message}`, 'error');
if (!sourceOrigin) {
channels = []; currentM3UContent = null; currentM3UName = null; currentGroupOrder = [];
filterAndRenderChannels();
}
} finally { showLoading(false); }
}
function loadFile(event) {
const file = event.target.files[0]; if (!file) return;
showLoading(true, `Leyendo archivo "${escapeHtml(file.name)}"...`);
if (typeof hideXtreamInfoBar === 'function') hideXtreamInfoBar();
channels = [];
currentGroupOrder = [];
currentM3UName = null;
const reader = new FileReader();
reader.onload = async (e) => {
try {
const content = e.target.result;
if (!content || content.trim() === '') throw new Error('Archivo vacío.');
processM3UContent(content, file.name, true);
if (userSettings.autoSaveM3U) {
if (content.length < 4 * 1024 * 1024) {
await saveAppConfigValue('lastM3UFileContent', content);
await saveAppConfigValue('lastM3UFileName', currentM3UName);
await deleteAppConfigValue('lastM3UUrl');
await deleteAppConfigValue('currentXtreamServerInfo');
} else {
showNotification('Archivo local grande (>4MB), no se guardará para recarga automática.', 'info');
await deleteAppConfigValue('lastM3UFileContent');
await deleteAppConfigValue('lastM3UFileName');
await deleteAppConfigValue('lastM3UUrl');
await deleteAppConfigValue('currentXtreamServerInfo');
}
}
showNotification(`Lista "${escapeHtml(file.name)}" cargada (${channels.length} canales).`, 'success');
} catch (err) {
showNotification(`Error procesando archivo: ${err.message}`, 'error');
channels = []; currentM3UContent = null; currentM3UName = null; currentGroupOrder = [];
filterAndRenderChannels();
} finally { showLoading(false); $('#fileInput').val(''); }
};
reader.onerror = (e) => {
showNotification('Error al leer archivo: ' + e.target.error, 'error');
showLoading(false); $('#fileInput').val('');
};
reader.readAsText(file);
}
function processM3UContent(content, sourceName, isFullLoad = false) {
currentM3UContent = content;
if (isFullLoad) {
if (sourceName.startsWith('http')) {
try {
const url = new URL(sourceName);
currentM3UName = url.pathname.split('/').pop() || url.search.substring(1) || url.hostname || 'lista_url';
currentM3UName = decodeURIComponent(currentM3UName).replace(/\.(m3u8?|txt|pls|m3uplus)$/i, '').replace(/[\/\\]/g,'_');
if (!currentM3UName || currentM3UName.length > 50) currentM3UName = currentM3UName.substring(0, 47) + '...';
if(currentM3UName.length === 0) currentM3UName = 'lista_remota';
} catch(e) { currentM3UName = 'lista_url_malformada'; }
} else {
currentM3UName = sourceName.replace(/\.(m3u8?|txt|pls|m3uplus)$/i, '').replace(/[\/\\]/g,'_');
if (!currentM3UName || currentM3UName.length > 50) currentM3UName = currentM3UName.substring(0, 47) + '...';
if(currentM3UName.length === 0) currentM3UName = 'lista_local';
}
if (channels.length > 0 || currentGroupOrder.length > 0) {
channels = [];
currentGroupOrder = [];
}
}
const parseResult = typeof parseM3U === 'function' ? parseM3U(content, sourceName) : { channels: [], groupOrder: [] };
channels.push(...parseResult.channels);
const existingGroupsSet = new Set(currentGroupOrder);
parseResult.groupOrder.forEach(group => {
if (!existingGroupsSet.has(group)) {
currentGroupOrder.push(group);
}
});
const allCurrentGroups = new Set(channels.map(c => c['group-title']).filter(Boolean));
currentGroupOrder = currentGroupOrder.filter(g => allCurrentGroups.has(g));
allCurrentGroups.forEach(g => {
if (!currentGroupOrder.includes(g)) currentGroupOrder.push(g);
});
currentPage = 1;
if (typeof matchChannelsWithEpg === 'function') {
matchChannelsWithEpg();
}
let initialGroupToSelect = "";
if (userSettings.persistFilters && userSettings.lastSelectedGroup && currentGroupOrder.includes(userSettings.lastSelectedGroup)) {
initialGroupToSelect = userSettings.lastSelectedGroup;
}
$('#groupFilterSidebar').val(initialGroupToSelect);
filterAndRenderChannels();
if (channels.length === 0) {
showNotification(`No se encontraron canales válidos en "${escapeHtml(currentM3UName || sourceName)}". Revisa el formato del M3U.`, 'warning');
}
}
function removeChannelsBySourceOrigin(originToRemove) {
if (!originToRemove) return;
const originalChannelCount = channels.length;
channels = channels.filter(channel => channel.sourceOrigin !== originToRemove);
const channelsRemovedCount = originalChannelCount - channels.length;
if (channelsRemovedCount > 0) {
if (channels.length > 0) {
regenerateCurrentM3UContentFromString();
} else {
currentM3UContent = null;
currentM3UName = null;
}
const activeGroups = new Set(channels.map(ch => ch['group-title']));
currentGroupOrder = currentGroupOrder.filter(group => activeGroups.has(group));
}
}
async function appendM3UContent(newM3UString, sourceNameForAppend) {
showLoading(true, `Agregando canales de ${escapeHtml(sourceNameForAppend)}...`);
const parseResult = typeof parseM3U === 'function' ? parseM3U(newM3UString, sourceNameForAppend) : { channels: [], groupOrder: [] };
const newChannelsFromAppend = parseResult.channels;
const newGroupOrderFromAppend = parseResult.groupOrder;
const wasChannelsEmpty = channels.length === 0;
if (newChannelsFromAppend.length === 0) {
showNotification(`No se encontraron canales válidos en ${escapeHtml(sourceNameForAppend)} para agregar.`, 'warning');
showLoading(false);
if (wasChannelsEmpty) {
currentM3UName = null;
currentM3UContent = null;
currentGroupOrder = [];
if (typeof filterAndRenderChannels === 'function') filterAndRenderChannels();
}
return;
}
if (wasChannelsEmpty) {
channels = newChannelsFromAppend;
currentGroupOrder = newGroupOrderFromAppend;
currentM3UContent = newM3UString;
currentM3UName = sourceNameForAppend;
} else {
channels.push(...newChannelsFromAppend);
const existingGroupsSet = new Set(currentGroupOrder);
newGroupOrderFromAppend.forEach(group => {
if (!existingGroupsSet.has(group)) {
currentGroupOrder.push(group);
}
});
const allCurrentGroups = new Set(channels.map(c => c['group-title']).filter(Boolean));
currentGroupOrder = currentGroupOrder.filter(g => allCurrentGroups.has(g));
allCurrentGroups.forEach(g => {
if (!currentGroupOrder.includes(g)) currentGroupOrder.push(g);
});
await regenerateCurrentM3UContentFromString();
}
currentPage = 1;
if (typeof matchChannelsWithEpg === 'function') {
matchChannelsWithEpg();
}
filterAndRenderChannels();
let notificationMessage;
const addedOrLoaded = wasChannelsEmpty ? 'cargados' : 'agregados/actualizados';
notificationMessage = `${newChannelsFromAppend.length} canales de ${escapeHtml(sourceNameForAppend)} ${addedOrLoaded}.`;
if (userSettings.autoSaveM3U) {
if (currentM3UContent && currentM3UContent.length < 4 * 1024 * 1024) {
await saveAppConfigValue('lastM3UFileContent', currentM3UContent);
await saveAppConfigValue('lastM3UFileName', currentM3UName);
await deleteAppConfigValue('lastM3UUrl');
if (currentM3UName && !currentM3UName.startsWith('Xtream:')) {
await deleteAppConfigValue('currentXtreamServerInfo');
}
else if (!sourceNameForAppend.startsWith('Xtream:') && await getAppConfigValue('currentXtreamServerInfo')) {
await deleteAppConfigValue('currentXtreamServerInfo');
}
notificationMessage += " Lista actual guardada para recarga automática.";
} else if (currentM3UContent) {
await deleteAppConfigValue('lastM3UFileContent');
await deleteAppConfigValue('lastM3UFileName');
await deleteAppConfigValue('lastM3UUrl');
await deleteAppConfigValue('currentXtreamServerInfo');
notificationMessage += " Lista actual demasiado grande, no se guardó para recarga automática.";
}
}
showNotification(notificationMessage, 'success');
showLoading(false);
}
async function regenerateCurrentM3UContentFromString() {
if (!channels || channels.length === 0) {
currentM3UContent = null;
return;
}
let newM3U = "#EXTM3U\n";
channels.forEach(ch => {
let extinfLine = `#EXTINF:${ch.attributes?.duration || -1}`;
const tempAttrs = {...ch.attributes};
delete tempAttrs.duration;
if (ch['tvg-id']) tempAttrs['tvg-id'] = ch['tvg-id'];
if (ch['tvg-name']) tempAttrs['tvg-name'] = ch['tvg-name'];
if (ch['tvg-logo']) tempAttrs['tvg-logo'] = ch['tvg-logo'];
if (ch['group-title']) tempAttrs['group-title'] = ch['group-title'];
if (ch.attributes && ch.attributes['ch-number']) tempAttrs['ch-number'] = ch.attributes['ch-number'];
if (ch.sourceOrigin) tempAttrs['source-origin'] = ch.sourceOrigin;
for (const key in tempAttrs) {
if (tempAttrs[key] || typeof tempAttrs[key] === 'number') {
extinfLine += ` ${key}="${tempAttrs[key]}"`;
}
}
extinfLine += `,${ch.name}\n`;
newM3U += extinfLine;
if (ch.kodiProps) {
Object.entries(ch.kodiProps).forEach(([key, value]) => {
newM3U += `#KODIPROP:${key}=${value}\n`;
});
}
if (ch.vlcOptions) {
Object.entries(ch.vlcOptions).forEach(([key, value]) => {
if (key === 'description' && value) {
newM3U += `#EXTVLCOPT:description=${value.replace(/[\n\r]+/g, ' ').replace(/"/g, "'")}\n`;
} else {
newM3U += `#EXTVLCOPT:${key}=${value}\n`;
}
});
}
if (ch.extHttp && Object.keys(ch.extHttp).length > 0) {
newM3U += `#EXTHTTP:${JSON.stringify(ch.extHttp)}\n`;
}
newM3U += `${ch.url}\n`;
});
currentM3UContent = newM3U;
if (userSettings.autoSaveM3U && currentM3UContent && currentM3UName) {
if (currentM3UContent.length < 4 * 1024 * 1024) {
await saveAppConfigValue('lastM3UFileContent', currentM3UContent);
await saveAppConfigValue('lastM3UFileName', currentM3UName);
await deleteAppConfigValue('lastM3UUrl');
if (currentM3UName && !currentM3UName.startsWith('Xtream:')) {
await deleteAppConfigValue('currentXtreamServerInfo');
}
} else {
showNotification("Lista M3U actualizada es muy grande (>4MB), no se guardará para recarga automática.", "warning");
await deleteAppConfigValue('lastM3UFileContent');
await deleteAppConfigValue('lastM3UFileName');
await deleteAppConfigValue('lastM3UUrl');
await deleteAppConfigValue('currentXtreamServerInfo');
}
}
}
function downloadCurrentM3U() {
if (!currentM3UContent) {
showNotification('No hay lista M3U cargada para descargar.', 'info');
return;
}
const fileName = (currentM3UName ? currentM3UName.replace(/\.\.\.$/, '') : 'lista_player') + '.m3u';
const blob = new Blob([currentM3UContent], { type: 'audio/mpegurl;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showNotification(`Descargando lista como "${escapeHtml(fileName)}"`, 'success');
}