| const { createClient } = require('./httpClient') |
| const streamDb = require('./streamDb') |
|
|
| const BODY_ERROR_KEYWORDS = ['error', 'not found', '404', 'no encontrado', 'expired', 'removed'] |
|
|
| const validatorClient = createClient({ |
| timeout: 8000, |
| maxRedirects: 3, |
| headers: { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36', |
| }, |
| }) |
|
|
| async function validateUrl(url) { |
| const inicio = Date.now() |
| try { |
| const res = await validatorClient.get(url, { |
| maxRedirects: 3, |
| validateStatus: () => true, |
| }) |
| const responseMs = Date.now() - inicio |
| const statusCode = res.status |
| const body = typeof res.data === 'string' ? res.data : String(res.data || '') |
|
|
| if (statusCode < 200 || statusCode >= 400) { |
| return { isAlive: false, responseMs, statusCode, error: `HTTP ${statusCode}` } |
| } |
|
|
| const bodyLower = body.toLowerCase() |
| for (const keyword of BODY_ERROR_KEYWORDS) { |
| if (bodyLower.includes(keyword)) { |
| return { isAlive: false, responseMs, statusCode, error: `Body contiene "${keyword}"` } |
| } |
| } |
|
|
| return { isAlive: true, responseMs, statusCode, error: null } |
| } catch (err) { |
| return { isAlive: false, responseMs: Date.now() - inicio, statusCode: null, error: err.message } |
| } |
| } |
|
|
| function refreshInBackground(tmdbId, oldStreams, newScrapeResults, providerName, contentType) { |
| setImmediate(async () => { |
| try { |
| const oldUrls = new Set(oldStreams.map(s => s.url)) |
| const newUrls = new Set(newScrapeResults.map(s => s.url)) |
|
|
| |
| if (newScrapeResults.length > 0) { |
| const title = newScrapeResults[0]?.title || null |
| await streamDb.logStreams(tmdbId, providerName, contentType, newScrapeResults, title) |
| } |
|
|
| |
| const disappeared = oldStreams.filter(s => !newUrls.has(s.url)) |
| for (const stream of disappeared) { |
| const result = await validateUrl(stream.url) |
| await streamDb.logCheck(stream.url, result) |
|
|
| if (result.isAlive) { |
| await streamDb.markAlive(stream.url, stream.provider) |
| } else { |
| await streamDb.markDead(stream.url, stream.provider) |
| } |
| } |
|
|
| |
| const fresh = newScrapeResults.filter(s => !oldUrls.has(s.url)) |
| for (const stream of fresh) { |
| const result = await validateUrl(stream.url) |
| await streamDb.logCheck(stream.url, result) |
|
|
| if (!result.isAlive) { |
| await streamDb.markDead(stream.url, providerName) |
| } |
| } |
| } catch (err) { |
| console.error(`[StreamValidator] Error en refresh para ${tmdbId}:`, err.message) |
| } |
| }) |
| } |
|
|
| module.exports = { validateUrl, refreshInBackground } |
|
|