fix tmdb
This commit is contained in:
parent
e614cb387a
commit
eb4bc982de
@ -346,5 +346,7 @@
|
||||
"m3uInstruction1": { "message": "Wählen Sie einen Server aus der Liste aus." },
|
||||
"m3uInstruction2": { "message": "Wählen Sie eine oder mehrere Bibliotheken aus, die Sie einschließen möchten." },
|
||||
"m3uInstruction3": { "message": "Klicken Sie auf die Download-Schaltfläche." },
|
||||
"m3uInstruction4": { "message": "Importieren Sie die .m3u-Datei in Ihren kompatiblen Player." }
|
||||
"m3uInstruction4": { "message": "Importieren Sie die .m3u-Datei in Ihren kompatiblen Player." },
|
||||
"settingsRegionLabel": { "message": "Region für die Inhaltsentdeckung" },
|
||||
"allRegions": { "message": "Alle Regionen" }
|
||||
}
|
@ -343,5 +343,7 @@
|
||||
"m3uInstruction1": { "message": "Choose a server from the list." },
|
||||
"m3uInstruction2": { "message": "Select one or more libraries to include." },
|
||||
"m3uInstruction3": { "message": "Click the download button." },
|
||||
"m3uInstruction4": { "message": "Import the .m3u file into your compatible player." }
|
||||
"m3uInstruction4": { "message": "Import the .m3u file into your compatible player." },
|
||||
"settingsRegionLabel": { "message": "Region for content discovery" },
|
||||
"allRegions": { "message": "All regions" }
|
||||
}
|
@ -343,5 +343,7 @@
|
||||
"m3uInstruction1": { "message": "Elige un servidor de la lista." },
|
||||
"m3uInstruction2": { "message": "Selecciona una o más bibliotecas para incluir." },
|
||||
"m3uInstruction3": { "message": "Haz clic en el botón de descarga." },
|
||||
"m3uInstruction4": { "message": "Importa el archivo .m3u en tu reproductor compatible." }
|
||||
"m3uInstruction4": { "message": "Importa el archivo .m3u en tu reproductor compatible." },
|
||||
"settingsRegionLabel": { "message": "Región para descubrimiento de contenido" },
|
||||
"allRegions": { "message": "Todas las regiones" }
|
||||
}
|
@ -343,5 +343,7 @@
|
||||
"m3uInstruction1": { "message": "Choisissez un serveur dans la liste." },
|
||||
"m3uInstruction2": { "message": "Sélectionnez une ou plusieurs bibliothèques à inclure." },
|
||||
"m3uInstruction3": { "message": "Cliquez sur le bouton de téléchargement." },
|
||||
"m3uInstruction4": { "message": "Importez le fichier .m3u dans votre lecteur compatible." }
|
||||
"m3uInstruction4": { "message": "Importez le fichier .m3u dans votre lecteur compatible." },
|
||||
"settingsRegionLabel": { "message": "Région pour la découverte de contenu" },
|
||||
"allRegions": { "message": "Toutes les régions" }
|
||||
}
|
@ -343,5 +343,7 @@
|
||||
"m3uInstruction1": { "message": "Scegli un server dalla lista." },
|
||||
"m3uInstruction2": { "message": "Seleziona una o più librerie da includere." },
|
||||
"m3uInstruction3": { "message": "Clicca sul pulsante di download." },
|
||||
"m3uInstruction4": { "message": "Importa il file .m3u nel tuo lettore compatibile." }
|
||||
"m3uInstruction4": { "message": "Importa il file .m3u nel tuo lettore compatibile." },
|
||||
"settingsRegionLabel": { "message": "Regione per la scoperta di contenuti" },
|
||||
"allRegions": { "message": "Tutte le regioni" }
|
||||
}
|
@ -343,5 +343,7 @@
|
||||
"m3uInstruction1": { "message": "Escolha um servidor da lista." },
|
||||
"m3uInstruction2": { "message": "Selecione uma ou mais bibliotecas para incluir." },
|
||||
"m3uInstruction3": { "message": "Clique no botão de download." },
|
||||
"m3uInstruction4": { "message": "Importe o arquivo .m3u para o seu reprodutor compatível." }
|
||||
"m3uInstruction4": { "message": "Importe o arquivo .m3u para o seu reprodutor compatível." },
|
||||
"settingsRegionLabel": { "message": "Região para descoberta de conteúdo" },
|
||||
"allRegions": { "message": "Todas as regiões" }
|
||||
}
|
24
js/api.js
24
js/api.js
@ -5,31 +5,31 @@ import { getFromDB } from './db.js';
|
||||
import { _ } from './utils.js';
|
||||
|
||||
export async function fetchTMDB(endpoint, params = {}, signal) {
|
||||
const langMap = {
|
||||
'es': 'es-ES',
|
||||
'en': 'en-US',
|
||||
'fr': 'fr-FR',
|
||||
'de': 'de-DE',
|
||||
'it': 'it-IT',
|
||||
'pt': 'pt-BR'
|
||||
const region = state.settings.watchRegion || 'US';
|
||||
|
||||
const regionToLangMap = {
|
||||
'ES': 'es-ES', 'MX': 'es-MX',
|
||||
'FR': 'fr-FR', 'CA': 'fr-CA',
|
||||
'DE': 'de-DE',
|
||||
'IT': 'it-IT',
|
||||
'PT': 'pt-PT', 'BR': 'pt-BR',
|
||||
'US': 'en-US', 'GB': 'en-GB'
|
||||
};
|
||||
const tmdbLang = langMap[state.settings.language] || 'en-US';
|
||||
|
||||
const tmdbLang = regionToLangMap[region] || 'en-US';
|
||||
|
||||
const [path, existingQuery] = endpoint.split('?');
|
||||
const finalParams = new URLSearchParams(existingQuery);
|
||||
|
||||
finalParams.set('api_key', state.settings.apiKey);
|
||||
finalParams.set('language', tmdbLang);
|
||||
finalParams.set('watch_region', region);
|
||||
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value) {
|
||||
finalParams.set(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (state.settings.watchRegion && !finalParams.has('watch_region')) {
|
||||
finalParams.set('watch_region', state.settings.watchRegion);
|
||||
}
|
||||
|
||||
const url = `https://api.themoviedb.org/3/${path}?${finalParams.toString()}`;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
export const config = {
|
||||
defaultApiKey: '4e44d9029b1270a757cddc766a1bcb63',
|
||||
dbName: 'PlexDB',
|
||||
dbVersion: 7,
|
||||
dbVersion: 9,
|
||||
};
|
20
js/constants.js
Normal file
20
js/constants.js
Normal file
@ -0,0 +1,20 @@
|
||||
export const API_URLS = {
|
||||
TMDB_BASE: 'https://api.themoviedb.org/3',
|
||||
TMDB_IMAGE_BASE: 'https://image.tmdb.org/t/p',
|
||||
PLEX_TV: 'https://plex.tv/api/resources',
|
||||
YOUTUBE_EMBED: 'https://www.youtube.com/embed/',
|
||||
IMDB_TITLE: 'https://www.imdb.com/title/'
|
||||
};
|
||||
|
||||
export const CONFIG = {
|
||||
DEFAULT_API_KEY: '4e44d9029b1270a757cddc766a1bcb63',
|
||||
DB_NAME: 'PlexDB',
|
||||
DB_VERSION: 6
|
||||
};
|
||||
|
||||
export const STORAGE_KEYS = {
|
||||
USER_HISTORY: 'cineplex_userHistory',
|
||||
USER_PREFERENCES: 'cineplex_userPreferences',
|
||||
FAVORITES: 'cineplex_favorites',
|
||||
RECOMMENDATIONS_CACHE: 'cineplex_recommendations'
|
||||
};
|
26
js/main.js
26
js/main.js
@ -13,7 +13,15 @@ async function loadSettings() {
|
||||
const settingsData = await getFromDB('settings');
|
||||
if (settingsData && settingsData.length > 0) {
|
||||
state.settings = { ...state.settings, ...settingsData[0] };
|
||||
} else {
|
||||
}
|
||||
|
||||
// Ensure a default region is set if none exists
|
||||
if (!state.settings.watchRegion) {
|
||||
state.settings.watchRegion = 'US';
|
||||
}
|
||||
|
||||
// Ensure language is always set, fallback to UI language
|
||||
if (!state.settings.language) {
|
||||
state.settings.language = chrome.i18n.getUILanguage().split('-')[0];
|
||||
}
|
||||
|
||||
@ -21,19 +29,6 @@ async function loadSettings() {
|
||||
state.settings.apiKey = config.defaultApiKey;
|
||||
}
|
||||
|
||||
if (!state.settings.watchRegion) {
|
||||
const tmdbLangMap = {
|
||||
'es': 'es-ES',
|
||||
'en': 'en-US',
|
||||
'fr': 'fr-FR',
|
||||
'de': 'de-DE',
|
||||
'it': 'it-IT',
|
||||
'pt': 'pt-BR'
|
||||
};
|
||||
const fullLangCode = tmdbLangMap[state.settings.language] || 'en-US';
|
||||
state.settings.watchRegion = fullLangCode.split('-')[1] || 'US';
|
||||
}
|
||||
|
||||
const jellyfinSettingsData = await getFromDB('jellyfin_settings');
|
||||
if (jellyfinSettingsData && jellyfinSettingsData.length > 0) {
|
||||
state.jellyfinSettings = { ...state.jellyfinSettings, ...jellyfinSettingsData[0] };
|
||||
@ -41,7 +36,8 @@ async function loadSettings() {
|
||||
|
||||
} catch (error) {
|
||||
console.error("Could not load settings from DB, using defaults.", error);
|
||||
state.settings.language = chrome.i18n.getUILanguage().split('-')[0];
|
||||
// Fallback to defaults in case of any error
|
||||
state.settings.watchRegion = 'US';
|
||||
}
|
||||
}
|
||||
|
||||
|
59
js/php-script-generator.js
Normal file
59
js/php-script-generator.js
Normal file
@ -0,0 +1,59 @@
|
||||
import { showNotification, _ } from './utils.js';
|
||||
|
||||
export const phpScriptGenerator = (() => {
|
||||
let dom = {};
|
||||
|
||||
function cacheDom() {
|
||||
const settingsModal = document.getElementById('settingsModal');
|
||||
if (!settingsModal) return false;
|
||||
|
||||
dom.secretKeyCheck = settingsModal.querySelector('#phpSecretKeyCheck');
|
||||
dom.secretKey = settingsModal.querySelector('#phpSecretKey');
|
||||
dom.savePath = settingsModal.querySelector('#phpSavePath');
|
||||
dom.filename = settingsModal.querySelector('#phpFilename');
|
||||
dom.fileActionAppendRadio = settingsModal.querySelector('#phpFileActionAppend');
|
||||
dom.generatedCode = settingsModal.querySelector('#generatedPhpCode');
|
||||
dom.generateBtn = settingsModal.querySelector('#generatePhpScriptBtn');
|
||||
dom.copyBtn = settingsModal.querySelector('#copyPhpScriptBtn');
|
||||
|
||||
return dom.generateBtn && dom.copyBtn;
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!cacheDom()) {
|
||||
return;
|
||||
}
|
||||
dom.generateBtn.addEventListener('click', generatePhpScript);
|
||||
dom.copyBtn.addEventListener('click', copyScript);
|
||||
}
|
||||
|
||||
function generatePhpScript() {
|
||||
const useSecretKey = dom.secretKeyCheck.checked;
|
||||
const secretKey = dom.secretKey.value.trim();
|
||||
const savePath = dom.savePath.value.trim();
|
||||
const filename = dom.filename.value.trim() || 'CinePlex_Playlist.m3u';
|
||||
const appendToFile = dom.fileActionAppendRadio.checked;
|
||||
|
||||
let script = `<?php\nheader('Content-Type: application/json');\nheader('Access-Control-Allow-Origin: *');\nheader('Access-Control-Allow-Methods: POST, OPTIONS');\nheader('Access-Control-Allow-Headers: Content-Type, Origin, X-Secret-Key');\n\nif ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {\n http_response_code(200);\n exit(0);\n}\n\ndefine('SAVE_DIRECTORY', '${savePath.replace(/'/g, "\\'")}');\ndefine('FILENAME', '${filename.replace(/'/g, "\\'")}');\ndefine('FILE_ACTION_APPEND', ${appendToFile ? 'true' : 'false'});\n${useSecretKey ? `define('SECRET_KEY', '${secretKey.replace(/'/g, "\\'")}');` : ''}\n\nfunction sendResponse($success, $message, $filename = '', $http_code = 200) {\n if (!$success && $http_code === 200) {\n $http_code = 400;\n }\n http_response_code($http_code);\n echo json_encode(['success' => $success, 'message' => $message, 'filename' => $filename]);\n exit;\n}\n`;
|
||||
if (useSecretKey) {
|
||||
script += `\n$auth_key = isset($_SERVER['HTTP_X_SECRET_KEY']) ? $_SERVER['HTTP_X_SECRET_KEY'] : '';\nif (!defined('SECRET_KEY') || SECRET_KEY === '' || $auth_key !== SECRET_KEY) {\n sendResponse(false, 'Acceso no autorizado. Clave secreta inválida o no proporcionada.', '', 403);\n}\n`;
|
||||
}
|
||||
script += `\n$json_data = file_get_contents('php://input');\n$data = json_decode($json_data, true);\n\nif (json_last_error() !== JSON_ERROR_NONE) {\n sendResponse(false, 'Error: Datos JSON inválidos.');\n}\n\nif (!isset($data['streams']) || !is_array($data['streams']) || empty($data['streams'])) {\n sendResponse(false, 'Error: El JSON debe contener un array "streams" no vacío.');\n}\n\n$save_dir = SAVE_DIRECTORY !== '' ? rtrim(SAVE_DIRECTORY, '/\\') : __DIR__;\n\nif (!is_dir($save_dir) || !is_writable($save_dir)) {\n sendResponse(false, 'Error del servidor: El directorio de destino no existe o no tiene permisos de escritura.', '', 500);\n}\n\n$safe_filename = preg_replace('/[^\\w\\s._-]/', '', basename(FILENAME));\n$safe_filename = preg_replace('/\\s+/', '_', $safe_filename);\n$target_path = $save_dir . DIRECTORY_SEPARATOR . $safe_filename;\n\n$content_to_write = "";\n\nif (FILE_ACTION_APPEND) {\n $file_exists = file_exists($target_path);\n if (!$file_exists) {\n $content_to_write .= "#EXTM3U\\n";\n }\n foreach ($data['streams'] as $stream) {\n if (isset($stream['extinf'], $stream['url'])) {\n $content_to_write .= trim($stream['extinf']) . "\\n";\n $content_to_write .= trim($stream['url']) . "\\n";\n }\n }\n if (file_put_contents($target_path, $content_to_write, FILE_APPEND | LOCK_EX) !== false) {\n sendResponse(true, 'Streams añadidos correctamente al archivo.', $safe_filename, 200);\n } else {\n sendResponse(false, 'Error del servidor: No se pudo añadir contenido al archivo.', '', 500);\n }\n} else { // Overwrite mode\n $content_to_write = "#EXTM3U\\n";\n foreach ($data['streams'] as $stream) {\n if (isset($stream['extinf'], $stream['url'])) {\n $content_to_write .= trim($stream['extinf']) . "\\n";\n $content_to_write .= trim($stream['url']) . "\\n";\n }\n }\n if (file_put_contents($target_path, $content_to_write, LOCK_EX) !== false) {\n sendResponse(true, 'Archivo de streams sobrescrito correctamente.', $safe_filename, 201);\n } else {\n sendResponse(false, 'Error del servidor: No se pudo escribir el archivo.', '', 500);\n }\n}\n?>`;
|
||||
dom.generatedCode.value = script;
|
||||
showNotification(_("scriptGenerated"), "success");
|
||||
}
|
||||
|
||||
function copyScript() {
|
||||
if (!dom.generatedCode.value || dom.generatedCode.value.trim() === '') {
|
||||
showNotification(_("errorGeneratingScript"), "warning");
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(dom.generatedCode.value).then(() => {
|
||||
showNotification(_("scriptCopied"), "success");
|
||||
}).catch(err => {
|
||||
showNotification(_("errorCopyingScript"), "error");
|
||||
});
|
||||
}
|
||||
|
||||
return { init };
|
||||
})();
|
@ -104,13 +104,11 @@ export function renderProviders(providers) {
|
||||
export async function getProviderItems(providerId, page = 1) {
|
||||
try {
|
||||
const watchRegion = state.settings.watchRegion || 'US';
|
||||
const language = state.settings.language || 'en-US';
|
||||
|
||||
const params = {
|
||||
with_watch_providers: providerId,
|
||||
watch_region: watchRegion,
|
||||
page: page,
|
||||
language: language
|
||||
};
|
||||
|
||||
const moviesResponse = await fetchTMDB('discover/movie', params);
|
||||
|
13
js/ui.js
13
js/ui.js
@ -1557,7 +1557,6 @@ export function activateSettingsTab(tabId) {
|
||||
|
||||
export function openSettingsModal() {
|
||||
document.getElementById('tmdbApiKey').value = state.settings.apiKey;
|
||||
document.getElementById('appLanguage').value = state.settings.language;
|
||||
document.getElementById('phpScriptUrl').value = state.settings.phpScriptUrl || '';
|
||||
document.getElementById('lightModeToggle').checked = state.settings.theme === 'light';
|
||||
document.getElementById('showHeroToggle').checked = state.settings.showHero;
|
||||
@ -1580,11 +1579,10 @@ export function openSettingsModal() {
|
||||
}
|
||||
|
||||
export async function saveSettings() {
|
||||
const oldLanguage = state.settings.language;
|
||||
const oldRegion = state.settings.watchRegion;
|
||||
const newSettings = {
|
||||
id: 'user_settings',
|
||||
apiKey: document.getElementById('tmdbApiKey').value.trim(),
|
||||
language: document.getElementById('appLanguage').value,
|
||||
theme: document.getElementById('lightModeToggle').checked ? 'light' : 'dark',
|
||||
showHero: document.getElementById('showHeroToggle').checked,
|
||||
phpScriptUrl: document.getElementById('phpScriptUrl').value.trim(),
|
||||
@ -1615,11 +1613,12 @@ export async function saveSettings() {
|
||||
showNotification(_('settingsSavedSuccess'), 'success');
|
||||
applyTheme(state.settings.theme);
|
||||
applyHeroVisibility(state.settings.showHero);
|
||||
bootstrap.Modal.getInstance(document.getElementById('settingsModal'))?.hide();
|
||||
if (newSettings.language !== oldLanguage) {
|
||||
showNotification(_('languageChangeReload'), 'info', 4000);
|
||||
setTimeout(() => window.location.reload(), 4000);
|
||||
|
||||
if (newSettings.watchRegion !== oldRegion) {
|
||||
loadInitialContent();
|
||||
}
|
||||
|
||||
bootstrap.Modal.getInstance(document.getElementById('settingsModal'))?.hide();
|
||||
} catch (error) {
|
||||
showNotification(_('errorSavingSettings'), 'error');
|
||||
}
|
||||
|
11
plex.html
11
plex.html
@ -400,17 +400,6 @@
|
||||
<label for="tmdbApiKey" class="form-label">__MSG_settingsTmdbApiLabel__</label>
|
||||
<input type="password" class="form-control" id="tmdbApiKey" placeholder="__MSG_settingsTmdbApiPlaceholder__">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="appLanguage" class="form-label">__MSG_settingsTmdbLangLabel__</label>
|
||||
<select class="form-control filter-select" id="appLanguage">
|
||||
<option value="es">__MSG_lang_es__</option>
|
||||
<option value="en">__MSG_lang_en__</option>
|
||||
<option value="fr">__MSG_lang_fr__</option>
|
||||
<option value="de">__MSG_lang_de__</option>
|
||||
<option value="it">__MSG_lang_it__</option>
|
||||
<option value="pt">__MSG_lang_pt__</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="region-filter" class="form-label">__MSG_settingsRegionLabel__</label>
|
||||
<select class="form-control filter-select" id="region-filter">
|
||||
|
Loading…
x
Reference in New Issue
Block a user