Update osn.com Api and channels.

Signed-off-by: Toha <tohenk@yahoo.com>
This commit is contained in:
Toha committed 2026-06-28 19:51:04 +07:00
1 parent 148cbabafa
commit 2adc10b567
7 files changed
+178 -52

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"encrypted":"I4izOSmRQbWl4t6Rpx7eVpU4ic79al7mqyBooPr/wCz2Rd0q9Nm1H3JgztkmPTGRlN18a925kNTZV9i/FWZRZhiuVXisgacl09cAABDfSWytuJTsfFYx38ism0MEufM9Ehwr6XezRQ5aLsnlcsKchQseHTR8jmREn/Y/0W5nSADf8iVJzf0OdJV3aTo70Dtf73dod/fdfBmpdzmIN29J+eQx7LcKDehwFqS6gJ2oDf2eo1Mu2r2Ms4C9zXChCFWXzSeWO/t9kINdeXL3RsXhyubQUJXgYRk+/hDImaU9lt6hR4aLJikHUE0mF1bhWPkvEKM5KI8i+Ata8wWaZOzBdKjVMfvUMbqUKeyeBLVcjpoGGZeyRwOCiU2FA1nzprxbfU3ArTlFluF9g6zsbtDytp+vk9QwJoTirmIB1Q3utN+zIftNUgkgnYoiwdqEQDZnN3l66uiZhieLPSGvNezDRNCDmLjekOdWBNN/1fwIi2DhNYoSZA51WdKMjiwHmRx0D14z0BiSebJDD5IutvYS2OOhMdIg7lbzrLdCF18nSK+AFO/7Fp/VbBfZTWRKd8wB8FkAzdv+2ENoeROGJ5XlphaWgmYbepuV07bGTOvtFC4+Z7nnfMLvbIeG7E/Yiy6zOH8DNSsL+TCiMreR8xY18C4mrR2bjwJVhqtUROqHLvY5pBu/0KxNHFjjJG2zwaoFcxpocKUDmwXGN6nSK6pVhmOGJX798HZD3NxLOAeO1vdWZ6dwUdDhb9oZznfet1nVad6UxifxmSJbaJ1sdBhxwen1KyLOWvYwg6MEL1TtNQrtOY8BHoF7u85Scd1YUZvJMYK1UjiKRK5ax9RxdRiEDJwm1PkiqSI2Oh9s9FtKu6VaHinLfSbW3fshL5wi0UHTvDfKUB0dSU33AA/uGZagFY8E1mePcmMqKDpYS0IsfyrgVjQ3HFhatDcBqdWj5fFVGihEoOmM4Jlj0O1/aF5LZ67S47/vtpDT/X0sjtyx3BxpexQ67tevuFWESbHmMWRF9FJkFkzfO8f62bZkiuAB4LN4XsRO+3QUrr1DzeFL9IgpgBadPAEfbJvk3418AW2Jk7Nh+OhMDBDinf9ID0JnaeuFcizXDKhNco9kt1edo04oAWwHIKQa4DX5nzazHycNtSMLt1RUyNFiP4MrzO/xqtlQV8BiPCNlsAJq6QZcvTfedhkqexFsUZCmjyZGiyHZkRQRaBnGIi97JK6CN2cCQmdEip3QCzcTBAlKzKropFj5+CEjUWTm5VfW74LIHORCTN5G94E9jiwrvRxvQdApJNMtgfSen3sXJp+qUUT5sjHq20gfzVOuQ8Q9svkuBtvqQKSiKm43ZVOC6W/6m/sc2kOWZ6FzHyCx1iZfj3iCt3uL9MMeLMmFHWj06oDtVgvZkQTTUNx8AVliOQhqfzrh0l9LCu68A++XNf9iL82e2CzE8u87PHxlinQXooBjHMh62s0uVB1WP/p1HLOmSWWBBkkZELBHRClYQiDtoaj4/yS7owYTPbYm9y/AsB/9TqUSMIbsUYdg8z9qPSuT56jSGjuOO9usrdBwv7NVVoTS/tc69W218l0CPZ0bQibmgwPRwSnC+eR/JCqFcD7nrNP3gIqK7QqtCnF3y6s4yrKmOJ8LXebdVRg1uppdbFtgGScGnqLKKSFc/mfs7qjDJThiykgxp/TXEYvYzyV++KGokyqq9oJQ39/BHkRbL2z+9/qzdwkyZlRko3y6wsEwkWLRk1LSafsS+ZaU9maK4y18vVn35i9Qnci+eCFlHSqfnwqPRoYKhi0A0vkuzfUw5xDmhWINhCKbcBIAYwul32LLWkCKjhNwS8XGomokNL86YTESqZRS9ZVvnMmKrreSvX3LpW9/q37NG5jZbYEb+gKKmgPElYEbEaaxjR+Ao/agBTs7qX0kYofiIC8Fzz9cMYjCjY8dQMaKWWkfok46X9mDrWx4ML+PdWScNhKj56HKlf+jKfq2/+DjZtwWDxlkQxFclWXVFVGYy+W12HMDo2LZFXj/t7SJXwe26wczjkLuIh4X5/OQWSPWJ9vlLSZvpH0Pbo5jP/0UNcuW6OMhQwJMT6CEjene4XG0yUo1PrUECgFJYwMANcyvX9Rc1P8/e7+0Yf5wgDYxPIsklj0TeCkV2U9z95z1MzJ81jyLYbfEFXuHNIG34o2e9vCtVbZWwqwwQRhEYSvfqJQHYRdbFP8RREWU73DtcktYHeuKe8BxRIoI0/eVz69UXJmgO/0gCP9LMEazWcFUtQmDr+A0iBaHoOd6Hay496dYeNQMLIxg7TTdve+N3rA+/SwBgUYFUQw16rYRBHPoC0hBQrDvPJgn0OfNC8RdOcMrTnJed0f7kdrFbcH69YFPb9U2aMxK8+SzEi8VFrj4VqMIwpGzfvfWCogqYMrATicEzSDd1uv/I/0MFbmym1UNE0LBUZBQh4tMKQnPa0By+sAKLlB7JGHGsSLCdyi1EBSl0PKvKmqs9DVuZgeS6kpbCVL1R/2BYRJq2gpzokEjIVuyeqXdhq25Nuiq2o+5vGRysToyVpcPXh7X5m2zRl8vCyS6vZWG3uixEYW1m/1KOjFy6N1MctJzEBuMBolzoHgNXyA4E0EiI5djAz7IOFW9OUfPsrmcYXge9ZUuUg5BlGvKiu3MTi9Hzs9RAIsi4OoiG/m4gsoZxBiEMXyTeHKRUe/NSjoRS6NwJWH18MLYTWxYBuAiOlLoAcWFSUMzfiQbxLYMGJhmgwItwwr5qDCVZ8gjoqWbpAezbabeLnDt/NAPeYv1FcZ/i1j3LYmNZuaDBfpL/kX4pctJ+dN7bBiAkJypIZPh4CIIYLZ2jFzXR6HsgbOPIfpqFAl7SIOBOv2sgdTrPdOOQZlssSid5NnH8pfuBKVZTcrvT2k3lk8eXlQhZtxY3r0zcD3278Xezu2Tt+n+4VnV5iazDBrmZyRezv3LEGItu8DrTR0KqHqgXXCF8qsV9EPTgAWTS5sj+hEKUPjoEFK+t+gLP5gVW6CCb7+uz9ep4um/sh4YpypXb5Kj2ZV093wJRlE9cOkXmpQMdr5Ccqpw4cC3FkPByi51KTX8+U9yoF1pRgO0HVre1q3uJ3Cr47O02F/sKfYcqd+2ga1pZnqtmWpxzuMb4TBqJk1jJr03IdtMDd9ZfwZMFqpodbWjAWzV/CYq3qHWrqlBE043wedVwSq2E0wfLJlVXl6cXZTMntgBzJrUkoYzw+2KcqJFDJ2PSO81kNUPVa6eCQvjsFpadRfPiQvmajci8QBbC7C1nVPptkDmyZ5KZRHN3i3uYWiWA+MdrUZaXmcjS9fpL5EyuNnagyZm6b4G3Qw7uF68jT4vA5LO4aWB5p9VEhqVA2D0P9CfSWJO8DIuBOAPeF91sOM6LhLA1+Yl68tv+BxA+cbJalL+C1whWCSnBYRCD0tY4tFcxEWH3V2QfZZLhnqssrmpifdbkcjJVsl/3hWiyL5mB1+vIVBRN7tp+mqQHR4wqYg9I+3yDZgPDkdM/JsGSagER0QPsrD5HSxD3dUabT3mQ5Tmjg8Mhah1dX5bIba1Gy8mTuEmmVIS28x2R+m1mKJCzAkEVjZldOT0Nb5nyRbOswreuqe7VOCqU/nrVP/bLAYp8rYUImOG/5fgSXqJpoPF7M0gCCf5k91kF9bGkSZIaQ1OQmi4/2puCocuH+jiXa68o1/VQhuAyfI6z5o7mDoElc9P/xYeYBGjiAXqE4BgOsAQm9hLEfAVg7RiyP8/JjdP2O3R4+31leSueetp1wuRlLTlsaB8l9Xw7qPYvbf/ySStvnobMuorKf/ciXn34Bmv2ZN0YfgDMSsb4aufEXBQlpWIf6nf8ra3jOFKEaOstKpWbvZMJT2By1T5pByhgK7ooFCSUDdgkWJ4z1KPTH2oDFjbV1egza04ben0E+ALybyYo7SlXEJWCkj0mJl85bZcMuc8tOmw6QqICYeYCvnkBiUpCNw+xqLriDP0EM401ffYPoOkbuTEHYZbkqAfqKJMtwwCNrFXTVUVdl2hpFSJklud68Gn4j+IF4uY+RcenwlruXVA+bkULKOYV7HS7zdNKqNu6XK/wFC9oGEhAm7LgXkvuina6c7Ys08jp93eIDoE/Kx3WRZG1qRIpQkK4rpHrOYa7yc4KmAjt7gRQ19rgZ9vrRn5nkcjUu1knmfwQnexocYL5WeLzKgB+Et67/7JzOy4b9lrZEuOimjJEwoQwAl7j8x0l6h+4K/pOrWmAytBb1rhJRYYQ1GT73QScRwuNazARSflvCfPlyKV/QWsEQrhuFday5ppVW3nmC7GtG/3dLexQnaDxiauCNpXZhiZfMncqN+tTDwd0gKWx1A+wXC/a8Y7DCnvmPlG311tCWR4NuMl0dzUwKNu4pBNSb3l0l/ogIi1dNmklPahgnkX2uMvltDxd3FumcRYFc0FFV+vvUFH9iv0jMM9526PEt4EurIAe+o67a+9zTcDQZNLAaStq65d45DvFtDZH2bLvcI3uQDwuRyoDAkUPLleeqBAnxUUzIc3NTnl/v821BcAR83S+N1PAx+khMUFNawB3Du9UDAeQaN31ACloWhk51cW+hh6Ezb/h2EB6jb25BHNMfm8DfsCNDmIfZldeLurL9b51iBPnIIo1EKwiFwUUokQEIkjer5yz0kg/VF+Y3sUQNU47nI6mxP8H8H904ZtSZIXDariFMFYejOKjcLFn6/QC2CyazW7QQkz3yDLo4z+P3geO2+o0FFZcRCN4gT3IzOkXYo0LE3O+K55EHdFO6Zvl/AyAj8W+frwztflsRAOR28y9WabhUQEq6FJjM4AobtwSo1kIhOtJkuyI1m6B9azrxhgVj3zi3x7pIjoSCPzTvDjmS7TFbVZF+krgxLzPNyGyZKjlDm+r0ItFvfjrLzL0VprLZDdV5DzVpe+3YO1f0F5cE9wOmxDG/g/wfd/scYqw7ai/3BIu2HK3SM9ujO8hwr+hKtilQ+HtLine truncated
+1
View File
@@ -0,0 +1 @@
{}
+130 -36
View File
@@ -7,6 +7,11 @@ const crypto = require('crypto')
dayjs.extend(utc)
dayjs.extend(timezone)
/**
* OSN.COM note:
* - API allows only at max 5 channels, otherwise returns 406
*/
// https://www.osn.com/_next/static/chunks/87cf1e375968671c.js
const cipherName = 'aes-256-cbc'
@@ -15,36 +20,50 @@ const cipherIv = Buffer.from('b7cc0a48d6d023bc1a2a670953ec5622', 'hex')
const tz = dayjs.tz.guess()
const oneDayMs = 864e5
const headers = {
'User-Agent':
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 OPR/104.0.0.0'
}
let batch = 0
const headers = {}
const caches = {}
let allChannels
module.exports = {
site: 'osn.com',
days: 2,
url({ channel, date }) {
const data = getChannelData({ channel, date })
return `https://www.osn.com/apidata/tv-schedule-timeline?t=batch${++batch}-time${data.startTime}-${data.endTime}-boxAndroid`
async url({ date }) {
return (await getUrlData(date)).url
},
request: {
headers({ channel, date }) {
return {
...headers,
'X-Encrypted-Data': encrypt(JSON.stringify(getChannelData({ channel, date })))
}
cache: {
ttl: 24 * 60 * 60 * 1000 // 1 day
},
async headers({ date }) {
return await getHeaders(date)
}
},
parser({ content, channel }) {
async parser({ content, channel, date }) {
const programs = []
if (typeof content === 'string' || Buffer.isBuffer(content)) {
content = JSON.parse(content)
}
if (content?.encrypted) {
content = JSON.parse(decrypt(content.encrypted))
}
content = getDecryptedData(content)
if (Array.isArray(content?.entries)) {
// get remaining segments entries
const cacheId = date.format('YYYYMMDD')
if (caches[cacheId] === undefined) {
caches[cacheId] = []
const segments = await getUrlData(date, false)
while (segments.length) {
const segment = segments.shift()
const result = await axios
.get(segment.url, { headers: await getHeaders(segment.data) })
.then(res => getDecryptedData(res.data))
.catch(err => console.error(`${segment.url}: ${err.message}!`))
if (Array.isArray(result?.entries)) {
caches[cacheId].push(...result.entries)
}
}
// add 5s delay to avoid 406
await new Promise(resolve => setTimeout(resolve, 5000))
}
// add entries from cache
if (Array.isArray(caches[cacheId])) {
content.entries.push(...caches[cacheId])
}
content.entries
.filter(entry => entry.guid == channel.site_id)
.forEach(entry => {
@@ -83,42 +102,117 @@ module.exports = {
},
async channels({ lang = 'ar' }) {
const channels = []
const result = await axios
.get('https://www.osn.com/apidata/channels?platform=Android', { headers })
.then(response => response.data)
.catch(console.error)
if (result?.encrypted) {
const items = JSON.parse(decrypt(result.encrypted))
const items = await fetchChannels()
if (Array.isArray(items)) {
channels.push(...items.map(item => ({
lang,
site_id: item.guid,
name: item.title
})))
}
return channels
}
}
async function fetchChannels() {
if (allChannels === undefined) {
const result = await axios
.get('https://www.osn.com/apidata/channels?platform=Android', { headers: await getHeaders() })
.then(res => getDecryptedData(res.data))
.catch(console.error)
if (Array.isArray(result)) {
allChannels = result
}
}
return allChannels
}
async function getHeaders(data) {
if (dayjs.isDayjs(data)) {
data = (await getUrlData(data)).data
}
const res = { ...headers }
if (data) {
res['X-Encrypted-Data'] = getEncryptedData(data)
}
return res
}
async function getUrlData(date, first = true) {
const parts = []
const startOfDay = date.tz(tz).startOf('d')
const endOfDay = startOfDay.add(oneDayMs, 'ms')
const startStop = {
startTime: startOfDay.valueOf(),
endTime: endOfDay.valueOf()
}
await fetchChannels()
const segments = await getUrlSegments()
let i = 0
for (const v of segments) {
const data = {
url: `https://www.osn.com/apidata/tv-schedule-timeline?t=batch${++i}-time${
startStop.startTime
}-${
startStop.endTime
}-boxAndroid`,
data: {
channelGuid: v.join('|'),
...startStop
}
}
if (first) {
return data
} else if (i === 1) {
continue
}
parts.push(data)
}
return parts
}
async function getUrlSegments() {
await fetchChannels()
const segments = []
const _channels = [...allChannels.map(item => item.guid)]
while (_channels.length) {
segments.push(_channels.splice(0, 5))
}
return segments
}
function getEncryptedData(data) {
return encrypt(JSON.stringify(data))
}
function getDecryptedData(data) {
if (typeof data === 'string' || Buffer.isBuffer(data)) {
data = JSON.parse(data)
}
if (data?.encrypted) {
data = JSON.parse(decrypt(data.encrypted))
}
return data
}
function encrypt(data, encoding = 'utf8') {
const cipher = crypto.createCipheriv(cipherName, cipherKey, cipherIv)
const encrypted = cipher.update(data, encoding, 'base64')
return (encrypted + cipher.final('base64'))
}
function decrypt(data, encoding = 'utf8') {
const decipher = crypto.createDecipheriv(cipherName, cipherKey, cipherIv)
const decrypted = decipher.update(data, 'base64', encoding)
return (decrypted + decipher.final(encoding))
}
function getChannelData({ channel, date }) {
const startOfDay = date.tz(tz).startOf('d')
return {
channelGuid: channel.site_id,
startTime: startOfDay.valueOf(),
endTime: startOfDay.valueOf() + oneDayMs
}
return (decrypted + decipher.final(encoding))
}
function dotProp(o, prop) {
+41 -11
View File
@@ -1,4 +1,5 @@
const { parser, url, request } = require('./osn.com.config')
const axios = require('axios')
const fs = require('fs')
const path = require('path')
const dayjs = require('dayjs')
@@ -8,28 +9,57 @@ const customParseFormat = require('dayjs/plugin/customParseFormat')
dayjs.extend(customParseFormat)
dayjs.extend(utc)
jest.mock('axios')
const date = dayjs.utc('2026-05-16').startOf('d')
const channelAR = { site_id: '4506', xmltv_id: 'OSNKids.ae@SD', lang: 'ar' }
const channelEN = { site_id: '4506', xmltv_id: 'OSNKids.ae@SD', lang: 'en' }
const content = fs.readFileSync(path.join(__dirname, '/__data__/content.json'))
it('can generate valid request headers', () => {
const result = request.headers({ channel: channelAR, date })
axios.get.mockImplementation(url => {
const urls = {
'https://www.osn.com/apidata/channels?platform=Android': 'channel.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch1-time1778846400000-1778932800000-boxAndroid': 'content.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch2-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch3-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch4-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch5-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch6-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch7-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch8-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch9-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch10-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch11-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch12-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch13-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch14-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch15-time1778846400000-1778932800000-boxAndroid': 'empty.json',
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch16-time1778846400000-1778932800000-boxAndroid': 'empty.json'
}
let data = ''
if (urls[url] !== undefined) {
data = fs.readFileSync(path.join(__dirname, '__data__', urls[url])).toString()
}
return Promise.resolve({ data })
})
it('can generate valid request headers', async () => {
const result = await request.headers({ date })
expect(result).toMatchObject({
'X-Encrypted-Data':
'e0srp4UFarSGxZt4iWaXSZTCg6vB46NRZmY5V9wEzxHeB1bwR1HXrAuTI8FCVH7i3+uVqkDQSgRxjRFPYdrXhqedzEogwcjDnjRPmLtFxEA='
'e0srp4UFarSGxZt4iWaXSSZCApUQYlb/CPhOH0/r/rUzyU/aVEst9+dVQueAwTe3A/or1iCzDEaiVfQ/VTxF9ZdIzOZeb3SSRap3YK7IFkj7Km6L8aXxpsNeQua/L/Ar'
})
})
it('can generate valid url', () => {
const result = url({ channel: channelAR, date })
it('can generate valid url', async () => {
const result = await url({ date })
expect(result).toBe(
'https://www.osn.com/apidata/tv-schedule-timeline?t=batch1-time1778846400000-1778932800000-boxAndroid'
)
})
it('can parse response (ar)', () => {
const result = parser({ date, channel: channelAR, content })
it('can parse response (ar)', async () => {
const result = (await parser({ date, channel: channelAR, content }))
.map(a => {
a.start = a.start.toJSON()
a.stop = a.stop.toJSON()
@@ -51,8 +81,8 @@ it('can parse response (ar)', () => {
})
})
it('can parse response (en)', () => {
const result = parser({ date, channel: channelEN, content })
it('can parse response (en)', async () => {
const result = (await parser({ date, channel: channelEN, content }))
.map(a => {
a.start = a.start.toJSON()
a.stop = a.stop.toJSON()
@@ -74,7 +104,7 @@ it('can parse response (en)', () => {
})
})
it('can handle empty guide', () => {
const result = parser({ date, channel: channelAR, content: '[]' })
it('can handle empty guide', async () => {
const result = await parser({ date, channel: channelAR, content: '[]' })
expect(result).toMatchObject([])
})
+2 -2
View File
@@ -7,11 +7,12 @@
<channel site="osn.com" site_id="225" lang="ar" xmltv_id="">OSNtv Movies Horror</channel>
<channel site="osn.com" site_id="307" lang="ar" xmltv_id="">OSNtv Crime</channel>
<channel site="osn.com" site_id="314" lang="ar" xmltv_id="">OSNtv Documentary</channel>
<channel site="osn.com" site_id="411" lang="ar" xmltv_id="">MBC Mood</channel>
<channel site="osn.com" site_id="1101" lang="ar" xmltv_id="">KTV Channel 1 HD</channel>
<channel site="osn.com" site_id="4502" lang="ar" xmltv_id="">OSNtv Now</channel>
<channel site="osn.com" site_id="5666" lang="ar" xmltv_id="">OSNtv Movies Family</channel>
<channel site="osn.com" site_id="5669" lang="ar" xmltv_id="">OSNtv Movies Comedy</channel>
<channel site="osn.com" site_id="5672" lang="ar" xmltv_id="">OSNtv Pop Up Beyond Earth</channel>
<channel site="osn.com" site_id="5672" lang="ar" xmltv_id="">OSNtv Pop Up 90 Day Fiance</channel>
<channel site="osn.com" site_id="6607" lang="ar" xmltv_id="">eClutch Access</channel>
<channel site="osn.com" site_id="6609" lang="ar" xmltv_id="">Esport 24</channel>
<channel site="osn.com" site_id="6610" lang="ar" xmltv_id="">eClutch LIVE</channel>
@@ -78,5 +79,4 @@
<channel site="osn.com" site_id="9047" lang="ar" xmltv_id="SaudiThaqafiyaTV.sa@SD">Al Thaqafeya</channel>
<channel site="osn.com" site_id="2015" lang="ar" xmltv_id="SkyNewsArabia.ae@SD">Sky News Arabia HD</channel>
<channel site="osn.com" site_id="308" lang="ar" xmltv_id="TLCArabia.us@SD">TLC HD</channel>
<channel site="osn.com" site_id="411" lang="ar" xmltv_id="Wanasah.ae@SD">Wanasah</channel>
</channels>
+2 -2
View File
@@ -7,11 +7,12 @@
<channel site="osn.com" site_id="225" lang="en" xmltv_id="">OSNtv Movies Horror</channel>
<channel site="osn.com" site_id="307" lang="en" xmltv_id="">OSNtv Crime</channel>
<channel site="osn.com" site_id="314" lang="en" xmltv_id="">OSNtv Documentary</channel>
<channel site="osn.com" site_id="411" lang="en" xmltv_id="">MBC Mood</channel>
<channel site="osn.com" site_id="1101" lang="en" xmltv_id="">KTV Channel 1 HD</channel>
<channel site="osn.com" site_id="4502" lang="en" xmltv_id="">OSNtv Now</channel>
<channel site="osn.com" site_id="5666" lang="en" xmltv_id="">OSNtv Movies Family</channel>
<channel site="osn.com" site_id="5669" lang="en" xmltv_id="">OSNtv Movies Comedy</channel>
<channel site="osn.com" site_id="5672" lang="en" xmltv_id="">OSNtv Pop Up Beyond Earth</channel>
<channel site="osn.com" site_id="5672" lang="en" xmltv_id="">OSNtv Pop Up 90 Day Fiance</channel>
<channel site="osn.com" site_id="6607" lang="en" xmltv_id="">eClutch Access</channel>
<channel site="osn.com" site_id="6609" lang="en" xmltv_id="">Esport 24</channel>
<channel site="osn.com" site_id="6610" lang="en" xmltv_id="">eClutch LIVE</channel>
@@ -78,5 +79,4 @@
<channel site="osn.com" site_id="9047" lang="en" xmltv_id="SaudiThaqafiyaTV.sa@SD">Al Thaqafeya</channel>
<channel site="osn.com" site_id="2015" lang="en" xmltv_id="SkyNewsArabia.ae@SD">Sky News Arabia HD</channel>
<channel site="osn.com" site_id="308" lang="en" xmltv_id="TLCArabia.us@SD">TLC HD</channel>
<channel site="osn.com" site_id="411" lang="en" xmltv_id="Wanasah.ae@SD">Wanasah</channel>
</channels>
+1 -1
View File
@@ -1,6 +1,6 @@
# osn.com
https://www.osn.com/en-sa/osntv _[Geo-blocked]_
https://www.osn.com/en-sa/watch/tv-schedule _[Geo-blocked]_
### Download the guide