Merge pull request #1156 from iptv-org/update-mncvision.id

Update mncvision.id
This commit is contained in:
Aleksandr Statciuk 2022-10-05 17:06:44 +03:00 committed by GitHub
commit 51140d6a45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 2409 additions and 207 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,27 @@
<div id="id-schedule-detail" class="panel-body">
<h1 class="page-header">Synopsis</h1>
<div id="id-schedule-detail" class="tm-sinopsys-container">
<h2 class='page-header title text-info program-title'>Adventures With Miao Mi, Ep 1</h2>
<div class='text-warning showtime'><b>05 Oct 2022, 12:00 AM</b><br><small class="showtime-range">00:00 - 00:06 [duration:00:06]</small></div>
<blockquote class="bloquet synopsis">
When children begin to disappear, a group of young kids have to face their biggest fears when they square off against a murderous, evil clown. </blockquote>
<div class="tm-channel-info">
<img class="tm-channel-image" src="userfiles/image/channel/miawme150x150.jpg" alt="Miao Mi" title="Miao Mi #channel:38" />
<div class='tm-channel-label'>
<span class='tm-channel-label'>
Channel <span class='tm-channel-no'>38</span>
</span>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,27 @@
<div id="id-schedule-detail" class="panel-body">
<h1 class="page-header">Synopsis</h1>
<div id="id-schedule-detail" class="tm-sinopsys-container">
<h2 class='page-header title text-info program-title'>Adventures With Miao Mi, Ep 1</h2>
<div class='text-warning showtime'><b>05 Oct 2022, 12:00 AM</b><br><small class="showtime-range">00:00 - 00:06 [durasi:00:06]</small></div>
<blockquote class="bloquet synopsis">
Ketika anak-anak mulai menghilang, sekelompok anak kecil harus menghadapi ketakutan terbesar mereka ketika mereka melawan sesosok badut pembunuh yang jahat. </blockquote>
<div class="tm-channel-info">
<img class="tm-channel-image" src="userfiles/image/channel/miawme150x150.jpg" alt="Miao Mi" title="Miao Mi #channel:38" />
<div class='tm-channel-label'>
<span class='tm-channel-label'>
Channel <span class='tm-channel-no'>38</span>
</span>
</div>
</div>
</div>
</div>

View file

@ -1,4 +1,4 @@
const FormData = require('form-data') const _ = require('lodash')
const axios = require('axios') const axios = require('axios')
const cheerio = require('cheerio') const cheerio = require('cheerio')
const dayjs = require('dayjs') const dayjs = require('dayjs')
@ -16,8 +16,7 @@ module.exports = {
request: { request: {
method: 'POST', method: 'POST',
data: function ({ channel, date }) { data: function ({ channel, date }) {
const formData = new FormData() const formData = new URLSearchParams()
formData.setBoundary('X-EPG-BOUNDARY')
formData.append('search_model', 'channel') formData.append('search_model', 'channel')
formData.append('af0rmelement', 'aformelement') formData.append('af0rmelement', 'aformelement')
formData.append('fdate', date.format('YYYY-MM-DD')) formData.append('fdate', date.format('YYYY-MM-DD'))
@ -27,29 +26,36 @@ module.exports = {
return formData return formData
}, },
headers: { headers: {
'Content-Type': 'multipart/form-data; boundary=X-EPG-BOUNDARY' 'Content-Type': 'application/x-www-form-urlencoded'
} },
jar: null
}, },
async parser({ content, date, headers, channel }) { async parser({ content, date, headers, channel }) {
const programs = [] const programs = []
const cookies = parseCookies(headers)
if (!cookies) return programs
let items = parseItems(content) let items = parseItems(content)
if (!items.length) return programs if (!items.length) return programs
const pages = parsePages(content) const pages = parsePages(content)
const cookies = headers && headers['set-cookie'] ? headers['set-cookie'].join(';') : ''
for (let url of pages) { for (let url of pages) {
items = items.concat(parseItems(await loadNextPage(url, cookies))) items = items.concat(parseItems(await loadNextPage(url, cookies)))
} }
const langCookies = await loadLangCookies(channel) const langCookies = await loadLangCookies(channel)
if (!langCookies) return programs
for (const item of items) { for (const item of items) {
const start = parseStart(item, date) const $item = cheerio.load(item)
const duration = parseDuration(item) const start = parseStart($item, date)
const duration = parseDuration($item)
const stop = start.add(duration, 'm') const stop = start.add(duration, 'm')
const description = await loadDescription($item, langCookies)
programs.push({ programs.push({
title: parseTitle(item), title: parseTitle($item),
description: await loadDescription(item, langCookies), season: parseSeason($item),
episode: parseEpisode($item),
description,
start, start,
stop stop
}) })
@ -61,7 +67,7 @@ module.exports = {
const data = await axios const data = await axios
.get('https://www.mncvision.id/schedule') .get('https://www.mncvision.id/schedule')
.then(response => response.data) .then(response => response.data)
.catch(console.log) .catch(console.error)
const $ = cheerio.load(data) const $ = cheerio.load(data)
const items = $('select[name="fchannel"] option').toArray() const items = $('select[name="fchannel"] option').toArray()
@ -79,46 +85,22 @@ module.exports = {
} }
} }
async function loadNextPage(url, cookies) { function parseSeason($item) {
return axios const title = parseTitle($item)
.get(url, { const [_, season] = title.match(/ S(\d+)/) || [null, null]
headers: {
Cookie: cookies return season ? parseInt(season) : null
}
})
.then(r => r.data)
.catch(console.log)
} }
async function loadLangCookies(channel) { function parseEpisode($item) {
const lang = channel.lang === 'en' ? 'english' : 'indonesia' const title = parseTitle($item)
const url = `https://www.mncvision.id/language_switcher/setlang/${lang}/` const [_, episode] = title.match(/ Ep (\d+)/) || [null, null]
return axios return episode ? parseInt(episode) : null
.get(url)
.then(r => r.headers['set-cookie'].join(';'))
.catch(console.error)
} }
async function loadDescription(item, cookies) { function parseDuration($item) {
const $item = cheerio.load(item) let duration = $item('td:nth-child(3)').text()
const progUrl = $item('a').attr('href')
if (!progUrl) return null
const data = await axios
.get(progUrl, { headers: { 'X-Requested-With': 'XMLHttpRequest', Cookie: cookies } })
.then(r => r.data)
.catch(console.log)
if (!data) return null
const $page = cheerio.load(data)
const description = $page('.synopsis').text().trim()
if (description === '-') return null
return description
}
function parseDuration(item) {
const $ = cheerio.load(item)
let duration = $('td:nth-child(3)').text()
const match = duration.match(/(\d{2}):(\d{2})/) const match = duration.match(/(\d{2}):(\d{2})/)
const hours = parseInt(match[1]) const hours = parseInt(match[1])
const minutes = parseInt(match[2]) const minutes = parseInt(match[2])
@ -126,18 +108,15 @@ function parseDuration(item) {
return hours * 60 + minutes return hours * 60 + minutes
} }
function parseStart(item, date) { function parseStart($item, date) {
const $ = cheerio.load(item) let time = $item('td:nth-child(1)').text()
let time = $('td:nth-child(1)').text()
time = `${date.format('DD/MM/YYYY')} ${time}` time = `${date.format('DD/MM/YYYY')} ${time}`
return dayjs.tz(time, 'DD/MM/YYYY HH:mm', 'Asia/Jakarta') return dayjs.tz(time, 'DD/MM/YYYY HH:mm', 'Asia/Jakarta')
} }
function parseTitle(item) { function parseTitle($item) {
const $ = cheerio.load(item) return $item('td:nth-child(2) > a').text()
return $('td:nth-child(2) > a').text()
} }
function parseItems(content) { function parseItems(content) {
@ -148,13 +127,57 @@ function parseItems(content) {
function parsePages(content) { function parsePages(content) {
const $ = cheerio.load(content) const $ = cheerio.load(content)
const links = $('#schedule > div.schedule_search_result_container > div.box.well > a').toArray() const links = $('#schedule > div.schedule_search_result_container > div.box.well > a')
.map((i, el) => {
return $(el).attr('href')
})
.get()
const pages = {} return _.uniq(links)
for (let link of links) { }
const url = $(link).attr('href')
pages[url] = true function loadNextPage(url, cookies) {
} return axios
.get(url, { headers: { Cookie: cookies }, timeout: 30000 })
return Object.keys(pages) .then(r => r.data)
.catch(err => {
console.log(err.message)
return null
})
}
function loadLangCookies(channel) {
const languages = {
en: 'english',
id: 'indonesia'
}
const url = `https://www.mncvision.id/language_switcher/setlang/${languages[channel.lang]}/`
return axios
.get(url, { timeout: 30000 })
.then(r => parseCookies(r.headers))
.catch(err => null)
}
async function loadDescription($item, cookies) {
const url = $item('a').attr('href')
if (!url) return null
const content = await axios
.get(url, {
headers: { 'X-Requested-With': 'XMLHttpRequest', Cookie: cookies },
timeout: 30000
})
.then(r => r.data)
.catch(err => null)
if (!content) return null
const $page = cheerio.load(content)
const description = $page('.synopsis').text().trim()
return description !== '-' ? description : null
}
function parseCookies(headers) {
return Array.isArray(headers['set-cookie']) ? headers['set-cookie'].join(';') : null
} }

View file

@ -1,8 +1,10 @@
// npx epg-grabber --config=sites/mncvision.id/mncvision.id.config.js --channels=sites/mncvision.id/mncvision.id_id-id.channels.xml --output=guide.xml --timeout=30000 --days=2
// npx epg-grabber --config=sites/mncvision.id/mncvision.id.config.js --channels=sites/mncvision.id/mncvision.id_id-en.channels.xml --output=guide.xml --timeout=30000 --days=2
// node ./scripts/channels.js --config=./sites/mncvision.id/mncvision.id.config.js --output=./sites/mncvision.id/mncvision.id_id.channels.xml // node ./scripts/channels.js --config=./sites/mncvision.id/mncvision.id.config.js --output=./sites/mncvision.id/mncvision.id_id.channels.xml
// npx epg-grabber --config=sites/mncvision.id/mncvision.id.config.js --channels=sites/mncvision.id/mncvision.id_id-id.channels.xml --output=guide.xml --days=2
// npx epg-grabber --config=sites/mncvision.id/mncvision.id.config.js --channels=sites/mncvision.id/mncvision.id_id-en.channels.xml --output=guide.xml --days=2
const { parser, url, request } = require('./mncvision.id.config.js') const { parser, url, request } = require('./mncvision.id.config.js')
const fs = require('fs')
const path = require('path')
const axios = require('axios') const axios = require('axios')
const dayjs = require('dayjs') const dayjs = require('dayjs')
const utc = require('dayjs/plugin/utc') const utc = require('dayjs/plugin/utc')
@ -12,181 +14,138 @@ dayjs.extend(utc)
jest.mock('axios') jest.mock('axios')
const date = dayjs.utc('2021-11-12', 'YYYY-MM-DD').startOf('d') const date = dayjs.utc('2022-10-05', 'YYYY-MM-DD').startOf('d')
const channelID = { const channel = {
site_id: '203', site_id: '38',
xmltv_id: 'AnimalPlanetSoutheastAsia.us', xmltv_id: 'MiaoMi.hk',
lang: 'id' lang: 'id'
} }
const channelEN = { const headers = {
site_id: '203', 'set-cookie': [
xmltv_id: 'AnimalPlanetSoutheastAsia.us', 's1nd0vL=05e9pr6gi112tdmutsn7big93o75r0b0; expires=Wed, 05-Oct-2022 14:18:22 GMT; Max-Age=7200; path=/; HttpOnly'
lang: 'en' ]
} }
const setCookie = [
's1nd0vL=0qpsmm7dpjmi7nt8d2h5epf16rmgg8a8; expires=Sat, 05-Mar-2022 15:44:22 GMT; Max-Age=7200; path=/; HttpOnly'
]
const content0 = `<!DOCTYPE html><html lang="en"> <head></head> <body> <div id="id-content-schedule-table" class="lang-indonesia tm-content-container"> <div class="container content-schedule-table"> <h1 class="page-header tm">Jadwal Tayang Cari</h1> <div id="schedule" class="schedule_table panel tm tm-block tm-block-1"> <div class="schedule_search_result_container"> <div class='schedule_search_desc_container'><span class="subpage-title">Jadwal Tayang</span> Channel: <span class='label label-default'>41</span> Tanggal: <span class='label label-default'>2022-03-05</span></div><table class="table table-striped table-bordered table-hover table-condensed" cellspacing="0" cellpadding="0" border="0"> <tr> <th width='10%'>Jam Tayang</th> <th width='80%' align='left'>Program Acara</th> <th width='10%'>Durasi</th> </tr><tr valign="top"> <td class="text-center">00:00</td><td><a href="https://www.mncvision.id/schedule/detail/2022030500000041/Hey-Duggee-S3-Ep-22/1" title="Hey Duggee S3, Ep 22" rel="facebox">Hey Duggee S3, Ep 22</a></td><td class="text-center">00:07</td></tr></table> <div align="center" class="box well">page: <strong>1</strong><a href="https://www.mncvision.id/schedule/table/startno/50" data-ci-pagination-page="2">2</a><a href="https://www.mncvision.id/schedule/table/startno/50" data-ci-pagination-page="2" rel="next">&gt;</a></div></div></div></div></div></body></html>`
const content50 = `<!DOCTYPE html><html lang="en"> <head></head> <body> <div id="id-content-schedule-table" class="lang-indonesia tm-content-container"> <div class="container content-schedule-table"> <h1 class="page-header tm">Jadwal Tayang Cari</h1> <div id="schedule" class="schedule_table panel tm tm-block tm-block-1"> <div class="schedule_search_result_container"> <div class='schedule_search_desc_container'><span class="subpage-title">Jadwal Tayang</span> Channel: <span class='label label-default'>41</span> Tanggal: <span class='label label-default'>2022-03-05</span></div><table class="table table-striped table-bordered table-hover table-condensed" cellspacing="0" cellpadding="0" border="0"> <tr> <th width='10%'>Jam Tayang</th> <th width='80%' align='left'>Program Acara</th> <th width='10%'>Durasi</th> </tr><tr valign="top"> <td class="text-center">08:25</td><td><a href="https://www.mncvision.id/schedule/detail/2022030508250041/Hey-Duggee-S1-Ep-46/1" title="Hey Duggee S1, Ep 46" rel="facebox">Hey Duggee S1, Ep 46</a></td><td class="text-center">00:07</td></tr></table> <div align="center" class="box well">page: <a href="https://www.mncvision.id/schedule/table/startno/" data-ci-pagination-page="1" rel="prev">&lt;</a><a href="https://www.mncvision.id/schedule/table/startno/" data-ci-pagination-page="1" rel="start">1</a><strong>2</strong><a href="https://www.mncvision.id/schedule/table/startno/100" data-ci-pagination-page="3" rel="next">&gt;</a></div></div></div></div></div></body></html>`
it('can generate valid url', () => { it('can generate valid url', () => {
expect(url).toBe('https://mncvision.id/schedule/table') expect(url).toBe('https://mncvision.id/schedule/table')
}) })
it('can generate valid request method', () => {
expect(request.method).toBe('POST')
})
it('can generate valid request headers', () => { it('can generate valid request headers', () => {
expect(request.headers).toMatchObject({ expect(request.headers).toMatchObject({
'Content-Type': 'multipart/form-data; boundary=X-EPG-BOUNDARY' 'Content-Type': 'application/x-www-form-urlencoded'
}) })
}) })
it('can generate valid request data', () => { it('can generate valid request data', () => {
const result = request.data({ channel: channelID, date }) const data = request.data({ channel, date })
expect(result._boundary).toBe('X-EPG-BOUNDARY') expect(data.get('search_model')).toBe('channel')
expect(data.get('af0rmelement')).toBe('aformelement')
expect(data.get('fdate')).toBe('2022-10-05')
expect(data.get('fchannel')).toBe('38')
expect(data.get('submit')).toBe('Search')
}) })
it('can parse response in Indonesian', done => { it('can parse response', async () => {
const setLangCookie = [ const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
's1nd0vL=oj87dbpo7cqbg8fit3295075kodja8hl; expires=Fri, 11-Mar-2022 14:40:17 GMT; Max-Age=7200; path=/; HttpOnly' const indonesiaHeaders = {
'set-cookie': [
's1nd0vL=e3vjb0oaf9vijiqsg7cml4i7fdkq16db; expires=Wed, 05-Oct-2022 14:54:16 GMT; Max-Age=7200; path=/; HttpOnly'
] ]
}
axios.get.mockImplementation((url, options = {}) => { const englishHeaders = {
'set-cookie': [
's1nd0vL=hfd6hpnpr6gvgart0d8rf7ef6t4gi7nr; expires=Wed, 05-Oct-2022 15:08:55 GMT; Max-Age=7200; path=/; HttpOnly'
]
}
axios.get.mockImplementation((url, opts) => {
if ( if (
url === 'https://www.mncvision.id/schedule/table/startno/50' && url === 'https://www.mncvision.id/schedule/table/startno/50' &&
options.headers && opts.headers['Cookie'] === headers['set-cookie'][0]
options.headers['Cookie'] === setCookie.join(';')
) { ) {
return Promise.resolve({ return Promise.resolve({
data: content50 data: fs.readFileSync(path.resolve(__dirname, '__data__/content_p2.html'))
}) })
} else if (url === 'https://www.mncvision.id/language_switcher/setlang/indonesia/') { } else if (url === 'https://www.mncvision.id/language_switcher/setlang/indonesia/') {
return Promise.resolve({ return Promise.resolve({
headers: { headers: indonesiaHeaders
'set-cookie': setLangCookie
}
})
} else if (
url === 'https://www.mncvision.id/schedule/detail/2022030500000041/Hey-Duggee-S3-Ep-22/1' &&
options.headers &&
options.headers['X-Requested-With'] === 'XMLHttpRequest' &&
options.headers['Cookie'] === setLangCookie.join(';')
) {
return Promise.resolve({
data: `<!DOCTYPE html><html lang="en"><head></head><body><blockquote class="bloquet synopsis">
Nikmati suasana kehidupan koloni anjing laut di kawasan pantai barat Afrika Selatan. </blockquote></body></html>`
})
}
return Promise.resolve({ data: '' })
})
parser({ date, content: content0, headers: { 'set-cookie': setCookie }, channel: channelID })
.then(result => {
result = result.map(p => {
p.start = p.start.toJSON()
p.stop = p.stop.toJSON()
return p
})
expect(result).toMatchObject([
{
start: '2021-11-11T17:00:00.000Z',
stop: '2021-11-11T17:07:00.000Z',
title: 'Hey Duggee S3, Ep 22',
description:
'Nikmati suasana kehidupan koloni anjing laut di kawasan pantai barat Afrika Selatan.'
},
{
start: '2021-11-12T01:25:00.000Z',
stop: '2021-11-12T01:32:00.000Z',
title: 'Hey Duggee S1, Ep 46',
description: null
}
])
done()
})
.catch(error => {
done(error)
})
})
it('can parse response in English', done => {
const setLangCookie = [
's1nd0vL=4li9qu3olhjl9djrl971opl2tgb27p1v; expires=Fri, 11-Mar-2022 14:26:55 GMT; Max-Age=7200; path=/; HttpOnly'
]
axios.get.mockImplementation((url, options = {}) => {
if (
url === 'https://www.mncvision.id/schedule/table/startno/50' &&
options.headers &&
options.headers['Cookie'] === setCookie.join(';')
) {
return Promise.resolve({
data: content50
}) })
} else if (url === 'https://www.mncvision.id/language_switcher/setlang/english/') { } else if (url === 'https://www.mncvision.id/language_switcher/setlang/english/') {
return Promise.resolve({ return Promise.resolve({
headers: { headers: englishHeaders
'set-cookie': setLangCookie
}
}) })
} else if ( } else if (
url === 'https://www.mncvision.id/schedule/detail/2022030500000041/Hey-Duggee-S3-Ep-22/1' && url ===
options.headers && 'https://mncvision.id/schedule/detail/2022100500000038/Adventures-With-Miao-Mi-Ep-1/1' &&
options.headers['X-Requested-With'] === 'XMLHttpRequest' && opts.headers['Cookie'] === indonesiaHeaders['set-cookie'][0]
options.headers['Cookie'] === setLangCookie.join(';')
) { ) {
return Promise.resolve({ return Promise.resolve({
data: `<!DOCTYPE html><html lang="en"><head></head><body><blockquote class="bloquet synopsis"> data: fs.readFileSync(path.resolve(__dirname, '__data__/program_id.html'))
While Castiel investigates the disappearance of a local teen, Sam and Dean are visited by an old friend. </blockquote></body></html>` })
} else if (
url ===
'https://mncvision.id/schedule/detail/2022100500000038/Adventures-With-Miao-Mi-Ep-1/1' &&
opts.headers['Cookie'] === englishHeaders['set-cookie'][0]
) {
return Promise.resolve({
data: fs.readFileSync(path.resolve(__dirname, '__data__/program_en.html'))
}) })
} }
return Promise.resolve({ data: '' }) return Promise.resolve({ data: '' })
}) })
parser({ date, content: content0, headers: { 'set-cookie': setCookie }, channel: channelEN }) let indonesiaResults = await parser({ date, content, channel, headers })
.then(result => { indonesiaResults = indonesiaResults.map(p => {
result = result.map(p => {
p.start = p.start.toJSON() p.start = p.start.toJSON()
p.stop = p.stop.toJSON() p.stop = p.stop.toJSON()
return p return p
}) })
expect(result).toMatchObject([ expect(indonesiaResults[0]).toMatchObject({
{ start: '2022-10-04T17:00:00.000Z',
start: '2021-11-11T17:00:00.000Z', stop: '2022-10-04T17:06:00.000Z',
stop: '2021-11-11T17:07:00.000Z', title: 'Adventures With Miao Mi, Ep 1',
title: 'Hey Duggee S3, Ep 22', episode: 1,
description: description:
'While Castiel investigates the disappearance of a local teen, Sam and Dean are visited by an old friend.' 'Ketika anak-anak mulai menghilang, sekelompok anak kecil harus menghadapi ketakutan terbesar mereka ketika mereka melawan sesosok badut pembunuh yang jahat.'
},
{
start: '2021-11-12T01:25:00.000Z',
stop: '2021-11-12T01:32:00.000Z',
title: 'Hey Duggee S1, Ep 46',
description: null
}
])
done()
}) })
.catch(error => {
done(error) expect(indonesiaResults[4]).toMatchObject({
start: '2022-10-04T17:33:00.000Z',
stop: '2022-10-04T17:46:00.000Z',
title: 'Leo Wildlife Ranger S2, Ep 27',
season: 2,
episode: 27
})
let englishResults = await parser({ date, content, channel: { ...channel, lang: 'en' }, headers })
englishResults = englishResults.map(p => {
p.start = p.start.toJSON()
p.stop = p.stop.toJSON()
return p
})
expect(englishResults[0]).toMatchObject({
start: '2022-10-04T17:00:00.000Z',
stop: '2022-10-04T17:06:00.000Z',
title: 'Adventures With Miao Mi, Ep 1',
episode: 1,
description:
'When children begin to disappear, a group of young kids have to face their biggest fears when they square off against a murderous, evil clown.'
}) })
}) })
it('can handle empty guide', done => { it('can handle empty guide', async () => {
parser({ const content = fs.readFileSync(path.resolve(__dirname, '__data__/no_content.html'))
let results = await parser({
date, date,
channel: channelID, channel,
content: `<!DOCTYPE html><html lang="en"><head></head><body></body></html>`, content,
headers: { 'set-cookie': setCookie } headers
})
.then(result => {
expect(result).toMatchObject([])
done()
})
.catch(error => {
done(error)
}) })
expect(results).toMatchObject([])
}) })

View file

@ -49,9 +49,9 @@
<channel lang="en" xmltv_id="Hits.sg" site_id="160">Hits</channel> <channel lang="en" xmltv_id="Hits.sg" site_id="160">Hits</channel>
<channel lang="en" xmltv_id="HitsMovies.sg" site_id="11">Hits Movies</channel> <channel lang="en" xmltv_id="HitsMovies.sg" site_id="11">Hits Movies</channel>
<channel lang="en" xmltv_id="IDXChannel.id" site_id="100">IDX Channel</channel> <channel lang="en" xmltv_id="IDXChannel.id" site_id="100">IDX Channel</channel>
<channel lang="en" xmltv_id="Ie.id" site_id="96">Ie</channel>
<channel lang="en" xmltv_id="IMC.id" site_id="14">IMC</channel> <channel lang="en" xmltv_id="IMC.id" site_id="14">IMC</channel>
<channel lang="en" xmltv_id="Indosiar.id" site_id="78">Indosiar</channel> <channel lang="en" xmltv_id="Indosiar.id" site_id="78">Indosiar</channel>
<channel lang="en" xmltv_id="Ie.id" site_id="96">Ie</channel>
<channel lang="en" xmltv_id="INews.id" site_id="83">INews</channel> <channel lang="en" xmltv_id="INews.id" site_id="83">INews</channel>
<channel lang="en" xmltv_id="JakTV.id" site_id="113">Jak TV</channel> <channel lang="en" xmltv_id="JakTV.id" site_id="113">Jak TV</channel>
<channel lang="en" xmltv_id="KidsTV.id" site_id="46">Kids TV</channel> <channel lang="en" xmltv_id="KidsTV.id" site_id="46">Kids TV</channel>
@ -92,8 +92,8 @@
<channel lang="en" xmltv_id="TLCSoutheastAsia.us" site_id="248">TLC Southeast Asia</channel> <channel lang="en" xmltv_id="TLCSoutheastAsia.us" site_id="248">TLC Southeast Asia</channel>
<channel lang="en" xmltv_id="Trans7.id" site_id="110">Trans 7</channel> <channel lang="en" xmltv_id="Trans7.id" site_id="110">Trans 7</channel>
<channel lang="en" xmltv_id="TransTV.id" site_id="87">Trans TV</channel> <channel lang="en" xmltv_id="TransTV.id" site_id="87">Trans TV</channel>
<channel lang="en" xmltv_id="TVNMovies.hk" site_id="25">TVN Movies Indonesia</channel>
<channel lang="en" xmltv_id="TVNAsia.hk" site_id="158">TVN Premium Indonesia</channel> <channel lang="en" xmltv_id="TVNAsia.hk" site_id="158">TVN Premium Indonesia</channel>
<channel lang="en" xmltv_id="TVNMovies.hk" site_id="25">TVN Movies Indonesia</channel>
<channel lang="en" xmltv_id="tvOne.id" site_id="97">TVOne</channel> <channel lang="en" xmltv_id="tvOne.id" site_id="97">TVOne</channel>
<channel lang="en" xmltv_id="TVRINasional.id" site_id="118">TVRI Nasional</channel> <channel lang="en" xmltv_id="TVRINasional.id" site_id="118">TVRI Nasional</channel>
<channel lang="en" xmltv_id="VisionPrime.id" site_id="94">Vision Prime</channel> <channel lang="en" xmltv_id="VisionPrime.id" site_id="94">Vision Prime</channel>

View file

@ -49,9 +49,9 @@
<channel lang="id" xmltv_id="Hits.sg" site_id="160">Hits</channel> <channel lang="id" xmltv_id="Hits.sg" site_id="160">Hits</channel>
<channel lang="id" xmltv_id="HitsMovies.sg" site_id="11">Hits Movies</channel> <channel lang="id" xmltv_id="HitsMovies.sg" site_id="11">Hits Movies</channel>
<channel lang="id" xmltv_id="IDXChannel.id" site_id="100">IDX Channel</channel> <channel lang="id" xmltv_id="IDXChannel.id" site_id="100">IDX Channel</channel>
<channel lang="id" xmltv_id="Ie.id" site_id="96">Ie</channel>
<channel lang="id" xmltv_id="IMC.id" site_id="14">IMC</channel> <channel lang="id" xmltv_id="IMC.id" site_id="14">IMC</channel>
<channel lang="id" xmltv_id="Indosiar.id" site_id="78">Indosiar</channel> <channel lang="id" xmltv_id="Indosiar.id" site_id="78">Indosiar</channel>
<channel lang="id" xmltv_id="Ie.id" site_id="96">Ie</channel>
<channel lang="id" xmltv_id="INews.id" site_id="83">INews</channel> <channel lang="id" xmltv_id="INews.id" site_id="83">INews</channel>
<channel lang="id" xmltv_id="JakTV.id" site_id="113">Jak TV</channel> <channel lang="id" xmltv_id="JakTV.id" site_id="113">Jak TV</channel>
<channel lang="id" xmltv_id="KidsTV.id" site_id="46">Kids TV</channel> <channel lang="id" xmltv_id="KidsTV.id" site_id="46">Kids TV</channel>
@ -92,8 +92,8 @@
<channel lang="id" xmltv_id="TLCSoutheastAsia.us" site_id="248">TLC</channel> <channel lang="id" xmltv_id="TLCSoutheastAsia.us" site_id="248">TLC</channel>
<channel lang="id" xmltv_id="Trans7.id" site_id="110">Trans 7</channel> <channel lang="id" xmltv_id="Trans7.id" site_id="110">Trans 7</channel>
<channel lang="id" xmltv_id="TransTV.id" site_id="87">Trans TV</channel> <channel lang="id" xmltv_id="TransTV.id" site_id="87">Trans TV</channel>
<channel lang="id" xmltv_id="TVNMovies.hk" site_id="25">TVN Movies Indonesia</channel>
<channel lang="id" xmltv_id="TVNAsia.hk" site_id="158">TVN Premium Indonesia</channel> <channel lang="id" xmltv_id="TVNAsia.hk" site_id="158">TVN Premium Indonesia</channel>
<channel lang="id" xmltv_id="TVNMovies.hk" site_id="25">TVN Movies Indonesia</channel>
<channel lang="id" xmltv_id="tvOne.id" site_id="97">TVOne</channel> <channel lang="id" xmltv_id="tvOne.id" site_id="97">TVOne</channel>
<channel lang="id" xmltv_id="TVRINasional.id" site_id="118">TVRI Nasional</channel> <channel lang="id" xmltv_id="TVRINasional.id" site_id="118">TVRI Nasional</channel>
<channel lang="id" xmltv_id="VisionPrime.id" site_id="94">Vision Prime</channel> <channel lang="id" xmltv_id="VisionPrime.id" site_id="94">Vision Prime</channel>