From 1d8423d61adc886daaab3852b23098bae3a76a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Candela?= Date: Mon, 19 Jan 2026 20:28:54 +0100 Subject: [PATCH] Fixed movistarplus.es returning 400 errors --- .../movistarplus.es/movistarplus.es.config.js | 141 ++++++++++++------ 1 file changed, 94 insertions(+), 47 deletions(-) diff --git a/sites/movistarplus.es/movistarplus.es.config.js b/sites/movistarplus.es/movistarplus.es.config.js index c58b5190..00930b95 100644 --- a/sites/movistarplus.es/movistarplus.es.config.js +++ b/sites/movistarplus.es/movistarplus.es.config.js @@ -1,6 +1,11 @@ const axios = require('axios') const cheerio = require('cheerio') const dayjs = require('dayjs') +const utc = require('dayjs/plugin/utc') +const timezone = require('dayjs/plugin/timezone') + +dayjs.extend(utc) +dayjs.extend(timezone) module.exports = { site: 'movistarplus.es', @@ -8,35 +13,87 @@ module.exports = { url({ channel, date }) { return `https://www.movistarplus.es/programacion-tv/${channel.site_id}/${date.format('YYYY-MM-DD')}` }, - async parser({ content }) { + request: { + headers: { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', + 'Accept-Language': 'es-ES,es;q=0.9,en;q=0.8', + Referer: 'https://www.movistarplus.es/programacion-tv' + }, + maxRedirects: 5 + }, + async parser({ content, date }) { let programs = [] - let items = parseItems(content) - if (!items.length) return programs - const $ = cheerio.load(content) - const programElements = $('div[id^="ele-"]').get() - for (let i = 0; i < items.length; i++) { - const el = items[i] - let description = null + const programDivs = $('div[id^="ele-"]').toArray() - if (programElements[i]) { - const programDiv = $(programElements[i]) - const programLink = programDiv.find('a').attr('href') - - if (programLink) { - const idMatch = programLink.match(/id=(\d+)/) - if (idMatch && idMatch[1]) { - description = await getProgramDescription(programLink).catch(() => null) + for (let i = 0; i < programDivs.length; i++) { + const el = $(programDivs[i]) + + const title = el.find('li.title').text().trim() + if (!title) continue + + const timeText = el.find('li.time').text().trim() + if (!timeText) continue + + const [hours, minutes] = timeText.split(':').map(h => parseInt(h, 10)) + + // Parse time in Spain timezone (Europe/Madrid) + let startDate = dayjs.tz( + `${date.format('YYYY-MM-DD')} ${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`, + 'YYYY-MM-DD HH:mm', + 'Europe/Madrid' + ) + + // If the time is in early morning (before 5 AM), it's the next day + if (hours < 5) { + startDate = startDate.add(1, 'day') + } + + // Calculate end time from next program's start time + let endDate + if (i < programDivs.length - 1) { + const nextEl = $(programDivs[i + 1]) + const nextTimeText = nextEl.find('li.time').text().trim() + if (nextTimeText) { + const [nextHours, nextMinutes] = nextTimeText.split(':').map(h => parseInt(h, 10)) + endDate = dayjs.tz( + `${date.format('YYYY-MM-DD')} ${nextHours.toString().padStart(2, '0')}:${nextMinutes.toString().padStart(2, '0')}`, + 'YYYY-MM-DD HH:mm', + 'Europe/Madrid' + ) + + // If the next time is in early morning (before 5 AM), it's the next day + if (nextHours < 5) { + endDate = endDate.add(1, 'day') + } + + // If end time is still before or same as start time, add another day + if (endDate.isBefore(startDate) || endDate.isSame(startDate)) { + endDate = endDate.add(1, 'day') } } } + // If no end time, use start of next day + if (!endDate) { + endDate = startDate.add(1, 'day').startOf('day') + } + + const programLink = el.find('a').attr('href') + let description = null + + if (programLink) { + description = await getProgramDescription(programLink).catch(() => null) + } + programs.push({ - title: el.item.name, - description: description, - start: dayjs(el.item.startDate), - stop: dayjs(el.item.endDate) + title, + description, + start: startDate, + stop: endDate }) } @@ -44,7 +101,13 @@ module.exports = { }, async channels() { const html = await axios - .get('https://www.movistarplus.es/programacion-tv') + .get('https://www.movistarplus.es/programacion-tv', { + headers: { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8' + } + }) .then(r => r.data) .catch(console.log) @@ -65,33 +128,17 @@ module.exports = { } } -function parseItems(content) { - try { - const $ = cheerio.load(content) - let scheme = $('script:contains("@type": "ItemList")').html() - scheme = JSON.parse(scheme) - if (!scheme || !Array.isArray(scheme.itemListElement)) return [] - - return scheme.itemListElement - } catch { - return [] - } -} - async function getProgramDescription(programUrl) { - try { - const response = await axios.get(programUrl, { - headers: { - 'Referer': 'https://www.movistarplus.es/programacion-tv/' - } - }) + const response = await axios.get(programUrl, { + headers: { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + Referer: 'https://www.movistarplus.es/programacion-tv/' + } + }) - const $ = cheerio.load(response.data) - const description = $('.show-content .text p').first().text().trim() || null + const $ = cheerio.load(response.data) + const description = $('.show-content .text p').first().text().trim() || null - return description - } catch (error) { - console.error(`Error fetching description from ${programUrl}:`, error.message) - return null - } + return description }