mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-09 08:30:06 -04:00
Fix linter issues in sites/
This commit is contained in:
parent
d6d20b6413
commit
5df982bb7c
129 changed files with 3316 additions and 3226 deletions
|
@ -92,7 +92,7 @@ function parseItems(content, channel) {
|
|||
const [, channelId] = channel.site_id.split('#')
|
||||
const channelData = data.schedule.find(i => i.channel == channelId)
|
||||
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { parser, url, request } = require('./awilime.com.config.js')
|
||||
const { parser, url } = require('./awilime.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
@ -62,7 +61,7 @@ function parseItems(content) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const { parser, url } = require('./beinsports.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
const axios = require('axios');
|
||||
const cheerio = require('cheerio');
|
||||
const dayjs = require('dayjs');
|
||||
const utc = require('dayjs/plugin/utc');
|
||||
const timezone = require('dayjs/plugin/timezone');
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'chada.ma',
|
||||
|
@ -19,36 +18,38 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
url() {
|
||||
return 'https://chada.ma/fr/chada-tv/grille-tv/';
|
||||
return 'https://chada.ma/fr/chada-tv/grille-tv/'
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const $ = cheerio.load(content);
|
||||
const programs = [];
|
||||
const $ = cheerio.load(content)
|
||||
const programs = []
|
||||
|
||||
$('#stopfix .posts-area h2').each((i, element) => {
|
||||
const timeRange = $(element).text().trim();
|
||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()));
|
||||
const timeRange = $(element).text().trim()
|
||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()))
|
||||
|
||||
const titleElement = $(element).next('div').next('h3');
|
||||
const title = titleElement.text().trim();
|
||||
const titleElement = $(element).next('div').next('h3')
|
||||
const title = titleElement.text().trim()
|
||||
|
||||
const description = titleElement.next('div').text().trim() || 'No description available';
|
||||
const description = titleElement.next('div').text().trim() || 'No description available'
|
||||
|
||||
programs.push({
|
||||
title,
|
||||
description,
|
||||
start,
|
||||
stop
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
return programs;
|
||||
return programs
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function parseProgramTime(timeStr) {
|
||||
const timeZone = 'Africa/Casablanca';
|
||||
const currentDate = dayjs().format('YYYY-MM-DD');
|
||||
const timeZone = 'Africa/Casablanca'
|
||||
const currentDate = dayjs().format('YYYY-MM-DD')
|
||||
|
||||
return dayjs.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone).format('YYYY-MM-DDTHH:mm:ssZ');
|
||||
return dayjs
|
||||
.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone)
|
||||
.format('YYYY-MM-DDTHH:mm:ssZ')
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
const { parser, url } = require('./chada.ma.config.js')
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const cheerio = require('cheerio')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
@ -27,11 +25,11 @@ const mockHtmlContent = `
|
|||
<div class="ssprogramme row"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
|
||||
});
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = mockHtmlContent
|
||||
|
@ -44,8 +42,8 @@ it('can parse response', () => {
|
|||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Bloc Prime + Clips",
|
||||
description: "No description available",
|
||||
title: 'Bloc Prime + Clips',
|
||||
description: 'No description available',
|
||||
start: dayjs.tz('00:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ'),
|
||||
stop: dayjs.tz('09:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ module.exports = {
|
|||
},
|
||||
async channels() {
|
||||
const html = await axios
|
||||
.get(`https://chaines-tv.orange.fr/programme-tv?filtres=all`)
|
||||
.get('https://chaines-tv.orange.fr/programme-tv?filtres=all')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
},
|
||||
async channels() {
|
||||
const data = await axios
|
||||
.get(`https://contenthub-api.eco.astro.com.my/channel/all.json`)
|
||||
.get('https://contenthub-api.eco.astro.com.my/channel/all.json')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
@ -85,7 +85,7 @@ function parseItems(content, date) {
|
|||
const schedules = data.response.schedule
|
||||
|
||||
return schedules[date.format('YYYY-MM-DD')] || []
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,13 @@ module.exports = {
|
|||
},
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'referer': 'https://www.cosmotetv.gr/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
'Accept': '*/*',
|
||||
referer: 'https://www.cosmotetv.gr/',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
Accept: '*/*',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||
'Origin': 'https://www.cosmotetv.gr',
|
||||
Origin: 'https://www.cosmotetv.gr',
|
||||
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
|
@ -30,12 +31,12 @@ module.exports = {
|
|||
'Sec-Fetch-Site': 'cross-site'
|
||||
}
|
||||
},
|
||||
url: function ({date, channel}) {
|
||||
url: function ({ date, channel }) {
|
||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
||||
return `https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
||||
},
|
||||
parser: function ({ date, content }) {
|
||||
parser: function ({ content }) {
|
||||
let programs = []
|
||||
const data = JSON.parse(content)
|
||||
data.channels.forEach(channel => {
|
||||
|
@ -57,16 +58,19 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('axios')
|
||||
try {
|
||||
const response = await axios.get('https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el', {
|
||||
headers: this.request.headers
|
||||
})
|
||||
const response = await axios.get(
|
||||
'https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el',
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
const data = response.data
|
||||
|
||||
if (data && data.channels) {
|
||||
return data.channels.map(item => ({
|
||||
lang: 'el',
|
||||
site_id: item.callSign,
|
||||
name: item.title,
|
||||
name: item.title
|
||||
//logo: item.logos.square
|
||||
}))
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
const { parser, url, channels } = require('./cosmotetv.gr.config.js')
|
||||
const { parser, url } = require('./cosmotetv.gr.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const axios = require('axios')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
@ -14,34 +13,22 @@ jest.mock('axios')
|
|||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = { site_id: 'vouli', xmltv_id: 'HellenicParliamentTV.gr' }
|
||||
|
||||
const mockChannelData = {
|
||||
"channels": [
|
||||
{
|
||||
"guid": "XTV100000954",
|
||||
"title": "ΒΟΥΛΗ HD",
|
||||
"callSign": "vouli",
|
||||
"logos": {
|
||||
"square": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-normal.png",
|
||||
"wide": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-wide.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mockEpgData = {
|
||||
"channels": [
|
||||
channels: [
|
||||
{
|
||||
"items": [
|
||||
items: [
|
||||
{
|
||||
"startTime": "2024-12-26T23:00:00+00:00",
|
||||
"endTime": "2024-12-27T00:00:00+00:00",
|
||||
"title": "Τι Λέει ο Νόμος",
|
||||
"description": "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
||||
"qoe": {
|
||||
"genre": "Special"
|
||||
startTime: '2024-12-26T23:00:00+00:00',
|
||||
endTime: '2024-12-27T00:00:00+00:00',
|
||||
title: 'Τι Λέει ο Νόμος',
|
||||
description:
|
||||
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||
qoe: {
|
||||
genre: 'Special'
|
||||
},
|
||||
"thumbnails": {
|
||||
"standard": "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg"
|
||||
thumbnails: {
|
||||
standard:
|
||||
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -52,7 +39,9 @@ const mockEpgData = {
|
|||
it('can generate valid url', () => {
|
||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
||||
expect(url({ date, channel })).toBe(`https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`)
|
||||
expect(url({ date, channel })).toBe(
|
||||
`https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
@ -65,17 +54,23 @@ it('can parse response', () => {
|
|||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Τι Λέει ο Νόμος",
|
||||
description: "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
||||
category: "Special",
|
||||
image: "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg",
|
||||
start: "2024-12-26T23:00:00.000Z",
|
||||
stop: "2024-12-27T00:00:00.000Z"
|
||||
title: 'Τι Λέει ο Νόμος',
|
||||
description:
|
||||
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||
category: 'Special',
|
||||
image:
|
||||
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg',
|
||||
start: '2024-12-26T23:00:00.000Z',
|
||||
stop: '2024-12-27T00:00:00.000Z'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({ date, channel, content: '{"date":"2024-12-26","categories":[],"channels":[]}' });
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: '{"date":"2024-12-26","categories":[],"channels":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -9,7 +9,9 @@ module.exports = {
|
|||
site: 'cubmu.com',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format('YYYY-MM-DD')}&channel_id=${channel.site_id}`
|
||||
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}&channel_id=${channel.site_id}`
|
||||
},
|
||||
parser({ content, channel }) {
|
||||
const programs = []
|
||||
|
@ -46,13 +48,19 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
// login to service bus
|
||||
const token = await axios
|
||||
.post(`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`, options)
|
||||
await axios
|
||||
.post(
|
||||
`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`,
|
||||
options
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
// list channels
|
||||
const subscribedChannels = await axios
|
||||
.post(`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`, options)
|
||||
.post(
|
||||
`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`,
|
||||
options
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
|
@ -98,5 +106,9 @@ function parseStart(item) {
|
|||
}
|
||||
|
||||
function parseStop(item) {
|
||||
return dayjs.tz([item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '), 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
||||
return dayjs.tz(
|
||||
[item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '),
|
||||
'YYYY-MM-DD HH:mm:ss',
|
||||
'Asia/Jakarta'
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
site: 'dens.tv',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format('YYYY-MM-DD')}&id_channel=${
|
||||
channel.site_id
|
||||
}&app_type=10`
|
||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}&id_channel=${channel.site_id}&app_type=10`
|
||||
},
|
||||
parser({ content }) {
|
||||
// parsing
|
||||
|
@ -25,8 +25,9 @@ module.exports = {
|
|||
if (Array.isArray(response?.data)) {
|
||||
response.data.forEach(item => {
|
||||
const title = item.title
|
||||
const [, , , season, , , episode] = title.match(/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/) ||
|
||||
[null, null, null, null, null, null, null]
|
||||
const [, , , season, , , episode] = title.match(
|
||||
/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/
|
||||
) || [null, null, null, null, null, null, null]
|
||||
programs.push({
|
||||
title,
|
||||
description: item.description,
|
||||
|
@ -52,7 +53,7 @@ module.exports = {
|
|||
const channels = []
|
||||
for (const id_category of Object.values(categories)) {
|
||||
const data = await axios
|
||||
.get(`https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory`, {
|
||||
.get('https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory', {
|
||||
params: { id_category }
|
||||
})
|
||||
.then(r => r.data)
|
||||
|
|
|
@ -10,7 +10,9 @@ const date = dayjs.utc('2024-11-24').startOf('d')
|
|||
const channel = { site_id: '38', xmltv_id: 'AniplusAsia.sg', lang: 'id' }
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe('https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10')
|
||||
expect(url({ channel, date })).toBe(
|
||||
'https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
|
|
@ -65,7 +65,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://www.digiturk.com.tr/`, {
|
||||
.get('https://www.digiturk.com.tr/', {
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
|||
$('.pgrid').each((i, el) => {
|
||||
const onclick = $(el).find('.chnl-logo').attr('onclick')
|
||||
const number = $(el).find('.cnl-fav > a > span').text().trim()
|
||||
const [, name, site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
||||
const [, , site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
||||
null,
|
||||
'',
|
||||
''
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = {
|
|||
const channels = []
|
||||
for (let provider of providers) {
|
||||
const data = await axios
|
||||
.post(`https://www.guida.tv/guide/schedule`, null, {
|
||||
.post('https://www.guida.tv/guide/schedule', null, {
|
||||
params: {
|
||||
provider,
|
||||
region: 'Italy',
|
||||
|
@ -81,7 +81,7 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
|
||||
function parseStart($item, date, channel) {
|
||||
function parseStart($item, date) {
|
||||
const timeString = $item('td:eq(0)').text().trim()
|
||||
const dateString = `${date.format('YYYY-MM-DD')} ${timeString}`
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://guidatv.sky.it/canali`)
|
||||
.get('https://guidatv.sky.it/canali')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = {
|
|||
url: function ({ channel, date }) {
|
||||
return `https://epg-file.hoy.tv/hoy/OTT${channel.site_id}${date.format('YYYYMMDD')}.xml`
|
||||
},
|
||||
parser({ content, channel, date }) {
|
||||
parser({ content, date }) {
|
||||
const data = convert.xml2js(content, {
|
||||
compact: true,
|
||||
ignoreDeclaration: true,
|
||||
|
@ -28,7 +28,7 @@ module.exports = {
|
|||
for (let item of data.ProgramGuide.Channel.EpgItem) {
|
||||
const start = dayjs.tz(item.EpgStartDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||
|
||||
if (! date.isSame(start, 'day')) {
|
||||
if (!date.isSame(start, 'day')) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -40,13 +40,13 @@ module.exports = {
|
|||
sub_title: subtitle,
|
||||
description: item.EpisodeInfo.EpisodeLongDescription._text,
|
||||
start,
|
||||
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong'),
|
||||
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang }) {
|
||||
async channels() {
|
||||
const data = await axios
|
||||
.get('https://api2.hoy.tv/api/v2/a/channel')
|
||||
.then(r => r.data)
|
||||
|
@ -56,7 +56,7 @@ module.exports = {
|
|||
return {
|
||||
site_id: c.videos.id,
|
||||
name: c.name.zh_hk,
|
||||
lang: 'zh',
|
||||
lang: 'zh'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -87,9 +87,7 @@ const content = `<?xml version="1.0" encoding="UTF-8" ?>
|
|||
</ProgramGuide>`
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe(
|
||||
'https://epg-file.hoy.tv/hoy/OTT7620240913.xml'
|
||||
)
|
||||
expect(url({ channel, date })).toBe('https://epg-file.hoy.tv/hoy/OTT7620240913.xml')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
@ -104,13 +102,14 @@ it('can parse response', () => {
|
|||
start: '2024-09-13T03:30:00.000Z',
|
||||
stop: '2024-09-13T04:30:00.000Z',
|
||||
title: '點講都係一家人[PG]',
|
||||
sub_title: '第46集',
|
||||
sub_title: '第46集'
|
||||
},
|
||||
{
|
||||
start: '2024-09-13T04:30:00.000Z',
|
||||
stop: '2024-09-13T05:30:00.000Z',
|
||||
title: '麝香之路',
|
||||
description: 'Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world',
|
||||
description:
|
||||
'Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
|
@ -172,14 +172,17 @@ function parseItems(content, channel, date) {
|
|||
if (!data || !Array.isArray(data.programs)) return []
|
||||
|
||||
return data.programs
|
||||
.filter(p => p.channel === site_id && dayjs(p.start, 'YYYYMMDDHHmmss ZZ').isBetween(curr_day, next_day))
|
||||
.filter(
|
||||
p =>
|
||||
p.channel === site_id && dayjs(p.start, 'YYYYMMDDHHmmss ZZ').isBetween(curr_day, next_day)
|
||||
)
|
||||
.map(p => {
|
||||
if (Array.isArray(p.date) && p.date.length) {
|
||||
p.date = p.date[0]
|
||||
}
|
||||
return p
|
||||
})
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,27 +12,30 @@ module.exports = {
|
|||
site: 'ipko.tv',
|
||||
timezone: 'Europe/Belgrade',
|
||||
days: 5,
|
||||
url({ date, channel }) { return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData' },
|
||||
url() {
|
||||
return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Host': 'stargate.ipko.tv',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
Host: 'stargate.ipko.tv',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sq',
|
||||
'Origin': 'https://ipko.tv',
|
||||
Origin: 'https://ipko.tv',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
'Connection': 'keep-alive'
|
||||
Connection: 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix();
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
||||
const todayEpoch = date.startOf('day').unix()
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||
return JSON.stringify({
|
||||
ch_ext_id: channel.site_id,
|
||||
from: todayEpoch,
|
||||
|
@ -41,11 +44,11 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const programs = [];
|
||||
const data = JSON.parse(content);
|
||||
const programs = []
|
||||
const data = JSON.parse(content)
|
||||
data.shows.forEach(show => {
|
||||
const start = dayjs.unix(show.show_start).utc();
|
||||
const stop = dayjs.unix(show.show_end).utc();
|
||||
const start = dayjs.unix(show.show_start).utc()
|
||||
const stop = dayjs.unix(show.show_end).utc()
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
|
@ -58,15 +61,19 @@ module.exports = {
|
|||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post('https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
||||
headers: this.request.headers
|
||||
});
|
||||
const response = await axios.post(
|
||||
'https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList',
|
||||
JSON.stringify({ includeRadioStations: true }),
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
|
||||
const data = response.data.data;
|
||||
const data = response.data.data
|
||||
return data.map(item => ({
|
||||
lang: 'sq',
|
||||
name: String(item.channel.title),
|
||||
site_id: String(item.channel.id),
|
||||
site_id: String(item.channel.id)
|
||||
//logo: String(item.channel.logo)
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -76,33 +76,29 @@ it('can parse response', () => {
|
|||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content, channel }).map(p => {
|
||||
p.start = p.start
|
||||
p.stop = p.stop
|
||||
return p
|
||||
})
|
||||
const result = parser({ content, channel })
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
description: "No description available",
|
||||
start: "2024-12-24T04:00:00.000Z",
|
||||
stop: "2024-12-24T06:00:00.000Z",
|
||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
||||
title: 'IPKO Promo',
|
||||
description: 'No description available',
|
||||
start: '2024-12-24T04:00:00.000Z',
|
||||
stop: '2024-12-24T06:00:00.000Z',
|
||||
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||
},
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
description: "No description available",
|
||||
start: "2024-12-24T06:00:00.000Z",
|
||||
stop: "2024-12-24T08:00:00.000Z",
|
||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
||||
title: 'IPKO Promo',
|
||||
description: 'No description available',
|
||||
start: '2024-12-24T06:00:00.000Z',
|
||||
stop: '2024-12-24T08:00:00.000Z',
|
||||
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||
},
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
description: "No description available",
|
||||
start: "2024-12-24T08:00:00.000Z",
|
||||
stop: "2024-12-24T10:00:00.000Z",
|
||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
||||
title: 'IPKO Promo',
|
||||
description: 'No description available',
|
||||
start: '2024-12-24T08:00:00.000Z',
|
||||
stop: '2024-12-24T10:00:00.000Z',
|
||||
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://m.tv.sms.cz/?zmen_stanice=true`)
|
||||
.get('https://m.tv.sms.cz/?zmen_stanice=true')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -77,8 +77,8 @@ function parseItems(content) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://player.maxtvtogo.tportal.hr:8082/OTT4Proxy/proxy/epg/channels`)
|
||||
.get('https://player.maxtvtogo.tportal.hr:8082/OTT4Proxy/proxy/epg/channels')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ function parseStop($item) {
|
|||
|
||||
try {
|
||||
return dayjs(timeString, 'YYYY-MM-DD HH:mm:ssZZ')
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,31 +10,35 @@ dayjs.extend(timezone)
|
|||
module.exports = {
|
||||
site: 'mediasetinfinity.mediaset.it',
|
||||
days: 2,
|
||||
url: function ({date, channel}) {
|
||||
url: function ({ date, channel }) {
|
||||
// Get the epoch timestamp
|
||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||
// Get the epoch timestamp for the next day
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
||||
return `https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=${todayEpoch}~${nextDayEpoch}&byCallSign=${channel.site_id}`
|
||||
},
|
||||
parser: function ({content}) {
|
||||
parser: function ({ content }) {
|
||||
const programs = []
|
||||
const data = JSON.parse(content)
|
||||
|
||||
if (!data.response || !data.response.entries || !data.response.entries[0] || !data.response.entries[0].listings) {
|
||||
if (
|
||||
!data.response ||
|
||||
!data.response.entries ||
|
||||
!data.response.entries[0] ||
|
||||
!data.response.entries[0].listings
|
||||
) {
|
||||
// If the structure is not as expected, return an empty array
|
||||
return programs
|
||||
}
|
||||
|
||||
const listings = data.response.entries[0].listings
|
||||
|
||||
listings.forEach((listing) => {
|
||||
listings.forEach(listing => {
|
||||
const title = listing.mediasetlisting$epgTitle
|
||||
const subTitle = listing.program.title
|
||||
const season = parseSeason(listing)
|
||||
const episode = parseEpisode(listing)
|
||||
|
||||
|
||||
if (listing.program.title && listing.startTime && listing.endTime) {
|
||||
programs.push({
|
||||
title: title || subTitle,
|
||||
|
@ -54,7 +58,6 @@ module.exports = {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
function parseTime(timestamp) {
|
||||
return dayjs(timestamp).utc().format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
@ -77,17 +80,18 @@ function getMaxResolutionThumbnails(item) {
|
|||
|
||||
for (const key in thumbnails) {
|
||||
const type = key.split('-')[0] // Estrarre il tipo di thumbnail
|
||||
const {width, height, url, title} = thumbnails[key]
|
||||
const { width, height, url, title } = thumbnails[key]
|
||||
|
||||
if (!maxResolutionThumbnails[type] ||
|
||||
(width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height)) {
|
||||
maxResolutionThumbnails[type] = {width, height, url, title}
|
||||
if (
|
||||
!maxResolutionThumbnails[type] ||
|
||||
width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height
|
||||
) {
|
||||
maxResolutionThumbnails[type] = { width, height, url, title }
|
||||
}
|
||||
}
|
||||
if (maxResolutionThumbnails.image_keyframe_poster)
|
||||
return maxResolutionThumbnails.image_keyframe_poster.url
|
||||
else if (maxResolutionThumbnails.image_header_poster)
|
||||
return maxResolutionThumbnails.image_header_poster.url
|
||||
else
|
||||
return null
|
||||
else return null
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const {parser, url} = require('./mediasetinfinity.mediaset.it.config.js')
|
||||
const { parser, url } = require('./mediasetinfinity.mediaset.it.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
|
@ -9,19 +9,24 @@ dayjs.extend(utc)
|
|||
|
||||
const date = dayjs.utc('2024-01-20', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'LB', xmltv_id: '20.it'
|
||||
site_id: 'LB',
|
||||
xmltv_id: '20.it'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({
|
||||
channel,
|
||||
date
|
||||
})).toBe('https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB')
|
||||
expect(
|
||||
url({
|
||||
channel,
|
||||
date
|
||||
})
|
||||
).toBe(
|
||||
'https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||
const results = parser({content, date}).map(p => {
|
||||
const results = parser({ content, date }).map(p => {
|
||||
return p
|
||||
})
|
||||
|
||||
|
@ -30,11 +35,13 @@ it('can parse response', () => {
|
|||
stop: '2024-01-20 02:54',
|
||||
title: 'Chicago Fire',
|
||||
sub_title: 'Ep. 22 - Io non ti lascio',
|
||||
description: 'Severide e Kidd continuano a indagare su un vecchio caso doloso di Benny. Notizie inaspettate portano Brett a meditare su una grande decisione.',
|
||||
description:
|
||||
'Severide e Kidd continuano a indagare su un vecchio caso doloso di Benny. Notizie inaspettate portano Brett a meditare su una grande decisione.',
|
||||
category: 'Intrattenimento',
|
||||
season: '7',
|
||||
episode: '22',
|
||||
image: 'https://static2.mediasetplay.mediaset.it/Mediaset_Italia_Production_-_Main/F309370301002204/media/0/0/1ef76b73-3173-43bd-9c16-73986a0ec131/46896726-11e7-4438-b947-d2ae53f58c0b.jpg'
|
||||
image:
|
||||
'https://static2.mediasetplay.mediaset.it/Mediaset_Italia_Production_-_Main/F309370301002204/media/0/0/1ef76b73-3173-43bd-9c16-73986a0ec131/46896726-11e7-4438-b947-d2ae53f58c0b.jpg'
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.post(`https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getGridAnon`, null, {
|
||||
.post('https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getGridAnon', null, {
|
||||
headers: {
|
||||
Origin: 'https://www.meo.pt'
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ module.exports = {
|
|||
.catch(console.error)
|
||||
|
||||
if (content) {
|
||||
const [ $, items ] = getItems(content)
|
||||
const [$, items] = getItems(content)
|
||||
if (seq === 0) {
|
||||
queues.push(...items.map(category => baseUrl + $(category).attr('href')))
|
||||
} else {
|
||||
|
@ -86,7 +86,11 @@ function parseItems(content, date) {
|
|||
data.season = parseInt(ep[1])
|
||||
data.episode = parseInt(ep[2])
|
||||
}
|
||||
data.start = dayjs.tz(`${lastDate} ${$item.find('.time').text()}`, 'DD/MM/YYYY HH:mm', 'America/Sao_Paulo')
|
||||
data.start = dayjs.tz(
|
||||
`${lastDate} ${$item.find('.time').text()}`,
|
||||
'DD/MM/YYYY HH:mm',
|
||||
'America/Sao_Paulo'
|
||||
)
|
||||
result.push(data)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const data = await axios
|
||||
.get(`https://www.mewatch.sg/channel-guide`)
|
||||
.get('https://www.mewatch.sg/channel-guide')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ dayjs.extend(utc)
|
|||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setCheckResult(false)
|
||||
.setDebugger(debug)
|
||||
doFetch.setCheckResult(false).setDebugger(debug)
|
||||
|
||||
const languages = { en: 'english', id: 'indonesia' }
|
||||
const cookies = {}
|
||||
|
@ -125,7 +123,7 @@ async function parseItems(content, date, cookies) {
|
|||
const url = $item.find('a').attr('href')
|
||||
const headers = {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
Cookie: cookies,
|
||||
Cookie: cookies
|
||||
}
|
||||
queues.push({ i: $item, url, params: { headers, timeout } })
|
||||
}
|
||||
|
|
|
@ -48,8 +48,10 @@ function parseItems(context) {
|
|||
schDayPrograms.forEach((program, i) => {
|
||||
const itemDay = {
|
||||
progStart: parseStart($(schDayMonth), $(program)),
|
||||
progStop: parseStop($(schDayMonth), schDayPrograms[i + 1] ?
|
||||
$(schDayPrograms[i + 1]) : null),
|
||||
progStop: parseStop(
|
||||
$(schDayMonth),
|
||||
schDayPrograms[i + 1] ? $(schDayPrograms[i + 1]) : null
|
||||
),
|
||||
progTitle: parseTitle($(program)),
|
||||
progDesc: parseDescription($(program))
|
||||
}
|
||||
|
@ -91,7 +93,9 @@ function parseStop(schDayMonth, itemNext) {
|
|||
)
|
||||
} else {
|
||||
return dayjs.tz(
|
||||
`${currentYear}-${monthDate[0]}-${(parseInt(monthDate[1]) + 1).toString().padStart(2, '0')} 00:00`,
|
||||
`${currentYear}-${monthDate[0]}-${(parseInt(monthDate[1]) + 1)
|
||||
.toString()
|
||||
.padStart(2, '0')} 00:00`,
|
||||
'YYYY-MMM-DD HH:mm',
|
||||
tz
|
||||
)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -41,7 +41,7 @@ module.exports = {
|
|||
const pages = Array.from(Array(totalPages).keys())
|
||||
for (let page of pages) {
|
||||
const data = await axios
|
||||
.get(`https://mtel.ba/oec/epg/program`, {
|
||||
.get('https://mtel.ba/oec/epg/program', {
|
||||
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -65,7 +65,7 @@ module.exports = {
|
|||
|
||||
async function getTotalPageCount() {
|
||||
const data = await axios
|
||||
.get(`https://mtel.ba/oec/epg/program`, {
|
||||
.get('https://mtel.ba/oec/epg/program', {
|
||||
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
const pages = Array.from(Array(totalPages).keys())
|
||||
for (let page of pages) {
|
||||
const data = await axios
|
||||
.get(`https://mts.rs/oec/epg/program`, {
|
||||
.get('https://mts.rs/oec/epg/program', {
|
||||
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -67,7 +67,7 @@ module.exports = {
|
|||
|
||||
async function getTotalPageCount() {
|
||||
const data = await axios
|
||||
.get(`https://mts.rs/oec/epg/program`, {
|
||||
.get('https://mts.rs/oec/epg/program', {
|
||||
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -84,8 +84,8 @@ function parseContent(content, channel) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
if (!data || !data.channels || !data.channels.length) return null
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = {
|
|||
|
||||
const data = await axios
|
||||
.post(
|
||||
`https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php`,
|
||||
'https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php',
|
||||
params,
|
||||
{
|
||||
headers: {
|
||||
|
@ -86,7 +86,7 @@ function parseItems(content) {
|
|||
if (!data) return []
|
||||
const programmes = data['tv-program-programmes'].programme
|
||||
return programmes && Array.isArray(programmes) ? programmes : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ dayjs.extend(customParseFormat)
|
|||
|
||||
const headers = {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -26,12 +26,11 @@ it('can generate valid url for today', () => {
|
|||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||
const results = parser({ content, date })
|
||||
.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
const results = parser({ content, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2022-11-19T23:00:00.000Z',
|
||||
|
|
|
@ -23,14 +23,15 @@ module.exports = {
|
|||
channel.site_id
|
||||
}.html?dt=${date.format('YYYY-MM-DD')}`
|
||||
},
|
||||
async parser({ content, date, channel }) {
|
||||
async parser({ content, date }) {
|
||||
const programs = []
|
||||
|
||||
if (content) {
|
||||
const queues = []
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
$('table.table > tbody > tr').toArray()
|
||||
$('table.table > tbody > tr')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
const td = $(el).find('td:eq(1)')
|
||||
const title = td.find('h5 a')
|
||||
|
@ -66,12 +67,16 @@ module.exports = {
|
|||
const subTitle = parseText($('.tab-pane > h5 > strong'))
|
||||
const description = parseText($('.tab-pane > .tvbody > p'))
|
||||
const image = $('.program-media-image img').attr('src')
|
||||
const category = $('.schedule-attributes-genres span').toArray()
|
||||
const category = $('.schedule-attributes-genres span')
|
||||
.toArray()
|
||||
.map(el => $(el).text())
|
||||
const casts = $('.single-cast-head:not([id])').toArray()
|
||||
const casts = $('.single-cast-head:not([id])')
|
||||
.toArray()
|
||||
.map(el => {
|
||||
const cast = { name: parseText($(el).find('a')) }
|
||||
const [, role] = $(el).text().match(/\((.*)\)/) || [null, null]
|
||||
const [, role] = $(el)
|
||||
.text()
|
||||
.match(/\((.*)\)/) || [null, null]
|
||||
if (role) {
|
||||
cast.role = role
|
||||
}
|
||||
|
@ -102,7 +107,7 @@ module.exports = {
|
|||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,11 +120,17 @@ module.exports = {
|
|||
// process form -> provider
|
||||
if (queue.t === 'p') {
|
||||
const $ = cheerio.load(res)
|
||||
$('#guide_provider option').toArray()
|
||||
$('#guide_provider option')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
const opt = $(el)
|
||||
const provider = opt.attr('value')
|
||||
queues.push({ t: 'r', method: 'post', url: 'https://www.mytelly.co.uk/getregions', params: { provider } })
|
||||
queues.push({
|
||||
t: 'r',
|
||||
method: 'post',
|
||||
url: 'https://www.mytelly.co.uk/getregions',
|
||||
params: { provider }
|
||||
})
|
||||
})
|
||||
}
|
||||
// process provider -> region
|
||||
|
@ -135,26 +146,30 @@ module.exports = {
|
|||
u_time: now.format('HHmm'),
|
||||
is_mobile: 1
|
||||
}
|
||||
queues.push({ t: 's', method: 'post', url: 'https://www.mytelly.co.uk/tv-guide/schedule', params })
|
||||
queues.push({
|
||||
t: 's',
|
||||
method: 'post',
|
||||
url: 'https://www.mytelly.co.uk/tv-guide/schedule',
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
// process schedule -> channels
|
||||
if (queue.t === 's') {
|
||||
const $ = cheerio.load(res)
|
||||
$('.channelname')
|
||||
.each((i, el) => {
|
||||
const name = $(el).find('center > a:eq(1)').text()
|
||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
const site_id = `${number}/${slug}`
|
||||
if (channels[site_id] === undefined) {
|
||||
channels[site_id] = {
|
||||
lang: 'en',
|
||||
site_id,
|
||||
name
|
||||
}
|
||||
$('.channelname').each((i, el) => {
|
||||
const name = $(el).find('center > a:eq(1)').text()
|
||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
const site_id = `${number}/${slug}`
|
||||
if (channels[site_id] === undefined) {
|
||||
channels[site_id] = {
|
||||
lang: 'en',
|
||||
site_id,
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -178,13 +193,10 @@ function parseTime(date, time) {
|
|||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text()
|
||||
.replace(/\t/g, '')
|
||||
.replace(/\n/g, ' ')
|
||||
.trim()
|
||||
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||
while (true) {
|
||||
if (text.match(/ /)) {
|
||||
text = text.replace(/ /g, ' ')
|
||||
if (text.match(/\s\s/)) {
|
||||
text = text.replace(/\s\s/g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
|
|
|
@ -17,16 +17,18 @@ const channel = {
|
|||
xmltv_id: 'BBCOneLondon.uk'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
axios.get.mockImplementation(url => {
|
||||
if (
|
||||
url === 'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=1906433&tm=2024-12-07+00%3A00%3A00'
|
||||
url ===
|
||||
'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=1906433&tm=2024-12-07+00%3A00%3A00'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme.html'))
|
||||
})
|
||||
}
|
||||
if (
|
||||
url === 'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=5656624&tm=2024-12-07+23%3A35%3A00'
|
||||
url ===
|
||||
'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=5656624&tm=2024-12-07+23%3A35%3A00'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme2.html'))
|
||||
|
@ -57,7 +59,8 @@ it('can parse response', async () => {
|
|||
title: 'Captain Phillips',
|
||||
description:
|
||||
'An American cargo ship sets a dangerous course around the coast of Somalia, while inland, four men are pressed into service as pirates by the local warlords. The captain is taken hostage when the raiding party hijacks the vessel, resulting in a tense five-day crisis. Fact-based thriller, starring Tom Hanks and Barkhad Abdi',
|
||||
image: 'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/c44ce7b0d3ae602c0c93ece5af140815.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4dsylOCGGE7OWlqwSWt0cd0Qtrin4DkEMC0Zzdp8ZeNk2vNIQzjMF0DG0h3IeTR5NM%3D',
|
||||
image:
|
||||
'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/c44ce7b0d3ae602c0c93ece5af140815.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4dsylOCGGE7OWlqwSWt0cd0Qtrin4DkEMC0Zzdp8ZeNk2vNIQzjMF0DG0h3IeTR5NM%3D',
|
||||
category: ['Factual', 'Movie/Drama', 'Thriller']
|
||||
})
|
||||
expect(results[1]).toMatchObject({
|
||||
|
@ -67,7 +70,8 @@ it('can parse response', async () => {
|
|||
subTitle: 'Past and Pressure Season 6, Episode 5',
|
||||
description:
|
||||
'The artists are tasked with writing a song about their heritage. For some, the pressure of the competition proves too much for them to match. In their final challenge, they are put face to face with industry experts who grill them about their plans after the competition. Some impress, while others leave the mentors confused',
|
||||
image: 'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/2039278182b27cc279570b9ab9b89379.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4cDhR7jXTNFW3tgwQCdOPUobhXwlT81mIsqOe93HPusDG6tw1aoeYOgafojtynNWxc%3D',
|
||||
image:
|
||||
'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/2039278182b27cc279570b9ab9b89379.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4cDhR7jXTNFW3tgwQCdOPUobhXwlT81mIsqOe93HPusDG6tw1aoeYOgafojtynNWxc%3D',
|
||||
category: ['Challenge/Reality Show', 'Show/Game Show'],
|
||||
season: 6,
|
||||
episode: 5
|
||||
|
|
|
@ -12,27 +12,30 @@ module.exports = {
|
|||
site: 'neo.io',
|
||||
timezone: 'Europe/Ljubljana',
|
||||
days: 5,
|
||||
url({ date, channel }) { return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData' },
|
||||
url() {
|
||||
return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Host': 'stargate.telekom.si',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
Host: 'stargate.telekom.si',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sl',
|
||||
'Origin': 'https://neo.io',
|
||||
Origin: 'https://neo.io',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
'Connection': 'keep-alive'
|
||||
Connection: 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix();
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
||||
const todayEpoch = date.startOf('day').unix()
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||
return JSON.stringify({
|
||||
ch_ext_id: channel.site_id,
|
||||
from: todayEpoch,
|
||||
|
@ -41,11 +44,11 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const programs = [];
|
||||
const data = JSON.parse(content);
|
||||
const programs = []
|
||||
const data = JSON.parse(content)
|
||||
data.shows.forEach(show => {
|
||||
const start = dayjs.unix(show.show_start).utc();
|
||||
const stop = dayjs.unix(show.show_end).utc();
|
||||
const start = dayjs.unix(show.show_start).utc()
|
||||
const stop = dayjs.unix(show.show_end).utc()
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
|
@ -58,15 +61,19 @@ module.exports = {
|
|||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post('https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
||||
headers: this.request.headers
|
||||
});
|
||||
const response = await axios.post(
|
||||
'https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList',
|
||||
JSON.stringify({ includeRadioStations: true }),
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
|
||||
const data = response.data.data;
|
||||
const data = response.data.data
|
||||
return data.map(item => ({
|
||||
lang: 'sq',
|
||||
name: String(item.channel.title),
|
||||
site_id: String(item.channel.id),
|
||||
site_id: String(item.channel.id)
|
||||
//logo: String(item.channel.logo)
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ const channel = {
|
|||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date, channel })).toBe('https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData')
|
||||
expect(url({ date, channel })).toBe(
|
||||
'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
@ -82,33 +84,34 @@ it('can parse response', () => {
|
|||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content, channel }).map(p => {
|
||||
p.start = p.start
|
||||
p.stop = p.stop
|
||||
return p
|
||||
})
|
||||
const result = parser({ content, channel })
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Napovedujemo",
|
||||
description: "Vabilo k ogledu naših oddaj.",
|
||||
start: "2024-12-26T04:05:00.000Z",
|
||||
stop: "2024-12-26T05:50:00.000Z",
|
||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg"
|
||||
title: 'Napovedujemo',
|
||||
description: 'Vabilo k ogledu naših oddaj.',
|
||||
start: '2024-12-26T04:05:00.000Z',
|
||||
stop: '2024-12-26T05:50:00.000Z',
|
||||
thumbnail:
|
||||
'https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg'
|
||||
},
|
||||
{
|
||||
title: "S0E0 - Hrabri zajčki: Prvi sneg",
|
||||
description: "Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.",
|
||||
start: "2024-12-26T05:50:00.000Z",
|
||||
stop: "2024-12-26T06:00:00.000Z",
|
||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg"
|
||||
title: 'S0E0 - Hrabri zajčki: Prvi sneg',
|
||||
description:
|
||||
'Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.',
|
||||
start: '2024-12-26T05:50:00.000Z',
|
||||
stop: '2024-12-26T06:00:00.000Z',
|
||||
thumbnail:
|
||||
'https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg'
|
||||
},
|
||||
{
|
||||
title: "Dobro jutro",
|
||||
description: "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
||||
start: "2024-12-26T06:00:00.000Z",
|
||||
stop: "2024-12-26T09:05:00.000Z",
|
||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg"
|
||||
title: 'Dobro jutro',
|
||||
description:
|
||||
'Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.',
|
||||
start: '2024-12-26T06:00:00.000Z',
|
||||
stop: '2024-12-26T09:05:00.000Z',
|
||||
thumbnail:
|
||||
'https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
|
@ -50,7 +50,7 @@ function parseItems(content, date) {
|
|||
if (!data || !data.item || !Array.isArray(data.item.episodes)) return []
|
||||
|
||||
return data.item.episodes.filter(ep => ep.schedule.startsWith(date.format('YYYY-MM-DD')))
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ module.exports = {
|
|||
// I'm not sure what `endDate` represents but they only return 1 day of
|
||||
// results, with `endTime`s ocassionally in the following day.
|
||||
days: 1,
|
||||
url: ({ date }) => `https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split("T")[0]}`,
|
||||
url: ({ date }) =>
|
||||
`https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split('T')[0]}`,
|
||||
parser({ content }) {
|
||||
const programs = []
|
||||
const items = parseItems(content)
|
||||
|
@ -13,7 +14,7 @@ module.exports = {
|
|||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description === item.title ? undefined : item.description,
|
||||
category: "Sports",
|
||||
category: 'Sports',
|
||||
// image: parseImage(item),
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item)
|
||||
|
|
|
@ -10,9 +10,7 @@ dayjs.extend(utc)
|
|||
const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d')
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe(
|
||||
'https://api-web.nhle.com/v1/network/tv-schedule/2024-11-21'
|
||||
)
|
||||
expect(url({ date })).toBe('https://api-web.nhle.com/v1/network/tv-schedule/2024-11-21')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
@ -28,17 +26,19 @@ it('can parse response', () => {
|
|||
start: '2024-11-21T12:00:00.000Z',
|
||||
stop: '2024-11-21T13:00:00.000Z',
|
||||
title: 'On The Fly',
|
||||
category: 'Sports',
|
||||
category: 'Sports'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({ content: JSON.stringify({
|
||||
// extra props not necessary but they form a valid response
|
||||
date: "2024-11-21",
|
||||
startDate: "2024-11-07",
|
||||
endDate: "2024-12-05",
|
||||
broadcasts: [],
|
||||
}) })
|
||||
const results = parser({
|
||||
content: JSON.stringify({
|
||||
// extra props not necessary but they form a valid response
|
||||
date: '2024-11-21',
|
||||
startDate: '2024-11-07',
|
||||
endDate: '2024-12-05',
|
||||
broadcasts: []
|
||||
})
|
||||
})
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -55,7 +55,7 @@ module.exports = {
|
|||
.map(function () {
|
||||
return {
|
||||
lang: 'es',
|
||||
site_id: $(this).attr('alt').replace(/\&/gi, '&'),
|
||||
site_id: $(this).attr('alt').replace(/&/gi, '&'),
|
||||
name: $(this).attr('alt')
|
||||
}
|
||||
})
|
||||
|
|
|
@ -27,7 +27,7 @@ module.exports = {
|
|||
if (item.episodeNum) {
|
||||
item.episodeNum.forEach(ep => {
|
||||
if (ep.system === 'xmltv_ns') {
|
||||
const [season, episode, _] = ep.value.split('.')
|
||||
const [season, episode] = ep.value.split('.')
|
||||
program.season = parseInt(season) + 1
|
||||
program.episode = parseInt(episode) + 1
|
||||
return true
|
||||
|
|
|
@ -132,7 +132,7 @@ module.exports = {
|
|||
const $ = cheerio.load(data)
|
||||
$('.channelname').each((i, el) => {
|
||||
let name = $(el).find('center > a:eq(1)').text()
|
||||
name = name.replace(/\-\-/gi, '-')
|
||||
name = name.replace(/--/gi, '-')
|
||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||
if (!url) return
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
|
|
|
@ -5,7 +5,8 @@ const axios = require('axios')
|
|||
dayjs.extend(utc)
|
||||
|
||||
const API_PROGRAM_ENDPOINT = 'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO'
|
||||
const API_CHANNEL_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json'
|
||||
const API_CHANNEL_ENDPOINT =
|
||||
'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json'
|
||||
const API_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
|
||||
|
||||
module.exports = {
|
||||
|
@ -25,15 +26,9 @@ module.exports = {
|
|||
if (!items.length) return programs
|
||||
|
||||
const promises = [
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`,
|
||||
),
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`,
|
||||
),
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`,
|
||||
),
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`),
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`),
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`)
|
||||
]
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
|
@ -42,7 +37,9 @@ module.exports = {
|
|||
if (r.status === 'fulfilled') {
|
||||
const parsed = parseItems(r.value.data, channel)
|
||||
|
||||
items = items.filter((item, index) => items.findIndex(oi => oi.id === item.id) === index).concat(parsed)
|
||||
items = items
|
||||
.filter((item, index) => items.findIndex(oi => oi.id === item.id) === index)
|
||||
.concat(parsed)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -57,7 +54,7 @@ module.exports = {
|
|||
episode: item.episodeId || null,
|
||||
icon: parseIcon(item),
|
||||
start: dayjs.utc(item.startDate) || null,
|
||||
stop: dayjs.utc(item.endDate) || null,
|
||||
stop: dayjs.utc(item.endDate) || null
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -72,42 +69,40 @@ module.exports = {
|
|||
return data.response.map(item => {
|
||||
return {
|
||||
lang: 'es',
|
||||
name: item.name,
|
||||
name: item.name,
|
||||
site_id: item.externalChannelId
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function parseIcon(item){
|
||||
function parseIcon(item) {
|
||||
if (item.attachments.length > 0) {
|
||||
const cover = item.attachments.find(i => i.name === 'COVER' || i.name === 'cover')
|
||||
|
||||
if(item.attachments.length > 0){
|
||||
const cover = item.attachments.find(i => i.name === "COVER" || i.name === "cover")
|
||||
|
||||
if(cover)
|
||||
{
|
||||
return `${API_IMAGE_ENDPOINT}${cover.value}`;
|
||||
if (cover) {
|
||||
return `${API_IMAGE_ENDPOINT}${cover.value}`
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function parseGenres(item){
|
||||
return item.genres.map(i => i.name);
|
||||
function parseGenres(item) {
|
||||
return item.genres.map(i => i.name)
|
||||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const json = typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||
const json =
|
||||
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||
|
||||
if (!Array.isArray(json)) {
|
||||
return [];
|
||||
return []
|
||||
}
|
||||
|
||||
const channelData = json.find(i => i.channelExternalId == channel.site_id);
|
||||
const channelData = json.find(i => i.channelExternalId == channel.site_id)
|
||||
|
||||
if(!channelData)
|
||||
return [];
|
||||
if (!channelData) return []
|
||||
|
||||
return channelData.programs;
|
||||
return channelData.programs
|
||||
}
|
||||
|
|
|
@ -14,11 +14,15 @@ const channel = {
|
|||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe(`https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format('YYYYMMDD')}_8h_1.json`)
|
||||
expect(url({ date })).toBe(
|
||||
`https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format(
|
||||
'YYYYMMDD'
|
||||
)}_8h_1.json`
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json')).toString()
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json')).toString()
|
||||
let results = await parser({ content, channel, date })
|
||||
results = results.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
|
@ -28,13 +32,14 @@ it('can parse response', async () => {
|
|||
|
||||
expect(results.length).toBe(4)
|
||||
|
||||
var sampleResult = results[0];
|
||||
var sampleResult = results[0]
|
||||
|
||||
expect(sampleResult).toMatchObject({
|
||||
start: '2024-11-30T22:36:51.000Z',
|
||||
stop: '2024-11-30T23:57:25.000Z',
|
||||
category: ['Cine', 'Romance', 'Comedia', 'Comedia Romántica'],
|
||||
description: 'Charlie trabaja como director en una escuela de primaria y goza de una placentera existencia junto a sus amigos. A pesar de ello, no es feliz porque cada vez que se enamora pierde la cordura.',
|
||||
description:
|
||||
'Charlie trabaja como director en una escuela de primaria y goza de una placentera existencia junto a sus amigos. A pesar de ello, no es feliz porque cada vez que se enamora pierde la cordura.',
|
||||
title: 'Loco de amor'
|
||||
})
|
||||
})
|
||||
|
|
|
@ -5,7 +5,12 @@ const timezone = require('dayjs/plugin/timezone')
|
|||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
const packages = { 'OSNTV CONNECT': 3720, 'OSNTV PRIME': 3733, 'ALFA': 1281, 'OSN PINOY PLUS EXTRA': 3519 }
|
||||
const packages = {
|
||||
'OSNTV CONNECT': 3720,
|
||||
'OSNTV PRIME': 3733,
|
||||
ALFA: 1281,
|
||||
'OSN PINOY PLUS EXTRA': 3519
|
||||
}
|
||||
const country = 'AE'
|
||||
const tz = 'Asia/Dubai'
|
||||
|
||||
|
@ -13,11 +18,9 @@ module.exports = {
|
|||
site: 'osn.com',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${
|
||||
encodeURIComponent(date.format('MM/DD/YYYY'))
|
||||
}&co=${country}&ch=${
|
||||
channel.site_id
|
||||
}&mo=false&hr=0`
|
||||
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${encodeURIComponent(
|
||||
date.format('MM/DD/YYYY')
|
||||
)}&co=${country}&ch=${channel.site_id}&mo=false&hr=0`
|
||||
},
|
||||
request: {
|
||||
headers({ channel }) {
|
||||
|
@ -46,7 +49,9 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
for (const pkg of Object.values(packages)) {
|
||||
const channels = await axios
|
||||
.get(`https://www.osn.com/api/tvchannels.ashx?culture=en-US&packageId=${pkg}&country=${country}`)
|
||||
.get(
|
||||
`https://www.osn.com/api/tvchannels.ashx?culture=en-US&packageId=${pkg}&country=${country}`
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
|
|
|
@ -28,32 +28,30 @@ it('can generate valid url', () => {
|
|||
})
|
||||
|
||||
it('can parse response (ar)', () => {
|
||||
const result = parser({ date, channel: channelAR, content })
|
||||
.map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
const result = parser({ date, channel: channelAR, content }).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
expect(result.length).toBe(29)
|
||||
expect(result[1]).toMatchObject({
|
||||
start: '2024-11-26T20:50:00.000Z',
|
||||
stop: '2024-11-26T21:45:00.000Z',
|
||||
title: 'بيت الحلويات: الحلقة 3',
|
||||
title: 'بيت الحلويات: الحلقة 3'
|
||||
})
|
||||
})
|
||||
|
||||
it('can parse response (en)', () => {
|
||||
const result = parser({ date, channel: channelEN, content })
|
||||
.map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
const result = parser({ date, channel: channelEN, content }).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
expect(result.length).toBe(29)
|
||||
expect(result[1]).toMatchObject({
|
||||
start: '2024-11-26T20:50:00.000Z',
|
||||
stop: '2024-11-26T21:45:00.000Z',
|
||||
title: 'House Of Desserts: Episode 3',
|
||||
title: 'House Of Desserts: Episode 3'
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ function parseItems(content, date) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(json)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -129,36 +129,34 @@ module.exports = {
|
|||
)
|
||||
}
|
||||
}
|
||||
function fetchApiVersion() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
async function fetchApiVersion() {
|
||||
// you'll never find what happened here :)
|
||||
// load the pickx page and get the hash from the MWC configuration.
|
||||
// it's not the best way to get the version but it's the only way to get it.
|
||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids'
|
||||
const hashData = await axios
|
||||
.get(hashUrl)
|
||||
.then(r => {
|
||||
const re = /"hashes":\["(.*)"\]/
|
||||
const match = r.data.match(re)
|
||||
if (match && match[1]) {
|
||||
return match[1]
|
||||
} else {
|
||||
throw new Error('React app version hash not found')
|
||||
}
|
||||
})
|
||||
.catch(console.error)
|
||||
|
||||
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
||||
const response = await axios.get(versionUrl, {
|
||||
headers: {
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
}
|
||||
})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
// you'll never find what happened here :)
|
||||
// load the pickx page and get the hash from the MWC configuration.
|
||||
// it's not the best way to get the version but it's the only way to get it.
|
||||
|
||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids';
|
||||
|
||||
const hashData = await axios.get(hashUrl)
|
||||
.then(r => {
|
||||
const re = /"hashes":\["(.*)"\]/
|
||||
const match = r.data.match(re)
|
||||
if (match && match[1]) {
|
||||
return match[1]
|
||||
} else {
|
||||
throw new Error('React app version hash not found')
|
||||
}
|
||||
})
|
||||
.catch(console.error);
|
||||
|
||||
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
||||
|
||||
const response = await axios.get(versionUrl, {
|
||||
headers: {
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
apiVersion = response.data.version
|
||||
resolve()
|
||||
|
|
|
@ -6,14 +6,7 @@ jest.mock('./pickx.be.config.js', () => {
|
|||
}
|
||||
})
|
||||
|
||||
const {
|
||||
parser,
|
||||
url,
|
||||
request,
|
||||
fetchApiVersion,
|
||||
setApiVersion,
|
||||
getApiVersion
|
||||
} = require('./pickx.be.config.js')
|
||||
const { parser, url, request, setApiVersion } = require('./pickx.be.config.js')
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
@ -36,7 +29,7 @@ beforeEach(() => {
|
|||
it('can generate valid url', async () => {
|
||||
const generatedUrl = await url({ channel, date })
|
||||
expect(generatedUrl).toBe(
|
||||
`https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels`
|
||||
'https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels'
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@ module.exports = {
|
|||
site: 'player.ee.co.uk',
|
||||
days: 2,
|
||||
url({ date, channel, hour = 0 }) {
|
||||
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${
|
||||
encodeURIComponent(channel.site_id)
|
||||
}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2,'0')}Z/PT12H`
|
||||
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${encodeURIComponent(
|
||||
channel.site_id
|
||||
)}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2, '0')}Z/PT12H`
|
||||
},
|
||||
request: {
|
||||
headers: {
|
||||
|
@ -39,7 +39,7 @@ module.exports = {
|
|||
const stop = start.add(item.publishedDuration, 's')
|
||||
const description = item.synopsis
|
||||
if (description) {
|
||||
const matches = description.trim().match(/\(?S(\d+)[\/\s]Ep(\d+)\)?/)
|
||||
const matches = description.trim().match(/\(?S(\d+)[/\s]Ep(\d+)\)?/)
|
||||
if (matches) {
|
||||
if (matches[1]) {
|
||||
season = parseInt(matches[1])
|
||||
|
@ -79,7 +79,7 @@ module.exports = {
|
|||
'BTSubscriptionCodesExtension'
|
||||
]
|
||||
const result = await axios
|
||||
.get(`https://api.youview.tv/metadata/linear/v2/linear-services`, {
|
||||
.get('https://api.youview.tv/metadata/linear/v2/linear-services', {
|
||||
params: {
|
||||
contentTargetingToken: token,
|
||||
extensions: extensions.join(',')
|
||||
|
@ -89,14 +89,16 @@ module.exports = {
|
|||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
return result?.items
|
||||
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: 'en',
|
||||
site_id: channel.serviceLocator,
|
||||
name: channel.fullName
|
||||
}
|
||||
}) || []
|
||||
return (
|
||||
result?.items
|
||||
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: 'en',
|
||||
site_id: channel.serviceLocator,
|
||||
name: channel.fullName
|
||||
}
|
||||
}) || []
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@ const channel = {
|
|||
xmltv_id: 'HGTV.uk'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
if (url === 'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T12Z/PT12H') {
|
||||
axios.get.mockImplementation(url => {
|
||||
if (
|
||||
url ===
|
||||
'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T12Z/PT12H'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/data1.json')))
|
||||
})
|
||||
|
@ -33,12 +36,11 @@ it('can generate valid url', () => {
|
|||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||
const result = (await parser({ content, channel, date }))
|
||||
.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
const result = (await parser({ content, channel, date })).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.post(
|
||||
`https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel`,
|
||||
'https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel',
|
||||
{ isReturnAllMedia: '0' },
|
||||
{
|
||||
params: {
|
||||
|
@ -74,7 +74,7 @@ function parseItems(content, channel) {
|
|||
|
||||
const channelData = data.find(i => i.id == channel.site_id)
|
||||
return channelData.items && Array.isArray(channelData.items) ? channelData.items : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,10 @@ module.exports = {
|
|||
|
||||
return programs
|
||||
},
|
||||
async channels({ country, lang }) {
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://www.programetv.ro/api/station/index/`)
|
||||
.get('https://www.programetv.ro/api/station/index/')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ module.exports = {
|
|||
$('.channelList-listItemsLink').each((i, el) => {
|
||||
const name = $(el).attr('title')
|
||||
const url = $(el).attr('href')
|
||||
const [, site_id] = url.match(/\/programme\-(.*)\.html$/i)
|
||||
const [, site_id] = url.match(/\/programme-(.*)\.html$/i)
|
||||
|
||||
channels.push({
|
||||
lang: 'fr',
|
||||
|
|
|
@ -51,7 +51,7 @@ module.exports = {
|
|||
|
||||
return data.programmes.map(item => {
|
||||
const site_id = item.url.replace('/', '')
|
||||
const name = site_id.replace(/\-/gi, ' ')
|
||||
const name = site_id.replace(/-/gi, ' ')
|
||||
|
||||
return {
|
||||
lang: 'fr',
|
||||
|
|
|
@ -13,19 +13,17 @@ module.exports = {
|
|||
site: 'programme.tvb.com',
|
||||
days: 2,
|
||||
url({ channel, date, time = null }) {
|
||||
return `https://programme.tvb.com/api/schedule?input_date=${
|
||||
date.format('YYYYMMDD')
|
||||
}&network_code=${channel.site_id}&_t=${time ? time : parseInt(Date.now() / 1000)}`
|
||||
return `https://programme.tvb.com/api/schedule?input_date=${date.format(
|
||||
'YYYYMMDD'
|
||||
)}&network_code=${channel.site_id}&_t=${time ? time : parseInt(Date.now() / 1000)}`
|
||||
},
|
||||
parser({ content, channel, date }) {
|
||||
const programs = []
|
||||
const data = content ? JSON.parse(content) : {}
|
||||
if (Array.isArray(data.data?.list)) {
|
||||
const dt = date.format('YYYY-MM-DD')
|
||||
for (const d of data.data.list) {
|
||||
if (Array.isArray(d.schedules)) {
|
||||
const schedules = d.schedules
|
||||
.filter(s => s.network_code === channel.site_id)
|
||||
const schedules = d.schedules.filter(s => s.network_code === channel.site_id)
|
||||
schedules.forEach((s, i) => {
|
||||
const start = dayjs.tz(s.event_datetime, 'YYYY-MM-DD HH:mm:ss', tz)
|
||||
let stop
|
||||
|
@ -64,7 +62,7 @@ module.exports = {
|
|||
if (assets) {
|
||||
queues.push(...assets.map(a => base + '/' + a))
|
||||
} else {
|
||||
const metadata = content.match(/e\=(\[(.*?)\])/)
|
||||
const metadata = content.match(/e=(\[(.*?)\])/)
|
||||
if (metadata) {
|
||||
const infos = eval(metadata[1])
|
||||
if (Array.isArray(infos)) {
|
||||
|
|
|
@ -34,7 +34,7 @@ it('can parse response (en)', () => {
|
|||
expect(results[1]).toMatchObject({
|
||||
start: '2024-12-06T15:55:00.000Z',
|
||||
stop: '2024-12-06T16:55:00.000Z',
|
||||
title: 'Line Walker: Bull Fight#16[Can][PG]',
|
||||
title: 'Line Walker: Bull Fight#16[Can][PG]'
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -51,7 +51,7 @@ it('can parse response (zh)', () => {
|
|||
stop: '2024-12-06T16:55:00.000Z',
|
||||
title: '使徒行者3#16[粵][PG]',
|
||||
description:
|
||||
'文鼎從淑梅手上救走大聖爺兒子,大聖爺還恩於歡喜,答允支持九指強。崇聯社定下選舉日子,恰巧是韋傑出獄之日,頭目們顧念舊日恩義,紛紛轉投浩洋。浩洋帶亞希逛傢俬店,憧憬二人未來。亞希向家強承認愛上浩洋,要求退出臥底任務。作榮與歡喜暗中會面,將國際犯罪組織「永恆幫」情報交給他。阿火遭家強出賣,到沐足店搶錢。家強逮住阿火,惟被合星誤會而受拘捕。家強把正植遺下的頸鏈和學生證交還,合星意識到家強已知悉正植身世。',
|
||||
'文鼎從淑梅手上救走大聖爺兒子,大聖爺還恩於歡喜,答允支持九指強。崇聯社定下選舉日子,恰巧是韋傑出獄之日,頭目們顧念舊日恩義,紛紛轉投浩洋。浩洋帶亞希逛傢俬店,憧憬二人未來。亞希向家強承認愛上浩洋,要求退出臥底任務。作榮與歡喜暗中會面,將國際犯罪組織「永恆幫」情報交給他。阿火遭家強出賣,到沐足店搶錢。家強逮住阿火,惟被合星誤會而受拘捕。家強把正植遺下的頸鏈和學生證交還,合星意識到家強已知悉正植身世。'
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ module.exports = {
|
|||
$('ul.channelList a').each((i, el) => {
|
||||
const name = $(el).text()
|
||||
const url = $(el).attr('href')
|
||||
const [, site_id] = url.match(/^\/program\-tv\/(.*)$/i)
|
||||
const [, site_id] = url.match(/^\/program-tv\/(.*)$/i)
|
||||
|
||||
channels.push({
|
||||
lang: 'pl',
|
||||
|
|
|
@ -58,7 +58,7 @@ function parseItems(content, channel) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ dayjs.extend(timezone)
|
|||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setCheckResult(false)
|
||||
.setDebugger(debug)
|
||||
doFetch.setCheckResult(false).setDebugger(debug)
|
||||
|
||||
const tz = 'Asia/Riyadh'
|
||||
const defaultHeaders = {
|
||||
|
@ -47,7 +45,7 @@ module.exports = {
|
|||
headers: {
|
||||
...defaultHeaders,
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
cookie: cookies[channel.lang],
|
||||
cookie: cookies[channel.lang]
|
||||
}
|
||||
}
|
||||
queues.push({ i: item, url, params })
|
||||
|
@ -61,7 +59,7 @@ module.exports = {
|
|||
},
|
||||
async channels({ lang = 'en' }) {
|
||||
const result = await axios
|
||||
.get(`https://rotana.net/api/channels`)
|
||||
.get('https://rotana.net/api/channels')
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
|
@ -88,34 +86,37 @@ function parseProgram(item, result) {
|
|||
item.description = desc
|
||||
}
|
||||
}
|
||||
break;
|
||||
break
|
||||
case 'Element':
|
||||
if (el.name === 'span') {
|
||||
const [k, v] = $(el).text().split(':').map(a => a.trim())
|
||||
const [k, v] = $(el)
|
||||
.text()
|
||||
.split(':')
|
||||
.map(a => a.trim())
|
||||
switch (k) {
|
||||
case 'Category':
|
||||
case 'التصنيف':
|
||||
item.category = v;
|
||||
break;
|
||||
item.category = v
|
||||
break
|
||||
case 'Country':
|
||||
case 'البلد':
|
||||
item.country = v;
|
||||
break;
|
||||
item.country = v
|
||||
break
|
||||
case 'Director':
|
||||
case 'المخرج':
|
||||
item.director = v;
|
||||
break;
|
||||
item.director = v
|
||||
break
|
||||
case 'Language':
|
||||
case 'اللغة':
|
||||
item.language = v;
|
||||
break;
|
||||
item.language = v
|
||||
break
|
||||
case 'Release Year':
|
||||
case 'سنة الإصدار':
|
||||
item.date = v;
|
||||
break;
|
||||
item.date = v
|
||||
break
|
||||
}
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,7 +143,9 @@ function parseItems(content, date) {
|
|||
const heading = top.find('.iq-accordion-title .big-title')
|
||||
if (heading.length) {
|
||||
const progId = top.attr('id')
|
||||
const title = heading.find('span:eq(1)').text()
|
||||
const title = heading
|
||||
.find('span:eq(1)')
|
||||
.text()
|
||||
.split('\n')
|
||||
.map(a => a.trim())
|
||||
.join(' ')
|
||||
|
@ -151,7 +154,7 @@ function parseItems(content, date) {
|
|||
items.push({
|
||||
program: progId.substr(progId.indexOf('-') + 1),
|
||||
title: title ? title.trim() : title,
|
||||
start: `${y}-${m}-${d} ${time.trim()}`,
|
||||
start: `${y}-${m}-${d} ${time.trim()}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ const channel = {
|
|||
}
|
||||
const channelAr = Object.assign({}, channel, { lang: 'ar' })
|
||||
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
axios.get.mockImplementation(url => {
|
||||
if (url === 'https://rotana.net/en/streams?channel=439&itemId=736970') {
|
||||
return Promise.resolve({
|
||||
data: fs.readFileSync(path.resolve(__dirname, '__data__/program_en.html'))
|
||||
|
@ -52,11 +52,13 @@ it('can generate valid arabic url', () => {
|
|||
})
|
||||
|
||||
it('can parse english response', async () => {
|
||||
const result = (await parser({
|
||||
channel,
|
||||
date,
|
||||
content: fs.readFileSync(path.join(__dirname, '/__data__/content_en.html'))
|
||||
})).map(a => {
|
||||
const result = (
|
||||
await parser({
|
||||
channel,
|
||||
date,
|
||||
content: fs.readFileSync(path.join(__dirname, '/__data__/content_en.html'))
|
||||
})
|
||||
).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
|
@ -69,17 +71,20 @@ it('can parse english response', async () => {
|
|||
title: 'Khiyana Mashroua',
|
||||
description:
|
||||
'Hisham knows that his father has given all his wealth to his elder brother. This leads him to plan to kill his brother to make it look like a defense of honor, which he does by killing his wife along...',
|
||||
image: 'https://s3.eu-central-1.amazonaws.com/rotana.website/spider_storage/1398X1000/1687084565',
|
||||
image:
|
||||
'https://s3.eu-central-1.amazonaws.com/rotana.website/spider_storage/1398X1000/1687084565',
|
||||
category: 'Movie'
|
||||
})
|
||||
})
|
||||
|
||||
it('can parse arabic response', async () => {
|
||||
const result = (await parser({
|
||||
channel: channelAr,
|
||||
date,
|
||||
content: fs.readFileSync(path.join(__dirname, '/__data__/content_ar.html'))
|
||||
})).map(a => {
|
||||
const result = (
|
||||
await parser({
|
||||
channel: channelAr,
|
||||
date,
|
||||
content: fs.readFileSync(path.join(__dirname, '/__data__/content_ar.html'))
|
||||
})
|
||||
).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
|
@ -92,7 +97,8 @@ it('can parse arabic response', async () => {
|
|||
title: 'خيانة مشروعة',
|
||||
description:
|
||||
'يعلم هشام البحيري أن والده قد حرمه من الميراث، ووهب كل ثروته لشقيقه اﻷكبر، وهو ما يدفعه لتدبير جريمة قتل شقيقه لتبدو وكأنها دفاع عن الشرف، وذلك حين يقتل هشام زوجته مع شقيقه.',
|
||||
image: 'https://s3.eu-central-1.amazonaws.com/rotana.website/spider_storage/1398X1000/1687084565',
|
||||
image:
|
||||
'https://s3.eu-central-1.amazonaws.com/rotana.website/spider_storage/1398X1000/1687084565',
|
||||
category: 'فيلم'
|
||||
})
|
||||
})
|
||||
|
|
|
@ -53,7 +53,7 @@ async function parseItems(buffer) {
|
|||
let data
|
||||
try {
|
||||
data = await pdf(buffer)
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const _ = require('lodash')
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const dayjs = require('dayjs')
|
||||
const duration = require("dayjs/plugin/duration")
|
||||
const duration = require('dayjs/plugin/duration')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
@ -10,72 +10,74 @@ dayjs.extend(customParseFormat)
|
|||
dayjs.extend(duration)
|
||||
|
||||
module.exports = {
|
||||
site: 's.mxtv.jp',
|
||||
days: 1,
|
||||
lang: 'ja',
|
||||
url: function ({ date, channel }) {
|
||||
const id = `SV${channel.site_id}EPG${date.format('YYYYMMDD')}`
|
||||
return `https://s.mxtv.jp/bangumi_file/json01/${id}.json`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
let programs = []
|
||||
const items = parseItems(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.Event_name,
|
||||
description: item.Event_text,
|
||||
category: parseCategory(item),
|
||||
image: parseImage(item),
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item)
|
||||
})
|
||||
})
|
||||
return programs
|
||||
},
|
||||
channels() {
|
||||
return [
|
||||
{
|
||||
lang: 'ja',
|
||||
site_id: '1',
|
||||
name: 'Tokyo MX1',
|
||||
xmltv_id: 'TokyoMX1.jp'
|
||||
},
|
||||
{
|
||||
lang: 'ja',
|
||||
site_id: '2',
|
||||
name: 'Tokyo MX2',
|
||||
xmltv_id: 'TokyoMX2.jp'
|
||||
}
|
||||
]
|
||||
}
|
||||
site: 's.mxtv.jp',
|
||||
days: 1,
|
||||
lang: 'ja',
|
||||
url: function ({ date, channel }) {
|
||||
const id = `SV${channel.site_id}EPG${date.format('YYYYMMDD')}`
|
||||
return `https://s.mxtv.jp/bangumi_file/json01/${id}.json`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
let programs = []
|
||||
const items = parseItems(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.Event_name,
|
||||
description: item.Event_text,
|
||||
category: parseCategory(item),
|
||||
image: parseImage(item),
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item)
|
||||
})
|
||||
})
|
||||
return programs
|
||||
},
|
||||
channels() {
|
||||
return [
|
||||
{
|
||||
lang: 'ja',
|
||||
site_id: '1',
|
||||
name: 'Tokyo MX1',
|
||||
xmltv_id: 'TokyoMX1.jp'
|
||||
},
|
||||
{
|
||||
lang: 'ja',
|
||||
site_id: '2',
|
||||
name: 'Tokyo MX2',
|
||||
xmltv_id: 'TokyoMX2.jp'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function parseImage(item) {
|
||||
// Should return a string if we can output an image URL
|
||||
// Might be done with `https://s.mxtv.jp/bangumi/link/weblinkU.csv?1722421896752` ?
|
||||
return null
|
||||
function parseImage() {
|
||||
// Should return a string if we can output an image URL
|
||||
// Might be done with `https://s.mxtv.jp/bangumi/link/weblinkU.csv?1722421896752` ?
|
||||
return null
|
||||
}
|
||||
|
||||
function parseCategory(item) {
|
||||
// Should return a string if we can determine the category
|
||||
// Might be done with `https://s.mxtv.jp/index_set/csv/ranking_bangumi_allU.csv` ?
|
||||
return null
|
||||
function parseCategory() {
|
||||
// Should return a string if we can determine the category
|
||||
// Might be done with `https://s.mxtv.jp/index_set/csv/ranking_bangumi_allU.csv` ?
|
||||
return null
|
||||
}
|
||||
|
||||
function parseStart(item) {
|
||||
return dayjs.tz(item.Start_time.toString(), 'YYYY年MM月DD日HH時mm分ss秒', 'Asia/Tokyo')
|
||||
return dayjs.tz(item.Start_time.toString(), 'YYYY年MM月DD日HH時mm分ss秒', 'Asia/Tokyo')
|
||||
}
|
||||
|
||||
function parseStop(item) {
|
||||
// Add the duration to the start time
|
||||
const durationDate = dayjs(item.Duration, 'HH:mm:ss');
|
||||
return parseStart(item).add(dayjs.duration({
|
||||
hours: durationDate.hour(),
|
||||
minutes: durationDate.minute(),
|
||||
seconds: durationDate.second()
|
||||
}))
|
||||
// Add the duration to the start time
|
||||
const durationDate = dayjs(item.Duration, 'HH:mm:ss')
|
||||
return parseStart(item).add(
|
||||
dayjs.duration({
|
||||
hours: durationDate.hour(),
|
||||
minutes: durationDate.minute(),
|
||||
seconds: durationDate.second()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
return JSON.parse(content) || []
|
||||
return JSON.parse(content) || []
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ const channel = {
|
|||
name: 'Tokyo MX2',
|
||||
xmltv_id: 'TokyoMX2.jp'
|
||||
}
|
||||
const content = `[{ "Event_id": "0x6a57", "Start_time": "2024年07月27日05時00分00秒", "Duration": "01:00:00", "Event_name": "ヒーリングタイム&ヘッドラインニュース", "Event_text": "ねこの足跡", "Component": "480i 16:9 パンベクトルなし", "Sound": "ステレオ", "Event_detail": ""}]`
|
||||
const content =
|
||||
'[{ "Event_id": "0x6a57", "Start_time": "2024年07月27日05時00分00秒", "Duration": "01:00:00", "Event_name": "ヒーリングタイム&ヘッドラインニュース", "Event_text": "ねこの足跡", "Component": "480i 16:9 パンベクトルなし", "Sound": "ステレオ", "Event_detail": ""}]'
|
||||
|
||||
it('can generate valid url', () => {
|
||||
const result = url({ date, channel })
|
||||
|
|
|
@ -114,7 +114,7 @@ module.exports = {
|
|||
const $ = cheerio.load(data)
|
||||
$('.main-container-channels-events > .container-channel-events').each((i, el) => {
|
||||
const name = $(el).find('.channel-title').text().trim()
|
||||
const channelId = name.replace(/\s\&\s/gi, ' & ')
|
||||
const channelId = name.replace(/\s&\s/gi, ' & ')
|
||||
|
||||
if (!name) return
|
||||
|
||||
|
|
|
@ -10,31 +10,36 @@ dayjs.extend(customParseFormat)
|
|||
module.exports = {
|
||||
site: 'shahid.mbc.net',
|
||||
days: 2,
|
||||
url({ channel, date}) {
|
||||
return `https://api2.shahid.net/proxy/v2.1/shahid-epg-api/?csvChannelIds=${channel.site_id}&from=${date.format('YYYY-MM-DD')}T00:00:00.000Z&to=${date.format('YYYY-MM-DD')}T23:59:59.999Z&country=SA&language=${channel.lang}&Accept-Language=${channel.lang}`
|
||||
url({ channel, date }) {
|
||||
return `https://api2.shahid.net/proxy/v2.1/shahid-epg-api/?csvChannelIds=${
|
||||
channel.site_id
|
||||
}&from=${date.format('YYYY-MM-DD')}T00:00:00.000Z&to=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}T23:59:59.999Z&country=SA&language=${channel.lang}&Accept-Language=${channel.lang}`
|
||||
},
|
||||
parser({ content, channel }) {
|
||||
const programs = parseItems(content, channel)
|
||||
.map(item => {
|
||||
return {
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
session: item.seasonNumber,
|
||||
episode: item.episodeNumber,
|
||||
start: dayjs.tz(item.actualFrom, 'Asia/Riyadh').toISOString(),
|
||||
stop: dayjs.tz(item.actualTo, 'Asia/Riyadh').toISOString()
|
||||
}
|
||||
})
|
||||
const programs = parseItems(content, channel).map(item => {
|
||||
return {
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
session: item.seasonNumber,
|
||||
episode: item.episodeNumber,
|
||||
start: dayjs.tz(item.actualFrom, 'Asia/Riyadh').toISOString(),
|
||||
stop: dayjs.tz(item.actualTo, 'Asia/Riyadh').toISOString()
|
||||
}
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({lang = 'en'}) {
|
||||
async channels({ lang = 'en' }) {
|
||||
const axios = require('axios')
|
||||
const items = []
|
||||
let page = 0
|
||||
while (true) {
|
||||
const result = await axios
|
||||
.get(`https://api2.shahid.net/proxy/v2.1/product/filter?filter=%7B"pageNumber":${page},"pageSize":100,"productType":"LIVESTREAM","productSubType":"LIVE_CHANNEL"%7D&country=SA&language=${lang}&Accept-Language=${lang}`)
|
||||
.get(
|
||||
`https://api2.shahid.net/proxy/v2.1/product/filter?filter=%7B"pageNumber":${page},"pageSize":100,"productType":"LIVESTREAM","productSubType":"LIVE_CHANNEL"%7D&country=SA&language=${lang}&Accept-Language=${lang}`
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
if (result.productList) {
|
||||
|
@ -44,7 +49,7 @@ module.exports = {
|
|||
continue
|
||||
}
|
||||
}
|
||||
break;
|
||||
break
|
||||
}
|
||||
const channels = items.map(channel => {
|
||||
return {
|
||||
|
|
|
@ -9,7 +9,11 @@ const channel = { site_id: '996520', xmltv_id: 'AlAanTV.ae', lang: 'en' }
|
|||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe(
|
||||
`https://api2.shahid.net/proxy/v2.1/shahid-epg-api/?csvChannelIds=${channel.site_id}&from=${date.format('YYYY-MM-DD')}T00:00:00.000Z&to=${date.format('YYYY-MM-DD')}T23:59:59.999Z&country=SA&language=${channel.lang}&Accept-Language=${channel.lang}`
|
||||
`https://api2.shahid.net/proxy/v2.1/shahid-epg-api/?csvChannelIds=${
|
||||
channel.site_id
|
||||
}&from=${date.format('YYYY-MM-DD')}T00:00:00.000Z&to=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}T23:59:59.999Z&country=SA&language=${channel.lang}&Accept-Language=${channel.lang}`
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -22,9 +26,9 @@ it('can parse response', () => {
|
|||
{
|
||||
start: '2023-11-10T21:00:00.000Z',
|
||||
stop: '2023-11-10T21:30:00.000Z',
|
||||
title: 'Menassaatona Fi Osboo\'',
|
||||
title: "Menassaatona Fi Osboo'",
|
||||
description:
|
||||
'The presenter reviews the most prominent episodes of news programs produced by the channel\'s team on a weekly basis, which include the most important global updates and developments at all levels.'
|
||||
"The presenter reviews the most prominent episodes of news programs produced by the channel's team on a weekly basis, which include the most important global updates and developments at all levels."
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://www.singtel.com/personal/products-services/tv/tv-programme-guide`)
|
||||
.get('https://www.singtel.com/personal/products-services/tv/tv-programme-guide')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
@ -62,7 +62,7 @@ function parseItems(content, channel) {
|
|||
try {
|
||||
const data = JSON.parse(content)
|
||||
return data && data[channel.site_id] ? data[channel.site_id] : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://sjonvarp.is/`)
|
||||
.get('https://sjonvarp.is/')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -12,9 +12,7 @@ module.exports = {
|
|||
site: 'sky.com',
|
||||
days: 2,
|
||||
url({ date, channel }) {
|
||||
return `https://awk.epgsky.com/hawk/linear/schedule/${
|
||||
date.format('YYYYMMDD')
|
||||
}/${
|
||||
return `https://awk.epgsky.com/hawk/linear/schedule/${date.format('YYYYMMDD')}/${
|
||||
channel.site_id
|
||||
}`
|
||||
},
|
||||
|
@ -27,19 +25,18 @@ module.exports = {
|
|||
.filter(schedule => schedule.sid === channel.site_id)
|
||||
.forEach(schedule => {
|
||||
if (Array.isArray(schedule.events)) {
|
||||
schedule.events
|
||||
.forEach(event => {
|
||||
const start = dayjs.utc(event.st * 1000)
|
||||
const stop = start.add(event.d, 's')
|
||||
programs.push({
|
||||
title: event.t,
|
||||
description: event.sy,
|
||||
season: event.seasonnumber,
|
||||
episode: event.episodenumber,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
schedule.events.forEach(event => {
|
||||
const start = dayjs.utc(event.st * 1000)
|
||||
const stop = start.add(event.d, 's')
|
||||
programs.push({
|
||||
title: event.t,
|
||||
description: event.sy,
|
||||
season: event.seasonnumber,
|
||||
episode: event.episodenumber,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -55,10 +52,12 @@ module.exports = {
|
|||
if (queue.t === 'r') {
|
||||
const $ = cheerio.load(res)
|
||||
const initialData = JSON.parse(decodeURIComponent($('#initialData').text()))
|
||||
initialData.state.epgData.regions
|
||||
.forEach(region => {
|
||||
queues.push({ t: 'c', url: `https://awk.epgsky.com/hawk/linear/services/${region.bouquet}/${region.subBouquet}` })
|
||||
initialData.state.epgData.regions.forEach(region => {
|
||||
queues.push({
|
||||
t: 'c',
|
||||
url: `https://awk.epgsky.com/hawk/linear/services/${region.bouquet}/${region.subBouquet}`
|
||||
})
|
||||
})
|
||||
}
|
||||
// process channels
|
||||
if (queue.t === 'c') {
|
||||
|
|
|
@ -15,9 +15,7 @@ const channel = {
|
|||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe(
|
||||
'https://awk.epgsky.com/hawk/linear/schedule/20241214/4086'
|
||||
)
|
||||
expect(url({ channel, date })).toBe('https://awk.epgsky.com/hawk/linear/schedule/20241214/4086')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
|
@ -34,7 +32,7 @@ it('can parse response', () => {
|
|||
stop: '2024-12-13T23:00:00.000Z',
|
||||
title: 'The UnXplained With...',
|
||||
description:
|
||||
'The Hunt for Jack the Ripper: Jack the Ripper\'s identity has eluded police, historians and armchair detectives for over a century. What do we know about the notorious killer? (S3, ep 21)',
|
||||
"The Hunt for Jack the Ripper: Jack the Ripper's identity has eluded police, historians and armchair detectives for over a century. What do we know about the notorious killer? (S3, ep 21)",
|
||||
season: 4,
|
||||
episode: 14
|
||||
})
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { parser, url, request } = require('./skylife.co.kr.config.js')
|
||||
const { parser, url } = require('./skylife.co.kr.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
|
|
1
sites/skyperfectv.co.jp/__data__/content.html
Normal file
1
sites/skyperfectv.co.jp/__data__/content.html
Normal file
File diff suppressed because one or more lines are too long
1
sites/skyperfectv.co.jp/__data__/empty.html
Normal file
1
sites/skyperfectv.co.jp/__data__/empty.html
Normal file
File diff suppressed because one or more lines are too long
|
@ -12,103 +12,110 @@ dayjs.extend(customParseFormat)
|
|||
dayjs.extend(duration)
|
||||
|
||||
const exported = {
|
||||
site: 'skyperfectv.co.jp',
|
||||
days: 1,
|
||||
lang: 'ja',
|
||||
url: function ({ date, channel }) {
|
||||
let [type, ...code] = channel.site_id.split('_')
|
||||
code = code.join('_')
|
||||
return `https://www.skyperfectv.co.jp/program/schedule/${type}/channel:${code}/date:${date.format('YYMMDD')}`
|
||||
},
|
||||
logo: function ({ channel }) {
|
||||
return `https://www.skyperfectv.co.jp/library/common/img/channel/icon/basic/m_${channel.site_id.toLowerCase()}.gif`
|
||||
},
|
||||
// Specific function that permits to gather NSFW channels (needs confirmation)
|
||||
async fetchSchedule({ date, channel }) {
|
||||
const url = exported.url({ date, channel })
|
||||
const response = await axios.get(url, {
|
||||
headers: {
|
||||
'Cookie': 'adult_auth=true'
|
||||
}
|
||||
site: 'skyperfectv.co.jp',
|
||||
days: 1,
|
||||
lang: 'ja',
|
||||
url: function ({ date, channel }) {
|
||||
let [type, ...code] = channel.site_id.split('_')
|
||||
code = code.join('_')
|
||||
return `https://www.skyperfectv.co.jp/program/schedule/${type}/channel:${code}/date:${date.format(
|
||||
'YYMMDD'
|
||||
)}`
|
||||
},
|
||||
logo: function ({ channel }) {
|
||||
return `https://www.skyperfectv.co.jp/library/common/img/channel/icon/basic/m_${channel.site_id.toLowerCase()}.gif`
|
||||
},
|
||||
// Specific function that permits to gather NSFW channels (needs confirmation)
|
||||
async fetchSchedule({ date, channel }) {
|
||||
const url = exported.url({ date, channel })
|
||||
const response = await axios.get(url, {
|
||||
headers: {
|
||||
Cookie: 'adult_auth=true'
|
||||
}
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
parser({ content, date }) {
|
||||
const $ = cheerio.load(content)
|
||||
const programs = []
|
||||
|
||||
const sections = [
|
||||
{ id: 'js-am', addition: 0 },
|
||||
{ id: 'js-pm', addition: 0 },
|
||||
{ id: 'js-md', addition: 1 }
|
||||
]
|
||||
|
||||
sections.forEach(({ id, addition }) => {
|
||||
$(`#${id} > td`).each((index, element) => {
|
||||
// `td` is a column for a day
|
||||
// the next `td` will be the next day
|
||||
const today = date.add(index + addition, 'd').tz('Asia/Tokyo')
|
||||
|
||||
const parseTime = timeString => {
|
||||
// timeString is in the format "HH:mm"
|
||||
// replace `today` with the time from timeString
|
||||
const [hour, minute] = timeString.split(':').map(Number)
|
||||
return today.hour(hour).minute(minute)
|
||||
}
|
||||
|
||||
const $element = $(element) // Wrap element with Cheerio
|
||||
$element.find('.p-program__item').each((itemIndex, itemElement) => {
|
||||
const $itemElement = $(itemElement) // Wrap itemElement with Cheerio
|
||||
const [start, stop] = $itemElement
|
||||
.find('.p-program__range')
|
||||
.first()
|
||||
.text()
|
||||
.split('〜')
|
||||
.map(parseTime)
|
||||
const title = $itemElement.find('.p-program__name').first().text()
|
||||
const image = $itemElement.find('.js-program_thumbnail').first().attr('data-lazysrc')
|
||||
programs.push({
|
||||
title,
|
||||
start,
|
||||
stop,
|
||||
image
|
||||
})
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
parser({ content, date }) {
|
||||
const $ = cheerio.load(content)
|
||||
const programs = []
|
||||
})
|
||||
})
|
||||
|
||||
const sections = [
|
||||
{ id: 'js-am', addition: 0 },
|
||||
{ id: 'js-pm', addition: 0 },
|
||||
{ id: 'js-md', addition: 1 }
|
||||
]
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const pageParser = (content, type) => {
|
||||
// type: "basic" | "premium"
|
||||
// Returns an array of channel objects
|
||||
|
||||
sections.forEach(({ id, addition }) => {
|
||||
$(`#${id} > td`).each((index, element) => {
|
||||
// `td` is a column for a day
|
||||
// the next `td` will be the next day
|
||||
const today = date.add(index + addition, 'd').tz('Asia/Tokyo')
|
||||
const $ = cheerio.load(content)
|
||||
const channels = []
|
||||
|
||||
const parseTime = (timeString) => {
|
||||
// timeString is in the format "HH:mm"
|
||||
// replace `today` with the time from timeString
|
||||
const [hour, minute] = timeString.split(':').map(Number)
|
||||
return today.hour(hour).minute(minute)
|
||||
}
|
||||
$('.p-channel').each((index, element) => {
|
||||
const site_id = `${type}_${$(element).find('.p-channel__id').text()}`
|
||||
const name = $(element).find('.p-channel__name').text()
|
||||
channels.push({ site_id, name, lang: 'ja' })
|
||||
})
|
||||
|
||||
const $element = $(element) // Wrap element with Cheerio
|
||||
$element.find('.p-program__item').each((itemIndex, itemElement) => {
|
||||
const $itemElement = $(itemElement) // Wrap itemElement with Cheerio
|
||||
const [start, stop] = $itemElement.find('.p-program__range').first().text().split('〜').map(parseTime)
|
||||
const title = $itemElement.find('.p-program__name').first().text()
|
||||
const image = $itemElement.find('.js-program_thumbnail').first().attr('data-lazysrc')
|
||||
programs.push({
|
||||
title,
|
||||
start,
|
||||
stop,
|
||||
image
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const pageParser = (content, type) => {
|
||||
// type: "basic" | "premium"
|
||||
// Returns an array of channel objects
|
||||
|
||||
const $ = cheerio.load(content)
|
||||
const channels = []
|
||||
|
||||
$('.p-channel').each((index, element) => {
|
||||
const site_id = `${type}_${$(element).find('.p-channel__id').text()}`
|
||||
const name = $(element).find('.p-channel__name').text()
|
||||
channels.push({ site_id, name, lang: 'ja' })
|
||||
})
|
||||
|
||||
return channels
|
||||
}
|
||||
|
||||
const getChannels = async (type) => {
|
||||
const response = await axios.get(`https://www.skyperfectv.co.jp/program/schedule/${type}/`, {
|
||||
headers: {
|
||||
'Cookie': 'adult_auth=true;'
|
||||
}
|
||||
})
|
||||
return pageParser(response.data, type)
|
||||
}
|
||||
|
||||
const fetchAllChannels = async () => {
|
||||
const basicChannels = await getChannels('basic')
|
||||
const premiumChannels = await getChannels('premium')
|
||||
const results = [...basicChannels, ...premiumChannels]
|
||||
return results
|
||||
}
|
||||
|
||||
return await fetchAllChannels()
|
||||
return channels
|
||||
}
|
||||
|
||||
const getChannels = async type => {
|
||||
const response = await axios.get(`https://www.skyperfectv.co.jp/program/schedule/${type}/`, {
|
||||
headers: {
|
||||
Cookie: 'adult_auth=true;'
|
||||
}
|
||||
})
|
||||
return pageParser(response.data, type)
|
||||
}
|
||||
|
||||
const fetchAllChannels = async () => {
|
||||
const basicChannels = await getChannels('basic')
|
||||
const premiumChannels = await getChannels('premium')
|
||||
const results = [...basicChannels, ...premiumChannels]
|
||||
return results
|
||||
}
|
||||
|
||||
return await fetchAllChannels()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exported
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,4 @@
|
|||
const cheerio = require('cheerio')
|
||||
const { DateTime } = require('luxon')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
@ -9,15 +8,6 @@ dayjs.extend(utc)
|
|||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const channel = [{ site_id: '1208', xmltv_id: 'AlAoula.ma', lang: 'ar' },
|
||||
{ site_id: '4069', xmltv_id: 'Laayoune.ma', lang: 'ar' },
|
||||
{ site_id: '4070', xmltv_id: 'Arryadia.ma', lang: 'ar' },
|
||||
{ site_id: '4071', xmltv_id: 'Athaqafia.ma', lang: 'ar' },
|
||||
{ site_id: '4072', xmltv_id: 'AlMaghribia.ma', lang: 'ar' },
|
||||
{ site_id: '4073', xmltv_id: 'Assadissa.ma', lang: 'ar' },
|
||||
{ site_id: '4075', xmltv_id: 'Tamazight.ma', lang: 'ar' }]
|
||||
|
||||
|
||||
module.exports = {
|
||||
site: 'snrt.ma',
|
||||
channels: 'snrt.ma.channels.xml',
|
||||
|
@ -68,7 +58,7 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseStart($item, date) {
|
||||
const timeString = $item('.grille-time').text().trim()
|
||||
const timeString = $item('.grille-time').text().trim()
|
||||
const [hours, minutes] = timeString.split('H').map(Number)
|
||||
const formattedTime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:00`
|
||||
|
||||
|
@ -77,7 +67,6 @@ function parseStart($item, date) {
|
|||
return dayjs.tz(dateString, 'YYYY-MM-DD HH:mm:ss', 'Africa/Casablanca')
|
||||
}
|
||||
|
||||
|
||||
function parseTitle($item) {
|
||||
return $item('.program-title-sm').text().trim()
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const { parser, url } = require('./snrt.ma.config.js')
|
||||
const cheerio = require('cheerio')
|
||||
const { DateTime } = require('luxon')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
@ -25,16 +23,14 @@ it('can parse response', () => {
|
|||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
"category": "القرآن الكريم",
|
||||
"description": "",
|
||||
"start": "2024-12-19T06:00:00.000Z",
|
||||
"stop": "2024-12-19T06:10:00.000Z",
|
||||
"stop": "2024-12-19T06:30:00.000Z",
|
||||
"title": "ﺍﻟﺴﻼﻡ ﺍﻟﻮﻃﻨﻲ + ﺍﻟﻘﺮﺁﻥ ﺍﻟﻜﺮﻳﻢ"
|
||||
category: 'القرآن الكريم',
|
||||
description: '',
|
||||
start: '2024-12-19T06:00:00.000Z',
|
||||
stop: '2024-12-19T06:30:00.000Z',
|
||||
title: 'ﺍﻟﺴﻼﻡ ﺍﻟﻮﻃﻨﻲ + ﺍﻟﻘﺮﺁﻥ ﺍﻟﻜﺮﻳﻢ'
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
|
|
|
@ -9,15 +9,9 @@ module.exports = {
|
|||
url({ date, channel }) {
|
||||
return `https://waf-starhub-metadata-api-p001.ifs.vubiquity.com/v3.1/epg/schedules?locale=${
|
||||
languages[channel.lang]
|
||||
}&locale_default=${
|
||||
languages[channel.lang]
|
||||
}&device=1&in_channel_id=${
|
||||
}&locale_default=${languages[channel.lang]}&device=1&in_channel_id=${
|
||||
channel.site_id
|
||||
}>_end=${
|
||||
date.unix()
|
||||
}<_start=${
|
||||
date.add(1, 'd').unix()
|
||||
}&limit=100&page=1`
|
||||
}>_end=${date.unix()}<_start=${date.add(1, 'd').unix()}&limit=100&page=1`
|
||||
},
|
||||
async parser({ content, date, channel }) {
|
||||
const programs = []
|
||||
|
@ -29,7 +23,11 @@ module.exports = {
|
|||
}
|
||||
if (res.page && res.page.current < res.page.total) {
|
||||
res = await axios
|
||||
.get(module.exports.url({ date, channel }).replace(/page=(\d+)/, `page=${res.page.current + 1}`))
|
||||
.get(
|
||||
module.exports
|
||||
.url({ date, channel })
|
||||
.replace(/page=(\d+)/, `page=${res.page.current + 1}`)
|
||||
)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
} else {
|
||||
|
@ -39,7 +37,7 @@ module.exports = {
|
|||
}
|
||||
const season = s => {
|
||||
if (s) {
|
||||
const [ , , n ] = s.match(/(S|Season )(\d+)/) || [null, null, null]
|
||||
const [, , n] = s.match(/(S|Season )(\d+)/) || [null, null, null]
|
||||
if (n) {
|
||||
return parseInt(n)
|
||||
}
|
||||
|
@ -66,11 +64,9 @@ module.exports = {
|
|||
let page = 1
|
||||
while (true) {
|
||||
const items = await axios
|
||||
.get(`https://waf-starhub-metadata-api-p001.ifs.vubiquity.com/v3.1/epg/channels?locale=${
|
||||
languages[lang]
|
||||
}&locale_default=${
|
||||
languages[lang]
|
||||
}&device=1&limit=50&page=${page}`)
|
||||
.get(
|
||||
`https://waf-starhub-metadata-api-p001.ifs.vubiquity.com/v3.1/epg/channels?locale=${languages[lang]}&locale_default=${languages[lang]}&device=1&limit=50&page=${page}`
|
||||
)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
if (items.resources) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { parser, url, request } = require('./starhubtvplus.com.config.js')
|
||||
const { parser, url } = require('./starhubtvplus.com.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
@ -36,9 +36,12 @@ it('can parse response', async () => {
|
|||
title: 'Northern Rexposure',
|
||||
subTitle: 'Hudson & Rex (Season 5)',
|
||||
description:
|
||||
'When Jesse\'s sister contacts him for help, he, Sarah and Rex head to Northern Ontario and find themselves in the middle of a deadly situation.',
|
||||
"When Jesse's sister contacts him for help, he, Sarah and Rex head to Northern Ontario and find themselves in the middle of a deadly situation.",
|
||||
category: ['Drama'],
|
||||
image: ['https://poster.starhubgo.com/poster/ch511_hudson_rex5.jpg?w=960&h=540', 'https://poster.starhubgo.com/poster/ch511_hudson_rex5.jpg?w=341&h=192'],
|
||||
image: [
|
||||
'https://poster.starhubgo.com/poster/ch511_hudson_rex5.jpg?w=960&h=540',
|
||||
'https://poster.starhubgo.com/poster/ch511_hudson_rex5.jpg?w=341&h=192'
|
||||
],
|
||||
season: 5,
|
||||
episode: 15,
|
||||
rating: 'PG13'
|
||||
|
|
|
@ -8,9 +8,7 @@ const debug = require('debug')('site:startimestv.com')
|
|||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setDebugger(debug)
|
||||
.setMaxWorker(5)
|
||||
doFetch.setDebugger(debug).setMaxWorker(5)
|
||||
|
||||
module.exports = {
|
||||
site: 'startimestv.com',
|
||||
|
@ -24,7 +22,8 @@ module.exports = {
|
|||
const programs = []
|
||||
if (content) {
|
||||
const $ = cheerio.load(content)
|
||||
$('.box .mask').toArray()
|
||||
$('.box .mask')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
let title = parseText($(el).find('h4'))
|
||||
const [s, e] = title.substr(0, title.indexOf(' ')).split('-') || [null, null]
|
||||
|
@ -53,7 +52,8 @@ module.exports = {
|
|||
// process area-id
|
||||
if (queue.t === 'a') {
|
||||
const $ = cheerio.load(res)
|
||||
$('dd.update-areaID').toArray()
|
||||
$('dd.update-areaID')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
const dd = $(el)
|
||||
const areaId = dd.attr('area-id')
|
||||
|
@ -72,7 +72,8 @@ module.exports = {
|
|||
if (queue.t === 's') {
|
||||
if (res) {
|
||||
const $ = cheerio.load(res)
|
||||
$(`.channl .c`).toArray()
|
||||
$('.channl .c')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
// only process channel with schedule only
|
||||
const clazz = $(el).attr('class')
|
||||
|
@ -98,13 +99,10 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text()
|
||||
.replace(/\t/g, '')
|
||||
.replace(/\n/g, ' ')
|
||||
.trim()
|
||||
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||
while (true) {
|
||||
if (text.match(/ /)) {
|
||||
text = text.replace(/ /g, ' ')
|
||||
if (text.match(/\s\s/)) {
|
||||
text = text.replace(/\s\s/g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
|
|
|
@ -33,10 +33,10 @@ module.exports = {
|
|||
|
||||
return programs
|
||||
},
|
||||
async channels({ country, lang }) {
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://streamingtvguides.com/Preferences`)
|
||||
.get('https://streamingtvguides.com/Preferences')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ const dayjs = require('dayjs')
|
|||
|
||||
const API_STATIC_ENDPOINT = 'https://static.spark.telenet.tv/eng/web/epg-service-lite/be'
|
||||
const API_PROD_ENDPOINT = 'https://spark-prod-be.gnp.cloud.telenet.tv/eng/web/linear-service/v2'
|
||||
const API_IMAGE_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/image-service';
|
||||
const API_IMAGE_ENDPOINT = 'https://staticqbr-prod-be.gnp.cloud.telenet.tv/image-service'
|
||||
|
||||
module.exports = {
|
||||
site: 'telenet.tv',
|
||||
|
@ -94,7 +94,7 @@ module.exports = {
|
|||
|
||||
async function loadProgramDetails(item, channel) {
|
||||
if (!item.id) return {}
|
||||
const url = `${API_PROD_ENDPOINT}/replayEvent/${item.id}?returnLinearContent=true&language=${channel.lang}`
|
||||
const url = `${API_PROD_ENDPOINT}/replayEvent/${item.id}?returnLinearContent=true&language=${channel.lang}`
|
||||
const data = await axios
|
||||
.get(url)
|
||||
.then(r => r.data)
|
||||
|
@ -134,5 +134,5 @@ function parseEpisode(detail) {
|
|||
}
|
||||
|
||||
function parseIcon(item) {
|
||||
return `${API_IMAGE_ENDPOINT}/intent/${item.id}/posterTile`;
|
||||
return `${API_IMAGE_ENDPOINT}/intent/${item.id}/posterTile`
|
||||
}
|
|
@ -27,10 +27,10 @@ module.exports = {
|
|||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang }) {
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://telkussa.fi/API/Channels`)
|
||||
.get('https://telkussa.fi/API/Channels')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@ dayjs.extend(utc)
|
|||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setDebugger(debug)
|
||||
doFetch.setDebugger(debug)
|
||||
|
||||
const tz = 'Asia/Jakarta'
|
||||
|
||||
|
@ -20,17 +19,14 @@ module.exports = {
|
|||
site: 'tivie.id',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://tivie.id/channel/${
|
||||
channel.site_id
|
||||
}/${
|
||||
date.format('YYYYMMDD')
|
||||
}`
|
||||
return `https://tivie.id/channel/${channel.site_id}/${date.format('YYYYMMDD')}`
|
||||
},
|
||||
async parser({ content, date }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const $ = cheerio.load(content)
|
||||
const items = $('ul[x-data] > li[id*="event-"] > div.w-full').toArray()
|
||||
const items = $('ul[x-data] > li[id*="event-"] > div.w-full')
|
||||
.toArray()
|
||||
.map(item => {
|
||||
const $item = $(item)
|
||||
const time = $item.find('div:nth-child(1) span:nth-child(1)')
|
||||
|
@ -47,7 +43,12 @@ module.exports = {
|
|||
p.title = parseText(info)
|
||||
}
|
||||
if (p.title) {
|
||||
const [, , season, episode] = p.title.match(/( S(\d+))?, Ep\. (\d+)/) || [null, null, null, null]
|
||||
const [, , season, episode] = p.title.match(/( S(\d+))?, Ep\. (\d+)/) || [
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
if (season) {
|
||||
p.season = parseInt(season)
|
||||
}
|
||||
|
@ -63,7 +64,7 @@ module.exports = {
|
|||
.map(i => {
|
||||
const url = i.url
|
||||
delete i.url
|
||||
return {i, url}
|
||||
return { i, url }
|
||||
})
|
||||
if (queues.length) {
|
||||
await doFetch(queues, (queue, res) => {
|
||||
|
@ -84,7 +85,11 @@ module.exports = {
|
|||
if (i < items.length - 1) {
|
||||
items[i].stop = items[i + 1].start
|
||||
} else {
|
||||
items[i].stop = dayjs.tz(`${date.add(1, 'd').format('YYYY-MM-DD')} 00:00`, 'YYYY-MM-DD HH:mm', tz)
|
||||
items[i].stop = dayjs.tz(
|
||||
`${date.add(1, 'd').format('YYYY-MM-DD')} 00:00`,
|
||||
'YYYY-MM-DD HH:mm',
|
||||
tz
|
||||
)
|
||||
}
|
||||
}
|
||||
// add programs
|
||||
|
@ -116,13 +121,10 @@ module.exports = {
|
|||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text()
|
||||
.replace(/\t/g, '')
|
||||
.replace(/\n/g, ' ')
|
||||
.trim()
|
||||
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||
while (true) {
|
||||
if (text.match(/ /)) {
|
||||
text = text.replace(/ /g, ' ')
|
||||
if (text.match(/\s\s/)) {
|
||||
text = text.replace(/\s\s/g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
|
|
|
@ -20,10 +20,8 @@ const channel = {
|
|||
|
||||
axios.get.mockImplementation(url => {
|
||||
const urls = {
|
||||
'https://tivie.id/film/white-house-down-nwzDnwz9nAv6':
|
||||
'program01.html',
|
||||
'https://tivie.id/program/hudson-rex-s6-e14-nwzDnwvBmQr9':
|
||||
'program02.html',
|
||||
'https://tivie.id/film/white-house-down-nwzDnwz9nAv6': 'program01.html',
|
||||
'https://tivie.id/program/hudson-rex-s6-e14-nwzDnwvBmQr9': 'program02.html'
|
||||
}
|
||||
let data = ''
|
||||
if (urls[url] !== undefined) {
|
||||
|
@ -38,9 +36,7 @@ it('can generate valid url', () => {
|
|||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.join(__dirname, '__data__', 'content.html'))
|
||||
const results = (
|
||||
await parser({ date, content, channel })
|
||||
).map(p => {
|
||||
const results = (await parser({ date, content, channel })).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
|
@ -53,7 +49,8 @@ it('can parse response', async () => {
|
|||
title: 'White House Down',
|
||||
description:
|
||||
'Saat melakukan tur di Gedung Putih bersama putrinya yang masih kecil, seorang perwira polisi beraksi untuk melindungi anaknya dan presiden dari sekelompok penjajah paramiliter bersenjata lengkap.',
|
||||
image: 'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2023/09/65116c78791c2-1695640694.jpg?resize=480,270',
|
||||
image:
|
||||
'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2023/09/65116c78791c2-1695640694.jpg?resize=480,270'
|
||||
})
|
||||
expect(results[2]).toMatchObject({
|
||||
start: '2024-12-30T18:00:00.000Z',
|
||||
|
@ -61,9 +58,10 @@ it('can parse response', async () => {
|
|||
title: 'Hudson & Rex S6, Ep. 14',
|
||||
description:
|
||||
'Saat guru musik Jesse terbunuh di studio rekamannya, Charlie dan Rex menghubungkan kejahatan tersebut dengan pembunuhan yang tampaknya tak ada hubungannya.',
|
||||
image: 'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2024/07/668b7ced47b25-1720417517.jpg?resize=480,270',
|
||||
image:
|
||||
'https://i0.wp.com/is3.cloudhost.id/tivie/poster/2024/07/668b7ced47b25-1720417517.jpg?resize=480,270',
|
||||
season: 6,
|
||||
episode: 14,
|
||||
episode: 14
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -71,7 +69,7 @@ it('can handle empty guide', async () => {
|
|||
const results = await parser({
|
||||
date,
|
||||
channel,
|
||||
content: '',
|
||||
content: ''
|
||||
})
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -73,7 +73,7 @@ function parseItems(content, channel) {
|
|||
let parsed
|
||||
try {
|
||||
parsed = JSON.parse(content)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
if (!parsed || !parsed.k) return []
|
||||
|
|
|
@ -70,7 +70,7 @@ module.exports = {
|
|||
|
||||
async function getTotalPageCount(region) {
|
||||
const data = await axios
|
||||
.get(`https://tv.mail.ru/ajax/channel/list/`, {
|
||||
.get('https://tv.mail.ru/ajax/channel/list/', {
|
||||
params: { page: 0 },
|
||||
headers: {
|
||||
cookie: `s=fver=0|geo=${region};`
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
let offset = 0
|
||||
while (offset !== undefined) {
|
||||
const data = await axios
|
||||
.get(`https://web-api.tv.nu/tableauLinearChannels`, {
|
||||
.get('https://web-api.tv.nu/tableauLinearChannels', {
|
||||
params: {
|
||||
modules,
|
||||
date: dayjs().format('YYYY-MM-DD'),
|
||||
|
|
|
@ -24,7 +24,7 @@ module.exports = {
|
|||
|
||||
return programs
|
||||
},
|
||||
async channels({ token, lang = en }) {
|
||||
async channels({ token, lang = 'en' }) {
|
||||
const axios = require('axios')
|
||||
const ACCESS_TOKEN = token
|
||||
? token
|
||||
|
|
|
@ -2,9 +2,7 @@ const dayjs = require('dayjs')
|
|||
const doFetch = require('@ntlab/sfetch')
|
||||
const debug = require('debug')('site:tv.yandex.ru')
|
||||
|
||||
doFetch
|
||||
.setDebugger(debug)
|
||||
.setMaxWorker(10)
|
||||
doFetch.setDebugger(debug).setMaxWorker(10)
|
||||
|
||||
// enable to fetch guide description but its take a longer time
|
||||
const detailedGuide = true
|
||||
|
@ -12,14 +10,16 @@ const detailedGuide = true
|
|||
// update this data by heading to https://tv.yandex.ru and change the values accordingly
|
||||
const cookies = {
|
||||
i: 'eIUfSP+/mzQWXcH+Cuz8o1vY+D2K8fhBd6Sj0xvbPZeO4l3cY+BvMp8fFIuM17l6UE1Z5+R2a18lP00ex9iYVJ+VT+c=',
|
||||
spravka: 'dD0xNzM0MjA0NjM4O2k9MTI1LjE2NC4xNDkuMjAwO0Q9QTVCQ0IyOTI5RDQxNkU5NkEyOTcwMTNDMzZGMDAzNjRDNTFFNDM4QkE2Q0IyOTJDRjhCOTZDRDIzODdBQzk2MzRFRDc5QTk2Qjc2OEI1MUY5MTM5M0QzNkY3OEQ2OUY3OTUwNkQ3RjBCOEJGOEJDMjAwMTQ0RDUwRkFCMDNEQzJFMDI2OEI5OTk5OUJBNEFERUYwOEQ1MjUwQTE0QTI3RDU1MEQwM0U0O3U9MTczNDIwNDYzODUyNDYyNzg1NDtoPTIxNTc0ZTc2MDQ1ZjcwMDBkYmY0NTVkM2Q2ZWMyM2Y1',
|
||||
spravka:
|
||||
'dD0xNzM0MjA0NjM4O2k9MTI1LjE2NC4xNDkuMjAwO0Q9QTVCQ0IyOTI5RDQxNkU5NkEyOTcwMTNDMzZGMDAzNjRDNTFFNDM4QkE2Q0IyOTJDRjhCOTZDRDIzODdBQzk2MzRFRDc5QTk2Qjc2OEI1MUY5MTM5M0QzNkY3OEQ2OUY3OTUwNkQ3RjBCOEJGOEJDMjAwMTQ0RDUwRkFCMDNEQzJFMDI2OEI5OTk5OUJBNEFERUYwOEQ1MjUwQTE0QTI3RDU1MEQwM0U0O3U9MTczNDIwNDYzODUyNDYyNzg1NDtoPTIxNTc0ZTc2MDQ1ZjcwMDBkYmY0NTVkM2Q2ZWMyM2Y1',
|
||||
yandexuid: '1197179041732383499',
|
||||
yashr: '4682342911732383504',
|
||||
yuidss: '1197179041732383499',
|
||||
user_display: 824,
|
||||
user_display: 824
|
||||
}
|
||||
const headers = {
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.0.0.0'
|
||||
}
|
||||
const caches = {}
|
||||
|
||||
|
@ -50,7 +50,9 @@ module.exports = {
|
|||
}
|
||||
caches[cacheid].forEach(schedule => {
|
||||
schedule.events
|
||||
.filter(event => event.channelFamilyId == channel.site_id && date.isSame(event.start, 'day'))
|
||||
.filter(
|
||||
event => event.channelFamilyId == channel.site_id && date.isSame(event.start, 'day')
|
||||
)
|
||||
.forEach(event => {
|
||||
if (events.indexOf(event.id) < 0) {
|
||||
events.push(event.id)
|
||||
|
@ -171,7 +173,10 @@ function parseContent(content, date, checkOnly = false) {
|
|||
content = content.toString()
|
||||
}
|
||||
// got captcha, its look like our cookies has expired
|
||||
if (content?.type === 'captcha' || (typeof content === 'string' && content.match(/SmartCaptcha/))) {
|
||||
if (
|
||||
content?.type === 'captcha' ||
|
||||
(typeof content === 'string' && content.match(/SmartCaptcha/))
|
||||
) {
|
||||
throw new Error('Got captcha, please goto https://tv.yandex.ru and update cookies!')
|
||||
}
|
||||
if (typeof content === 'object') {
|
||||
|
@ -210,35 +215,41 @@ function parseContent(content, date, checkOnly = false) {
|
|||
headers['X-User-Session-Id'] = sessionId
|
||||
}
|
||||
if (checkOnly && region && tvSk.key && sessionId) {
|
||||
valid = true;
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkOnly ? valid : [queues, schedules]
|
||||
return checkOnly ? valid : [queues, schedules]
|
||||
}
|
||||
|
||||
function parseCookies(headers) {
|
||||
if (Array.isArray(headers['set-cookie'])) {
|
||||
headers['set-cookie']
|
||||
.forEach(cookie => {
|
||||
const [key, value] = cookie.split('; ')[0].split('=')
|
||||
if (cookies[key] !== value) {
|
||||
cookies[key] = value
|
||||
debug(`Update cookie ${key}=${value}`)
|
||||
}
|
||||
})
|
||||
headers['set-cookie'].forEach(cookie => {
|
||||
const [key, value] = cookie.split('; ')[0].split('=')
|
||||
if (cookies[key] !== value) {
|
||||
cookies[key] = value
|
||||
debug(`Update cookie ${key}=${value}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getSchedules(schedules) {
|
||||
return schedules.filter(schedule => schedule.events.length);
|
||||
return schedules.filter(schedule => schedule.events.length)
|
||||
}
|
||||
|
||||
function getHeaders(data = {}) {
|
||||
return Object.assign({}, headers, {
|
||||
Cookie: Object.keys(cookies).map(cookie => `${cookie}=${cookies[cookie]}`).join('; ')
|
||||
}, data)
|
||||
return Object.assign(
|
||||
{},
|
||||
headers,
|
||||
{
|
||||
Cookie: Object.keys(cookies)
|
||||
.map(cookie => `${cookie}=${cookies[cookie]}`)
|
||||
.join('; ')
|
||||
},
|
||||
data
|
||||
)
|
||||
}
|
||||
|
||||
function getUrl(date, region = null, page = null, event = null) {
|
||||
|
@ -253,7 +264,9 @@ function getUrl(date, region = null, page = null, event = null) {
|
|||
url += `${url.endsWith('/') ? '' : '/'}event?eventId=${event.id}&programCoId=`
|
||||
}
|
||||
if (date) {
|
||||
url += `${url.indexOf('?') < 0 ? '?' : '&'}date=${date.format('YYYY-MM-DD')}${!page ? '&grid=all' : ''}&period=all-day`
|
||||
url += `${url.indexOf('?') < 0 ? '?' : '&'}date=${date.format('YYYY-MM-DD')}${
|
||||
!page ? '&grid=all' : ''
|
||||
}&period=all-day`
|
||||
}
|
||||
if (page && page.id !== undefined && page.offset !== undefined) {
|
||||
url += `${url.indexOf('?') < 0 ? '?' : '&'}offset=${page.offset}`
|
||||
|
@ -266,7 +279,7 @@ function getUrl(date, region = null, page = null, event = null) {
|
|||
|
||||
function getQueue(url, referer) {
|
||||
const data = {
|
||||
'Origin': 'https://tv.yandex.ru',
|
||||
Origin: 'https://tv.yandex.ru'
|
||||
}
|
||||
if (referer) {
|
||||
data['Referer'] = referer
|
||||
|
|
|
@ -16,7 +16,7 @@ const channel = {
|
|||
site_id: '16',
|
||||
xmltv_id: 'ChannelOne.ru'
|
||||
}
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
axios.get.mockImplementation(url => {
|
||||
if (url === 'https://tv.yandex.ru/?date=2023-11-26&grid=all&period=all-day') {
|
||||
return Promise.resolve({
|
||||
headers: {},
|
||||
|
@ -29,7 +29,10 @@ axios.get.mockImplementation((url, opts) => {
|
|||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/schedule.json')))
|
||||
})
|
||||
}
|
||||
if (url === 'https://tv.yandex.ru/api/120809/main/chunk?page=0&date=2023-11-26&period=all-day&offset=0&limit=11') {
|
||||
if (
|
||||
url ===
|
||||
'https://tv.yandex.ru/api/120809/main/chunk?page=0&date=2023-11-26&period=all-day&offset=0&limit=11'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
headers: {},
|
||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/schedule0.json')))
|
||||
|
@ -44,9 +47,7 @@ axios.get.mockImplementation((url, opts) => {
|
|||
})
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe(
|
||||
'https://tv.yandex.ru/?date=2023-11-26&grid=all&period=all-day'
|
||||
)
|
||||
expect(url({ date })).toBe('https://tv.yandex.ru/?date=2023-11-26&grid=all&period=all-day')
|
||||
})
|
||||
|
||||
it('can generate valid request headers', () => {
|
||||
|
@ -63,9 +64,7 @@ it('can generate valid request headers', () => {
|
|||
|
||||
it('can parse response', async () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||
const result = (
|
||||
await parser({ content, date, channel })
|
||||
).map(p => {
|
||||
const result = (await parser({ content, date, channel })).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
|
@ -77,7 +76,8 @@ it('can parse response', async () => {
|
|||
stop: '2023-11-26T02:10:00.000Z',
|
||||
title: 'ПОДКАСТ.ЛАБ. Мелодии моей жизни',
|
||||
category: 'досуг',
|
||||
description: 'Впереди вся ночь и есть о чем поговорить. Фильмы, музыка, любовь, звезды, еда, мода, анекдоты, спорт, деньги, настоящее, будущее - все это в творческом эксперименте.\nЛариса Гузеева читает любовные письма. Леонид Якубович рассказывает, кого не берут в пилоты. Арина Холина - какой секс способен довести до мужа или до развода. Валерий Сюткин на ходу сочиняет песню для Карины Кросс и Вали Карнавал. Дмитрий Дибров дарит новую жизнь любимой \"Антропологии\". Денис Казанский - все о футболе, хоккее и не только.\n\"ПОДКАСТЫ. ЛАБ\" - серия подкастов разной тематики, которые невозможно проспать. Интеллектуальные дискуссии после полуночи с самыми компетентными экспертами и актуальными спикерами.'
|
||||
description:
|
||||
'Впереди вся ночь и есть о чем поговорить. Фильмы, музыка, любовь, звезды, еда, мода, анекдоты, спорт, деньги, настоящее, будущее - все это в творческом эксперименте.\nЛариса Гузеева читает любовные письма. Леонид Якубович рассказывает, кого не берут в пилоты. Арина Холина - какой секс способен довести до мужа или до развода. Валерий Сюткин на ходу сочиняет песню для Карины Кросс и Вали Карнавал. Дмитрий Дибров дарит новую жизнь любимой "Антропологии". Денис Казанский - все о футболе, хоккее и не только.\n"ПОДКАСТЫ. ЛАБ" - серия подкастов разной тематики, которые невозможно проспать. Интеллектуальные дискуссии после полуночи с самыми компетентными экспертами и актуальными спикерами.'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue