mikmc5/comedy
0
1const { addonBuilder, serveHTTP } = require('stremio-addon-sdk');2const axios = require('axios');3const fs = require('fs').promises;4const path = require('path');5const winston = require('winston');6const schedule = require('node-schedule');7 8// Winston logger configuration9const logger = winston.createLogger({10 level: 'debug',11 format: winston.format.combine(12 winston.format.timestamp(),13 winston.format.printf(({ level, message, timestamp }) => {14 return `${timestamp} ${level}: ${message}`;15 })16 ),17 transports: [18 new winston.transports.Console(),19 new winston.transports.File({ filename: 'addon.log' })20 ]21});22 23const TMDB_API_KEY = 'f051e7366c6105ad4f9aafe4733d9dae';24const TMDB_BASE_URL = 'https://api.themoviedb.org/3';25const CACHE_DIR = path.join(__dirname, 'cache');26const CATALOG_CACHE_FILE = path.join(CACHE_DIR, 'catalog_cache.json');27const META_CACHE_FILE = path.join(CACHE_DIR, 'meta_cache.json');28 29let catalogCache = {};30let metaCache = {};31 32const manifest = {33 id: 'org.stremio.uk-comedy-movies',34 version: '1.0.0',35 name: 'UK Comedy Movies',36 description: 'Stremio Add-on to showcase UK comedy movies from 2015 onward with a rating above 4',37 catalogs: [38 {39 id: 'uk-comedy-movies-catalog',40 name: 'UK Comedy Movies',41 type: 'Easynews',42 pageSize: 25,43 extra: [{ name: 'search', isRequired: false }]44 }45 ],46 resources: ['catalog', 'meta'],47 types: ['movie'],48 idPrefixes: ['tmdb']49};50 51const builder = new addonBuilder(manifest);52 53async function ensureCacheDirectory() {54 try {55 await fs.mkdir(CACHE_DIR, { recursive: true });56 logger.debug('Cache directory ensured');57 } catch (error) {58 logger.error('Error creating cache directory:', error);59 }60}61 62async function loadCache() {63 try {64 const catalogData = await fs.readFile(CATALOG_CACHE_FILE, 'utf8');65 catalogCache = JSON.parse(catalogData);66 logger.debug(`Catalog cache loaded. Items: ${catalogCache.data ? catalogCache.data.length : 0}`);67 } catch (error) {68 logger.info('No existing catalog cache found. Will create a new one.');69 }70 71 try {72 const metaData = await fs.readFile(META_CACHE_FILE, 'utf8');73 metaCache = JSON.parse(metaData);74 logger.debug(`Meta cache loaded. Items: ${Object.keys(metaCache).length}`);75 } catch (error) {76 logger.info('No existing meta cache found. Will create a new one.');77 }78}79 80async function saveCache() {81 try {82 await fs.writeFile(CATALOG_CACHE_FILE, JSON.stringify(catalogCache));83 await fs.writeFile(META_CACHE_FILE, JSON.stringify(metaCache));84 logger.debug('Cache saved successfully');85 } catch (error) {86 logger.error('Error saving cache:', error);87 }88}89 90async function fetchUKComedyMovies() {91 let page = 1;92 let totalPages = 1;93 const allResults = [];94 95 try {96 do {97 const url = `${TMDB_BASE_URL}/discover/movie?api_key=${TMDB_API_KEY}&with_genres=35&with_original_language=en&with_origin_country=GB&primary_release_date.gte=2015-01-01&primary_release_date.lte=2024-12-31&page=${page}`;98 logger.debug(`Requesting TMDB: ${url}`);99 100 const response = await axios.get(url);101 const { results, total_pages } = response.data;102 totalPages = total_pages;103 104 logger.debug(`Page ${page}/${totalPages} fetched. Results: ${results.length}`);105 106 // Filter movies with a rating above 4107 const filteredResults = results.filter(movie => movie.vote_average > 4);108 allResults.push(...filteredResults);109 110 page++;111 } while (page <= totalPages);112 113 allResults.sort((a, b) => new Date(b.release_date) - new Date(a.release_date));114 logger.info(`Total UK Comedy movies fetched after filtering: ${allResults.length}`);115 return allResults;116 } catch (error) {117 logger.error('Error fetching UK Comedy movies:', error);118 return [];119 }120}121 122async function fetchMovieMetadata(movieId) {123 try {124 const url = `${TMDB_BASE_URL}/movie/${movieId}?api_key=${TMDB_API_KEY}`;125 const response = await axios.get(url);126 return response.data;127 } catch (error) {128 logger.error(`Error fetching metadata for movie ID ${movieId}:`, error.message);129 return { error: 'Failed to fetch movie data' };130 }131}132 133async function updateCatalogAndMetaCache() {134 logger.info('Updating catalog and meta cache...');135 try {136 const results = await fetchUKComedyMovies();137 catalogCache = {138 timestamp: Date.now(),139 data: results140 };141 142 // Fetch meta information for all movies143 for (const movie of results) {144 const movieId = movie.id;145 logger.debug(`Fetching meta for movie ID: ${movieId}`);146 const movieData = await fetchMovieMetadata(movieId);147 metaCache[movieId] = {148 timestamp: Date.now(),149 data: movieData150 };151 }152 153 await saveCache();154 logger.info(`Catalog and meta cache updated. Items: ${results.length}`);155 } catch (error) {156 logger.error('Error updating catalog and meta cache:', error);157 }158}159 160builder.defineCatalogHandler(async (args) => {161 const searchQuery = args.extra && args.extra.search ? args.extra.search : '';162 logger.debug(`Catalog request received. Query: "${searchQuery}"`);163 164 let results = catalogCache.data || [];165 logger.debug(`Catalog cache has ${results.length} items`);166 167 if (searchQuery) {168 results = results.filter(movie => 169 movie.title.toLowerCase().includes(searchQuery.toLowerCase())170 );171 logger.debug(`Filtered results for "${searchQuery}": ${results.length}`);172 }173 174 const metas = results.map(item => ({175 id: `tmdb-${item.id}`,176 type: 'movie',177 name: item.title,178 poster: item.poster_path ? `https://image.tmdb.org/t/p/w500${item.poster_path}` : null,179 description: item.overview || 'No description available',180 releaseInfo: item.release_date ? item.release_date.split('-')[0] : 'Unknown'181 }));182 183 logger.info(`Catalog request processed. Query: "${searchQuery}", Results: ${metas.length}`);184 return { metas };185});186 187builder.defineMetaHandler(async (args) => {188 const id = args.id.split('-')[1];189 logger.debug(`Meta request received for movie ID: ${id}`);190 191 if (!metaCache[id]) {192 logger.warn(`Meta not found for movie ID: ${id}`);193 return { meta: null };194 }195 196 const movieData = metaCache[id].data;197 198 if (movieData.error) {199 logger.warn(`Meta request failed for movie ID: ${id}`);200 return { meta: null };201 }202 203 const { 204 title, 205 overview, 206 poster_path, 207 release_date, 208 genres, 209 runtime, 210 vote_average, 211 backdrop_path 212 } = movieData;213 214 logger.info(`Meta request processed for movie ID: ${id}`);215 return {216 meta: {217 id: args.id,218 type: 'movie',219 name: title || 'Unknown Title',220 poster: poster_path ? `https://image.tmdb.org/t/p/w500${poster_path}` : null,221 background: backdrop_path ? `https://image.tmdb.org/t/p/w1280${backdrop_path}` : null,222 description: overview || 'No description available',223 releaseInfo: release_date ? release_date.split('-')[0] : 'Unknown',224 genre: genres && Array.isArray(genres) ? genres.map(g => g.name) : [],225 runtime: runtime ? `${runtime} min` : 'Unknown',226 rating: vote_average ? vote_average.toFixed(1) : 'N/A',227 }228 };229});230 231async function startServer() {232 await ensureCacheDirectory();233 await loadCache();234 235 async function refreshCache() {236 logger.info('Scheduled update: Refreshing catalog and meta cache');237 await updateCatalogAndMetaCache();238 }239 240 // Schedule cache refresh every day at midnight241 schedule.scheduleJob('0 0 * * *', refreshCache);242 243 // Initial cache refresh if needed244 if (!catalogCache.timestamp || Date.now() - catalogCache.timestamp > 24 * 60 * 60 * 1000) {245 logger.info('Initial cache refresh needed');246 await refreshCache();247 } else {248 logger.info('Cache is up to date. Next refresh scheduled for midnight');249 }250 251 const interface = builder.getInterface();252 const port = process.env.PORT || 3000;253 254 logger.info(`Starting Stremio Add-on server on port ${port}`);255 serveHTTP(interface, { port });256}257 258startServer();259 