From 15994210f1bc05dfdbf17e9c51aacfc314e132d2 Mon Sep 17 00:00:00 2001 From: mrcanelas Date: Sat, 7 Jun 2025 14:55:04 -0300 Subject: [PATCH] feat: add integration with MDBList for item and genre retrieval --- addon/lib/getCatalog.js | 11 +++++ addon/lib/getManifest.js | 28 ++++++++++++- addon/utils/mdbList.js | 91 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 addon/utils/mdbList.js diff --git a/addon/lib/getCatalog.js b/addon/lib/getCatalog.js index 155c882..a4e228c 100644 --- a/addon/lib/getCatalog.js +++ b/addon/lib/getCatalog.js @@ -4,9 +4,20 @@ const moviedb = new MovieDb(process.env.TMDB_API); const { getGenreList } = require("./getGenreList"); const { getLanguages } = require("./getLanguages"); const { parseMedia } = require("../utils/parseProps"); +const { fetchMDBListItems, parseMDBListItems } = require("../utils/mdbList"); const CATALOG_TYPES = require("../static/catalog-types.json"); async function getCatalog(type, language, page, id, genre, config) { + const mdblistKey = config.mdblistkey + + if (id.startsWith("mdblist.")) { + const listId = id.split(".")[1]; + const results = await fetchMDBListItems(listId, mdblistKey, language, page); + const parseResults = parseMDBListItems(results, type, genre); + + return parseResults + } + const genreList = await getGenreList(language, type); const parameters = await buildParameters(type, language, page, id, genre, genreList, config); diff --git a/addon/lib/getManifest.js b/addon/lib/getManifest.js index 5ab3461..6101df3 100644 --- a/addon/lib/getManifest.js +++ b/addon/lib/getManifest.js @@ -1,6 +1,7 @@ require("dotenv").config(); const { getGenreList } = require("./getGenreList"); const { getLanguages } = require("./getLanguages"); +const { getGenresFromMDBList } = require("../utils/mdbList"); const packageJson = require("../../package.json"); const catalogsTranslations = require("../static/translations.json"); const CATALOG_TYPES = require("../static/catalog-types.json"); @@ -98,6 +99,23 @@ function getOptionsForCatalog(catalogDef, type, showInHome, { years, genres_movi } } +async function createMDBListCatalog(userCatalog, mdblistKey) { + const listId = userCatalog.id.split(".")[1]; + const genres = await getGenresFromMDBList(listId, mdblistKey); + + return { + id: userCatalog.id, + type: userCatalog.type, + name: userCatalog.name, + pageSize: 20, + extra: [ + { name: "genre", options: genres, isRequired: false }, + { name: "skip" }, + ], + showInHome: userCatalog.showInHome, + }; +} + async function getManifest(config) { const language = config.language || DEFAULT_LANGUAGE; const tmdbPrefix = config.tmdbPrefix === "true"; @@ -124,16 +142,21 @@ async function getManifest(config) { const languagesArray = await getLanguages(); const filterLanguages = setOrderLanguage(language, languagesArray); + const isMDBList = (id) => id.startsWith("mdblist."); const options = { years, genres_movie, genres_series, filterLanguages }; - let catalogs = userCatalogs + let catalogs = await Promise.all(userCatalogs .filter(userCatalog => { const catalogDef = getCatalogDefinition(userCatalog.id); + if (isMDBList(userCatalog.id)) return true; if (!catalogDef) return false; if (catalogDef.requiresAuth && !sessionId) return false; return true; }) .map(userCatalog => { + if (isMDBList(userCatalog.id)) { + return createMDBListCatalog(userCatalog, config.mdblistkey); + } const catalogDef = getCatalogDefinition(userCatalog.id); const catalogOptions = getOptionsForCatalog(catalogDef, userCatalog.type, userCatalog.showInHome, options); @@ -146,7 +169,7 @@ async function getManifest(config) { translatedCatalogs, userCatalog.showInHome ); - }); + })); if (config.searchEnabled !== "false") { const searchCatalogMovie = { @@ -169,6 +192,7 @@ async function getManifest(config) { const activeConfigs = [ `Language: ${language}`, `TMDB Account: ${sessionId ? 'Connected' : 'Not Connected'}`, + `MDBList Integration: ${config.mdblistkey ? 'Connected' : 'Not Connected'}`, `IMDb Integration: ${provideImdbId ? 'Enabled' : 'Disabled'}`, `RPDB Integration: ${config.rpdbkey ? 'Enabled' : 'Disabled'}`, `Search: ${config.searchEnabled !== "false" ? 'Enabled' : 'Disabled'}`, diff --git a/addon/utils/mdbList.js b/addon/utils/mdbList.js new file mode 100644 index 0000000..e1542de --- /dev/null +++ b/addon/utils/mdbList.js @@ -0,0 +1,91 @@ +const axios = require("axios"); +const getPosterPath = (url) => url.split("/").pop(); + +async function fetchMDBListItems(listId, apiKey, language, page) { + const offset = (page * 20) - 20; + try { + const url = `https://api.mdblist.com/lists/${listId}/items?language=${'pt-BR'}&limit=20&offset=${offset}&apikey=${apiKey}&append_to_response=genre,poster`; + const response = await axios.get(url); + return [ + ...(response.data.movies || []), + ...(response.data.shows || []) + ]; + } catch (err) { + console.error("Error retrieving MDBList items:", err.message, err); + return []; + } +} + +async function getGenresFromMDBList(listId, apiKey) { + try { + const items = await fetchMDBListItems(listId, apiKey); + const genres = [ + ...new Set( + items.flatMap(item => + (item.genre || []).map(g => { + if (!g || typeof g !== "string") return null; + return g.charAt(0).toUpperCase() + g.slice(1).toLowerCase(); + }) + ).filter(Boolean) + ) + ].sort(); + return genres; + } catch(err) { + console.error("ERROR in getGenresFromMDBList:", err); + return []; + } +} + +function parseMDBListItems(items, type, genreFilter) { + const availableGenres = [ + ...new Set( + items.flatMap(item => + (item.genre || []) + .map(g => + typeof g === "string" + ? g.charAt(0).toUpperCase() + g.slice(1).toLowerCase() + : null + ) + .filter(Boolean) + ) + ) + ].sort(); + + let filteredItems = items; + if (genreFilter) { + filteredItems = filteredItems.filter(item => + Array.isArray(item.genre) && + item.genre.some( + g => + typeof g === "string" && + g.toLowerCase() === genreFilter.toLowerCase() + ) + ); + } + + const metas = filteredItems + .filter(item => { + if (type === "series") return item.mediatype === "show"; + if (type === "movie") return item.mediatype === "movie"; + return false; + }) + .map(item => ({ + id: item.id + ? `tmdb:${item.id}` + : item.imdb_id + ? `tt${item.imdb_id}` + : undefined, + name: item.title, + type, + poster: `https://image.tmdb.org/t/p/w500/${getPosterPath(item.poster)}`, + genre: item.genre, + year: item.release_year, + imdb_id: item.imdb_id + })) + .filter(meta => meta.id && meta.name && meta.poster); + + return { metas, availableGenres }; +} + + +module.exports = { fetchMDBListItems, getGenresFromMDBList, parseMDBListItems }; \ No newline at end of file