Merge pull request #440 from iptv-org/reduce-number-of-requests

Reduce number of requests
This commit is contained in:
Aleksandr Statciuk 2022-01-22 02:07:13 +03:00 committed by GitHub
commit ecd17eb24a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 292 additions and 252 deletions

View file

@ -14,7 +14,7 @@ jobs:
node-version: '14' node-version: '14'
cache: 'npm' cache: 'npm'
- run: npm install - run: npm install
- run: node scripts/commands/create-database.js - run: node scripts/commands/create-queue.js
- run: node scripts/commands/create-matrix.js - run: node scripts/commands/create-matrix.js
id: create-matrix id: create-matrix
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
@ -84,6 +84,10 @@ jobs:
with: with:
name: logs name: logs
path: scripts/logs path: scripts/logs
- uses: actions/upload-artifact@v2
with:
name: errors.log
path: scripts/logs/errors.log
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: channels.json name: channels.json
@ -119,13 +123,13 @@ jobs:
[1]: https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }} [1]: https://github.com/iptv-org/epg/actions/runs/${{ github.run_id }}
- uses: juliangruber/merge-pull-request-action@v1 - uses: juliangruber/merge-pull-request-action@v1
if: ${{ github.ref == 'refs/heads/master' }} if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
with: with:
github-token: ${{ secrets.PAT }} github-token: ${{ secrets.PAT }}
number: ${{ steps.pull-request.outputs.pr_number }} number: ${{ steps.pull-request.outputs.pr_number }}
method: squash method: squash
- uses: JamesIves/github-pages-deploy-action@4.1.1 - uses: JamesIves/github-pages-deploy-action@4.1.1
if: ${{ github.ref == 'refs/heads/master' }} if: ${{ !env.ACT && github.ref == 'refs/heads/master' }}
with: with:
branch: gh-pages branch: gh-pages
folder: .gh-pages folder: .gh-pages

View file

@ -1,6 +1,6 @@
const { db, file, parser, logger } = require('../core') const { db, file, parser, logger } = require('../core')
const { program } = require('commander') const { program } = require('commander')
const _ = require('lodash') const { shuffle } = require('lodash')
const options = program const options = program
.option( .option(
@ -13,23 +13,22 @@ const options = program
.parse(process.argv) .parse(process.argv)
.opts() .opts()
const channels = []
async function main() { async function main() {
logger.info('Starting...') logger.info('Starting...')
logger.info(`Number of clusters: ${options.maxClusters}`) logger.info(`Number of clusters: ${options.maxClusters}`)
await loadChannels() await saveToDatabase(await getChannels())
await saveToDatabase()
logger.info('Done') logger.info('Done')
} }
main() main()
async function loadChannels() { async function getChannels() {
logger.info(`Loading channels...`) logger.info(`Loading channels...`)
let channels = {}
const files = await file.list(options.channels) const files = await file.list(options.channels)
for (const filepath of files) { for (const filepath of files) {
const dir = file.dirname(filepath) const dir = file.dirname(filepath)
@ -39,25 +38,39 @@ async function loadChannels() {
const configPath = `${dir}/${site}.config.js` const configPath = `${dir}/${site}.config.js`
const config = require(file.resolve(configPath)) const config = require(file.resolve(configPath))
if (config.ignore) continue if (config.ignore) continue
const [__, gid] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null] const [__, region] = filename.match(/_([a-z-]+)\.channels\.xml/i) || [null, null]
const groupId = `${region}/${site}`
const items = await parser.parseChannels(filepath) const items = await parser.parseChannels(filepath)
for (const item of items) { for (const item of items) {
const countryCode = item.xmltv_id.split('.')[1] const key = `${item.site}:${item.site_id}`
item.country = countryCode ? countryCode.toUpperCase() : null if (!channels[key]) {
item.channelsPath = filepath const countryCode = item.xmltv_id.split('.')[1]
item.configPath = configPath item.country = countryCode ? countryCode.toUpperCase() : null
item.gid = gid item.channelsPath = filepath
channels.push(item) item.configPath = configPath
item.groups = []
channels[key] = item
}
if (!channels[key].groups.includes(groupId)) {
channels[key].groups.push(groupId)
}
} }
} }
channels = Object.values(channels)
logger.info(`Found ${channels.length} channels`) logger.info(`Found ${channels.length} channels`)
return channels
} }
async function saveToDatabase() { async function saveToDatabase(channels = []) {
logger.info('Saving to the database...') logger.info('Saving to the database...')
await db.channels.load() await db.channels.load()
await db.channels.reset() await db.channels.reset()
const chunks = split(_.shuffle(channels), options.maxClusters) const chunks = split(shuffle(channels), options.maxClusters)
for (const [i, chunk] of chunks.entries()) { for (const [i, chunk] of chunks.entries()) {
for (const item of chunk) { for (const item of chunk) {
item.cluster_id = i + 1 item.cluster_id = i + 1

View file

@ -47,18 +47,6 @@ async function main() {
}) })
await grabber.grab(channel, config, async (data, err) => { await grabber.grab(channel, config, async (data, err) => {
await file.append(
clusterLog,
JSON.stringify({
_id: channel._id,
site: channel.site,
country: channel.country,
logo: data.channel.logo,
gid: channel.gid,
programs: data.programs
}) + '\n'
)
logger.info( logger.info(
`[${i}/${total}] ${channel.site} - ${channel.xmltv_id} - ${data.date.format( `[${i}/${total}] ${channel.site} - ${channel.xmltv_id} - ${data.date.format(
'MMM D, YYYY' 'MMM D, YYYY'
@ -67,6 +55,15 @@ async function main() {
if (err) logger.error(err.message) if (err) logger.error(err.message)
const result = {
channel: data.channel,
programs: data.programs,
date: data.date.format(),
error: err ? err.message : null
}
await file.append(clusterLog, JSON.stringify(result) + '\n')
if (i < total) i++ if (i < total) i++
}) })
} }

View file

@ -4,8 +4,9 @@ const _ = require('lodash')
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs' const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
async function main() { async function main() {
const errorsLog = `${LOGS_DIR}/errors.log`
await file.create(errorsLog)
await db.channels.load() await db.channels.load()
await db.programs.load() await db.programs.load()
await db.programs.reset() await db.programs.reset()
const files = await file.list(`${LOGS_DIR}/load-cluster/cluster_*.log`) const files = await file.list(`${LOGS_DIR}/load-cluster/cluster_*.log`)
@ -13,8 +14,6 @@ async function main() {
logger.info(`Parsing "${filepath}"...`) logger.info(`Parsing "${filepath}"...`)
const results = await parser.parseLogs(filepath) const results = await parser.parseLogs(filepath)
for (const result of results) { for (const result of results) {
await db.channels.update({ _id: result._id }, { $set: { logo: result.logo } })
const programs = result.programs.map(program => { const programs = result.programs.map(program => {
return { return {
title: program.title, title: program.title,
@ -27,13 +26,25 @@ async function main() {
lang: program.lang, lang: program.lang,
start: program.start, start: program.start,
stop: program.stop, stop: program.stop,
site: result.site, site: result.channel.site,
country: result.country, _cid: result.channel._id
gid: result.gid
} }
}) })
await db.programs.insert(programs) await db.programs.insert(programs)
if (result.channel.logo) {
await db.channels.update(
{ _id: result.channel._id },
{ $set: { logo: result.channel.logo, programCount: result.programs.length } }
)
}
if (result.error) {
await file.append(
errorsLog,
JSON.stringify({ ...result.channel, date: result.date, error: result.error }) + '\n'
)
}
} }
} }

View file

@ -5,31 +5,45 @@ const DB_DIR = process.env.DB_DIR || 'scripts/database'
const API_DIR = process.env.API_DIR || '.gh-pages/api' const API_DIR = process.env.API_DIR || '.gh-pages/api'
async function main() { async function main() {
await generateChannelsJson() await saveToChannelsJson(await loadChannels())
await generateProgramsJson() await saveToProgramsJson(await loadPrograms())
logger.info(`Done`)
} }
main() main()
async function generateChannelsJson() { async function loadChannels() {
logger.info('Generating channels.json...') logger.info('Loading channels from database...')
const channels = await loadChannels() await db.channels.load()
const channelsPath = `${API_DIR}/channels.json` const channels = await db.channels.find({}).sort({ xmltv_id: 1 })
logger.info(`Saving to "${channelsPath}"...`)
await file.create(channelsPath, JSON.stringify(channels))
}
async function generateProgramsJson() { const output = {}
logger.info('Generating programs.json...') for (const channel of channels) {
if (!output[channel.xmltv_id]) {
output[channel.xmltv_id] = {
id: channel.xmltv_id,
name: [],
logo: channel.logo || null,
country: channel.country,
guides: []
}
} else {
output[channel.xmltv_id].logo = output[channel.xmltv_id].logo || channel.logo
}
const programs = await loadPrograms() output[channel.xmltv_id].name.push(channel.name)
output[channel.xmltv_id].name = _.uniq(output[channel.xmltv_id].name)
channel.groups.forEach(group => {
if (channel.programCount) {
output[channel.xmltv_id].guides.push(
`https://iptv-org.github.io/epg/guides/${group}.epg.xml`
)
}
})
}
const programsPath = `${API_DIR}/programs.json` return Object.values(output)
logger.info(`Saving to "${programsPath}"...`)
await file.create(programsPath, JSON.stringify(programs))
} }
async function loadPrograms() { async function loadPrograms() {
@ -37,9 +51,9 @@ async function loadPrograms() {
await db.programs.load() await db.programs.load()
let items = await db.programs.find({}) let programs = await db.programs.find({})
items = items.map(item => { programs = programs.map(item => {
const categories = Array.isArray(item.category) ? item.category : [item.category] const categories = Array.isArray(item.category) ? item.category : [item.category]
return { return {
@ -57,37 +71,19 @@ async function loadPrograms() {
} }
}) })
logger.info('Sort programs...') programs = _.sortBy(programs, ['channel', 'site', 'start'])
return _.sortBy(items, ['channel', 'site', 'start'])
return programs
} }
async function loadChannels() { async function saveToChannelsJson(channels = []) {
logger.info('Loading channels from database...') const channelsPath = `${API_DIR}/channels.json`
logger.info(`Saving to "${channelsPath}"...`)
await db.channels.load() await file.create(channelsPath, JSON.stringify(channels))
}
const items = await db.channels.find({}).sort({ xmltv_id: 1 })
async function saveToProgramsJson(programs = []) {
const output = {} const programsPath = `${API_DIR}/programs.json`
for (const item of items) { logger.info(`Saving to "${programsPath}"...`)
if (!output[item.xmltv_id]) { await file.create(programsPath, JSON.stringify(programs))
output[item.xmltv_id] = {
id: item.xmltv_id,
name: [],
logo: item.logo || null,
country: item.country,
guides: []
}
} else {
output[item.xmltv_id].logo = output[item.xmltv_id].logo || item.logo
}
output[item.xmltv_id].name.push(item.name)
output[item.xmltv_id].name = _.uniq(output[item.xmltv_id].name)
output[item.xmltv_id].guides.push(
`https://iptv-org.github.io/epg/guides/${item.gid}/${item.site}.epg.xml`
)
}
return Object.values(output)
} }

View file

@ -17,75 +17,57 @@ main()
async function generateGuides() { async function generateGuides() {
logger.info(`Generating guides/...`) logger.info(`Generating guides/...`)
const channels = await loadChannels() const grouped = groupByGroup(await loadChannels())
const programs = await loadPrograms()
const grouped = _.groupBy(programs, i => `${i.gid}_${i.site}`) logger.info('Loading "database/programs.db"...')
for (let key in grouped) { await db.programs.load()
const [gid, site] = key.split('_') || [null, null]
const filepath = `${PUBLIC_DIR}/guides/${gid}/${site}.epg.xml`
const groupProgs = grouped[key]
const groupChannels = Object.keys(_.groupBy(groupProgs, i => `${i.site}_${i.channel}`)).map(
key => {
let [site, channel] = key.split('_')
return channels.find(i => i.xmltv_id === channel && i.site === site) for (const key in grouped) {
} const filepath = `${PUBLIC_DIR}/guides/${key}.epg.xml`
) const channels = grouped[key]
const programs = await loadProgramsForChannels(channels)
const output = grabber.convertToXMLTV({ channels: groupChannels, programs: groupProgs }) const output = grabber.convertToXMLTV({ channels, programs })
logger.info(`Creating "${filepath}"...`) logger.info(`Creating "${filepath}"...`)
await file.create(filepath, output) await file.create(filepath, output)
await log({ await log({
gid, group: key,
site, count: programs.length
count: groupChannels.length,
status: 1
}) })
} }
logger.info(`Done`) logger.info(`Done`)
} }
function groupByGroup(channels = []) {
const groups = {}
channels.forEach(channel => {
channel.groups.forEach(key => {
if (!groups[key]) {
groups[key] = []
}
groups[key].push(channel)
})
})
return groups
}
async function loadChannels() { async function loadChannels() {
logger.info('Loading channels...') logger.info('Loading channels...')
await db.channels.load() await db.channels.load()
return await db.channels.find({}).sort({ xmltv_id: 1 }) return await db.channels.find({ programCount: { $gt: 0 } }).sort({ xmltv_id: 1 })
} }
async function loadPrograms() { async function loadProgramsForChannels(channels = []) {
logger.info('Loading programs...') const cids = channels.map(c => c._id)
logger.info('Loading "database/programs.db"...') return await db.programs.find({ _cid: { $in: cids } }).sort({ channel: 1, start: 1 })
await db.programs.load()
logger.info('Loading programs from "database/programs.db"...')
let programs = await db.programs.find({}).sort({ channel: 1, start: 1 })
programs = programs.map(program => {
return {
title: program.title,
description: program.description,
category: program.category,
icon: program.icon,
channel: program.channel,
lang: program.lang,
start: program.start,
stop: program.stop,
site: program.site,
country: program.country,
season: program.season,
episode: program.episode,
gid: program.gid,
_id: program._id
}
})
return programs
} }
async function setUp() { async function setUp() {

View file

@ -1,14 +1,11 @@
const { file, markdown, parser, logger } = require('../core') const { file, markdown, parser, logger } = require('../core')
const provinces = require('../data/ca-provinces.json')
const countries = require('../data/countries.json') const countries = require('../data/countries.json')
const states = require('../data/us-states.json') const states = require('../data/us-states.json')
const provinces = require('../data/ca-provinces.json')
const { program } = require('commander') const { program } = require('commander')
const _ = require('lodash') const _ = require('lodash')
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs' const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs'
const LOG_PATH = `${LOGS_DIR}/update-guides.log`
let log = []
const options = program const options = program
.option('-c, --config <config>', 'Set path to config file', '.readme/config.json') .option('-c, --config <config>', 'Set path to config file', '.readme/config.json')
@ -16,31 +13,28 @@ const options = program
.opts() .opts()
async function main() { async function main() {
await setUp() const records = await getLogRecords()
await generateCountriesTable(records)
await generateCountriesTable() await generateUSStatesTable(records)
await generateUSStatesTable() await generateCanadaProvincesTable(records)
await generateCanadaProvincesTable()
await updateReadme() await updateReadme()
} }
main() main()
async function generateCountriesTable() { async function generateCountriesTable(items = []) {
logger.info('Generating countries table...') logger.info('Generating countries table...')
const items = log.filter(i => i.gid.length === 2)
let rows = [] let rows = []
for (const item of items) { for (const item of items) {
const code = item.gid.toUpperCase() const country = countries[item.code]
const country = countries[code] if (!country) continue
rows.push({ rows.push({
flag: country.flag, flag: country.flag,
name: country.name, name: country.name,
channels: item.count, channels: item.count,
epg: `<code>https://iptv-org.github.io/epg/guides/${item.gid}/${item.site}.epg.xml</code>` epg: `<code>https://iptv-org.github.io/epg/guides/${item.group}.epg.xml</code>`
}) })
} }
@ -52,19 +46,18 @@ async function generateCountriesTable() {
await file.create('./.readme/_countries.md', table) await file.create('./.readme/_countries.md', table)
} }
async function generateUSStatesTable() { async function generateUSStatesTable(items = []) {
logger.info('Generating US states table...') logger.info('Generating US states table...')
const items = log.filter(i => i.gid.startsWith('us-'))
let rows = [] let rows = []
for (const item of items) { for (const item of items) {
const code = item.gid.toUpperCase() const state = states[item.code]
const state = states[code] if (!state) continue
rows.push({ rows.push({
name: state.name, name: state.name,
channels: item.count, channels: item.count,
epg: `<code>https://iptv-org.github.io/epg/guides/${item.gid}/${item.site}.epg.xml</code>` epg: `<code>https://iptv-org.github.io/epg/guides/${item.group}.epg.xml</code>`
}) })
} }
@ -76,19 +69,18 @@ async function generateUSStatesTable() {
await file.create('./.readme/_us-states.md', table) await file.create('./.readme/_us-states.md', table)
} }
async function generateCanadaProvincesTable() { async function generateCanadaProvincesTable(items = []) {
logger.info('Generating Canada provinces table...') logger.info('Generating Canada provinces table...')
const items = log.filter(i => i.gid.startsWith('ca-'))
let rows = [] let rows = []
for (const item of items) { for (const item of items) {
const code = item.gid.toUpperCase() const province = provinces[item.code]
const province = provinces[code] if (!province) continue
rows.push({ rows.push({
name: province.name, name: province.name,
channels: item.count, channels: item.count,
epg: `<code>https://iptv-org.github.io/epg/guides/${item.gid}/${item.site}.epg.xml</code>` epg: `<code>https://iptv-org.github.io/epg/guides/${item.group}.epg.xml</code>`
}) })
} }
@ -108,11 +100,19 @@ async function updateReadme() {
await markdown.compile(options.config) await markdown.compile(options.config)
} }
async function setUp() { async function getLogRecords() {
log = await parser.parseLogs(LOG_PATH) const logPath = `${LOGS_DIR}/update-guides.log`
const records = await parser.parseLogs(logPath)
if (!log.length) { if (!records.length) {
logger.error(`File "${LOG_PATH}" is empty`) logger.error(`File "${logPath}" is empty`)
process.exit(1) process.exit(1)
} }
return records.map(item => {
const code = item.group.split('/')[0] || ''
item.code = code.toUpperCase()
return item
})
} }

View file

@ -1 +1 @@
[{"id":"BravoEast.us","name":["Bravo East"],"logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","country":"US","guides":["https://iptv-org.github.io/epg/guides/us/directv.com.epg.xml"]},{"id":"CNNInternationalEurope.us","name":["CNN International Europe","CNN Int"],"logo":"https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg","country":"US","guides":["https://iptv-org.github.io/epg/guides/fr/chaines-tv.orange.fr.epg.xml","https://iptv-org.github.io/epg/guides/ge/magticom.ge.epg.xml"]},{"id":"MNetMovies2.za","name":["M-Net Movies 2"],"logo":"https://rndcdn.dstv.com/dstvcms/2020/08/31/M-Net_Movies_2_Logo_4-3_lightbackground_xlrg.png","country":"ZA","guides":["https://iptv-org.github.io/epg/guides/zw/dstv.com.epg.xml"]}] [{"id":"BravoEast.us","name":["Bravo East"],"logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","country":"US","guides":[]},{"id":"CNNInternationalEurope.us","name":["CNN International Europe","CNN Int"],"logo":"https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg","country":"US","guides":["https://iptv-org.github.io/epg/guides/fr/chaines-tv.orange.fr.epg.xml"]},{"id":"MNetMovies2.za","name":["M-Net Movies 2"],"logo":"https://rndcdn.dstv.com/dstvcms/2020/08/31/M-Net_Movies_2_Logo_4-3_lightbackground_xlrg.png","country":"ZA","guides":["https://iptv-org.github.io/epg/guides/zw/dstv.com.epg.xml"]}]

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?><tv> <?xml version="1.0" encoding="UTF-8" ?><tv>
<channel id="CNNInternationalEurope.us"><display-name>CNN Int</display-name><icon src="https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg"/><url>https://magticom.ge</url></channel> <channel id="CNNInternationalEurope.us"><display-name>CNN International Europe</display-name><url>https://chaines-tv.orange.fr</url></channel>
<programme start="20220110000000 +0000" stop="20220110010000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">CNN Newsroom Sunday</title><desc lang="ru">Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.</desc><category lang="ru">Category1</category><category lang="ru">Category2</category></programme> <programme start="20220110000000 +0000" stop="20220110010000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">CNN Newsroom Sunday</title><desc lang="ru">Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.</desc><category lang="ru">Category1</category><category lang="ru">Category2</category></programme>
<programme start="20220110010000 +0000" stop="20220110020000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">Fareed Zakaria GPS</title><desc lang="ru">Интервью с главными игроками мировой политики.</desc><category lang="ru">Category1</category></programme> <programme start="20220110010000 +0000" stop="20220110020000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">Fareed Zakaria GPS</title><desc lang="ru">Интервью с главными игроками мировой политики.</desc><category lang="ru">Category1</category></programme>
<programme start="20220110020000 +0000" stop="20220110023000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">African Voices Changemakers. 114-я серия</title><desc lang="ru">114-я серия. Африка сегодня - люди, новости, события.</desc></programme> <programme start="20220110020000 +0000" stop="20220110023000 +0000" channel="CNNInternationalEurope.us"><title lang="ru">African Voices Changemakers. 114-я серия</title><desc lang="ru">114-я серия. Африка сегодня - люди, новости, события.</desc></programme>

View file

@ -1,2 +1,2 @@
{"gid":"us","site":"magticom.ge","count":1,"status":1} {"group":"fr/chaines-tv.orange.fr","count":32}
{"gid":"za","site":"dstv.com","count":1,"status":1} {"group":"zw/dstv.com","count":14}

View file

@ -1,4 +1,4 @@
{"lang":"en","xmltv_id":"BravoEast.us","site_id":"237","logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","name":"Bravo East","site":"directv.com","channelsPath":"sites/directv.com/directv.com_us.channels.xml","configPath":"sites/directv.com/directv.com.config.js","gid":"us","cluster_id":84,"country":"US","_id":"00AluKCrCnfgrl8W"} {"lang":"en","xmltv_id":"BravoEast.us","site_id":"237","logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","name":"Bravo East","site":"directv.com","channelsPath":"sites/directv.com/directv.com_us.channels.xml","configPath":"sites/directv.com/directv.com.config.js","groups":["us/directv.com"],"cluster_id":84,"country":"US","programCount":0,"_id":"00AluKCrCnfgrl8W"}
{"lang":"fr","country":"US","xmltv_id":"CNNInternationalEurope.us","site_id":"53","logo":null,"name":"CNN International Europe","site":"chaines-tv.orange.fr","channelsPath":"sites/chaines-tv.orange.fr/chaines-tv.orange.fr_fr.channels.xml","configPath":"tests/__data__/input/sites/example.com.config.js","gid":"fr","cluster_id":1,"_id":"0Wefq0oMR3feCcuY"} {"lang":"fr","country":"US","xmltv_id":"CNNInternationalEurope.us","site_id":"53","logo":null,"name":"CNN International Europe","site":"chaines-tv.orange.fr","channelsPath":"sites/chaines-tv.orange.fr/chaines-tv.orange.fr_fr.channels.xml","configPath":"tests/__data__/input/sites/example.com.config.js","groups":["fr/chaines-tv.orange.fr"],"cluster_id":1,"programCount":32,"_id":"0Wefq0oMR3feCcuY"}
{"lang":"ru","country":"US","xmltv_id":"CNNInternationalEurope.us","site_id":"140","logo":"https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg","name":"CNN Int","site":"magticom.ge","channelsPath":"sites/magticom.ge/magticom.ge_ge.channels.xml","configPath":"tests/__data__/input/sites/example.com.config.js","gid":"ge","cluster_id":1,"_id":"1XzrxNkSF2AQNBrT"} {"lang":"ru","country":"US","xmltv_id":"CNNInternationalEurope.us","site_id":"140","logo":"https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg","name":"CNN Int","site":"magticom.ge","channelsPath":"sites/magticom.ge/magticom.ge_ge.channels.xml","configPath":"tests/__data__/input/sites/example.com.config.js","groups":["ge/magticom.ge"],"cluster_id":1,"programCount":0,"_id":"1XzrxNkSF2AQNBrT"}
{"lang":"en","country":"ZA","xmltv_id":"MNetMovies2.za","site_id":"404a052b-3dea-4cac-a19c-de9a7d6f191d#MAP","logo":"https://rndcdn.dstv.com/dstvcms/2020/08/31/M-Net_Movies_2_Logo_4-3_lightbackground_xlrg.png","name":"M-Net Movies 2","site":"dstv.com","channelsPath":"sites/dstv.com/dstv.com_zw.channels.xml","configPath":"sites/dstv.com/dstv.com.config.js","gid":"zw","cluster_id":120,"_id":"1lnhXpN7g0ER5XwN"} {"lang":"en","country":"ZA","xmltv_id":"MNetMovies2.za","site_id":"404a052b-3dea-4cac-a19c-de9a7d6f191d#MAP","logo":"https://rndcdn.dstv.com/dstvcms/2020/08/31/M-Net_Movies_2_Logo_4-3_lightbackground_xlrg.png","name":"M-Net Movies 2","site":"dstv.com","channelsPath":"sites/dstv.com/dstv.com_zw.channels.xml","configPath":"sites/dstv.com/dstv.com.config.js","groups":["zw/dstv.com"],"cluster_id":120,"programCount":14,"_id":"1lnhXpN7g0ER5XwN"}

View file

@ -1,46 +1,46 @@
{"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641825900,"stop":1641826800,"site":"magticom.ge","gid":"us","country":"US","_id":"12AJc0GeEJE9p4c3"} {"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641825900,"stop":1641826800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"12AJc0GeEJE9p4c3"}
{"title":"Connecting Africa. 114-я серия","description":"114-я серия. Проект, рассказывающий о людях и компаниях, которые совершают революцию в африканском бизнесе, и о тех, кто объединяет континент, выступая за свободную торговлю в Африке.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641843900,"stop":1641844800,"site":"magticom.ge","gid":"us","country":"US","_id":"1dxcT34nyxzOlxBL"} {"title":"Connecting Africa. 114-я серия","description":"114-я серия. Проект, рассказывающий о людях и компаниях, которые совершают революцию в африканском бизнесе, и о тех, кто объединяет континент, выступая за свободную торговлю в Африке.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641843900,"stop":1641844800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"1dxcT34nyxzOlxBL"}
{"title":"Connect the World","description":"Актуальная мировая информация с разных континентов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641826800,"stop":1641830400,"site":"magticom.ge","gid":"us","country":"US","_id":"2uJe4w2lgvjNOXo0"} {"title":"Connect the World","description":"Актуальная мировая информация с разных континентов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641826800,"stop":1641830400,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"2uJe4w2lgvjNOXo0"}
{"title":"The Lead with Jake Tapper","description":"Оперативная сводка новостей страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641844800,"stop":1641848400,"site":"magticom.ge","gid":"us","country":"US","_id":"6As6GzEVhb3OWM0M"} {"title":"The Lead with Jake Tapper","description":"Оперативная сводка новостей страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641844800,"stop":1641848400,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"6As6GzEVhb3OWM0M"}
{"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641817800,"stop":1641819600,"site":"magticom.ge","gid":"us","country":"US","_id":"6DXKlITWehX1Jx4F"} {"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641817800,"stop":1641819600,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"6DXKlITWehX1Jx4F"}
{"title":"CNN Newsroom with Michael Holmes","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641790800,"stop":1641794400,"site":"magticom.ge","gid":"us","country":"US","_id":"AadPdMZ3s72y8NMk"} {"title":"CNN Newsroom with Michael Holmes","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641790800,"stop":1641794400,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"AadPdMZ3s72y8NMk"}
{"title":"The Situation Room with Wolf Blitzer","description":"Командный центр новостей, политики и неординарных репортажей со всего мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641852000,"stop":1641855600,"site":"magticom.ge","gid":"us","country":"US","_id":"Az3ABKy3HnE7sJZk"} {"title":"The Situation Room with Wolf Blitzer","description":"Командный центр новостей, политики и неординарных репортажей со всего мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641852000,"stop":1641855600,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"Az3ABKy3HnE7sJZk"}
{"title":"One World with Zain Asher","description":"Освещаются важные новости с каждого континента, от политики и текущих дел до социальных вопросов и многого другого.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641830400,"stop":1641833100,"site":"magticom.ge","gid":"us","country":"US","_id":"DMurxgt5OD0E9OIE"} {"title":"One World with Zain Asher","description":"Освещаются важные новости с каждого континента, от политики и текущих дел до социальных вопросов и многого другого.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641830400,"stop":1641833100,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"DMurxgt5OD0E9OIE"}
{"title":"TBD","description":"Информационно-познавательный проект CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641783600,"stop":1641785400,"site":"magticom.ge","gid":"us","country":"US","_id":"HQJqM2kIa77llWbC"} {"title":"TBD","description":"Информационно-познавательный проект CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641783600,"stop":1641785400,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"HQJqM2kIa77llWbC"}
{"title":"Marketplace Africa. 548-я серия","description":"548-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641782700,"stop":1641783600,"site":"magticom.ge","gid":"us","country":"US","_id":"Jn3khh5n9Brkxq4U"} {"title":"Marketplace Africa. 548-я серия","description":"548-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641782700,"stop":1641783600,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"Jn3khh5n9Brkxq4U"}
{"title":"CNN Newsroom with Michael Holmes","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641787200,"stop":1641789900,"site":"magticom.ge","gid":"us","country":"US","_id":"KcrIoQTXtUdw74sO"} {"title":"CNN Newsroom with Michael Holmes","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641787200,"stop":1641789900,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"KcrIoQTXtUdw74sO"}
{"title":"The Global Brief with Bianca Nobilo","description":"Global Brief с Бьянкой Нобило проницательно исследует меняющийся мир для меняющейся аудитории, обеспечивая непревзойденную глубину и качество для занятых зрителей в быстро меняющемся мире.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641848400,"stop":1641850200,"site":"magticom.ge","gid":"us","country":"US","_id":"LGD7WmQogDRxZn01"} {"title":"The Global Brief with Bianca Nobilo","description":"Global Brief с Бьянкой Нобило проницательно исследует меняющийся мир для меняющейся аудитории, обеспечивая непревзойденную глубину и качество для занятых зрителей в быстро меняющемся мире.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641848400,"stop":1641850200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"LGD7WmQogDRxZn01"}
{"title":"CNN Newsroom with Rosemary Church","description":"Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641798000,"stop":1641805200,"site":"magticom.ge","gid":"us","country":"US","_id":"LyCBivUTdZFW9X53"} {"title":"CNN Newsroom with Rosemary Church","description":"Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641798000,"stop":1641805200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"LyCBivUTdZFW9X53"}
{"title":"Marketplace Africa. 549-я серия","description":"549-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641833100,"stop":1641834000,"site":"magticom.ge","gid":"us","country":"US","_id":"PbrZinuZKgBHqDVj"} {"title":"Marketplace Africa. 549-я серия","description":"549-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641833100,"stop":1641834000,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"PbrZinuZKgBHqDVj"}
{"title":"African Voices Changemakers. 114-я серия","description":"114-я серия. Африка сегодня - люди, новости, события.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641780000,"stop":1641781800,"site":"magticom.ge","gid":"us","country":"US","_id":"SvrCK31v78V5y7EA"} {"title":"African Voices Changemakers. 114-я серия","description":"114-я серия. Африка сегодня - люди, новости, события.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641780000,"stop":1641781800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"SvrCK31v78V5y7EA"}
{"title":"Anderson Cooper 360","description":"Уникальный взгляд Андерсона Купера на главные события мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641859200,"stop":1641862800,"site":"magticom.ge","gid":"us","country":"US","_id":"TFGrOFJGkaOs9pU7"} {"title":"Anderson Cooper 360","description":"Уникальный взгляд Андерсона Купера на главные события мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641859200,"stop":1641862800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"TFGrOFJGkaOs9pU7"}
{"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641850200,"stop":1641852000,"site":"magticom.ge","gid":"us","country":"US","_id":"UynlLeT41MsjFElg"} {"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641850200,"stop":1641852000,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"UynlLeT41MsjFElg"}
{"title":"New Day","description":"Свежий обзор событий в стране и мире.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641808800,"stop":1641817800,"site":"magticom.ge","gid":"us","country":"US","_id":"UyvhQ4wRNq5d5XRd"} {"title":"New Day","description":"Свежий обзор событий в стране и мире.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641808800,"stop":1641817800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"UyvhQ4wRNq5d5XRd"}
{"title":"Amanpour","description":"Сводка новостей от знаменитой ведущей канала CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641834000,"stop":1641837600,"site":"magticom.ge","gid":"us","country":"US","_id":"WbsOCkmPH5gjmo4M"} {"title":"Amanpour","description":"Сводка новостей от знаменитой ведущей канала CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641834000,"stop":1641837600,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"WbsOCkmPH5gjmo4M"}
{"title":"Early Start","description":"Новости дня с Кристиной Романс и Дейвом Бриггсом.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641805200,"stop":1641808800,"site":"magticom.ge","gid":"us","country":"US","_id":"YB96P2mMO4TA0pID"} {"title":"Early Start","description":"Новости дня с Кристиной Романс и Дейвом Бриггсом.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641805200,"stop":1641808800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"YB96P2mMO4TA0pID"}
{"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641789900,"stop":1641790800,"site":"magticom.ge","gid":"us","country":"US","_id":"aDdCAlgqLG2yxM1m"} {"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641789900,"stop":1641790800,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"aDdCAlgqLG2yxM1m"}
{"title":"CNN Newsroom Sunday","description":"Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.","category":["Category1","Category2"],"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641772800,"stop":1641776400,"site":"magticom.ge","gid":"us","country":"US","_id":"aYCk87dUOAkCJE9x"} {"title":"CNN Newsroom Sunday","description":"Свежая мировая информационная сводка от CNN. О политике, экономике, общественной жизни, культуре, спорте.","category":["Category1","Category2"],"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641772800,"stop":1641776400,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"aYCk87dUOAkCJE9x"}
{"title":"Fareed Zakaria GPS","description":"Интервью с главными игроками мировой политики.","category":"Category1","season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641776400,"stop":1641780000,"site":"magticom.ge","gid":"us","country":"US","_id":"c1nCoWVetBZ3mn5q"} {"title":"Fareed Zakaria GPS","description":"Интервью с главными игроками мировой политики.","category":"Category1","season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641776400,"stop":1641780000,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"c1nCoWVetBZ3mn5q"}
{"title":"Inside Africa. 586-я серия","description":"586-я серия. Своеобразное \"путешествие\" по Африке - почувствуйте все разнообразие культур различных стран и регионов континента.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641785400,"stop":1641787200,"site":"magticom.ge","gid":"us","country":"US","_id":"goaDr7BsGGm3LCfz"} {"title":"Inside Africa. 586-я серия","description":"586-я серия. Своеобразное \"путешествие\" по Африке - почувствуйте все разнообразие культур различных стран и регионов континента.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641785400,"stop":1641787200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"goaDr7BsGGm3LCfz"}
{"title":"CNN Newsroom with Robyn Curnow","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641794400,"stop":1641797100,"site":"magticom.ge","gid":"us","country":"US","_id":"nixd3gRF1S1K0ZOs"} {"title":"CNN Newsroom with Robyn Curnow","description":"Обзор самых важных и актуальных новостей и событий из жизни страны и мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641794400,"stop":1641797100,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"nixd3gRF1S1K0ZOs"}
{"title":"Marketplace Africa. 549-я серия","description":"549-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641781800,"stop":1641782700,"site":"magticom.ge","gid":"us","country":"US","_id":"r1b8EvZc0tYs88ga"} {"title":"Marketplace Africa. 549-я серия","description":"549-я серия. Информационная передача об экономических событиях африканского региона. Анализируются проблемы, даются экономические прогнозы.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641781800,"stop":1641782700,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"r1b8EvZc0tYs88ga"}
{"title":"Erin Burnett OutFront","description":"Обсуждение самых важных мировых тем в эфире канала CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641855600,"stop":1641859200,"site":"magticom.ge","gid":"us","country":"US","_id":"sIQtUtowtATc7dLj"} {"title":"Erin Burnett OutFront","description":"Обсуждение самых важных мировых тем в эфире канала CNN.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641855600,"stop":1641859200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"sIQtUtowtATc7dLj"}
{"title":"Connect the World","description":"Актуальная мировая информация с разных континентов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641823200,"stop":1641825900,"site":"magticom.ge","gid":"us","country":"US","_id":"tXBIZ2BZBIkhnoTZ"} {"title":"Connect the World","description":"Актуальная мировая информация с разных континентов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641823200,"stop":1641825900,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"tXBIZ2BZBIkhnoTZ"}
{"title":"Quest Means Business","description":"Ричард Квест возглавляет группу экспертов и корреспондентов, чтобы предоставить актуальные факты, цифры и анализ из делового мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641841200,"stop":1641843900,"site":"magticom.ge","gid":"us","country":"US","_id":"xlE5epkjzdfUQpXO"} {"title":"Quest Means Business","description":"Ричард Квест возглавляет группу экспертов и корреспондентов, чтобы предоставить актуальные факты, цифры и анализ из делового мира.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641841200,"stop":1641843900,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"xlE5epkjzdfUQpXO"}
{"title":"First Move with Julia Chatterley","description":"Несколько больших историй, связанных с открытием рынков в США.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641819600,"stop":1641823200,"site":"magticom.ge","gid":"us","country":"US","_id":"yEVXucyUomVmktMF"} {"title":"First Move with Julia Chatterley","description":"Несколько больших историй, связанных с открытием рынков в США.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641819600,"stop":1641823200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"yEVXucyUomVmktMF"}
{"title":"Hala Gorani Tonight","description":"Используя свой 25-летний журналистский опыт, Хала Горани будет освещать ключевые события в картине дня посредством диалога с гостями и экспертами-аналитиками.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641837600,"stop":1641841200,"site":"magticom.ge","gid":"us","country":"US","_id":"yPgmYrWwfxHW3WUA"} {"title":"Hala Gorani Tonight","description":"Используя свой 25-летний журналистский опыт, Хала Горани будет освещать ключевые события в картине дня посредством диалога с гостями и экспертами-аналитиками.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641837600,"stop":1641841200,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"yPgmYrWwfxHW3WUA"}
{"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641797100,"stop":1641798000,"site":"magticom.ge","gid":"us","country":"US","_id":"zX70wOz5drExRTJX"} {"title":"World Sport","description":"Все о главных спортивных событиях мира. Обзоры самых важных спортивных событий, аналитика, мнения экспертов.","category":null,"season":null,"episode":null,"icon":null,"channel":"CNNInternationalEurope.us","lang":"ru","start":1641797100,"stop":1641798000,"site":"chaines-tv.orange.fr","_cid":"0Wefq0oMR3feCcuY","_id":"zX70wOz5drExRTJX"}
{"title":"Robin Hood","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641822300,"stop":1641829200,"site":"dstv.com","gid":"za","country":"ZA","_id":"1AoKArQw6MxP6pVU"} {"title":"Robin Hood","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641822300,"stop":1641829200,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"1AoKArQw6MxP6pVU"}
{"title":"The Water Diviner","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641776100,"stop":1641782700,"site":"dstv.com","gid":"za","country":"ZA","_id":"6v7w0SB4IlnfEEu3"} {"title":"The Water Diviner","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641776100,"stop":1641782700,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"6v7w0SB4IlnfEEu3"}
{"title":"Bad Boys For Life","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641847200,"stop":1641850800,"site":"dstv.com","gid":"za","country":"ZA","_id":"83VRYvggmyfCzkOm"} {"title":"Bad Boys For Life","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641847200,"stop":1641850800,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"83VRYvggmyfCzkOm"}
{"title":"12 Strong","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641814500,"stop":1641822300,"site":"dstv.com","gid":"za","country":"ZA","_id":"DbjwscjIuVDY8TPx"} {"title":"12 Strong","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641814500,"stop":1641822300,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"DbjwscjIuVDY8TPx"}
{"title":"Backdraft","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641801300,"stop":1641809400,"site":"dstv.com","gid":"za","country":"ZA","_id":"IwuwkjCKqWvio7ba"} {"title":"Backdraft","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641801300,"stop":1641809400,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"IwuwkjCKqWvio7ba"}
{"title":"Force Of Nature","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641841200,"stop":1641847200,"site":"dstv.com","gid":"za","country":"ZA","_id":"LP56HczEup0ed3Xx"} {"title":"Force Of Nature","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641841200,"stop":1641847200,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"LP56HczEup0ed3Xx"}
{"title":"Mafia","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641809400,"stop":1641814500,"site":"dstv.com","gid":"za","country":"ZA","_id":"MM9DPxERAgGGak39"} {"title":"Mafia","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641809400,"stop":1641814500,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"MM9DPxERAgGGak39"}
{"title":"The Last Witch Hunter","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641834780,"stop":1641841200,"site":"dstv.com","gid":"za","country":"ZA","_id":"MciJOpN3YCodj6Na"} {"title":"The Last Witch Hunter","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641834780,"stop":1641841200,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"MciJOpN3YCodj6Na"}
{"title":"Beyond The Line","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641796500,"stop":1641801300,"site":"dstv.com","gid":"za","country":"ZA","_id":"ZKA2s6QrM0xRrfGz"} {"title":"Beyond The Line","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641796500,"stop":1641801300,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"ZKA2s6QrM0xRrfGz"}
{"title":"Paranoia","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641790200,"stop":1641796500,"site":"dstv.com","gid":"za","country":"ZA","_id":"ZpdIZeSRhPycDX9D"} {"title":"Paranoia","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641790200,"stop":1641796500,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"ZpdIZeSRhPycDX9D"}
{"title":"The Scorpion King","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641829200,"stop":1641834780,"site":"dstv.com","gid":"za","country":"ZA","_id":"doO4Lh1pAt6L6wHa"} {"title":"The Scorpion King","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641829200,"stop":1641834780,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"doO4Lh1pAt6L6wHa"}
{"title":"Fatman","description":null,"category":null,"season":9,"episode":257,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641761700,"stop":1641767700,"site":"dstv.com","gid":"za","country":"ZA","_id":"fHahGuzHnU7xVEJX"} {"title":"Fatman","description":null,"category":null,"season":9,"episode":257,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641761700,"stop":1641767700,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"fHahGuzHnU7xVEJX"}
{"title":"Outbreak","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641782700,"stop":1641790200,"site":"dstv.com","gid":"za","country":"ZA","_id":"mkvcMP4FMwL2a5ax"} {"title":"Outbreak","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641782700,"stop":1641790200,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"mkvcMP4FMwL2a5ax"}
{"title":"Motherless Brooklyn","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641767700,"stop":1641776100,"site":"dstv.com","gid":"za","country":"ZA","_id":"nxTIAJsBwyXztRun"} {"title":"Motherless Brooklyn","description":null,"category":null,"season":null,"episode":null,"icon":null,"channel":"MNetMovies2.za","lang":"en","start":1641767700,"stop":1641776100,"site":"dstv.com","_cid":"1lnhXpN7g0ER5XwN","_id":"nxTIAJsBwyXztRun"}

View file

@ -0,0 +1 @@
{"lang":"en","xmltv_id":"BravoEast.us","site_id":"237","logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","name":"Bravo East","site":"directv.com","channelsPath":"sites/directv.com/directv.com_us.channels.xml","configPath":"sites/directv.com/directv.com.config.js","groups":["us"],"cluster_id":84,"country":"US","_id":"00AluKCrCnfgrl8W","date":"2022-01-21T00:00:00Z","error":"Invalid header value char"}

View file

@ -1 +1,2 @@
{"_id":"0Wefq0oMR3feCcuY","site":"andorradifusio.ad","gid":"ad","country":"AD","logo":"https://example.com/logo.png","programs":[{"title":"InfoNeu ","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","start":1641711600,"stop":1641715200},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641715200,"stop":1641718800},{"title":"InfoNeu ","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","start":1641718800,"stop":1641729600},{"title":"Andorra Actualitat (RNA)","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641729600,"stop":1641730800},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641730800,"stop":1641732000},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641732000,"stop":1641732300},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641732300,"stop":1641733800},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641733800,"stop":1641736200},{"title":"La Terre vue du Sport","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641736200,"stop":1641736800},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641736800,"stop":1641738300},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641738300,"stop":1641741900},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641741900,"stop":1641743400},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641743400,"stop":1641750900},{"title":"La rotonda","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641750900,"stop":1641753600},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641753600,"stop":1641757200},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641757200,"stop":1641757500},{"title":"Informatiu vespre","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641757500,"stop":1641759000},{"title":"Recull setmanal","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641759000,"stop":1641761100},{"title":"Memòries d'arxiu: 10 anys d'ATV","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641761100,"stop":1641763800},{"title":"El cafè dels matins","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641763800,"stop":1641766800},{"title":"La Terre vue du Sport","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641766800,"stop":1641767400},{"title":"Informatiu vespre","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641767400,"stop":1641772800},{"title":"Àrea Andorra Difusió","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641772800,"stop":1641776400}]} {"channel":{"lang":"fr","country":"US","xmltv_id":"CNNInternationalEurope.us","site_id":"53","logo":"https://example.com/logo.png","name":"CNN International Europe","site":"chaines-tv.orange.fr","channelsPath":"sites/chaines-tv.orange.fr/chaines-tv.orange.fr_fr.channels.xml","configPath":"tests/__data__/input/sites/example.com.config.js","groups":["fr"],"cluster_id":1,"_id":"0Wefq0oMR3feCcuY"},"programs":[{"title":"InfoNeu ","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","start":1641711600,"stop":1641715200},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641715200,"stop":1641718800},{"title":"InfoNeu ","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","start":1641718800,"stop":1641729600},{"title":"Andorra Actualitat (RNA)","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641729600,"stop":1641730800},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641730800,"stop":1641732000},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641732000,"stop":1641732300},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641732300,"stop":1641733800},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641733800,"stop":1641736200},{"title":"La Terre vue du Sport","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641736200,"stop":1641736800},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641736800,"stop":1641738300},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641738300,"stop":1641741900},{"title":"Informatiu migdia","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641741900,"stop":1641743400},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641743400,"stop":1641750900},{"title":"La rotonda","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641750900,"stop":1641753600},{"title":"Club Piolet","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641753600,"stop":1641757200},{"title":"El Trànsit","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641757200,"stop":1641757500},{"title":"Informatiu vespre","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641757500,"stop":1641759000},{"title":"Recull setmanal","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641759000,"stop":1641761100},{"title":"Memòries d'arxiu: 10 anys d'ATV","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641761100,"stop":1641763800},{"title":"El cafè dels matins","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641763800,"stop":1641766800},{"title":"La Terre vue du Sport","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641766800,"stop":1641767400},{"title":"Informatiu vespre","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641767400,"stop":1641772800},{"title":"Àrea Andorra Difusió","description":null,"category":null,"icon":null,"channel":"AndorraTV.ad","lang":"ca","season":null,"episode":null,"start":1641772800,"stop":1641776400}],"date":"2022-01-21T00:00:00Z","error":null}
{"channel":{"lang":"en","xmltv_id":"BravoEast.us","site_id":"237","logo":"https://www.directv.com/images/logos/channels/dark/large/579.png","name":"Bravo East","site":"directv.com","channelsPath":"sites/directv.com/directv.com_us.channels.xml","configPath":"sites/directv.com/directv.com.config.js","groups":["us"],"cluster_id":84,"country":"US","_id":"00AluKCrCnfgrl8W"},"programs":[],"date":"2022-01-21T00:00:00Z","error":"Invalid header value char"}

View file

@ -1,7 +1,7 @@
{"gid":"us","site":"magticom.ge","count":74,"status":1} {"group":"us/magticom.ge","count":74}
{"gid":"za","site":"dstv.com","count":1,"status":1} {"group":"za/dstv.com","count":1}
{"gid":"us-pr","site":"tvtv.us","count":14,"status":1} {"group":"us-pr/tvtv.us","count":14}
{"gid":"us-pr","site":"gatotv.com","count":7,"status":1} {"group":"us-pr/gatotv.com","count":7}
{"gid":"us-pr","site":"directv.com","count":1,"status":1} {"group":"us-pr/directv.com","count":1}
{"gid":"ca-nl","site":"tvtv.us","count":1,"status":1} {"group":"ca-nl/tvtv.us","count":1}
{"gid":"us","site":"tvtv.us","count":372,"status":1} {"group":"us/tvtv.us","count":372}

View file

@ -2,5 +2,6 @@
<site site="example.com"> <site site="example.com">
<channels> <channels>
<channel lang="ru" xmltv_id="CNNInternationalEurope.us" site_id="140">CNN International Europe</channel> <channel lang="ru" xmltv_id="CNNInternationalEurope.us" site_id="140">CNN International Europe</channel>
<channel lang="en" xmltv_id="CNNInternationalEurope2.us" site_id="140">CNN International Europe 2</channel>
</channels> </channels>
</site> </site>

View file

@ -7,7 +7,7 @@ beforeEach(() => {
fs.mkdirSync('tests/__data__/output') fs.mkdirSync('tests/__data__/output')
const stdout = execSync( const stdout = execSync(
'DB_DIR=tests/__data__/output/database node scripts/commands/create-database.js --channels=tests/__data__/input/sites/*.channels.xml --max-clusters=1', 'DB_DIR=tests/__data__/output/database node scripts/commands/create-queue.js --channels=tests/__data__/input/sites/*.channels.xml --max-clusters=1',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )
}) })
@ -25,7 +25,7 @@ it('can create channels database', () => {
site: 'example.com', site: 'example.com',
channelsPath: 'tests/__data__/input/sites/example.com_ca-nl.channels.xml', channelsPath: 'tests/__data__/input/sites/example.com_ca-nl.channels.xml',
configPath: 'tests/__data__/input/sites/example.com.config.js', configPath: 'tests/__data__/input/sites/example.com.config.js',
gid: 'ca-nl', groups: ['ca-nl/example.com'],
cluster_id: 1 cluster_id: 1
} }
]) ])

View file

@ -1,7 +1,11 @@
const fs = require('fs') const fs = require('fs')
const path = require('path') const path = require('path')
const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc')
const { execSync } = require('child_process') const { execSync } = require('child_process')
dayjs.extend(utc)
beforeEach(() => { beforeEach(() => {
fs.rmdirSync('tests/__data__/temp', { recursive: true }) fs.rmdirSync('tests/__data__/temp', { recursive: true })
fs.rmdirSync('tests/__data__/output', { recursive: true }) fs.rmdirSync('tests/__data__/output', { recursive: true })
@ -21,20 +25,22 @@ beforeEach(() => {
it('can load cluster', () => { it('can load cluster', () => {
const output = content('tests/__data__/output/logs/load-cluster/cluster_1.log') const output = content('tests/__data__/output/logs/load-cluster/cluster_1.log')
expect(Object.keys(output[0]).sort()).toEqual(['channel', 'date', 'error', 'programs'])
expect(output[0]).toMatchObject({ expect(output[0]).toMatchObject({
_id: '0Wefq0oMR3feCcuY', channel: {
site: 'chaines-tv.orange.fr', _id: '0Wefq0oMR3feCcuY',
logo: 'https://example.com/logo.png', logo: 'https://example.com/logo.png'
country: 'US', },
gid: 'fr' date: dayjs.utc().startOf('d').format(),
error: null
}) })
expect(output[1]).toMatchObject({ expect(output[1]).toMatchObject({
_id: '1XzrxNkSF2AQNBrT', channel: {
site: 'magticom.ge', _id: '1XzrxNkSF2AQNBrT',
logo: 'https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg', logo: 'https://www.magticom.ge/images/channels/MjAxOC8wOS8xMC9lZmJhNWU5Yy0yMmNiLTRkMTAtOWY5Ny01ODM0MzY0ZTg0MmEuanBn.jpg'
country: 'US', }
gid: 'ge'
}) })
}) })

View file

@ -12,23 +12,22 @@ beforeEach(() => {
'tests/__data__/output/database/channels.db' 'tests/__data__/output/database/channels.db'
) )
execSync( const stdout = execSync(
'DB_DIR=tests/__data__/output/database LOGS_DIR=tests/__data__/input/logs node scripts/commands/save-results.js', 'DB_DIR=tests/__data__/output/database LOGS_DIR=tests/__data__/input/logs node scripts/commands/save-results.js',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )
}) })
it('can save results to database', () => { it('can save results', () => {
const output = content('tests/__data__/output/database/programs.db') const programs = content('tests/__data__/output/database/programs.db')
expect(Object.keys(output[0]).sort()).toEqual([ expect(Object.keys(programs[0]).sort()).toEqual([
'_cid',
'_id', '_id',
'category', 'category',
'channel', 'channel',
'country',
'description', 'description',
'episode', 'episode',
'gid',
'icon', 'icon',
'lang', 'lang',
'season', 'season',
@ -38,12 +37,41 @@ it('can save results to database', () => {
'title' 'title'
]) ])
const database = content('tests/__data__/output/database/channels.db') expect(programs[0]).toMatchObject({
_cid: '0Wefq0oMR3feCcuY'
})
expect(database[1]).toMatchObject({ const channels = content('tests/__data__/output/database/channels.db')
expect(Object.keys(channels[0]).sort()).toEqual([
'_id',
'channelsPath',
'cluster_id',
'configPath',
'country',
'groups',
'lang',
'logo',
'name',
'programCount',
'site',
'site_id',
'xmltv_id'
])
expect(channels[1]).toMatchObject({
_id: '0Wefq0oMR3feCcuY', _id: '0Wefq0oMR3feCcuY',
logo: 'https://example.com/logo.png' logo: 'https://example.com/logo.png'
}) })
const errors = content('tests/__data__/input/logs/errors.log')
expect(errors[0]).toMatchObject({
_id: '00AluKCrCnfgrl8W',
site: 'directv.com',
xmltv_id: 'BravoEast.us',
error: 'Invalid header value char'
})
}) })
function content(filepath) { function content(filepath) {

View file

@ -15,7 +15,7 @@ beforeEach(() => {
'tests/__data__/temp/database/programs.db' 'tests/__data__/temp/database/programs.db'
) )
execSync( const stdout = execSync(
'DB_DIR=tests/__data__/temp/database PUBLIC_DIR=tests/__data__/output LOGS_DIR=tests/__data__/output/logs node scripts/commands/update-guides.js', 'DB_DIR=tests/__data__/temp/database PUBLIC_DIR=tests/__data__/output LOGS_DIR=tests/__data__/output/logs node scripts/commands/update-guides.js',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )
@ -26,13 +26,13 @@ afterEach(() => {
}) })
it('can generate /guides', () => { it('can generate /guides', () => {
const output1 = content('tests/__data__/output/guides/us/magticom.ge.epg.xml') const output1 = content('tests/__data__/output/guides/fr/chaines-tv.orange.fr.epg.xml')
const expected1 = content('tests/__data__/expected/guides/us/magticom.ge.epg.xml') const expected1 = content('tests/__data__/expected/guides/fr/chaines-tv.orange.fr.epg.xml')
expect(output1).toBe(expected1) expect(output1).toBe(expected1)
const output2 = content('tests/__data__/output/guides/za/dstv.com.epg.xml') const output2 = content('tests/__data__/output/guides/zw/dstv.com.epg.xml')
const expected2 = content('tests/__data__/expected/guides/za/dstv.com.epg.xml') const expected2 = content('tests/__data__/expected/guides/zw/dstv.com.epg.xml')
expect(output2).toBe(expected2) expect(output2).toBe(expected2)

View file

@ -6,7 +6,7 @@ beforeEach(() => {
fs.rmdirSync('tests/__data__/output', { recursive: true }) fs.rmdirSync('tests/__data__/output', { recursive: true })
fs.mkdirSync('tests/__data__/output') fs.mkdirSync('tests/__data__/output')
execSync( const stdout = execSync(
'LOGS_DIR=tests/__data__/input/logs node scripts/commands/update-readme.js --config=tests/__data__/input/_readme.json', 'LOGS_DIR=tests/__data__/input/logs node scripts/commands/update-readme.js --config=tests/__data__/input/_readme.json',
{ encoding: 'utf8' } { encoding: 'utf8' }
) )