const axios = require('axios') const cheerio = require('cheerio') const dayjs = require('dayjs') const utc = require('dayjs/plugin/utc') const doFetch = require('@ntlab/sfetch') dayjs.extend(utc) const eventIds = {} const cookies = {} const caches = {} module.exports = { site: 'nowplayer.now.com', days: 2, url({ channel, date }) { const diff = date.diff(dayjs.utc().startOf('d'), 'd') + 1 return `https://nowplayer.now.com/tvguide/epglist?channelIdList[]=${channel.site_id}&day=${diff}` }, request: { cache: { ttl: 24 * 60 * 60 * 1000 // 1 day }, headers({ channel }) { return { 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 OPR/132.0.0.0', 'cookie': Object.entries({ ...cookies, LANG: channel.lang}) .map(a => `${a[0]}=${a[1]}`) .join('; ') } } }, async parser({ content, channel, headers }) { const programs = [] parseCookies(headers) const items = parseItems(content) if (items.length) { if (eventIds[channel.lang] === undefined) { eventIds[channel.lang] = [] } const queues = items .filter(item => item.vimProgramId && !eventIds[channel.lang].includes(item.vimProgramId)) .map(item => { const url = `https://nowplayer.now.com/tvguide/epgprogramdetail?programId=${item.vimProgramId}` if (caches[url] !== undefined) { item.detail = caches[url] } else { return { url, params: { headers: module.exports.request.headers({ channel }) }, item } } }) .filter(Boolean) const worker = process.env.EPG_DETAILED_GUIDE_WORKER || 10 const delay = process.env.EPG_DETAILED_GUIDE_DELAY || 500 await doFetch(queues, { worker }, async (queue, res, headers) => { parseCookies(headers) queue.item.detail = res caches[queue.url] = res await new Promise(resolve => setTimeout(resolve, delay)) }) items.forEach(item => { eventIds[channel.lang].push(item.vimProgramId) programs.push({ title: (channel.lang === 'zh' ? item.detail?.chiProgName : item.detail?.progName) || item.name, description: channel.lang === 'zh' ? item.detail?.chiSynopsis : item.detail?.synopsis, categories: [item.detail?.genre, item.detail?.subGenre].filter(Boolean), actor: item.detail?.actor?.split(',').map(a => a.trim()), director: item.detail?.director?.split(',').map(a => a.trim()), year: item.detail?.firstReleaseYear?.toString(), start: getDate(item.start), stop: getDate(item.end) }) }) } return programs }, async channels({ lang }) { const html = await axios .get('https://nowplayer.now.com/channels', { headers: { Accept: 'text/html' } }) .then(r => r.data) .catch(console.log) const channels = [] const $ = cheerio.load(html) $('body > div.container > .tv-guide-s-g > div > div').each((i, el) => { channels.push({ lang, site_id: $(el).find('.guide-g-play > p.channel').text().replace('CH', ''), name: $(el).find('.thumbnail > a > span.image > p').text() }) }) return channels } } function parseItems(content) { const data = JSON.parse(content) if (!data || !Array.isArray(data)) return [] return Array.isArray(data[0]) ? data[0] : [] } function parseCookies(headers) { if (headers && Array.isArray(headers['set-cookie'])) { headers['set-cookie'] .map(cookie => cookie.split(';')[0].trim()) .forEach(cookie => { const [k, v] = [ cookie.substr(0, cookie.indexOf('=')), cookie.substr(cookie.indexOf('=') + 1), ] cookies[k] = v }) } } function getDate(dt) { return dayjs.utc(dt) }