let userSettings = { language: 'en', enableEpgNameMatching: false, epgNameMatchThreshold: 0.80, sidebarCollapsed: window.innerWidth < 992, persistFilters: true, lastSelectedGroup: "", lastSelectedFilterTab: "all", playerBuffer: 30, preferredAudioLanguage: 'es', preferredTextLanguage: 'off', lowLatencyMode: true, liveCatchUpMode: false, globalUserAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', globalReferrer: '', additionalGlobalHeaders: '{}', channelCardSize: 180, persistentControls: false, maxVideoHeight: 0, autoSaveM3U: true, defaultEpgUrl: 'https://raw.githubusercontent.com/davidmuma/EPG_dobleM/refs/heads/master/EPG_dobleM.xml', lastEpgUrl: '', abrEnabled: true, abrDefaultBandwidthEstimate: 1000, streamingJumpLargeGaps: false, manifestRetryMaxAttempts: 2, manifestRetryTimeout: 15000, segmentRetryMaxAttempts: 2, segmentRetryTimeout: 15000, shakaDefaultPresentationDelay: 5, shakaAudioVideoSyncThreshold: 0.25, appTheme: 'default-green', appFont: 'system', particlesEnabled: true, particleOpacity: 0.02, channelsPerPage: 48, epgDensity: 200, cardShowGroup: true, cardShowEpg: true, cardShowFavButton: true, cardShowChannelNumber: false, cardLogoAspectRatio: "16/9", m3uUploadServerUrl: "", orangeTvUsername: "", orangeTvPassword: "", orangeTvSelectedGroups: [], barTvEmail: "", barTvPassword: "", movistarVodCacheDaysToKeep: 15, useMovistarVodAsEpg: true, xcodecCorsProxyUrl: "", xcodecIgnorePanelsOverStreams: 0, xcodecDefaultBatchSize: 15, xcodecDefaultTimeout: 8000, playerWindowOpacity: 1, compactCardView: false, enableHoverPreview: true, eqSettings: { enabled: true, preamp: 0, bands: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], compressor: { threshold: -24, knee: 30, ratio: 12, attack: 0.003, release: 0.25 }, customPresets: [] } }; let daznAuthTokenState = null; const DAZN_TOKEN_DB_KEY = 'daznAuthTokenKey'; const ORANGE_TV_AVAILABLE_GROUPS_FOR_SETTINGS = [ "Generalista", "Series", "Cine", "Documentales", "Lifestyle", "Juvenil", "Infantil", "Deportes", "Motor", "Anime", "Música", "Información", "Autonómicos", "Internacionales", "Adulto" ]; const availableLanguages = [ {code: 'es', name: 'Español'}, {code: 'en', name: 'Inglés'}, {code: 'pt', name: 'Portugués'}, {code: 'fr', name: 'Francés'}, {code: 'de', name: 'Alemán'}, {code: 'it', name: 'Italiano'}, {code: 'ja', name: 'Japonés'}, {code: 'qaa', name: 'Original'}, {code: 'und', name: 'Indefinido (und)'} ]; const availableTextLanguages = [ {code: 'off', name: 'Desactivado'}, ...availableLanguages ]; async function loadUserSettings() { const storedSettings = await getAppConfigValue('userSettings'); const defaultSettingsCopy = JSON.parse(JSON.stringify(userSettings)); if (!defaultSettingsCopy.orangeTvSelectedGroups || defaultSettingsCopy.orangeTvSelectedGroups.length === 0) { defaultSettingsCopy.orangeTvSelectedGroups = ORANGE_TV_AVAILABLE_GROUPS_FOR_SETTINGS.slice(); } if (storedSettings) { Object.assign(userSettings, defaultSettingsCopy, storedSettings); if(typeof userSettings.additionalGlobalHeaders !== 'string'){ userSettings.additionalGlobalHeaders = JSON.stringify(userSettings.additionalGlobalHeaders || {}); } if (!userSettings.orangeTvSelectedGroups || !Array.isArray(userSettings.orangeTvSelectedGroups)) { userSettings.orangeTvSelectedGroups = defaultSettingsCopy.orangeTvSelectedGroups; } } else { userSettings.orangeTvSelectedGroups = defaultSettingsCopy.orangeTvSelectedGroups; } favorites = await getAppConfigValue('favorites') || []; appHistory = await getAppConfigValue('history') || []; daznAuthTokenState = await getAppConfigValue(DAZN_TOKEN_DB_KEY) || null; } async function saveUserSettings() { userSettings.language = $('#appLanguageSelect').val(); userSettings.enableEpgNameMatching = $('#enableEpgNameMatchingCheck').is(':checked'); userSettings.epgNameMatchThreshold = parseFloat($('#epgNameMatchThreshold').val()) / 100; userSettings.sidebarCollapsed = $('#sidebar').hasClass('collapsed'); userSettings.persistFilters = $('#persistFiltersCheck').is(':checked'); userSettings.playerBuffer = parseInt($('#playerBufferInput').val(), 10); userSettings.preferredAudioLanguage = $('#preferredAudioLanguageInput').val(); userSettings.preferredTextLanguage = $('#preferredTextLanguageInput').val(); userSettings.lowLatencyMode = $('#lowLatencyModeCheck').is(':checked'); userSettings.liveCatchUpMode = $('#liveCatchUpModeCheck').is(':checked'); userSettings.abrEnabled = $('#abrEnabledCheck').is(':checked'); userSettings.abrDefaultBandwidthEstimate = parseInt($('#abrDefaultBandwidthEstimateInput').val(), 10); userSettings.streamingJumpLargeGaps = $('#streamingJumpLargeGapsCheck').is(':checked'); userSettings.shakaDefaultPresentationDelay = parseFloat($('#shakaDefaultPresentationDelayInput').val()); userSettings.shakaAudioVideoSyncThreshold = parseFloat($('#shakaAudioVideoSyncThresholdInput').val()); userSettings.manifestRetryMaxAttempts = parseInt($('#manifestRetryMaxAttemptsInput').val(), 10); userSettings.manifestRetryTimeout = parseInt($('#manifestRetryTimeoutInput').val(), 10); userSettings.segmentRetryMaxAttempts = parseInt($('#segmentRetryMaxAttemptsInput').val(), 10); userSettings.segmentRetryTimeout = parseInt($('#segmentRetryTimeoutInput').val(), 10); userSettings.globalUserAgent = $('#globalUserAgentInput').val().trim(); userSettings.globalReferrer = $('#globalReferrerInput').val().trim(); try { JSON.parse($('#additionalGlobalHeadersInput').val()); userSettings.additionalGlobalHeaders = $('#additionalGlobalHeadersInput').val(); } catch(e) { userSettings.additionalGlobalHeaders = '{}'; if (typeof showNotification === 'function') showNotification('Cabeceras Globales Adicionales no es un JSON válido. No se guardó.', 'warning'); } userSettings.channelCardSize = parseInt($('#channelCardSizeInput').val(), 10); userSettings.channelsPerPage = parseInt($('#channelsPerPageInput').val(), 10); userSettings.persistentControls = $('#persistentControlsCheck').is(':checked'); userSettings.maxVideoHeight = parseInt($('#maxVideoHeight').val(), 10); userSettings.autoSaveM3U = $('#autoSaveM3UCheck').is(':checked'); userSettings.defaultEpgUrl = $('#defaultEpgUrlInput').val().trim(); userSettings.appTheme = $('#appThemeSelect').val(); userSettings.appFont = $('#appFontSelect').val(); userSettings.particlesEnabled = $('#particlesEnabledCheck').is(':checked'); userSettings.particleOpacity = parseFloat($('#particleOpacityInput').val()) / 100; userSettings.epgDensity = parseInt($('#epgDensityInput').val(), 10); userSettings.cardShowGroup = $('#cardShowGroupCheck').is(':checked'); userSettings.cardShowEpg = $('#cardShowEpgCheck').is(':checked'); userSettings.cardShowFavButton = $('#cardShowFavButtonCheck').is(':checked'); userSettings.cardShowChannelNumber = $('#cardShowChannelNumberCheck').is(':checked'); userSettings.cardLogoAspectRatio = $('#cardLogoAspectRatioSelect').val(); userSettings.m3uUploadServerUrl = $('#m3uUploadServerUrlInput').val().trim(); userSettings.orangeTvUsername = $('#orangeTvUsernameInput').val().trim() || ""; userSettings.orangeTvPassword = $('#orangeTvPasswordInput').val() || ""; userSettings.orangeTvSelectedGroups = []; $('#orangeTvGroupSelectionContainer .form-check-input:checked').each(function() { userSettings.orangeTvSelectedGroups.push($(this).val()); }); userSettings.barTvEmail = $('#barTvEmailInput').val().trim(); userSettings.barTvPassword = $('#barTvPasswordInput').val(); const oldUseMovistarVodAsEpg = userSettings.useMovistarVodAsEpg; userSettings.useMovistarVodAsEpg = $('#useMovistarVodAsEpgCheck').is(':checked'); const oldMovistarVodCacheDays = userSettings.movistarVodCacheDaysToKeep; userSettings.movistarVodCacheDaysToKeep = parseInt($('#movistarVodCacheDaysToKeepInput').val(), 10) || 15; if (userSettings.movistarVodCacheDaysToKeep < 1) userSettings.movistarVodCacheDaysToKeep = 1; if (userSettings.movistarVodCacheDaysToKeep > 90) userSettings.movistarVodCacheDaysToKeep = 90; userSettings.xcodecCorsProxyUrl = $('#xcodecCorsProxyUrlInput').val().trim(); userSettings.xcodecIgnorePanelsOverStreams = parseInt($('#xcodecIgnorePanelsOverStreamsInput').val(), 10) || 0; userSettings.xcodecDefaultBatchSize = parseInt($('#xcodecDefaultBatchSizeInput').val(), 10) || 15; userSettings.xcodecDefaultTimeout = parseInt($('#xcodecDefaultTimeoutInput').val(), 10) || 8000; userSettings.playerWindowOpacity = parseFloat($('#playerWindowOpacityInput').val()); userSettings.compactCardView = $('#compactCardViewCheck').is(':checked'); userSettings.enableHoverPreview = $('#enableHoverPreviewCheck').is(':checked'); await saveAppConfigValue('userSettings', userSettings); const daznTokenFromInput = $('#daznAuthTokenSettingsInput').val().trim(); const currentTokenInDb = daznAuthTokenState; if (daznTokenFromInput && daznTokenFromInput !== currentTokenInDb) { daznAuthTokenState = daznTokenFromInput; await saveAppConfigValue(DAZN_TOKEN_DB_KEY, daznAuthTokenState); if (typeof showNotification === 'function') showNotification('Token DAZN guardado.', 'success'); } else if (!daznTokenFromInput && currentTokenInDb) { daznAuthTokenState = null; await deleteAppConfigValue(DAZN_TOKEN_DB_KEY); if (typeof showNotification === 'function') showNotification('Token DAZN eliminado.', 'info'); } if (oldMovistarVodCacheDays !== userSettings.movistarVodCacheDaysToKeep) { if (typeof deleteOldMovistarVodData === 'function') { try { const deletedCount = await deleteOldMovistarVodData(userSettings.movistarVodCacheDaysToKeep); if (typeof showNotification === 'function') showNotification(`Política de caché VOD actualizada. ${deletedCount} registros antiguos eliminados.`, 'info'); updateMovistarVodCacheStatsUI(); } catch (e) { if (typeof showNotification === 'function') showNotification(`Error aplicando nueva política de caché VOD: ${e.message}`, 'warning'); } } } if (oldUseMovistarVodAsEpg !== userSettings.useMovistarVodAsEpg) { if (typeof matchChannelsWithEpg === 'function') await matchChannelsWithEpg(true); if (userSettings.useMovistarVodAsEpg && typeof updateEpgWithMovistarVodData === 'function') { const today = new Date(); const yyyy = today.getFullYear(); const mm = String(today.getMonth() + 1).padStart(2, '0'); const dd = String(today.getDate()).padStart(2, '0'); await updateEpgWithMovistarVodData(`${yyyy}-${mm}-${dd}`); } else if (!userSettings.useMovistarVodAsEpg && typeof epgDataByChannelId !== 'undefined') { for (const key in epgDataByChannelId) { if (key.startsWith('movistar.')) { delete epgDataByChannelId[key]; } } if (typeof filterAndRenderChannels === 'function') filterAndRenderChannels(); if (typeof updateEPGProgressBarOnCards === 'function') updateEPGProgressBarOnCards(); } } if (typeof applyUISettings === 'function') applyUISettings(); if (typeof showNotification === 'function' && !daznTokenFromInput && !currentTokenInDb) { showNotification('Ajustes guardados y aplicados.', 'success'); } else if (typeof showNotification === 'function' && daznTokenFromInput && daznTokenFromInput === currentTokenInDb) { showNotification('Ajustes guardados y aplicados (Token DAZN sin cambios).', 'success'); } } function populateUserSettingsForm() { populateLanguageSelects(); $('#appLanguageSelect').val(userSettings.language); $('#enableEpgNameMatchingCheck').prop('checked', userSettings.enableEpgNameMatching); $('#epgNameMatchThreshold').val(userSettings.epgNameMatchThreshold * 100); $('#epgNameMatchThresholdValue').text((userSettings.epgNameMatchThreshold * 100).toFixed(0) + '%'); $('#persistFiltersCheck').prop('checked', userSettings.persistFilters); $('#playerBufferInput').val(userSettings.playerBuffer); $('#playerBufferValue').text(userSettings.playerBuffer + 's'); $('#preferredAudioLanguageInput').val(userSettings.preferredAudioLanguage); $('#preferredTextLanguageInput').val(userSettings.preferredTextLanguage); $('#lowLatencyModeCheck').prop('checked', userSettings.lowLatencyMode); $('#liveCatchUpModeCheck').prop('checked', userSettings.liveCatchUpMode); $('#abrEnabledCheck').prop('checked', userSettings.abrEnabled); $('#abrDefaultBandwidthEstimateInput').val(userSettings.abrDefaultBandwidthEstimate); $('#abrDefaultBandwidthEstimateValue').text(userSettings.abrDefaultBandwidthEstimate + ' Kbps'); $('#streamingJumpLargeGapsCheck').prop('checked', userSettings.streamingJumpLargeGaps); $('#shakaDefaultPresentationDelayInput').val(userSettings.shakaDefaultPresentationDelay); $('#shakaDefaultPresentationDelayValue').text(userSettings.shakaDefaultPresentationDelay.toFixed(userSettings.shakaDefaultPresentationDelay % 1 === 0 ? 0 : 1) + 's'); $('#shakaAudioVideoSyncThresholdInput').val(userSettings.shakaAudioVideoSyncThreshold); $('#shakaAudioVideoSyncThresholdValue').text(userSettings.shakaAudioVideoSyncThreshold.toFixed(userSettings.shakaAudioVideoSyncThreshold % 1 === 0 ? 0 : 2) + 's'); $('#manifestRetryMaxAttemptsInput').val(userSettings.manifestRetryMaxAttempts); $('#manifestRetryMaxAttemptsValue').text(userSettings.manifestRetryMaxAttempts); $('#manifestRetryTimeoutInput').val(userSettings.manifestRetryTimeout); $('#manifestRetryTimeoutValue').text(userSettings.manifestRetryTimeout); $('#segmentRetryMaxAttemptsInput').val(userSettings.segmentRetryMaxAttempts); $('#segmentRetryMaxAttemptsValue').text(userSettings.segmentRetryMaxAttempts); $('#segmentRetryTimeoutInput').val(userSettings.segmentRetryTimeout); $('#segmentRetryTimeoutValue').text(userSettings.segmentRetryTimeout); $('#globalUserAgentInput').val(userSettings.globalUserAgent); $('#globalReferrerInput').val(userSettings.globalReferrer); try { const parsedHeaders = JSON.parse(userSettings.additionalGlobalHeaders || '{}'); $('#additionalGlobalHeadersInput').val(JSON.stringify(parsedHeaders, null, 2)); } catch (e) { $('#additionalGlobalHeadersInput').val('{}'); } $('#channelCardSizeInput').val(userSettings.channelCardSize); $('#channelCardSizeValue').text(userSettings.channelCardSize + 'px'); $('#channelsPerPageInput').val(userSettings.channelsPerPage); $('#channelsPerPageValue').text(userSettings.channelsPerPage); $('#persistentControlsCheck').prop('checked', userSettings.persistentControls); $('#maxVideoHeight').val(userSettings.maxVideoHeight); $('#autoSaveM3UCheck').prop('checked', userSettings.autoSaveM3U); $('#defaultEpgUrlInput').val(userSettings.defaultEpgUrl); $('#appThemeSelect').val(userSettings.appTheme); $('#appFontSelect').val(userSettings.appFont); $('#particlesEnabledCheck').prop('checked', userSettings.particlesEnabled); $('#particleOpacityInput').val(userSettings.particleOpacity * 100); $('#particleOpacityValue').text((userSettings.particleOpacity * 100).toFixed(0) + '%'); $('#epgDensityInput').val(userSettings.epgDensity); $('#epgDensityValue').text(userSettings.epgDensity + 'px/h'); $('#cardShowGroupCheck').prop('checked', userSettings.cardShowGroup); $('#cardShowEpgCheck').prop('checked', userSettings.cardShowEpg); $('#cardShowFavButtonCheck').prop('checked', userSettings.cardShowFavButton); $('#cardShowChannelNumberCheck').prop('checked', userSettings.cardShowChannelNumber); $('#cardLogoAspectRatioSelect').val(userSettings.cardLogoAspectRatio); $('#m3uUploadServerUrlInput').val(userSettings.m3uUploadServerUrl); $('#orangeTvUsernameInput').val(userSettings.orangeTvUsername); $('#orangeTvPasswordInput').val(userSettings.orangeTvPassword); const orangeTvGroupContainer = $('#orangeTvGroupSelectionContainer'); orangeTvGroupContainer.empty(); const currentSelectedOrangeGroups = Array.isArray(userSettings.orangeTvSelectedGroups) ? userSettings.orangeTvSelectedGroups : ORANGE_TV_AVAILABLE_GROUPS_FOR_SETTINGS.slice(); ORANGE_TV_AVAILABLE_GROUPS_FOR_SETTINGS.forEach(group => { const isChecked = currentSelectedOrangeGroups.includes(group); const checkboxHtml = `