From be9078ec68ada0236a2dfaf2a4c3dc15c2d1e12f Mon Sep 17 00:00:00 2001 From: StrangeDrVN Date: Mon, 23 Feb 2026 14:56:06 +0530 Subject: [PATCH] crlf fix --- .../watch.whaletvplus.com.config.js | 374 +++++++++--------- 1 file changed, 187 insertions(+), 187 deletions(-) diff --git a/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js b/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js index a01c73c8..d65292e6 100644 --- a/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js +++ b/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js @@ -1,188 +1,188 @@ -const axios = require('axios') -const dayjs = require('dayjs') - -const HEADERS = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0', - 'Referer': 'https://watch.whaletvplus.com/', - 'Origin': 'https://watch.whaletvplus.com' -} -const apiToken = '4ef13b5f3d2744e3b0a569feb8dde298' - -let authTokenPromise = null - -module.exports = { - site: 'watch.whaletvplus.com', - days: 2, - - request: { - cache: { - ttl: 60 * 60 * 1000 // Cache 1 hour - }, - headers: async function() { - const token = await getAuthToken() - return { - ...HEADERS, - 'token': token - } - } - }, - - url: function ({ channel, date }) { - const start = date.valueOf() - const end = date.add(1, 'day').valueOf() - - return `https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg?channelIds=${channel.site_id}&startTime=${start}&endTime=${end}` - }, - - parser: async function ({ content }) { - let json - try { - json = JSON.parse(content) - } catch (e) { - console.error('Error parsing JSON:', e) - return [] - } - - if (!json.data || !Array.isArray(json.data) || !json.data[0] || !Array.isArray(json.data[0].ptList)) { - return [] - } - - const programs = json.data[0].ptList - const detailsCache = {} - - return await limit(programs, async (p) => { - const program = { - title: p.prgTitle, - start: dayjs(Number(p.prgStm)), - stop: dayjs(Number(p.prgEtm)) - } - - if (p.prgchId) { - if (!detailsCache[p.prgchId]) { - detailsCache[p.prgchId] = fetchProgramDetail(p.prgchId) - } - const detail = await detailsCache[p.prgchId] - if (detail) { - program.description = detail.prgDesc || null - program.season = detail.seasonNumber || null - program.episode = detail.episodeNumber || null - program.sub_title = detail.prgTitle || detail.seriesTitle || null - - if (program.title === program.sub_title) { - program.sub_title = null - } - - if (detail.images && Array.isArray(detail.images)) { - const bestImg = detail.images.find((i) => i.pimgWidth === '1920') || detail.images[0] - if (bestImg) program.image = bestImg.pimgUrl - } - } - } - return program - }) - }, - - async channels() { - const token = await getAuthToken() - - const countries = [ - 'IN', 'AU', 'NZ', 'ZA', 'US', 'BR', 'MX', 'AR', 'CO', 'CL', 'CA', - 'GB', 'DE', 'FR', 'IT', 'ES', 'PL', 'TR', 'AT', 'CH', 'NL', 'PT', - 'BE', 'SE', 'NO', 'DK', 'FI' - ] - - const requests = countries.map(country => - axios.get('https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/category/channels', { - params: { countryCode: country, langCode: 'en' }, - headers: { ...HEADERS, token } - }).then(r => r.data?.data || []).catch(() => []) - ) - - const results = await Promise.all(requests) - const allChannels = results.flat().flatMap(group => group.channels || []) - - const uniqueChannels = new Map() - for (const ch of allChannels) { - if (!uniqueChannels.has(ch.chlId)) { - uniqueChannels.set(ch.chlId, { - lang: (ch.chlLangCode ? ch.chlLangCode.split('-')[0] : 'en'), - site_id: ch.chlId, - name: ch.chlName.trim(), - short_title: ch.chlShortTitle, - // logo: ch.imageIdentifier ? `https://d3b6luslimvglo.cloudfront.net/images/79/rlaxximages/channels-rescaled/icon-white/${ch.imageIdentifier}_white.png` : null - }) - } - } - - return handleDuplicateNames(Array.from(uniqueChannels.values())) - } -} - -async function limit(items, fn, concurrency = 20) { - const results = [] - for (let i = 0; i < items.length; i += concurrency) { - const batch = items.slice(i, i + concurrency) - results.push(...(await Promise.all(batch.map(fn)))) - } - return results -} - -async function getAuthToken() { - if (authTokenPromise) return authTokenPromise - - authTokenPromise = (async () => { - try { - const response = await axios.get('https://rlaxx.zeasn.tv/livetv/api/v1/auth/access', { - params: { uuid: '1', apiToken, langCode: 'en' }, - headers: HEADERS - }) - - if (response.data && response.data.data && response.data.data.token) { - return response.data.data.token - } - - throw new Error('apiToken invalid or expired. Please update config.') - } catch (error) { - authTokenPromise = null - throw new Error(error.message) - } - })() - - return authTokenPromise -} - -async function fetchProgramDetail(programId) { - const token = await getAuthToken() - try { - const response = await axios.get(`https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg/detail/${programId}`, { - headers: { - ...HEADERS, - 'token': token - }, - timeout: 5000 - }) - return response.data && response.data.data ? response.data.data : null - } catch { - return null - } -} - -function handleDuplicateNames(channels) { - const counts = {} - channels.forEach(ch => counts[ch.name] = (counts[ch.name] || 0) + 1) - - channels.forEach(ch => { - if (counts[ch.name] > 1) { - let suffix = ch.short_title && ch.short_title.split('_').slice(1).join('_') - if (suffix) { - if (suffix.startsWith('en-') && suffix.length > 3) suffix = suffix.slice(3) - ch.name += ` (${suffix.replace(/-/g, '/').toUpperCase()})` - } else if (ch.lang) { - ch.name += ` (${ch.lang.toUpperCase()})` - } - } - delete ch.short_title - }) - - return channels +const axios = require('axios') +const dayjs = require('dayjs') + +const HEADERS = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0', + 'Referer': 'https://watch.whaletvplus.com/', + 'Origin': 'https://watch.whaletvplus.com' +} +const apiToken = '4ef13b5f3d2744e3b0a569feb8dde298' + +let authTokenPromise = null + +module.exports = { + site: 'watch.whaletvplus.com', + days: 2, + + request: { + cache: { + ttl: 60 * 60 * 1000 // Cache 1 hour + }, + headers: async function() { + const token = await getAuthToken() + return { + ...HEADERS, + 'token': token + } + } + }, + + url: function ({ channel, date }) { + const start = date.valueOf() + const end = date.add(1, 'day').valueOf() + + return `https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg?channelIds=${channel.site_id}&startTime=${start}&endTime=${end}` + }, + + parser: async function ({ content }) { + let json + try { + json = JSON.parse(content) + } catch (e) { + console.error('Error parsing JSON:', e) + return [] + } + + if (!json.data || !Array.isArray(json.data) || !json.data[0] || !Array.isArray(json.data[0].ptList)) { + return [] + } + + const programs = json.data[0].ptList + const detailsCache = {} + + return await limit(programs, async (p) => { + const program = { + title: p.prgTitle, + start: dayjs(Number(p.prgStm)), + stop: dayjs(Number(p.prgEtm)) + } + + if (p.prgchId) { + if (!detailsCache[p.prgchId]) { + detailsCache[p.prgchId] = fetchProgramDetail(p.prgchId) + } + const detail = await detailsCache[p.prgchId] + if (detail) { + program.description = detail.prgDesc || null + program.season = detail.seasonNumber || null + program.episode = detail.episodeNumber || null + program.sub_title = detail.prgTitle || detail.seriesTitle || null + + if (program.title === program.sub_title) { + program.sub_title = null + } + + if (detail.images && Array.isArray(detail.images)) { + const bestImg = detail.images.find((i) => i.pimgWidth === '1920') || detail.images[0] + if (bestImg) program.image = bestImg.pimgUrl + } + } + } + return program + }) + }, + + async channels() { + const token = await getAuthToken() + + const countries = [ + 'IN', 'AU', 'NZ', 'ZA', 'US', 'BR', 'MX', 'AR', 'CO', 'CL', 'CA', + 'GB', 'DE', 'FR', 'IT', 'ES', 'PL', 'TR', 'AT', 'CH', 'NL', 'PT', + 'BE', 'SE', 'NO', 'DK', 'FI' + ] + + const requests = countries.map(country => + axios.get('https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/category/channels', { + params: { countryCode: country, langCode: 'en' }, + headers: { ...HEADERS, token } + }).then(r => r.data?.data || []).catch(() => []) + ) + + const results = await Promise.all(requests) + const allChannels = results.flat().flatMap(group => group.channels || []) + + const uniqueChannels = new Map() + for (const ch of allChannels) { + if (!uniqueChannels.has(ch.chlId)) { + uniqueChannels.set(ch.chlId, { + lang: (ch.chlLangCode ? ch.chlLangCode.split('-')[0] : 'en'), + site_id: ch.chlId, + name: ch.chlName.trim(), + short_title: ch.chlShortTitle, + // logo: ch.imageIdentifier ? `https://d3b6luslimvglo.cloudfront.net/images/79/rlaxximages/channels-rescaled/icon-white/${ch.imageIdentifier}_white.png` : null + }) + } + } + + return handleDuplicateNames(Array.from(uniqueChannels.values())) + } +} + +async function limit(items, fn, concurrency = 20) { + const results = [] + for (let i = 0; i < items.length; i += concurrency) { + const batch = items.slice(i, i + concurrency) + results.push(...(await Promise.all(batch.map(fn)))) + } + return results +} + +async function getAuthToken() { + if (authTokenPromise) return authTokenPromise + + authTokenPromise = (async () => { + try { + const response = await axios.get('https://rlaxx.zeasn.tv/livetv/api/v1/auth/access', { + params: { uuid: '1', apiToken, langCode: 'en' }, + headers: HEADERS + }) + + if (response.data && response.data.data && response.data.data.token) { + return response.data.data.token + } + + throw new Error('apiToken invalid or expired. Please update config.') + } catch (error) { + authTokenPromise = null + throw new Error(error.message) + } + })() + + return authTokenPromise +} + +async function fetchProgramDetail(programId) { + const token = await getAuthToken() + try { + const response = await axios.get(`https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg/detail/${programId}`, { + headers: { + ...HEADERS, + 'token': token + }, + timeout: 5000 + }) + return response.data && response.data.data ? response.data.data : null + } catch { + return null + } +} + +function handleDuplicateNames(channels) { + const counts = {} + channels.forEach(ch => counts[ch.name] = (counts[ch.name] || 0) + 1) + + channels.forEach(ch => { + if (counts[ch.name] > 1) { + let suffix = ch.short_title && ch.short_title.split('_').slice(1).join('_') + if (suffix) { + if (suffix.startsWith('en-') && suffix.length > 3) suffix = suffix.slice(3) + ch.name += ` (${suffix.replace(/-/g, '/').toUpperCase()})` + } else if (ch.lang) { + ch.name += ` (${ch.lang.toUpperCase()})` + } + } + delete ch.short_title + }) + + return channels } \ No newline at end of file