apilink / controllers /stremioController.js
franyer24's picture
update logic
1ce7a42 verified
Raw
History Blame Contribute Delete
6.16 kB
const { getIdType } = require('../utils/idType')
const { getHandlersForId } = require('../handlers/index')
const { getProvidersByType, getExtraLocalesForType } = require('../providers')
const { runProvidersCached } = require('../utils/providerRunner')
const streamDb = require('../utils/streamDb')
const { refreshInBackground } = require('../utils/streamValidator')
const { classifyRarity } = require('../utils/rarityClassifier')
function buildProviderKey(providers) {
return providers.map(p => p.name).join(',')
}
async function getStreams(req, res) {
try {
const id = req.params.id
const type = 'movie'
const handlers = getHandlersForId(getIdType(id))
if (handlers.length === 0) {
return res.status(400).json({ error: 'Tipo de ID no soportado' })
}
const extraLocales = getExtraLocalesForType(type)
let movieData = null
for (const handler of handlers) {
movieData = handler.name === 'tmdb'
? await handler.run(id, type, extraLocales)
: await handler.run(id, extraLocales)
if (movieData) break
}
if (!movieData) {
return res.status(404).json({ error: 'Película no encontrada' })
}
// 1. Intentar servir de la DB
const tmdbId = String(movieData.id)
const freshStreams = await streamDb.getFreshStreams(tmdbId, null, null)
if (freshStreams.length > 0) {
return res.json({ streams: freshStreams })
}
// 2. No hay frescos → scrapear providers
const providers = getProvidersByType(type)
const cacheKey = [
'stremio', type, movieData.id, movieData.type, buildProviderKey(providers),
].join(':')
const scrapedStreams = await runProvidersCached(
cacheKey,
providers,
movieData,
{},
movieData.release_date,
)
// 3. Logear a DB
const providersUsed = [...new Set(scrapedStreams.map(s => s.provider).filter(Boolean))]
for (const providerName of providersUsed) {
const providerStreams = scrapedStreams.filter(s => s.provider === providerName)
if (providerStreams.length > 0) {
await streamDb.logStreams(tmdbId, providerName, type, providerStreams, movieData.original_title, null, null)
}
}
// 4. Background refresh si había streams viejos en DB
if (freshStreams.length === 0) {
const allOld = await streamDb.getAllStreams(tmdbId, null, null)
if (allOld.length > 0) {
for (const providerName of providersUsed) {
const providerStreams = scrapedStreams.filter(s => s.provider === providerName)
refreshInBackground(tmdbId, allOld, providerStreams, providerName, type)
}
}
}
res.json({ streams: scrapedStreams })
} catch (error) {
console.error(error.message)
res.status(500).json({ error: 'Error interno del servidor' })
}
}
async function getSerieStreams(req, res) {
try {
const decodedId = decodeURIComponent(req.params.id)
const [id, seasonStr, episodeStr] = decodedId.split(':')
const season = parseInt(seasonStr, 10) || 1
const episode = parseInt(episodeStr, 10) || 1
const handlers = getHandlersForId(getIdType(id))
if (handlers.length === 0)
return res.status(400).json({ error: 'Tipo de ID no soportado' })
const extraLocalesSerie = getExtraLocalesForType('serie')
const extraLocalesAnime = getExtraLocalesForType('anime')
const extraLocales = [...new Set([...extraLocalesSerie, ...extraLocalesAnime])]
let serieData = null
for (const handler of handlers) {
serieData = handler.name === 'tmdb'
? await handler.run(id, 'series', extraLocales)
: await handler.run(id, extraLocales)
if (serieData?.type === 'tv') break
}
if (!serieData || serieData.type !== 'tv') {
return res.status(404).json({ error: 'Serie no encontrada o tipo incorrecto' })
}
const providerType = serieData.isAnime ? 'anime' : 'serie'
const rarity = serieData.isAnime ? classifyRarity(serieData.popularity, serieData.vote_count) : null
const tmdbId = String(serieData.id)
// 1. Intentar servir de la DB
const freshStreams = await streamDb.getFreshStreams(tmdbId, season, episode)
if (freshStreams.length > 0) {
const limited = limitByLanguage(freshStreams)
return res.json({ streams: limited })
}
// 2. Scrapear providers
const providers = getProvidersByType(providerType, { rarity })
const cacheKey = [
'stremio', providerType, serieData.id, season, episode, buildProviderKey(providers),
].join(':')
const scrapedStreams = await runProvidersCached(
cacheKey,
providers,
serieData,
{ season, episode },
serieData.release_date,
)
// 3. Logear a DB
const providersUsed = [...new Set(scrapedStreams.map(s => s.provider).filter(Boolean))]
for (const providerName of providersUsed) {
const providerStreams = scrapedStreams.filter(s => s.provider === providerName)
if (providerStreams.length > 0) {
await streamDb.logStreams(tmdbId, providerName, providerType, providerStreams, serieData.original_title, season, episode)
}
}
// 4. Background refresh si había streams viejos en DB
const allOld = await streamDb.getAllStreams(tmdbId, season, episode)
if (allOld.length > 0) {
for (const providerName of providersUsed) {
const providerStreams = scrapedStreams.filter(s => s.provider === providerName)
refreshInBackground(tmdbId, allOld, providerStreams, providerName, providerType)
}
}
const limited = limitByLanguage(scrapedStreams)
res.json({ streams: limited })
} catch (error) {
console.trace(error)
console.error('Error interno en getSerieStreams:', error.message)
res.status(500).json({ error: 'Error interno del servidor' })
}
}
function limitByLanguage(streams) {
const grouped = {}
for (const s of streams) {
const lang = s.language || s.lang || 'unknown'
if (!grouped[lang]) grouped[lang] = []
if (grouped[lang].length < 3) grouped[lang].push(s)
}
return Object.values(grouped).flat()
}
module.exports = { getStreams, getSerieStreams }