mirror of
https://github.com/iptv-org/epg
synced 2026-05-07 01:46:59 -04:00
Fix linter issues
This commit is contained in:
@@ -1,65 +1,77 @@
|
|||||||
/**
|
/**
|
||||||
* Sorts an array by the result of running each element through an iteratee function.
|
* Sorts an array by the result of running each element through an iteratee function.
|
||||||
* Creates a shallow copy of the array before sorting to avoid mutating the original.
|
* Creates a shallow copy of the array before sorting to avoid mutating the original.
|
||||||
*
|
*
|
||||||
* @param {Array} arr - The array to sort
|
* @param {Array} arr - The array to sort
|
||||||
* @param {Function} fn - The iteratee function to compute sort values
|
* @param {Function} fn - The iteratee function to compute sort values
|
||||||
* @returns {Array} A new sorted array
|
* @returns {Array} A new sorted array
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const users = [{name: 'john', age: 30}, {name: 'jane', age: 25}];
|
* const users = [{name: 'john', age: 30}, {name: 'jane', age: 25}];
|
||||||
* sortBy(users, x => x.age); // [{name: 'jane', age: 25}, {name: 'john', age: 30}]
|
* sortBy(users, x => x.age); // [{name: 'jane', age: 25}, {name: 'john', age: 30}]
|
||||||
*/
|
*/
|
||||||
export const sortBy = <T>(arr: T[], fn: (item: T) => number | string): T[] => [...arr].sort((a, b) => fn(a) > fn(b) ? 1 : -1)
|
export const sortBy = <T>(arr: T[], fn: (item: T) => number | string): T[] =>
|
||||||
|
[...arr].sort((a, b) => (fn(a) > fn(b) ? 1 : -1))
|
||||||
/**
|
|
||||||
* Sorts an array by multiple criteria with customizable sort orders.
|
/**
|
||||||
* Supports ascending (default) and descending order for each criterion.
|
* Sorts an array by multiple criteria with customizable sort orders.
|
||||||
*
|
* Supports ascending (default) and descending order for each criterion.
|
||||||
* @param {Array} arr - The array to sort
|
*
|
||||||
* @param {Array<Function>} fns - Array of iteratee functions to compute sort values
|
* @param {Array} arr - The array to sort
|
||||||
* @param {Array<string>} orders - Array of sort orders ('asc' or 'desc'), defaults to all 'asc'
|
* @param {Array<Function>} fns - Array of iteratee functions to compute sort values
|
||||||
* @returns {Array} A new sorted array
|
* @param {Array<string>} orders - Array of sort orders ('asc' or 'desc'), defaults to all 'asc'
|
||||||
*
|
* @returns {Array} A new sorted array
|
||||||
* @example
|
*
|
||||||
* const users = [{name: 'john', age: 30}, {name: 'jane', age: 25}, {name: 'bob', age: 30}];
|
* @example
|
||||||
* orderBy(users, [x => x.age, x => x.name], ['desc', 'asc']);
|
* const users = [{name: 'john', age: 30}, {name: 'jane', age: 25}, {name: 'bob', age: 30}];
|
||||||
* // [{name: 'bob', age: 30}, {name: 'john', age: 30}, {name: 'jane', age: 25}]
|
* orderBy(users, [x => x.age, x => x.name], ['desc', 'asc']);
|
||||||
*/
|
* // [{name: 'bob', age: 30}, {name: 'john', age: 30}, {name: 'jane', age: 25}]
|
||||||
export const orderBy = (arr: Array<unknown>, fns: Array<(item: unknown) => string | number>, orders: Array<string> = []): Array<unknown> => [...arr].sort((a, b) =>
|
*/
|
||||||
fns.reduce((acc, fn, i) =>
|
export const orderBy = (
|
||||||
acc || ((orders[i] === 'desc' ? fn(b) > fn(a) : fn(a) > fn(b)) ? 1 : fn(a) === fn(b) ? 0 : -1), 0)
|
arr: Array<unknown>,
|
||||||
)
|
fns: Array<(item: unknown) => string | number>,
|
||||||
|
orders: Array<string> = []
|
||||||
/**
|
): Array<unknown> =>
|
||||||
* Creates a duplicate-free version of an array using an iteratee function to generate
|
[...arr].sort((a, b) =>
|
||||||
* the criterion by which uniqueness is computed. Only the first occurrence of each
|
fns.reduce(
|
||||||
* element is kept.
|
(acc, fn, i) =>
|
||||||
*
|
acc ||
|
||||||
* @param {Array} arr - The array to inspect
|
((orders[i] === 'desc' ? fn(b) > fn(a) : fn(a) > fn(b)) ? 1 : fn(a) === fn(b) ? 0 : -1),
|
||||||
* @param {Function} fn - The iteratee function to compute uniqueness criterion
|
0
|
||||||
* @returns {Array} A new duplicate-free array
|
)
|
||||||
*
|
)
|
||||||
* @example
|
|
||||||
* const users = [{id: 1, name: 'john'}, {id: 2, name: 'jane'}, {id: 1, name: 'john'}];
|
/**
|
||||||
* uniqBy(users, x => x.id); // [{id: 1, name: 'john'}, {id: 2, name: 'jane'}]
|
* Creates a duplicate-free version of an array using an iteratee function to generate
|
||||||
*/
|
* the criterion by which uniqueness is computed. Only the first occurrence of each
|
||||||
export const uniqBy = <T>(arr: T[], fn: (item: T) => unknown): T[] => arr.filter((item, index) => arr.findIndex(x => fn(x) === fn(item)) === index)
|
* element is kept.
|
||||||
|
*
|
||||||
/**
|
* @param {Array} arr - The array to inspect
|
||||||
* Converts a string to start case (capitalizes the first letter of each word).
|
* @param {Function} fn - The iteratee function to compute uniqueness criterion
|
||||||
* Handles camelCase, snake_case, kebab-case, and regular spaces.
|
* @returns {Array} A new duplicate-free array
|
||||||
*
|
*
|
||||||
* @param {string} str - The string to convert
|
* @example
|
||||||
* @returns {string} The start case string
|
* const users = [{id: 1, name: 'john'}, {id: 2, name: 'jane'}, {id: 1, name: 'john'}];
|
||||||
*
|
* uniqBy(users, x => x.id); // [{id: 1, name: 'john'}, {id: 2, name: 'jane'}]
|
||||||
* @example
|
*/
|
||||||
* startCase('hello_world'); // "Hello World"
|
export const uniqBy = <T>(arr: T[], fn: (item: T) => unknown): T[] =>
|
||||||
* startCase('helloWorld'); // "Hello World"
|
arr.filter((item, index) => arr.findIndex(x => fn(x) === fn(item)) === index)
|
||||||
* startCase('hello-world'); // "Hello World"
|
|
||||||
* startCase('hello world'); // "Hello World"
|
/**
|
||||||
*/
|
* Converts a string to start case (capitalizes the first letter of each word).
|
||||||
export const startCase = (str: string): string => str
|
* Handles camelCase, snake_case, kebab-case, and regular spaces.
|
||||||
.replace(/([a-z])([A-Z])/g, '$1 $2') // Split camelCase
|
*
|
||||||
.replace(/[_-]/g, ' ') // Replace underscores and hyphens with spaces
|
* @param {string} str - The string to convert
|
||||||
.replace(/\b\w/g, c => c.toUpperCase()) // Capitalize first letter of each word
|
* @returns {string} The start case string
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* startCase('hello_world'); // "Hello World"
|
||||||
|
* startCase('helloWorld'); // "Hello World"
|
||||||
|
* startCase('hello-world'); // "Hello World"
|
||||||
|
* startCase('hello world'); // "Hello World"
|
||||||
|
*/
|
||||||
|
export const startCase = (str: string): string =>
|
||||||
|
str
|
||||||
|
.replace(/([a-z])([A-Z])/g, '$1 $2') // Split camelCase
|
||||||
|
.replace(/[_-]/g, ' ') // Replace underscores and hyphens with spaces
|
||||||
|
.replace(/\b\w/g, c => c.toUpperCase()) // Capitalize first letter of each word
|
||||||
|
|||||||
@@ -1,333 +1,338 @@
|
|||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
require('dayjs/locale/fr')
|
require('dayjs/locale/fr')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
const PARIS_TZ = 'Europe/Paris'
|
const PARIS_TZ = 'Europe/Paris'
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'guidetnt.com',
|
site: 'guidetnt.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
const now = dayjs()
|
const now = dayjs()
|
||||||
const demain = now.add(1, 'd')
|
const demain = now.add(1, 'd')
|
||||||
if (date && date.isSame(demain, 'day')) {
|
if (date && date.isSame(demain, 'day')) {
|
||||||
return `https://www.guidetnt.com/tv-demain/programme-${channel.site_id}`
|
return `https://www.guidetnt.com/tv-demain/programme-${channel.site_id}`
|
||||||
} else if (!date || date.isSame(now, 'day')) {
|
} else if (!date || date.isSame(now, 'day')) {
|
||||||
return `https://www.guidetnt.com/tv/programme-${channel.site_id}`
|
return `https://www.guidetnt.com/tv/programme-${channel.site_id}`
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async parser({ content, date }) {
|
async parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const allItems = parseItems(content)
|
const allItems = parseItems(content)
|
||||||
const items = allItems?.rows
|
const items = allItems?.rows
|
||||||
const itemDate = allItems?.formattedDate
|
const itemDate = allItems?.formattedDate
|
||||||
for (const item of items) {
|
for (const item of items) {
|
||||||
const prev = programs[programs.length - 1]
|
const prev = programs[programs.length - 1]
|
||||||
const $item = cheerio.load(item)
|
const $item = cheerio.load(item)
|
||||||
const title = parseTitle($item)
|
const title = parseTitle($item)
|
||||||
let start = parseStart($item, itemDate)
|
let start = parseStart($item, itemDate)
|
||||||
|
|
||||||
if (!start || !title) return
|
if (!start || !title) return
|
||||||
if (prev) {
|
if (prev) {
|
||||||
if (start.isBefore(prev.start)) {
|
if (start.isBefore(prev.start)) {
|
||||||
start = start.add(1, 'd')
|
start = start.add(1, 'd')
|
||||||
date = date.add(1, 'd')
|
date = date.add(1, 'd')
|
||||||
}
|
}
|
||||||
prev.stop = start
|
prev.stop = start
|
||||||
}
|
}
|
||||||
let stop = start.add(30, 'm')
|
let stop = start.add(30, 'm')
|
||||||
|
|
||||||
let itemDetails = null
|
let itemDetails = null
|
||||||
let subTitle = null
|
let subTitle = null
|
||||||
//let duration = null
|
//let duration = null
|
||||||
let country = null
|
let country = null
|
||||||
let productionDate = null
|
let productionDate = null
|
||||||
let episode = null
|
let episode = null
|
||||||
let season = null
|
let season = null
|
||||||
let category = parseCategory($item)
|
let category = parseCategory($item)
|
||||||
let description = parseDescription($item)
|
let description = parseDescription($item)
|
||||||
const itemDetailsURL = parseDescriptionURL($item)
|
const itemDetailsURL = parseDescriptionURL($item)
|
||||||
if(itemDetailsURL) {
|
if (itemDetailsURL) {
|
||||||
const url = 'https://www.guidetnt.com' + itemDetailsURL
|
const url = 'https://www.guidetnt.com' + itemDetailsURL
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(url)
|
const response = await axios.get(url)
|
||||||
itemDetails = parseItemDetails(response.data)
|
itemDetails = parseItemDetails(response.data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Erreur lors du fetch des détails pour l'item: ${url}`, err)
|
console.error(`Erreur lors du fetch des détails pour l'item: ${url}`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const timeRange = parseTimeRange(itemDetails?.programHour, date.format('YYYY-MM-DD'))
|
const timeRange = parseTimeRange(itemDetails?.programHour, date.format('YYYY-MM-DD'))
|
||||||
start = timeRange?.start
|
start = timeRange?.start
|
||||||
stop = timeRange?.stop
|
stop = timeRange?.stop
|
||||||
|
|
||||||
subTitle = itemDetails?.subTitle
|
subTitle = itemDetails?.subTitle
|
||||||
if (title == subTitle) subTitle = null
|
if (title == subTitle) subTitle = null
|
||||||
description = itemDetails?.description
|
description = itemDetails?.description
|
||||||
|
|
||||||
const categoryDetails = parseCategoryText(itemDetails?.category)
|
const categoryDetails = parseCategoryText(itemDetails?.category)
|
||||||
//duration = categoryDetails?.duration
|
//duration = categoryDetails?.duration
|
||||||
country = categoryDetails?.country
|
country = categoryDetails?.country
|
||||||
productionDate = categoryDetails?.productionDate
|
productionDate = categoryDetails?.productionDate
|
||||||
season = categoryDetails?.season
|
season = categoryDetails?.season
|
||||||
episode = categoryDetails?.episode
|
episode = categoryDetails?.episode
|
||||||
}
|
}
|
||||||
// See https://www.npmjs.com/package/epg-parser for parameters
|
// See https://www.npmjs.com/package/epg-parser for parameters
|
||||||
programs.push({
|
programs.push({
|
||||||
title,
|
title,
|
||||||
subTitle: subTitle,
|
subTitle: subTitle,
|
||||||
description: description,
|
description: description,
|
||||||
image: itemDetails?.image,
|
image: itemDetails?.image,
|
||||||
category: category,
|
category: category,
|
||||||
directors: itemDetails?.directorActors?.Réalisateur,
|
directors: itemDetails?.directorActors?.Réalisateur,
|
||||||
actors: itemDetails?.directorActors?.Acteur,
|
actors: itemDetails?.directorActors?.Acteur,
|
||||||
country: country,
|
country: country,
|
||||||
date: productionDate,
|
date: productionDate,
|
||||||
//duration: duration, // Tried with length: too, but does not work ! (stop-start is not accurate because of Ads)
|
//duration: duration, // Tried with length: too, but does not work ! (stop-start is not accurate because of Ads)
|
||||||
season: season,
|
season: season,
|
||||||
episode: episode,
|
episode: episode,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const response = await axios.get('https://www.guidetnt.com')
|
const response = await axios.get('https://www.guidetnt.com')
|
||||||
const channels = []
|
const channels = []
|
||||||
const $ = cheerio.load(response.data)
|
const $ = cheerio.load(response.data)
|
||||||
|
|
||||||
// Look inside each .tvlogo container
|
// Look inside each .tvlogo container
|
||||||
$('.tvlogo').each((i, el) => {
|
$('.tvlogo').each((i, el) => {
|
||||||
// Find all descendants that have an alt attribute
|
// Find all descendants that have an alt attribute
|
||||||
$(el).find('[alt]').each((j, subEl) => {
|
$(el)
|
||||||
const alt = $(subEl).attr('alt')
|
.find('[alt]')
|
||||||
const href = $(subEl).attr('href')
|
.each((j, subEl) => {
|
||||||
if (href && alt && alt.trim() !== '') {
|
const alt = $(subEl).attr('alt')
|
||||||
const name = alt.trim()
|
const href = $(subEl).attr('href')
|
||||||
const site_id = href.replace(/^\/tv\/programme-/, '')
|
if (href && alt && alt.trim() !== '') {
|
||||||
channels.push({
|
const name = alt.trim()
|
||||||
lang: 'fr',
|
const site_id = href.replace(/^\/tv\/programme-/, '')
|
||||||
name,
|
channels.push({
|
||||||
site_id
|
lang: 'fr',
|
||||||
})
|
name,
|
||||||
}
|
site_id
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
return channels
|
})
|
||||||
}
|
})
|
||||||
}
|
return channels
|
||||||
|
}
|
||||||
function parseTimeRange(timeRange, baseDate) {
|
}
|
||||||
// Split times
|
|
||||||
const [startStr, endStr] = timeRange.split(' - ').map(s => s.trim())
|
function parseTimeRange(timeRange, baseDate) {
|
||||||
|
// Split times
|
||||||
// Parse with base date
|
const [startStr, endStr] = timeRange.split(' - ').map(s => s.trim())
|
||||||
const start = dayjs(`${baseDate} ${startStr}`, 'YYYY-MM-DD HH:mm')
|
|
||||||
let end = dayjs(`${baseDate} ${endStr}`, 'YYYY-MM-DD HH:mm')
|
// Parse with base date
|
||||||
|
const start = dayjs(`${baseDate} ${startStr}`, 'YYYY-MM-DD HH:mm')
|
||||||
// Handle possible day wrap (e.g., 23:30 - 00:15)
|
let end = dayjs(`${baseDate} ${endStr}`, 'YYYY-MM-DD HH:mm')
|
||||||
if (end.isBefore(start)) {
|
|
||||||
end = end.add(1, 'day')
|
// Handle possible day wrap (e.g., 23:30 - 00:15)
|
||||||
}
|
if (end.isBefore(start)) {
|
||||||
|
end = end.add(1, 'day')
|
||||||
// Calculate duration in minutes
|
}
|
||||||
const diffMinutes = end.diff(start, 'minute')
|
|
||||||
|
// Calculate duration in minutes
|
||||||
return {
|
const diffMinutes = end.diff(start, 'minute')
|
||||||
start: start.format(),
|
|
||||||
stop: end.format(),
|
return {
|
||||||
duration: diffMinutes
|
start: start.format(),
|
||||||
}
|
stop: end.format(),
|
||||||
}
|
duration: diffMinutes
|
||||||
|
}
|
||||||
function parseItemDetails(itemDetails) {
|
}
|
||||||
const $ = cheerio.load(itemDetails)
|
|
||||||
|
function parseItemDetails(itemDetails) {
|
||||||
const program = $('.program-wrapper').first()
|
const $ = cheerio.load(itemDetails)
|
||||||
|
|
||||||
const programHour = program.find('.program-hour').text().trim()
|
const program = $('.program-wrapper').first()
|
||||||
const programTitle = program.find('.program-title').text().trim()
|
|
||||||
const programElementBold = program.find('.program-element-bold').text().trim()
|
const programHour = program.find('.program-hour').text().trim()
|
||||||
const programArea1 = program.find('.program-element.program-area-1').text().trim()
|
const programTitle = program.find('.program-title').text().trim()
|
||||||
|
const programElementBold = program.find('.program-element-bold').text().trim()
|
||||||
let description = ''
|
const programArea1 = program.find('.program-element.program-area-1').text().trim()
|
||||||
const programElements = $('.program-element').filter((i, el) => {
|
|
||||||
const classAttr = $(el).attr('class')
|
let description = ''
|
||||||
// Return true only if it is exactly "program-element" (no extra classes)
|
const programElements = $('.program-element').filter((i, el) => {
|
||||||
return classAttr.trim() === 'program-element'
|
const classAttr = $(el).attr('class')
|
||||||
})
|
// Return true only if it is exactly "program-element" (no extra classes)
|
||||||
|
return classAttr.trim() === 'program-element'
|
||||||
programElements.each((i, el) => {
|
})
|
||||||
description += $(el).text().trim()
|
|
||||||
})
|
programElements.each((i, el) => {
|
||||||
|
description += $(el).text().trim()
|
||||||
const area2Node = $('.program-area-2').first()
|
})
|
||||||
const area2 = $(area2Node)
|
|
||||||
const data = {}
|
const area2Node = $('.program-area-2').first()
|
||||||
let currentLabel = null
|
const area2 = $(area2Node)
|
||||||
let texts = []
|
const data = {}
|
||||||
|
let currentLabel = null
|
||||||
area2.contents().each((i, node) => {
|
let texts = []
|
||||||
if (node.type === 'tag' && node.name === 'strong') {
|
|
||||||
// If we had collected some text for the previous label, save it
|
area2.contents().each((i, node) => {
|
||||||
if (currentLabel && texts.length) {
|
if (node.type === 'tag' && node.name === 'strong') {
|
||||||
data[currentLabel] = texts.join('').trim().replace(/,\s*$/, '') // Remove trailing comma
|
// If we had collected some text for the previous label, save it
|
||||||
}
|
if (currentLabel && texts.length) {
|
||||||
// New label - get text without colon
|
data[currentLabel] = texts.join('').trim().replace(/,\s*$/, '') // Remove trailing comma
|
||||||
currentLabel = $(node).text().replace(/:$/, '').trim()
|
}
|
||||||
texts = []
|
// New label - get text without colon
|
||||||
} else if (currentLabel) {
|
currentLabel = $(node).text().replace(/:$/, '').trim()
|
||||||
// Append the text content (text node or others)
|
texts = []
|
||||||
if (node.type === 'text') {
|
} else if (currentLabel) {
|
||||||
texts.push(node.data)
|
// Append the text content (text node or others)
|
||||||
} else if (node.type === 'tag' && node.name !== 'strong' && node.name !== 'br') {
|
if (node.type === 'text') {
|
||||||
texts.push($(node).text())
|
texts.push(node.data)
|
||||||
}
|
} else if (node.type === 'tag' && node.name !== 'strong' && node.name !== 'br') {
|
||||||
}
|
texts.push($(node).text())
|
||||||
})
|
}
|
||||||
|
}
|
||||||
// Save last label text
|
})
|
||||||
if (currentLabel && texts.length) {
|
|
||||||
data[currentLabel] = texts.join('').trim().replace(/,\s*$/, '')
|
// Save last label text
|
||||||
}
|
if (currentLabel && texts.length) {
|
||||||
|
data[currentLabel] = texts.join('').trim().replace(/,\s*$/, '')
|
||||||
const imgSrc = program.find('div[style*="float:left"]')?.find('img')?.attr('src') || null
|
}
|
||||||
|
|
||||||
return {
|
const imgSrc = program.find('div[style*="float:left"]')?.find('img')?.attr('src') || null
|
||||||
programHour,
|
|
||||||
title: programTitle,
|
return {
|
||||||
subTitle: programElementBold,
|
programHour,
|
||||||
category: programArea1,
|
title: programTitle,
|
||||||
description: description,
|
subTitle: programElementBold,
|
||||||
directorActors: data,
|
category: programArea1,
|
||||||
image: imgSrc
|
description: description,
|
||||||
}
|
directorActors: data,
|
||||||
}
|
image: imgSrc
|
||||||
|
}
|
||||||
function parseCategoryText(text) {
|
}
|
||||||
if (!text) return null
|
|
||||||
|
function parseCategoryText(text) {
|
||||||
const parts = text.split(',').map(s => s.trim()).filter(Boolean)
|
if (!text) return null
|
||||||
const len = parts.length
|
|
||||||
|
const parts = text
|
||||||
const category = parts[0] || null
|
.split(',')
|
||||||
|
.map(s => s.trim())
|
||||||
if (len < 3) {
|
.filter(Boolean)
|
||||||
return {
|
const len = parts.length
|
||||||
category: category,
|
|
||||||
duration: null,
|
const category = parts[0] || null
|
||||||
country: null,
|
|
||||||
productionDate: null,
|
if (len < 3) {
|
||||||
season: null,
|
return {
|
||||||
episode: null
|
category: category,
|
||||||
}
|
duration: null,
|
||||||
}
|
country: null,
|
||||||
|
productionDate: null,
|
||||||
// Check last part: date if numeric
|
season: null,
|
||||||
const dateCandidate = parts[len - 1]
|
episode: null
|
||||||
const productionDate = /^\d{4}$/.test(dateCandidate) ? dateCandidate : null
|
}
|
||||||
|
}
|
||||||
// Check for duration (first part containing "minutes")
|
|
||||||
let durationMinute = null
|
// Check last part: date if numeric
|
||||||
//let duration = null
|
const dateCandidate = parts[len - 1]
|
||||||
let episode = null
|
const productionDate = /^\d{4}$/.test(dateCandidate) ? dateCandidate : null
|
||||||
let season = null
|
|
||||||
let durationIndex = -1
|
// Check for duration (first part containing "minutes")
|
||||||
for (let i = 0; i < len; i++) {
|
let durationMinute = null
|
||||||
if (parts[i].toLowerCase().includes('minute')) {
|
//let duration = null
|
||||||
durationMinute = parts[i].trim()
|
let episode = null
|
||||||
durationMinute = durationMinute.replace('minutes', '')
|
let season = null
|
||||||
durationMinute = durationMinute.replace('minute', '')
|
let durationIndex = -1
|
||||||
//duration = [{ units: 'minutes', value: durationMinute }],
|
for (let i = 0; i < len; i++) {
|
||||||
durationIndex = i
|
if (parts[i].toLowerCase().includes('minute')) {
|
||||||
} else if (parts[i].toLowerCase().includes('épisode')) {
|
durationMinute = parts[i].trim()
|
||||||
const match = text.match(/épisode\s+(\d+)(?:\/(\d+))?/i)
|
durationMinute = durationMinute.replace('minutes', '')
|
||||||
if (match) {
|
durationMinute = durationMinute.replace('minute', '')
|
||||||
episode = parseInt(match[1], 10)
|
//duration = [{ units: 'minutes', value: durationMinute }],
|
||||||
}
|
durationIndex = i
|
||||||
} else if (parts[i].toLowerCase().includes('saison')) {
|
} else if (parts[i].toLowerCase().includes('épisode')) {
|
||||||
season = parts[i].replace('saison', '').trim()
|
const match = text.match(/épisode\s+(\d+)(?:\/(\d+))?/i)
|
||||||
}
|
if (match) {
|
||||||
}
|
episode = parseInt(match[1], 10)
|
||||||
|
}
|
||||||
// Country: second to last
|
} else if (parts[i].toLowerCase().includes('saison')) {
|
||||||
const countryIndex = len - 2
|
season = parts[i].replace('saison', '').trim()
|
||||||
let country = (durationIndex === countryIndex) ? null : parts[countryIndex]
|
}
|
||||||
|
}
|
||||||
return {
|
|
||||||
category,
|
// Country: second to last
|
||||||
durationMinute,
|
const countryIndex = len - 2
|
||||||
country,
|
let country = durationIndex === countryIndex ? null : parts[countryIndex]
|
||||||
productionDate,
|
|
||||||
season,
|
return {
|
||||||
episode
|
category,
|
||||||
}
|
durationMinute,
|
||||||
}
|
country,
|
||||||
|
productionDate,
|
||||||
function parseTitle($item) {
|
season,
|
||||||
return $item('.channel-programs-title a').text().trim()
|
episode
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function parseDescription($item) {
|
|
||||||
return $item('#descr').text().trim() || null
|
function parseTitle($item) {
|
||||||
}
|
return $item('.channel-programs-title a').text().trim()
|
||||||
|
}
|
||||||
function parseDescriptionURL($item) {
|
|
||||||
const descrLink = $item('#descr a')
|
function parseDescription($item) {
|
||||||
return descrLink.attr('href') || null
|
return $item('#descr').text().trim() || null
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCategory($item) {
|
function parseDescriptionURL($item) {
|
||||||
let type = null
|
const descrLink = $item('#descr a')
|
||||||
$item('.channel-programs-title span').each((i, span) => {
|
return descrLink.attr('href') || null
|
||||||
const className = $item(span).attr('class')
|
}
|
||||||
if (className && className.startsWith('text_bg')) {
|
|
||||||
type = $item(span).text().trim()
|
function parseCategory($item) {
|
||||||
}
|
let type = null
|
||||||
})
|
$item('.channel-programs-title span').each((i, span) => {
|
||||||
return type
|
const className = $item(span).attr('class')
|
||||||
}
|
if (className && className.startsWith('text_bg')) {
|
||||||
|
type = $item(span).text().trim()
|
||||||
function parseStart($item, itemDate) {
|
}
|
||||||
const dt = $item('.channel-programs-time a').text().trim()
|
})
|
||||||
if (!dt) return null
|
return type
|
||||||
|
}
|
||||||
const datetimeStr = `${itemDate} ${dt}`
|
|
||||||
return dayjs.tz(datetimeStr, 'YYYY-MM-DD HH:mm', PARIS_TZ)
|
function parseStart($item, itemDate) {
|
||||||
}
|
const dt = $item('.channel-programs-time a').text().trim()
|
||||||
|
if (!dt) return null
|
||||||
function parseItems(content) {
|
|
||||||
const $ = cheerio.load(content)
|
const datetimeStr = `${itemDate} ${dt}`
|
||||||
|
return dayjs.tz(datetimeStr, 'YYYY-MM-DD HH:mm', PARIS_TZ)
|
||||||
// Extract header information
|
}
|
||||||
const logoSrc = $('#logo img').attr('src')
|
|
||||||
const title = $('#title h1').text().trim()
|
function parseItems(content) {
|
||||||
const subtitle = $('#subtitle').text().trim()
|
const $ = cheerio.load(content)
|
||||||
const dateMatch = subtitle.match(/(\d{1,2} \w+ \d{4})/)
|
|
||||||
const dateStr = dateMatch ? dateMatch[1].toLowerCase() : null
|
// Extract header information
|
||||||
|
const logoSrc = $('#logo img').attr('src')
|
||||||
// Parse the French date string
|
const title = $('#title h1').text().trim()
|
||||||
const parsedDate = dayjs(dateStr, 'D MMMM YYYY', 'fr')
|
const subtitle = $('#subtitle').text().trim()
|
||||||
// Format it as YYYY-MM-DD
|
const dateMatch = subtitle.match(/(\d{1,2} \w+ \d{4})/)
|
||||||
const formattedDate = parsedDate.format('YYYY-MM-DD')
|
const dateStr = dateMatch ? dateMatch[1].toLowerCase() : null
|
||||||
|
|
||||||
const rows = $('.channel-row').toArray()
|
// Parse the French date string
|
||||||
|
const parsedDate = dayjs(dateStr, 'D MMMM YYYY', 'fr')
|
||||||
return {
|
// Format it as YYYY-MM-DD
|
||||||
rows,
|
const formattedDate = parsedDate.format('YYYY-MM-DD')
|
||||||
logoSrc,
|
|
||||||
title,
|
const rows = $('.channel-row').toArray()
|
||||||
formattedDate
|
|
||||||
}
|
return {
|
||||||
}
|
rows,
|
||||||
|
logoSrc,
|
||||||
|
title,
|
||||||
|
formattedDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,83 +1,85 @@
|
|||||||
const { parser, url } = require('./guidetnt.com.config.js')
|
const { parser, url } = require('./guidetnt.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
require('dayjs/locale/fr')
|
require('dayjs/locale/fr')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-07-01', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-07-01', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'tf1',
|
site_id: 'tf1',
|
||||||
xmltv_id: 'TF1.fr'
|
xmltv_id: 'TF1.fr'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://www.guidetnt.com/tv/programme-tf1')
|
expect(url({ channel })).toBe('https://www.guidetnt.com/tv/programme-tf1')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', async () => {
|
it('can parse response', async () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
let results = await parser({ content, date })
|
let results = await parser({ content, date })
|
||||||
results = results.map(p => {
|
results = results.map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results.length).toBe(29)
|
expect(results.length).toBe(29)
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
category: 'Série',
|
category: 'Série',
|
||||||
description: 'Grande effervescence pour toute l\'équipe du Camping Paradis, qui prépare les Olympiades. Côté arrivants, Hélène et sa fille Eva viennent passer quelques jours dans le but d\'optimiser les révisions d\'E...',
|
description:
|
||||||
start: '2025-06-30T22:55:00.000Z',
|
"Grande effervescence pour toute l'équipe du Camping Paradis, qui prépare les Olympiades. Côté arrivants, Hélène et sa fille Eva viennent passer quelques jours dans le but d'optimiser les révisions d'E...",
|
||||||
stop: '2025-06-30T23:45:00.000Z',
|
start: '2025-06-30T22:55:00.000Z',
|
||||||
title: 'Camping Paradis'
|
stop: '2025-06-30T23:45:00.000Z',
|
||||||
})
|
title: 'Camping Paradis'
|
||||||
expect(results[2]).toMatchObject({
|
})
|
||||||
category: 'Magazine',
|
expect(results[2]).toMatchObject({
|
||||||
description: 'Retrouvez tous vos programmes de nuit.',
|
category: 'Magazine',
|
||||||
start: '2025-07-01T00:55:00.000Z',
|
description: 'Retrouvez tous vos programmes de nuit.',
|
||||||
stop: '2025-07-01T04:00:00.000Z',
|
start: '2025-07-01T00:55:00.000Z',
|
||||||
title: 'Programmes de la nuit'
|
stop: '2025-07-01T04:00:00.000Z',
|
||||||
})
|
title: 'Programmes de la nuit'
|
||||||
expect(results[15]).toMatchObject({
|
})
|
||||||
category: 'Téléfilm',
|
expect(results[15]).toMatchObject({
|
||||||
description: 'La vie quasi parfaite de Riley bascule brutalement lorsqu\'un accident de voiture lui coûte la vie, laissant derrière elle sa famille. Alors que l\'enquête débute, l\'affaire prend une tournure étrange l...',
|
category: 'Téléfilm',
|
||||||
start: '2025-07-01T12:25:00.000Z',
|
description:
|
||||||
stop: '2025-07-01T14:00:00.000Z',
|
"La vie quasi parfaite de Riley bascule brutalement lorsqu'un accident de voiture lui coûte la vie, laissant derrière elle sa famille. Alors que l'enquête débute, l'affaire prend une tournure étrange l...",
|
||||||
title: 'Trahie par l\'amour'
|
start: '2025-07-01T12:25:00.000Z',
|
||||||
})
|
stop: '2025-07-01T14:00:00.000Z',
|
||||||
})
|
title: "Trahie par l'amour"
|
||||||
|
})
|
||||||
it('can parse response for current day', async () => {
|
})
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
|
||||||
let results = await parser({ content, date: dayjs.utc('2025-07-01', 'YYYY-MM-DD').startOf('d') })
|
it('can parse response for current day', async () => {
|
||||||
results = results.map(p => {
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
p.start = p.start.toJSON()
|
let results = await parser({ content, date: dayjs.utc('2025-07-01', 'YYYY-MM-DD').startOf('d') })
|
||||||
p.stop = p.stop.toJSON()
|
results = results.map(p => {
|
||||||
return p
|
p.start = p.start.toJSON()
|
||||||
}
|
p.stop = p.stop.toJSON()
|
||||||
)
|
return p
|
||||||
|
})
|
||||||
expect(results.length).toBe(29)
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results.length).toBe(29)
|
||||||
category: 'Série',
|
expect(results[0]).toMatchObject({
|
||||||
description: 'Grande effervescence pour toute l\'équipe du Camping Paradis, qui prépare les Olympiades. Côté arrivants, Hélène et sa fille Eva viennent passer quelques jours dans le but d\'optimiser les révisions d\'E...',
|
category: 'Série',
|
||||||
start: '2025-06-30T22:55:00.000Z',
|
description:
|
||||||
stop: '2025-06-30T23:45:00.000Z',
|
"Grande effervescence pour toute l'équipe du Camping Paradis, qui prépare les Olympiades. Côté arrivants, Hélène et sa fille Eva viennent passer quelques jours dans le but d'optimiser les révisions d'E...",
|
||||||
title: 'Camping Paradis'
|
start: '2025-06-30T22:55:00.000Z',
|
||||||
})
|
stop: '2025-06-30T23:45:00.000Z',
|
||||||
})
|
title: 'Camping Paradis'
|
||||||
|
})
|
||||||
it('can handle empty guide', async () => {
|
})
|
||||||
const results = await parser({
|
|
||||||
date,
|
it('can handle empty guide', async () => {
|
||||||
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'))
|
const results = await parser({
|
||||||
})
|
date,
|
||||||
|
content: fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'))
|
||||||
expect(results).toEqual([])
|
})
|
||||||
})
|
|
||||||
|
expect(results).toEqual([])
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,78 +1,82 @@
|
|||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'tataplay.com',
|
site: 'tataplay.com',
|
||||||
days: 1,
|
days: 1,
|
||||||
|
|
||||||
url({ date }) {
|
url({ date }) {
|
||||||
return `https://tm.tapi.videoready.tv/content-detail/pub/api/v2/channels/schedule?date=${date.format('DD-MM-YYYY')}`
|
return `https://tm.tapi.videoready.tv/content-detail/pub/api/v2/channels/schedule?date=${date.format(
|
||||||
},
|
'DD-MM-YYYY'
|
||||||
|
)}`
|
||||||
request: {
|
},
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
request: {
|
||||||
'Accept': '*/*',
|
method: 'POST',
|
||||||
'Origin': 'https://watch.tataplay.com',
|
headers: {
|
||||||
'Referer': 'https://watch.tataplay.com/',
|
Accept: '*/*',
|
||||||
'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',
|
Origin: 'https://watch.tataplay.com',
|
||||||
'content-type': 'application/json',
|
Referer: 'https://watch.tataplay.com/',
|
||||||
'locale': 'ENG',
|
'User-Agent':
|
||||||
'platform': 'web'
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||||
},
|
'content-type': 'application/json',
|
||||||
data({ channel }) {
|
locale: 'ENG',
|
||||||
return { id: channel.site_id }
|
platform: 'web'
|
||||||
}
|
},
|
||||||
},
|
data({ channel }) {
|
||||||
|
return { id: channel.site_id }
|
||||||
parser(context) {
|
}
|
||||||
let data = []
|
},
|
||||||
try {
|
|
||||||
const json = JSON.parse(context.content)
|
parser(context) {
|
||||||
const programs = json?.data?.epg || []
|
let data = []
|
||||||
|
try {
|
||||||
data = programs.map(program => ({
|
const json = JSON.parse(context.content)
|
||||||
title: program.title,
|
const programs = json?.data?.epg || []
|
||||||
start: program.startTime,
|
|
||||||
stop: program.endTime,
|
data = programs.map(program => ({
|
||||||
description: program.desc,
|
title: program.title,
|
||||||
category: program.category,
|
start: program.startTime,
|
||||||
icon: program.boxCoverImage
|
stop: program.endTime,
|
||||||
}))
|
description: program.desc,
|
||||||
} catch {
|
category: program.category,
|
||||||
data = []
|
icon: program.boxCoverImage
|
||||||
}
|
}))
|
||||||
return data
|
} catch {
|
||||||
},
|
data = []
|
||||||
|
}
|
||||||
async channels() {
|
return data
|
||||||
const headers = {
|
},
|
||||||
'Accept': '*/*',
|
|
||||||
'Origin': 'https://watch.tataplay.com',
|
async channels() {
|
||||||
'Referer': 'https://watch.tataplay.com/',
|
const 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: '*/*',
|
||||||
'content-type': 'application/json',
|
Origin: 'https://watch.tataplay.com',
|
||||||
'locale': 'ENG',
|
Referer: 'https://watch.tataplay.com/',
|
||||||
'platform': 'web'
|
'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',
|
||||||
|
'content-type': 'application/json',
|
||||||
const baseUrl = 'https://tm.tapi.videoready.tv/portal-search/pub/api/v1/channels/schedule'
|
locale: 'ENG',
|
||||||
const initialUrl = `${baseUrl}?date=&languageFilters=&genreFilters=&limit=20&offset=0`
|
platform: 'web'
|
||||||
const initialResponse = await axios.get(initialUrl, { headers })
|
}
|
||||||
const total = initialResponse.data?.data?.total || 0
|
|
||||||
const channels = []
|
const baseUrl = 'https://tm.tapi.videoready.tv/portal-search/pub/api/v1/channels/schedule'
|
||||||
|
const initialUrl = `${baseUrl}?date=&languageFilters=&genreFilters=&limit=20&offset=0`
|
||||||
for (let offset = 0; offset < total; offset += 20) {
|
const initialResponse = await axios.get(initialUrl, { headers })
|
||||||
const url = `${baseUrl}?date=&languageFilters=&genreFilters=&limit=20&offset=${offset}`
|
const total = initialResponse.data?.data?.total || 0
|
||||||
const response = await axios.get(url, { headers })
|
const channels = []
|
||||||
const page = response.data?.data?.channelList || []
|
|
||||||
channels.push(...page)
|
for (let offset = 0; offset < total; offset += 20) {
|
||||||
}
|
const url = `${baseUrl}?date=&languageFilters=&genreFilters=&limit=20&offset=${offset}`
|
||||||
|
const response = await axios.get(url, { headers })
|
||||||
return channels.map(channel => ({
|
const page = response.data?.data?.channelList || []
|
||||||
site_id: channel.id,
|
channels.push(...page)
|
||||||
name: channel.title,
|
}
|
||||||
lang: 'en',
|
|
||||||
icon: channel.transparentImageUrl || channel.thumbnailImage
|
return channels.map(channel => ({
|
||||||
}))
|
site_id: channel.id,
|
||||||
}
|
name: channel.title,
|
||||||
}
|
lang: 'en',
|
||||||
|
icon: channel.transparentImageUrl || channel.thumbnailImage
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,87 +1,89 @@
|
|||||||
const { parser, url, channels } = require('./tataplay.com.config.js')
|
const { parser, url, channels } = require('./tataplay.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-06-09', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2025-06-09', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = { site_id: '1001' }
|
const channel = { site_id: '1001' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe('https://tm.tapi.videoready.tv/content-detail/pub/api/v2/channels/schedule?date=09-06-2025')
|
expect(url({ channel, date })).toBe(
|
||||||
})
|
'https://tm.tapi.videoready.tv/content-detail/pub/api/v2/channels/schedule?date=09-06-2025'
|
||||||
|
)
|
||||||
it('can parse response', () => {
|
})
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
|
||||||
|
it('can parse response', () => {
|
||||||
const results = parser({ content, date })
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
|
|
||||||
expect(results.length).toBe(2)
|
const results = parser({ content, date })
|
||||||
expect(results[0]).toMatchObject({
|
|
||||||
title: 'Yeh Rishta Kya Kehlata Hai',
|
expect(results.length).toBe(2)
|
||||||
start: '2025-06-09T18:00:00.000Z',
|
expect(results[0]).toMatchObject({
|
||||||
stop: '2025-06-09T18:30:00.000Z',
|
title: 'Yeh Rishta Kya Kehlata Hai',
|
||||||
description: 'The story of the Rajshri family and their journey through life.',
|
start: '2025-06-09T18:00:00.000Z',
|
||||||
category: 'Drama',
|
stop: '2025-06-09T18:30:00.000Z',
|
||||||
icon: 'https://img.tataplay.com/thumbnails/1001/yeh-rishta.jpg'
|
description: 'The story of the Rajshri family and their journey through life.',
|
||||||
})
|
category: 'Drama',
|
||||||
expect(results[1]).toMatchObject({
|
icon: 'https://img.tataplay.com/thumbnails/1001/yeh-rishta.jpg'
|
||||||
title: 'Anupamaa',
|
})
|
||||||
start: '2025-06-09T18:30:00.000Z',
|
expect(results[1]).toMatchObject({
|
||||||
stop: '2025-06-09T19:00:00.000Z',
|
title: 'Anupamaa',
|
||||||
description: 'The story of Anupamaa, a housewife who rediscovers herself.',
|
start: '2025-06-09T18:30:00.000Z',
|
||||||
category: 'Drama',
|
stop: '2025-06-09T19:00:00.000Z',
|
||||||
icon: 'https://img.tataplay.com/thumbnails/1001/anupamaa.jpg'
|
description: 'The story of Anupamaa, a housewife who rediscovers herself.',
|
||||||
})
|
category: 'Drama',
|
||||||
})
|
icon: 'https://img.tataplay.com/thumbnails/1001/anupamaa.jpg'
|
||||||
|
})
|
||||||
it('can handle empty guide', () => {
|
})
|
||||||
const content = JSON.stringify({ data: { epg: [] } })
|
|
||||||
const results = parser({ content, date })
|
it('can handle empty guide', () => {
|
||||||
expect(results).toMatchObject([])
|
const content = JSON.stringify({ data: { epg: [] } })
|
||||||
})
|
const results = parser({ content, date })
|
||||||
|
expect(results).toMatchObject([])
|
||||||
it('can parse channel list', async () => {
|
})
|
||||||
const mockResponse = {
|
|
||||||
data: {
|
it('can parse channel list', async () => {
|
||||||
data: {
|
const mockResponse = {
|
||||||
total: 2,
|
data: {
|
||||||
channelList: [
|
data: {
|
||||||
{
|
total: 2,
|
||||||
id: '1001',
|
channelList: [
|
||||||
title: 'Star Plus',
|
{
|
||||||
transparentImageUrl: 'https://img.tataplay.com/channels/1001/logo.png'
|
id: '1001',
|
||||||
},
|
title: 'Star Plus',
|
||||||
{
|
transparentImageUrl: 'https://img.tataplay.com/channels/1001/logo.png'
|
||||||
id: '1002',
|
},
|
||||||
title: 'Sony TV',
|
{
|
||||||
transparentImageUrl: 'https://img.tataplay.com/channels/1002/logo.png'
|
id: '1002',
|
||||||
}
|
title: 'Sony TV',
|
||||||
]
|
transparentImageUrl: 'https://img.tataplay.com/channels/1002/logo.png'
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Mock axios.get to return our test data
|
}
|
||||||
const axios = require('axios')
|
|
||||||
axios.get = jest.fn().mockResolvedValue(mockResponse)
|
// Mock axios.get to return our test data
|
||||||
|
const axios = require('axios')
|
||||||
const results = await channels()
|
axios.get = jest.fn().mockResolvedValue(mockResponse)
|
||||||
|
|
||||||
expect(results.length).toBe(2)
|
const results = await channels()
|
||||||
expect(results[0]).toMatchObject({
|
|
||||||
site_id: '1001',
|
expect(results.length).toBe(2)
|
||||||
name: 'Star Plus',
|
expect(results[0]).toMatchObject({
|
||||||
lang: 'en',
|
site_id: '1001',
|
||||||
icon: 'https://img.tataplay.com/channels/1001/logo.png'
|
name: 'Star Plus',
|
||||||
})
|
lang: 'en',
|
||||||
expect(results[1]).toMatchObject({
|
icon: 'https://img.tataplay.com/channels/1001/logo.png'
|
||||||
site_id: '1002',
|
})
|
||||||
name: 'Sony TV',
|
expect(results[1]).toMatchObject({
|
||||||
lang: 'en',
|
site_id: '1002',
|
||||||
icon: 'https://img.tataplay.com/channels/1002/logo.png'
|
name: 'Sony TV',
|
||||||
})
|
lang: 'en',
|
||||||
})
|
icon: 'https://img.tataplay.com/channels/1002/logo.png'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user