wip
This commit is contained in:
parent
d604f35ba1
commit
9a4a62fd10
|
@ -8,6 +8,15 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download channels from API
|
||||
run: |
|
||||
mkdir -p scripts/data
|
||||
curl -L -o scripts/data/channels.json https://iptv-org.github.io/api/channels.json
|
||||
curl -L -o scripts/data/categories.json https://iptv-org.github.io/api/categories.json
|
||||
curl -L -o scripts/data/countries.json https://iptv-org.github.io/api/countries.json
|
||||
curl -L -o scripts/data/languages.json https://iptv-org.github.io/api/languages.json
|
||||
curl -L -o scripts/data/regions.json https://iptv-org.github.io/api/regions.json
|
||||
curl -L -o scripts/data/guides.json https://iptv-org.github.io/api/guides.json
|
||||
- uses: actions/setup-node@v2
|
||||
if: ${{ !env.ACT }}
|
||||
with:
|
||||
|
|
|
@ -1,109 +1,90 @@
|
|||
const { db, logger, generator, file } = require('../core')
|
||||
const { create: createPlaylist } = require('../core/playlist')
|
||||
const { db, logger, generator, file, api } = require('../core')
|
||||
const _ = require('lodash')
|
||||
|
||||
let languages = []
|
||||
let countries = []
|
||||
let categories = []
|
||||
let regions = []
|
||||
|
||||
const LOGS_PATH = process.env.LOGS_PATH || 'scripts/logs'
|
||||
const PUBLIC_PATH = process.env.PUBLIC_PATH || '.gh-pages'
|
||||
|
||||
async function main() {
|
||||
await setUp()
|
||||
const streams = await loadStreams()
|
||||
|
||||
await generateCategories()
|
||||
await generateCountries()
|
||||
await generateLanguages()
|
||||
await generateRegions()
|
||||
await generateIndex()
|
||||
await generateIndexNSFW()
|
||||
await generateIndexCategory()
|
||||
await generateIndexCountry()
|
||||
await generateIndexLanguage()
|
||||
await generateIndexRegion()
|
||||
logger.info(`generating categories/...`)
|
||||
await generator.generate('categories', streams)
|
||||
|
||||
await generateChannelsJson()
|
||||
// await generateCountries(streams)
|
||||
// await generateLanguages()
|
||||
// await generateRegions()
|
||||
// await generateIndex()
|
||||
// await generateIndexNSFW()
|
||||
// await generateIndexCategory()
|
||||
// await generateIndexCountry()
|
||||
// await generateIndexLanguage()
|
||||
// await generateIndexRegion()
|
||||
|
||||
// await generateChannelsJson()
|
||||
|
||||
// await saveLogs()
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
async function generateCategories() {
|
||||
logger.info(`Generating categories/...`)
|
||||
|
||||
for (const category of categories) {
|
||||
const { count } = await generator.generate(
|
||||
`${PUBLIC_PATH}/categories/${category.slug}.m3u`,
|
||||
{ categories: { $elemMatch: category } },
|
||||
{ saveEmpty: true, includeNSFW: true }
|
||||
)
|
||||
|
||||
await log('categories', {
|
||||
name: category.name,
|
||||
slug: category.slug,
|
||||
count
|
||||
})
|
||||
}
|
||||
|
||||
const { count: otherCount } = await generator.generate(
|
||||
`${PUBLIC_PATH}/categories/other.m3u`,
|
||||
{ categories: { $size: 0 } },
|
||||
{
|
||||
saveEmpty: true,
|
||||
onLoad: function (items) {
|
||||
return items.map(item => {
|
||||
item.group_title = 'Other'
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
await log('categories', {
|
||||
name: 'Other',
|
||||
slug: 'other',
|
||||
count: otherCount
|
||||
})
|
||||
}
|
||||
|
||||
async function generateCountries() {
|
||||
logger.info(`Generating countries/...`)
|
||||
|
||||
async function generateCountries(streams) {
|
||||
logger.info(`generating countries/...`)
|
||||
const countries = await loadCountries()
|
||||
const regions = await loadRegions()
|
||||
for (const country of countries) {
|
||||
const { count } = await generator.generate(
|
||||
let areaCodes = _.filter(regions, { countries: [country.code] }).map(r => r.code)
|
||||
areaCodes.push(country.code)
|
||||
const { count, items } = await generator.generate(
|
||||
`${PUBLIC_PATH}/countries/${country.code.toLowerCase()}.m3u`,
|
||||
streams,
|
||||
{
|
||||
countries: { $elemMatch: country }
|
||||
public: true,
|
||||
filter: s => _.intersection(areaCodes, s.broadcast_area).length
|
||||
}
|
||||
)
|
||||
|
||||
await log('countries', {
|
||||
log.countries.push({
|
||||
name: country.name,
|
||||
code: country.code,
|
||||
count
|
||||
})
|
||||
}
|
||||
|
||||
const { count: undefinedCount } = await generator.generate(
|
||||
`${PUBLIC_PATH}/countries/undefined.m3u`,
|
||||
{
|
||||
countries: { $size: 0 }
|
||||
},
|
||||
{
|
||||
onLoad: function (items) {
|
||||
return items.map(item => {
|
||||
item.group_title = 'Undefined'
|
||||
return item
|
||||
})
|
||||
}
|
||||
const { count } = await generator.generate(`${PUBLIC_PATH}/countries/undefined.m3u`, streams, {
|
||||
public: true,
|
||||
filter: s => !s.broadcast_area.length,
|
||||
onLoad: items => {
|
||||
return items.map(item => {
|
||||
item.group_title = 'Undefined'
|
||||
return item
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
await log('countries', {
|
||||
name: 'Undefined',
|
||||
code: 'UNDEFINED',
|
||||
count: undefinedCount
|
||||
})
|
||||
|
||||
log.countries.push({
|
||||
name: 'Undefined',
|
||||
id: 'UNDEFINED',
|
||||
count
|
||||
})
|
||||
|
||||
// const { count: undefinedCount } = await generator.generate(
|
||||
// `${PUBLIC_PATH}/countries/undefined.m3u`,
|
||||
// {
|
||||
// countries: { $size: 0 }
|
||||
// },
|
||||
// {
|
||||
// onLoad: function (items) {
|
||||
// return items.map(item => {
|
||||
// item.group_title = 'Undefined'
|
||||
// return item
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
|
||||
// await log('countries', {
|
||||
// name: 'Undefined',
|
||||
// code: 'UNDEFINED',
|
||||
// count: undefinedCount
|
||||
// })
|
||||
}
|
||||
|
||||
async function generateLanguages() {
|
||||
|
@ -395,47 +376,44 @@ async function generateIndexRegion() {
|
|||
)
|
||||
}
|
||||
|
||||
async function generateChannelsJson() {
|
||||
logger.info('Generating channels.json...')
|
||||
async function loadStreams() {
|
||||
await api.channels.load()
|
||||
let channels = await api.channels.all()
|
||||
channels = _.keyBy(channels, 'id')
|
||||
|
||||
await generator.generate(
|
||||
`${PUBLIC_PATH}/channels.json`,
|
||||
{},
|
||||
{ format: 'json', includeNSFW: true, uniqBy: null }
|
||||
)
|
||||
}
|
||||
|
||||
async function setUp() {
|
||||
logger.info(`Loading database...`)
|
||||
const items = await db.find({})
|
||||
categories = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.categories)), 'slug'), ['name']).filter(
|
||||
i => i
|
||||
)
|
||||
countries = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.countries)), 'code'), ['name']).filter(
|
||||
i => i
|
||||
)
|
||||
languages = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.languages)), 'code'), ['name']).filter(
|
||||
i => i
|
||||
)
|
||||
regions = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.regions)), 'code'), ['name']).filter(
|
||||
i => i
|
||||
)
|
||||
|
||||
const categoriesLog = `${LOGS_PATH}/generate-playlists/categories.log`
|
||||
const countriesLog = `${LOGS_PATH}/generate-playlists/countries.log`
|
||||
const languagesLog = `${LOGS_PATH}/generate-playlists/languages.log`
|
||||
const regionsLog = `${LOGS_PATH}/generate-playlists/regions.log`
|
||||
|
||||
logger.info(`Creating '${categoriesLog}'...`)
|
||||
await file.create(categoriesLog)
|
||||
logger.info(`Creating '${countriesLog}'...`)
|
||||
await file.create(countriesLog)
|
||||
logger.info(`Creating '${languagesLog}'...`)
|
||||
await file.create(languagesLog)
|
||||
logger.info(`Creating '${regionsLog}'...`)
|
||||
await file.create(regionsLog)
|
||||
}
|
||||
|
||||
async function log(type, data) {
|
||||
await file.append(`${LOGS_PATH}/generate-playlists/${type}.log`, JSON.stringify(data) + '\n')
|
||||
await api.countries.load()
|
||||
let countries = await api.countries.all()
|
||||
countries = _.keyBy(countries, 'code')
|
||||
|
||||
await api.categories.load()
|
||||
let categories = await api.categories.all()
|
||||
categories = _.keyBy(categories, 'id')
|
||||
|
||||
await api.languages.load()
|
||||
let languages = await api.languages.all()
|
||||
languages = _.keyBy(languages, 'code')
|
||||
|
||||
await api.guides.load()
|
||||
let guides = await api.guides.all()
|
||||
guides = _.groupBy(guides, 'channel')
|
||||
|
||||
await db.streams.load()
|
||||
let streams = await db.streams.find({})
|
||||
|
||||
return streams.map(stream => {
|
||||
const channel = channels[stream.channel_id] || null
|
||||
|
||||
stream.channel = channel
|
||||
stream.broadcast_area = channel
|
||||
? channel.broadcast_area.map(item => {
|
||||
const [_, code] = item.split('/')
|
||||
return code
|
||||
})
|
||||
: []
|
||||
stream.categories = channel ? channel.categories.map(id => categories[id]) : []
|
||||
stream.languages = channel ? channel.languages.map(code => languages[code]) : []
|
||||
stream.guides = guides[stream.channel_id] ? guides[stream.channel_id].map(g => g.url) : []
|
||||
|
||||
return stream
|
||||
})
|
||||
}
|
||||
|
|
|
@ -16,11 +16,25 @@ class API {
|
|||
find(query) {
|
||||
return _.find(this.collection, query)
|
||||
}
|
||||
|
||||
filter(query) {
|
||||
return _.filter(this.collection, query)
|
||||
}
|
||||
|
||||
all() {
|
||||
return this.collection
|
||||
}
|
||||
}
|
||||
|
||||
const api = {}
|
||||
|
||||
api.channels = new API(`${DATA_DIR}/channels.json`)
|
||||
api.countries = new API(`${DATA_DIR}/countries.json`)
|
||||
api.guides = new API(`${DATA_DIR}/guides.json`)
|
||||
api.categories = new API(`${DATA_DIR}/categories.json`)
|
||||
api.languages = new API(`${DATA_DIR}/languages.json`)
|
||||
api.regions = new API(`${DATA_DIR}/regions.json`)
|
||||
api.statuses = new API(`${DATA_DIR}/statuses.json`)
|
||||
api.blocklist = new API(`${DATA_DIR}/blocklist.json`)
|
||||
|
||||
module.exports = api
|
||||
|
|
|
@ -1,61 +1,75 @@
|
|||
const Database = require('nedb-promises')
|
||||
const nedb = require('nedb-promises')
|
||||
const file = require('./file')
|
||||
|
||||
const DB_FILEPATH = process.env.DB_FILEPATH || './scripts/channels.db'
|
||||
const DB_DIR = process.env.DB_DIR || './scripts/database'
|
||||
|
||||
const nedb = Database.create({
|
||||
filename: file.resolve(DB_FILEPATH),
|
||||
autoload: true,
|
||||
onload(err) {
|
||||
if (err) console.error(err)
|
||||
},
|
||||
compareStrings: (a, b) => {
|
||||
a = a.replace(/\s/g, '_')
|
||||
b = b.replace(/\s/g, '_')
|
||||
class Database {
|
||||
constructor(filepath) {
|
||||
this.filepath = filepath
|
||||
}
|
||||
|
||||
return a.localeCompare(b, undefined, {
|
||||
sensitivity: 'accent',
|
||||
numeric: true
|
||||
load() {
|
||||
this.db = nedb.create({
|
||||
filename: file.resolve(this.filepath),
|
||||
autoload: true,
|
||||
onload: err => {
|
||||
if (err) console.error(err)
|
||||
},
|
||||
compareStrings: (a, b) => {
|
||||
a = a.replace(/\s/g, '_')
|
||||
b = b.replace(/\s/g, '_')
|
||||
|
||||
return a.localeCompare(b, undefined, {
|
||||
sensitivity: 'accent',
|
||||
numeric: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
removeIndex(field) {
|
||||
return this.db.removeIndex(field)
|
||||
}
|
||||
|
||||
addIndex(options) {
|
||||
return this.db.ensureIndex(options)
|
||||
}
|
||||
|
||||
compact() {
|
||||
return this.db.persistence.compactDatafile()
|
||||
}
|
||||
|
||||
stopAutocompact() {
|
||||
return this.db.persistence.stopAutocompaction()
|
||||
}
|
||||
|
||||
reset() {
|
||||
return file.clear(this.filepath)
|
||||
}
|
||||
|
||||
count(query) {
|
||||
return this.db.count(query)
|
||||
}
|
||||
|
||||
insert(doc) {
|
||||
return this.db.insert(doc)
|
||||
}
|
||||
|
||||
update(query, update) {
|
||||
return this.db.update(query, update)
|
||||
}
|
||||
|
||||
find(query) {
|
||||
return this.db.find(query)
|
||||
}
|
||||
|
||||
remove(query, options) {
|
||||
return this.db.remove(query, options)
|
||||
}
|
||||
}
|
||||
|
||||
const db = {}
|
||||
|
||||
db.removeIndex = function (field) {
|
||||
return nedb.removeIndex(field)
|
||||
}
|
||||
|
||||
db.addIndex = function (options) {
|
||||
return nedb.ensureIndex(options)
|
||||
}
|
||||
|
||||
db.compact = function () {
|
||||
return nedb.persistence.compactDatafile()
|
||||
}
|
||||
|
||||
db.reset = function () {
|
||||
return file.clear(DB_FILEPATH)
|
||||
}
|
||||
|
||||
db.count = function (query) {
|
||||
return nedb.count(query)
|
||||
}
|
||||
|
||||
db.insert = function (doc) {
|
||||
return nedb.insert(doc)
|
||||
}
|
||||
|
||||
db.update = function (query, update) {
|
||||
return nedb.update(query, update)
|
||||
}
|
||||
|
||||
db.find = function (query) {
|
||||
return nedb.find(query)
|
||||
}
|
||||
|
||||
db.remove = function (query, options) {
|
||||
return nedb.remove(query, options)
|
||||
}
|
||||
db.streams = new Database(`${DB_DIR}/streams.db`)
|
||||
|
||||
module.exports = db
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
const { create: createPlaylist } = require('./playlist')
|
||||
const store = require('./store')
|
||||
const path = require('path')
|
||||
const glob = require('glob')
|
||||
const fs = require('mz/fs')
|
||||
const _ = require('lodash')
|
||||
|
||||
const file = {}
|
||||
|
||||
|
@ -64,4 +67,45 @@ file.basename = function (filepath) {
|
|||
return path.basename(filepath)
|
||||
}
|
||||
|
||||
// file.saveAsM3U = async function (filepath, items, options = {}) {
|
||||
// const playlist = createPlaylist(filepath)
|
||||
|
||||
// const header = {}
|
||||
// if (options.public) {
|
||||
// let guides = items.map(item => item.guides)
|
||||
// guides = _.uniq(_.flatten(guides)).sort().join(',')
|
||||
|
||||
// header['x-tvg-url'] = guides
|
||||
// }
|
||||
// playlist.setHeader(header)
|
||||
|
||||
// for (const item of items) {
|
||||
// const stream = store.create(item)
|
||||
|
||||
// let attrs
|
||||
// if (options.public) {
|
||||
// attrs = {
|
||||
// 'tvg-id': stream.get('tvg_id'),
|
||||
// 'tvg-country': stream.get('tvg_country'),
|
||||
// 'tvg-language': stream.get('tvg_language'),
|
||||
// 'tvg-logo': stream.get('tvg_logo'),
|
||||
// 'user-agent': stream.get('http.user-agent') || undefined,
|
||||
// 'group-title': stream.get('group_title')
|
||||
// }
|
||||
// } else {
|
||||
// attrs = {
|
||||
// 'tvg-id': stream.get('tvg_id'),
|
||||
// 'user-agent': stream.get('http.user-agent') || undefined
|
||||
// }
|
||||
// }
|
||||
|
||||
// playlist.add(stream.get('url'), stream.get('display_name'), attrs, {
|
||||
// 'http-referrer': stream.get('http.referrer') || undefined,
|
||||
// 'http-user-agent': stream.get('http.user-agent') || undefined
|
||||
// })
|
||||
// }
|
||||
|
||||
// return file.write(filepath, playlist.toString())
|
||||
// }
|
||||
|
||||
module.exports = file
|
||||
|
|
|
@ -1,119 +1,23 @@
|
|||
const { create: createPlaylist } = require('./playlist')
|
||||
const store = require('./store')
|
||||
const file = require('./file')
|
||||
const logger = require('./logger')
|
||||
const db = require('./db')
|
||||
const _ = require('lodash')
|
||||
const generators = require('../generators')
|
||||
|
||||
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs/generators'
|
||||
|
||||
const generator = {}
|
||||
|
||||
generator.generate = async function (filepath, query = {}, options = {}) {
|
||||
options = {
|
||||
...{
|
||||
format: 'm3u',
|
||||
saveEmpty: false,
|
||||
includeNSFW: false,
|
||||
includeGuides: true,
|
||||
includeBroken: false,
|
||||
onLoad: r => r,
|
||||
uniqBy: item => item.id || _.uniqueId(),
|
||||
sortBy: null
|
||||
},
|
||||
...options
|
||||
}
|
||||
|
||||
query['is_nsfw'] = options.includeNSFW ? { $in: [true, false] } : false
|
||||
query['is_broken'] = options.includeBroken ? { $in: [true, false] } : false
|
||||
|
||||
let items = await db
|
||||
.find(query)
|
||||
.sort({ name: 1, 'status.level': 1, 'resolution.height': -1, url: 1 })
|
||||
|
||||
items = _.uniqBy(items, 'url')
|
||||
if (!options.saveEmpty && !items.length) return { filepath, query, options, count: 0 }
|
||||
if (options.uniqBy) items = _.uniqBy(items, options.uniqBy)
|
||||
|
||||
items = options.onLoad(items)
|
||||
|
||||
if (options.sortBy) items = _.sortBy(items, options.sortBy)
|
||||
|
||||
switch (options.format) {
|
||||
case 'json':
|
||||
await saveAsJSON(filepath, items, options)
|
||||
break
|
||||
case 'm3u':
|
||||
default:
|
||||
await saveAsM3U(filepath, items, options)
|
||||
break
|
||||
}
|
||||
|
||||
return { filepath, query, options, count: items.length }
|
||||
}
|
||||
|
||||
async function saveAsM3U(filepath, items, options = {}) {
|
||||
const playlist = await createPlaylist(filepath)
|
||||
|
||||
const header = {}
|
||||
if (options.public) {
|
||||
let guides = items.map(item => item.guides)
|
||||
guides = _.uniq(_.flatten(guides)).sort().join(',')
|
||||
|
||||
header['x-tvg-url'] = guides
|
||||
}
|
||||
|
||||
await playlist.header(header)
|
||||
for (const item of items) {
|
||||
const stream = store.create(item)
|
||||
|
||||
let attrs
|
||||
if (options.public) {
|
||||
attrs = {
|
||||
'tvg-id': stream.get('tvg_id'),
|
||||
'tvg-country': stream.get('tvg_country'),
|
||||
'tvg-language': stream.get('tvg_language'),
|
||||
'tvg-logo': stream.get('tvg_logo'),
|
||||
'user-agent': stream.get('http.user-agent') || undefined,
|
||||
'group-title': stream.get('group_title')
|
||||
}
|
||||
} else {
|
||||
attrs = {
|
||||
'tvg-id': stream.get('tvg_id'),
|
||||
'user-agent': stream.get('http.user-agent') || undefined
|
||||
}
|
||||
generator.generate = async function (name, items = []) {
|
||||
if (typeof generators[name] === 'function') {
|
||||
try {
|
||||
const logs = await generators[name].bind()(items)
|
||||
await file.create(`${LOGS_DIR}/${name}.log`, logs.map(toJSON).join('\n'))
|
||||
} catch (error) {
|
||||
logger.error(`generators/${name}.js: ${error.message}`)
|
||||
}
|
||||
|
||||
await playlist.link(stream.get('url'), stream.get('display_name'), attrs, {
|
||||
'http-referrer': stream.get('http.referrer') || undefined,
|
||||
'http-user-agent': stream.get('http.user-agent') || undefined
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAsJSON(filepath, items, options) {
|
||||
const output = items.map(item => {
|
||||
const stream = store.create(item)
|
||||
const categories = stream.get('categories').map(c => ({ name: c.name, slug: c.slug }))
|
||||
const countries = stream.get('countries').map(c => ({ name: c.name, code: c.code }))
|
||||
|
||||
return {
|
||||
name: stream.get('name'),
|
||||
logo: stream.get('logo'),
|
||||
url: stream.get('url'),
|
||||
categories,
|
||||
countries,
|
||||
languages: stream.get('languages'),
|
||||
tvg: {
|
||||
id: stream.get('tvg_id'),
|
||||
name: stream.get('name'),
|
||||
url: stream.get('tvg_url')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await file.create(filepath, JSON.stringify(output))
|
||||
}
|
||||
|
||||
generator.saveAsM3U = saveAsM3U
|
||||
generator.saveAsJSON = saveAsJSON
|
||||
|
||||
module.exports = generator
|
||||
|
||||
function toJSON(item) {
|
||||
return JSON.stringify(item)
|
||||
}
|
||||
|
|
|
@ -1,49 +1,92 @@
|
|||
const file = require('./file')
|
||||
const store = require('./store')
|
||||
const _ = require('lodash')
|
||||
|
||||
const playlist = {}
|
||||
|
||||
playlist.create = async function (filepath) {
|
||||
playlist.filepath = filepath
|
||||
const dir = file.dirname(filepath)
|
||||
file.createDir(dir)
|
||||
await file.create(filepath, '')
|
||||
class Playlist {
|
||||
constructor() {
|
||||
this.links = []
|
||||
}
|
||||
|
||||
return playlist
|
||||
setHeader(attrs = {}) {
|
||||
this.header = attrs
|
||||
}
|
||||
|
||||
add(url, title, attrs, vlcOpts) {
|
||||
this.links.push({ url, title, attrs, vlcOpts })
|
||||
}
|
||||
|
||||
toString() {
|
||||
let output = `#EXTM3U`
|
||||
for (const attr in this.header) {
|
||||
const value = this.header[attr]
|
||||
output += ` ${attr}="${value}"`
|
||||
}
|
||||
output += `\n`
|
||||
|
||||
for (const link of this.links) {
|
||||
output += `#EXTINF:-1`
|
||||
for (const name in link.attrs) {
|
||||
const value = link.attrs[name]
|
||||
if (value !== undefined) {
|
||||
output += ` ${name}="${value}"`
|
||||
}
|
||||
}
|
||||
output += `,${link.title}\n`
|
||||
|
||||
for (const name in link.vlcOpts) {
|
||||
const value = link.vlcOpts[name]
|
||||
if (value !== undefined) {
|
||||
output += `#EXTVLCOPT:${name}=${value}\n`
|
||||
}
|
||||
}
|
||||
|
||||
output += `${link.url}\n`
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
||||
playlist.header = async function (attrs) {
|
||||
let header = `#EXTM3U`
|
||||
for (const name in attrs) {
|
||||
const value = attrs[name]
|
||||
header += ` ${name}="${value}"`
|
||||
playlist.create = function (items = [], options = {}) {
|
||||
const p = new Playlist()
|
||||
|
||||
const header = {}
|
||||
if (options.public) {
|
||||
let guides = items.map(item => item.guides)
|
||||
guides = _.uniq(_.flatten(guides)).sort().join(',')
|
||||
|
||||
header['x-tvg-url'] = guides
|
||||
}
|
||||
header += `\n`
|
||||
p.setHeader(header)
|
||||
|
||||
await file.append(playlist.filepath, header)
|
||||
for (const item of items) {
|
||||
const stream = store.create(item)
|
||||
|
||||
return playlist
|
||||
}
|
||||
|
||||
playlist.link = async function (url, title, attrs, vlcOpts) {
|
||||
let link = `#EXTINF:-1`
|
||||
for (const name in attrs) {
|
||||
const value = attrs[name]
|
||||
if (value !== undefined) {
|
||||
link += ` ${name}="${value}"`
|
||||
let attrs
|
||||
if (options.public) {
|
||||
attrs = {
|
||||
'tvg-id': stream.get('tvg_id'),
|
||||
'tvg-country': stream.get('tvg_country'),
|
||||
'tvg-language': stream.get('tvg_language'),
|
||||
'tvg-logo': stream.get('tvg_logo'),
|
||||
'user-agent': stream.get('http.user-agent') || undefined,
|
||||
'group-title': stream.get('group_title')
|
||||
}
|
||||
} else {
|
||||
attrs = {
|
||||
'tvg-id': stream.get('tvg_id'),
|
||||
'user-agent': stream.get('http.user-agent') || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
link += `,${title}\n`
|
||||
for (const name in vlcOpts) {
|
||||
const value = vlcOpts[name]
|
||||
if (value !== undefined) {
|
||||
link += `#EXTVLCOPT:${name}=${value}\n`
|
||||
}
|
||||
}
|
||||
link += `${url}\n`
|
||||
|
||||
await file.append(playlist.filepath, link)
|
||||
p.add(stream.get('url'), stream.get('title'), attrs, {
|
||||
'http-referrer': stream.get('http.referrer') || undefined,
|
||||
'http-user-agent': stream.get('http.user-agent') || undefined
|
||||
})
|
||||
}
|
||||
|
||||
return playlist
|
||||
return p
|
||||
}
|
||||
|
||||
module.exports = playlist
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
codes.json
|
|
@ -1,147 +0,0 @@
|
|||
{
|
||||
"auto": {
|
||||
"name": "Auto",
|
||||
"slug": "auto",
|
||||
"nsfw": false
|
||||
},
|
||||
"animation": {
|
||||
"name": "Animation",
|
||||
"slug": "animation",
|
||||
"nsfw": false
|
||||
},
|
||||
"business": {
|
||||
"name": "Business",
|
||||
"slug": "business",
|
||||
"nsfw": false
|
||||
},
|
||||
"classic": {
|
||||
"name": "Classic",
|
||||
"slug": "classic",
|
||||
"nsfw": false
|
||||
},
|
||||
"comedy": {
|
||||
"name": "Comedy",
|
||||
"slug": "comedy",
|
||||
"nsfw": false
|
||||
},
|
||||
"cooking": {
|
||||
"name": "Cooking",
|
||||
"slug": "cooking",
|
||||
"nsfw": false
|
||||
},
|
||||
"culture": {
|
||||
"name": "Culture",
|
||||
"slug": "culture",
|
||||
"nsfw": false
|
||||
},
|
||||
"documentary": {
|
||||
"name": "Documentary",
|
||||
"slug": "documentary",
|
||||
"nsfw": false
|
||||
},
|
||||
"education": {
|
||||
"name": "Education",
|
||||
"slug": "education",
|
||||
"nsfw": false
|
||||
},
|
||||
"entertainment": {
|
||||
"name": "Entertainment",
|
||||
"slug": "entertainment",
|
||||
"nsfw": false
|
||||
},
|
||||
"family": {
|
||||
"name": "Family",
|
||||
"slug": "family",
|
||||
"nsfw": false
|
||||
},
|
||||
"general": {
|
||||
"name": "General",
|
||||
"slug": "general",
|
||||
"nsfw": false
|
||||
},
|
||||
"kids": {
|
||||
"name": "Kids",
|
||||
"slug": "kids",
|
||||
"nsfw": false
|
||||
},
|
||||
"legislative": {
|
||||
"name": "Legislative",
|
||||
"slug": "legislative",
|
||||
"nsfw": false
|
||||
},
|
||||
"lifestyle": {
|
||||
"name": "Lifestyle",
|
||||
"slug": "lifestyle",
|
||||
"nsfw": false
|
||||
},
|
||||
"local": {
|
||||
"name": "Local",
|
||||
"slug": "local",
|
||||
"nsfw": false
|
||||
},
|
||||
"movies": {
|
||||
"name": "Movies",
|
||||
"slug": "movies",
|
||||
"nsfw": false
|
||||
},
|
||||
"music": {
|
||||
"name": "Music",
|
||||
"slug": "music",
|
||||
"nsfw": false
|
||||
},
|
||||
"news": {
|
||||
"name": "News",
|
||||
"slug": "news",
|
||||
"nsfw": false
|
||||
},
|
||||
"outdoor": {
|
||||
"name": "Outdoor",
|
||||
"slug": "outdoor",
|
||||
"nsfw": false
|
||||
},
|
||||
"relax": {
|
||||
"name": "Relax",
|
||||
"slug": "relax",
|
||||
"nsfw": false
|
||||
},
|
||||
"religious": {
|
||||
"name": "Religious",
|
||||
"slug": "religious",
|
||||
"nsfw": false
|
||||
},
|
||||
"series": {
|
||||
"name": "Series",
|
||||
"slug": "series",
|
||||
"nsfw": false
|
||||
},
|
||||
"science": {
|
||||
"name": "Science",
|
||||
"slug": "science",
|
||||
"nsfw": false
|
||||
},
|
||||
"shop": {
|
||||
"name": "Shop",
|
||||
"slug": "shop",
|
||||
"nsfw": false
|
||||
},
|
||||
"sports": {
|
||||
"name": "Sports",
|
||||
"slug": "sports",
|
||||
"nsfw": false
|
||||
},
|
||||
"travel": {
|
||||
"name": "Travel",
|
||||
"slug": "travel",
|
||||
"nsfw": false
|
||||
},
|
||||
"weather": {
|
||||
"name": "Weather",
|
||||
"slug": "weather",
|
||||
"nsfw": false
|
||||
},
|
||||
"xxx": {
|
||||
"name": "XXX",
|
||||
"slug": "xxx",
|
||||
"nsfw": true
|
||||
}
|
||||
}
|
|
@ -1,264 +0,0 @@
|
|||
{
|
||||
"AD": { "name": "Andorra", "code": "AD", "lang": "cat" },
|
||||
"AE": { "name": "United Arab Emirates", "code": "AE", "lang": "ara" },
|
||||
"AF": { "name": "Afghanistan", "code": "AF", "lang": "pus" },
|
||||
"AG": { "name": "Antigua and Barbuda", "code": "AG", "lang": "eng" },
|
||||
"AI": { "name": "Anguilla", "code": "AI", "lang": "eng" },
|
||||
"AL": { "name": "Albania", "code": "AL", "lang": "sqi" },
|
||||
"AM": { "name": "Armenia", "code": "AM", "lang": "hye" },
|
||||
"AO": { "name": "Angola", "code": "AO", "lang": "por" },
|
||||
"AQ": { "name": "Antarctica", "code": "AQ", "lang": null },
|
||||
"AR": { "name": "Argentina", "code": "AR", "lang": "spa" },
|
||||
"AS": { "name": "American Samoa", "code": "AS", "lang": "eng" },
|
||||
"AT": { "name": "Austria", "code": "AT", "lang": "deu" },
|
||||
"AU": { "name": "Australia", "code": "AU", "lang": "eng" },
|
||||
"AW": { "name": "Aruba", "code": "AW", "lang": "nld" },
|
||||
"AX": { "name": "Åland", "code": "AX", "lang": "swe" },
|
||||
"AZ": { "name": "Azerbaijan", "code": "AZ", "lang": "aze" },
|
||||
"BA": { "name": "Bosnia and Herzegovina", "code": "BA", "lang": "bos" },
|
||||
"BB": { "name": "Barbados", "code": "BB", "lang": "eng" },
|
||||
"BD": { "name": "Bangladesh", "code": "BD", "lang": "ben" },
|
||||
"BE": { "name": "Belgium", "code": "BE", "lang": "nld" },
|
||||
"BF": { "name": "Burkina Faso", "code": "BF", "lang": "fra" },
|
||||
"BG": { "name": "Bulgaria", "code": "BG", "lang": "bul" },
|
||||
"BH": { "name": "Bahrain", "code": "BH", "lang": "ara" },
|
||||
"BI": { "name": "Burundi", "code": "BI", "lang": "fra" },
|
||||
"BJ": { "name": "Benin", "code": "BJ", "lang": "fra" },
|
||||
"BL": { "name": "Saint Barthélemy", "code": "BL", "lang": "fra" },
|
||||
"BM": { "name": "Bermuda", "code": "BM", "lang": "eng" },
|
||||
"BN": { "name": "Brunei", "code": "BN", "lang": "msa" },
|
||||
"BO": { "name": "Bolivia", "code": "BO", "lang": "spa" },
|
||||
"BQ": { "name": "Bonaire", "code": "BQ", "lang": "nld" },
|
||||
"BR": { "name": "Brazil", "code": "BR", "lang": "por" },
|
||||
"BS": { "name": "Bahamas", "code": "BS", "lang": "eng" },
|
||||
"BT": { "name": "Bhutan", "code": "BT", "lang": "dzo" },
|
||||
"BV": { "name": "Bouvet Island", "code": "BV", "lang": "nor" },
|
||||
"BW": { "name": "Botswana", "code": "BW", "lang": "eng" },
|
||||
"BY": { "name": "Belarus", "code": "BY", "lang": "bel" },
|
||||
"BZ": { "name": "Belize", "code": "BZ", "lang": "eng" },
|
||||
"CA": { "name": "Canada", "code": "CA", "lang": "eng" },
|
||||
"CC": { "name": "Cocos [Keeling] Islands", "code": "CC", "lang": "eng" },
|
||||
"CD": {
|
||||
"name": "Democratic Republic of the Congo",
|
||||
"code": "CD",
|
||||
"lang": "fra"
|
||||
},
|
||||
"CF": { "name": "Central African Republic", "code": "CF", "lang": "fra" },
|
||||
"CG": { "name": "Republic of the Congo", "code": "CG", "lang": "fra" },
|
||||
"CH": { "name": "Switzerland", "code": "CH", "lang": "deu" },
|
||||
"CI": { "name": "Ivory Coast", "code": "CI", "lang": "fra" },
|
||||
"CK": { "name": "Cook Islands", "code": "CK", "lang": "eng" },
|
||||
"CL": { "name": "Chile", "code": "CL", "lang": "spa" },
|
||||
"CM": { "name": "Cameroon", "code": "CM", "lang": "eng" },
|
||||
"CN": { "name": "China", "code": "CN", "lang": "zho" },
|
||||
"CO": { "name": "Colombia", "code": "CO", "lang": "spa" },
|
||||
"CR": { "name": "Costa Rica", "code": "CR", "lang": "spa" },
|
||||
"CU": { "name": "Cuba", "code": "CU", "lang": "spa" },
|
||||
"CV": { "name": "Cape Verde", "code": "CV", "lang": "por" },
|
||||
"CW": { "name": "Curacao", "code": "CW", "lang": "nld" },
|
||||
"CX": { "name": "Christmas Island", "code": "CX", "lang": "eng" },
|
||||
"CY": { "name": "Cyprus", "code": "CY", "lang": "ell" },
|
||||
"CZ": { "name": "Czech Republic", "code": "CZ", "lang": "ces" },
|
||||
"DE": { "name": "Germany", "code": "DE", "lang": "deu" },
|
||||
"DJ": { "name": "Djibouti", "code": "DJ", "lang": "fra" },
|
||||
"DK": { "name": "Denmark", "code": "DK", "lang": "dan" },
|
||||
"DM": { "name": "Dominica", "code": "DM", "lang": "eng" },
|
||||
"DO": { "name": "Dominican Republic", "code": "DO", "lang": "spa" },
|
||||
"DZ": { "name": "Algeria", "code": "DZ", "lang": "ara" },
|
||||
"EC": { "name": "Ecuador", "code": "EC", "lang": "spa" },
|
||||
"EE": { "name": "Estonia", "code": "EE", "lang": "est" },
|
||||
"EG": { "name": "Egypt", "code": "EG", "lang": "ara" },
|
||||
"EH": { "name": "Western Sahara", "code": "EH", "lang": "spa" },
|
||||
"ER": { "name": "Eritrea", "code": "ER", "lang": "tir" },
|
||||
"ES": { "name": "Spain", "code": "ES", "lang": "spa" },
|
||||
"ET": { "name": "Ethiopia", "code": "ET", "lang": "amh" },
|
||||
"FI": { "name": "Finland", "code": "FI", "lang": "fin" },
|
||||
"FJ": { "name": "Fiji", "code": "FJ", "lang": "eng" },
|
||||
"FK": { "name": "Falkland Islands", "code": "FK", "lang": "eng" },
|
||||
"FM": { "name": "Micronesia", "code": "FM", "lang": "eng" },
|
||||
"FO": { "name": "Faroe Islands", "code": "FO", "lang": "fao" },
|
||||
"FR": { "name": "France", "code": "FR", "lang": "fra" },
|
||||
"GA": { "name": "Gabon", "code": "GA", "lang": "fra" },
|
||||
"UK": { "name": "United Kingdom", "code": "UK", "lang": "eng" },
|
||||
"GD": { "name": "Grenada", "code": "GD", "lang": "eng" },
|
||||
"GE": { "name": "Georgia", "code": "GE", "lang": "kat" },
|
||||
"GF": { "name": "French Guiana", "code": "GF", "lang": "fra" },
|
||||
"GG": { "name": "Guernsey", "code": "GG", "lang": "eng" },
|
||||
"GH": { "name": "Ghana", "code": "GH", "lang": "eng" },
|
||||
"GI": { "name": "Gibraltar", "code": "GI", "lang": "eng" },
|
||||
"GL": { "name": "Greenland", "code": "GL", "lang": "kal" },
|
||||
"GM": { "name": "Gambia", "code": "GM", "lang": "eng" },
|
||||
"GN": { "name": "Guinea", "code": "GN", "lang": "fra" },
|
||||
"GP": { "name": "Guadeloupe", "code": "GP", "lang": "fra" },
|
||||
"GQ": { "name": "Equatorial Guinea", "code": "GQ", "lang": "spa" },
|
||||
"GR": { "name": "Greece", "code": "GR", "lang": "ell" },
|
||||
"GS": {
|
||||
"name": "South Georgia and the South Sandwich Islands",
|
||||
"code": "GS",
|
||||
"lang": "eng"
|
||||
},
|
||||
"GT": { "name": "Guatemala", "code": "GT", "lang": "spa" },
|
||||
"GU": { "name": "Guam", "code": "GU", "lang": "eng" },
|
||||
"GW": { "name": "Guinea-Bissau", "code": "GW", "lang": "por" },
|
||||
"GY": { "name": "Guyana", "code": "GY", "lang": "eng" },
|
||||
"HK": { "name": "Hong Kong", "code": "HK", "lang": "zho" },
|
||||
"HM": { "name": "Heard Island and McDonald Islands", "code": "HM", "lang": "eng" },
|
||||
"HN": { "name": "Honduras", "code": "HN", "lang": "spa" },
|
||||
"HR": { "name": "Croatia", "code": "HR", "lang": "hrv" },
|
||||
"HT": { "name": "Haiti", "code": "HT", "lang": "fra" },
|
||||
"HU": { "name": "Hungary", "code": "HU", "lang": "hun" },
|
||||
"ID": { "name": "Indonesia", "code": "ID", "lang": "ind" },
|
||||
"IE": { "name": "Ireland", "code": "IE", "lang": "gle" },
|
||||
"IL": { "name": "Israel", "code": "IL", "lang": "heb" },
|
||||
"IM": { "name": "Isle of Man", "code": "IM", "lang": "eng" },
|
||||
"IN": { "name": "India", "code": "IN", "lang": "hin" },
|
||||
"IO": { "name": "British Indian Ocean Territory", "code": "IO", "lang": "eng" },
|
||||
"IQ": { "name": "Iraq", "code": "IQ", "lang": "ara" },
|
||||
"IR": { "name": "Iran", "code": "IR", "lang": "fas" },
|
||||
"IS": { "name": "Iceland", "code": "IS", "lang": "isl" },
|
||||
"IT": { "name": "Italy", "code": "IT", "lang": "ita" },
|
||||
"JE": { "name": "Jersey", "code": "JE", "lang": "eng" },
|
||||
"JM": { "name": "Jamaica", "code": "JM", "lang": "eng" },
|
||||
"JO": { "name": "Jordan", "code": "JO", "lang": "ara" },
|
||||
"JP": { "name": "Japan", "code": "JP", "lang": "jpn" },
|
||||
"KE": { "name": "Kenya", "code": "KE", "lang": "eng" },
|
||||
"KG": { "name": "Kyrgyzstan", "code": "KG", "lang": "kir" },
|
||||
"KH": { "name": "Cambodia", "code": "KH", "lang": "khm" },
|
||||
"KI": { "name": "Kiribati", "code": "KI", "lang": "eng" },
|
||||
"KM": { "name": "Comoros", "code": "KM", "lang": "ara" },
|
||||
"KN": { "name": "Saint Kitts and Nevis", "code": "KN", "lang": "eng" },
|
||||
"KP": { "name": "North Korea", "code": "KP", "lang": "kor" },
|
||||
"KR": { "name": "South Korea", "code": "KR", "lang": "kor" },
|
||||
"KW": { "name": "Kuwait", "code": "KW", "lang": "ara" },
|
||||
"KY": { "name": "Cayman Islands", "code": "KY", "lang": "eng" },
|
||||
"KZ": { "name": "Kazakhstan", "code": "KZ", "lang": "kaz" },
|
||||
"LA": { "name": "Laos", "code": "LA", "lang": "lao" },
|
||||
"LB": { "name": "Lebanon", "code": "LB", "lang": "ara" },
|
||||
"LC": { "name": "Saint Lucia", "code": "LC", "lang": "eng" },
|
||||
"LI": { "name": "Liechtenstein", "code": "LI", "lang": "deu" },
|
||||
"LK": { "name": "Sri Lanka", "code": "LK", "lang": "sin" },
|
||||
"LR": { "name": "Liberia", "code": "LR", "lang": "eng" },
|
||||
"LS": { "name": "Lesotho", "code": "LS", "lang": "eng" },
|
||||
"LT": { "name": "Lithuania", "code": "LT", "lang": "lit" },
|
||||
"LU": { "name": "Luxembourg", "code": "LU", "lang": "fra" },
|
||||
"LV": { "name": "Latvia", "code": "LV", "lang": "lav" },
|
||||
"LY": { "name": "Libya", "code": "LY", "lang": "ara" },
|
||||
"MA": { "name": "Morocco", "code": "MA", "lang": "ara" },
|
||||
"MC": { "name": "Monaco", "code": "MC", "lang": "fra" },
|
||||
"MD": { "name": "Moldova", "code": "MD", "lang": "ron" },
|
||||
"ME": { "name": "Montenegro", "code": "ME", "lang": "srp" },
|
||||
"MF": { "name": "Saint Martin", "code": "MF", "lang": "eng" },
|
||||
"MG": { "name": "Madagascar", "code": "MG", "lang": "fra" },
|
||||
"MH": { "name": "Marshall Islands", "code": "MH", "lang": "eng" },
|
||||
"MK": { "name": "North Macedonia", "code": "MK", "lang": "mkd" },
|
||||
"ML": { "name": "Mali", "code": "ML", "lang": "fra" },
|
||||
"MM": { "name": "Myanmar [Burma]", "code": "MM", "lang": "mya" },
|
||||
"MN": { "name": "Mongolia", "code": "MN", "lang": "mon" },
|
||||
"MO": { "name": "Macao", "code": "MO", "lang": "zho" },
|
||||
"MP": { "name": "Northern Mariana Islands", "code": "MP", "lang": "eng" },
|
||||
"MQ": { "name": "Martinique", "code": "MQ", "lang": "fra" },
|
||||
"MR": { "name": "Mauritania", "code": "MR", "lang": "ara" },
|
||||
"MS": { "name": "Montserrat", "code": "MS", "lang": "eng" },
|
||||
"MT": { "name": "Malta", "code": "MT", "lang": "mlt" },
|
||||
"MU": { "name": "Mauritius", "code": "MU", "lang": "eng" },
|
||||
"MV": { "name": "Maldives", "code": "MV", "lang": "div" },
|
||||
"MW": { "name": "Malawi", "code": "MW", "lang": "eng" },
|
||||
"MX": { "name": "Mexico", "code": "MX", "lang": "spa" },
|
||||
"MY": { "name": "Malaysia", "code": "MY", "lang": "msa" },
|
||||
"MZ": { "name": "Mozambique", "code": "MZ", "lang": "por" },
|
||||
"NA": { "name": "Namibia", "code": "NA", "lang": "eng" },
|
||||
"NC": { "name": "New Caledonia", "code": "NC", "lang": "fra" },
|
||||
"NE": { "name": "Niger", "code": "NE", "lang": "fra" },
|
||||
"NF": { "name": "Norfolk Island", "code": "NF", "lang": "eng" },
|
||||
"NG": { "name": "Nigeria", "code": "NG", "lang": "eng" },
|
||||
"NI": { "name": "Nicaragua", "code": "NI", "lang": "spa" },
|
||||
"NL": { "name": "Netherlands", "code": "NL", "lang": "nld" },
|
||||
"NO": { "name": "Norway", "code": "NO", "lang": "nor" },
|
||||
"NP": { "name": "Nepal", "code": "NP", "lang": "nep" },
|
||||
"NR": { "name": "Nauru", "code": "NR", "lang": "eng" },
|
||||
"NU": { "name": "Niue", "code": "NU", "lang": "eng" },
|
||||
"NZ": { "name": "New Zealand", "code": "NZ", "lang": "eng" },
|
||||
"OM": { "name": "Oman", "code": "OM", "lang": "ara" },
|
||||
"PA": { "name": "Panama", "code": "PA", "lang": "spa" },
|
||||
"PE": { "name": "Peru", "code": "PE", "lang": "spa" },
|
||||
"PF": { "name": "French Polynesia", "code": "PF", "lang": "fra" },
|
||||
"PG": { "name": "Papua New Guinea", "code": "PG", "lang": "eng" },
|
||||
"PH": { "name": "Philippines", "code": "PH", "lang": "eng" },
|
||||
"PK": { "name": "Pakistan", "code": "PK", "lang": "eng" },
|
||||
"PL": { "name": "Poland", "code": "PL", "lang": "pol" },
|
||||
"PM": { "name": "Saint Pierre and Miquelon", "code": "PM", "lang": "fra" },
|
||||
"PN": { "name": "Pitcairn Islands", "code": "PN", "lang": "eng" },
|
||||
"PR": { "name": "Puerto Rico", "code": "PR", "lang": "spa" },
|
||||
"PS": { "name": "Palestine", "code": "PS", "lang": "ara" },
|
||||
"PT": { "name": "Portugal", "code": "PT", "lang": "por" },
|
||||
"PW": { "name": "Palau", "code": "PW", "lang": "eng" },
|
||||
"PY": { "name": "Paraguay", "code": "PY", "lang": "spa" },
|
||||
"QA": { "name": "Qatar", "code": "QA", "lang": "ara" },
|
||||
"RE": { "name": "Réunion", "code": "RE", "lang": "fra" },
|
||||
"RO": { "name": "Romania", "code": "RO", "lang": "ron" },
|
||||
"RS": { "name": "Serbia", "code": "RS", "lang": "srp" },
|
||||
"RU": { "name": "Russia", "code": "RU", "lang": "rus" },
|
||||
"RW": { "name": "Rwanda", "code": "RW", "lang": "kin" },
|
||||
"SA": { "name": "Saudi Arabia", "code": "SA", "lang": "ara" },
|
||||
"SB": { "name": "Solomon Islands", "code": "SB", "lang": "eng" },
|
||||
"SC": { "name": "Seychelles", "code": "SC", "lang": "fra" },
|
||||
"SD": { "name": "Sudan", "code": "SD", "lang": "ara" },
|
||||
"SE": { "name": "Sweden", "code": "SE", "lang": "swe" },
|
||||
"SG": { "name": "Singapore", "code": "SG", "lang": "eng" },
|
||||
"SH": { "name": "Saint Helena", "code": "SH", "lang": "eng" },
|
||||
"SI": { "name": "Slovenia", "code": "SI", "lang": "slv" },
|
||||
"SJ": { "name": "Svalbard and Jan Mayen", "code": "SJ", "lang": "nor" },
|
||||
"SK": { "name": "Slovakia", "code": "SK", "lang": "slk" },
|
||||
"SL": { "name": "Sierra Leone", "code": "SL", "lang": "eng" },
|
||||
"SM": { "name": "San Marino", "code": "SM", "lang": "ita" },
|
||||
"SN": { "name": "Senegal", "code": "SN", "lang": "fra" },
|
||||
"SO": { "name": "Somalia", "code": "SO", "lang": "som" },
|
||||
"SR": { "name": "Suriname", "code": "SR", "lang": "nld" },
|
||||
"SS": { "name": "South Sudan", "code": "SS", "lang": "eng" },
|
||||
"ST": { "name": "São Tomé and Príncipe", "code": "ST", "lang": "por" },
|
||||
"SV": { "name": "El Salvador", "code": "SV", "lang": "spa" },
|
||||
"SX": { "name": "Sint Maarten", "code": "SX", "lang": "nld" },
|
||||
"SY": { "name": "Syria", "code": "SY", "lang": "ara" },
|
||||
"SZ": { "name": "Swaziland", "code": "SZ", "lang": "eng" },
|
||||
"TC": { "name": "Turks and Caicos Islands", "code": "TC", "lang": "eng" },
|
||||
"TD": { "name": "Chad", "code": "TD", "lang": "fra" },
|
||||
"TF": { "name": "French Southern Territories", "code": "TF", "lang": "fra" },
|
||||
"TG": { "name": "Togo", "code": "TG", "lang": "fra" },
|
||||
"TH": { "name": "Thailand", "code": "TH", "lang": "tha" },
|
||||
"TJ": { "name": "Tajikistan", "code": "TJ", "lang": "tgk" },
|
||||
"TK": { "name": "Tokelau", "code": "TK", "lang": "eng" },
|
||||
"TL": { "name": "East Timor", "code": "TL", "lang": "por" },
|
||||
"TM": { "name": "Turkmenistan", "code": "TM", "lang": "tuk" },
|
||||
"TN": { "name": "Tunisia", "code": "TN", "lang": "ara" },
|
||||
"TO": { "name": "Tonga", "code": "TO", "lang": "eng" },
|
||||
"TR": { "name": "Turkey", "code": "TR", "lang": "tur" },
|
||||
"TT": { "name": "Trinidad and Tobago", "code": "TT", "lang": "eng" },
|
||||
"TV": { "name": "Tuvalu", "code": "TV", "lang": "eng" },
|
||||
"TW": { "name": "Taiwan", "code": "TW", "lang": "zho" },
|
||||
"TZ": { "name": "Tanzania", "code": "TZ", "lang": "swa" },
|
||||
"UA": { "name": "Ukraine", "code": "UA", "lang": "ukr" },
|
||||
"UG": { "name": "Uganda", "code": "UG", "lang": "eng" },
|
||||
"UM": { "name": "U.S. Minor Outlying Islands", "code": "UM", "lang": "eng" },
|
||||
"US": { "name": "United States", "code": "US", "lang": "eng" },
|
||||
"UY": { "name": "Uruguay", "code": "UY", "lang": "spa" },
|
||||
"UZ": { "name": "Uzbekistan", "code": "UZ", "lang": "uzb" },
|
||||
"VA": { "name": "Vatican City", "code": "VA", "lang": "ita" },
|
||||
"VC": { "name": "Saint Vincent and the Grenadines", "code": "VC", "lang": "eng" },
|
||||
"VE": { "name": "Venezuela", "code": "VE", "lang": "spa" },
|
||||
"VG": { "name": "British Virgin Islands", "code": "VG", "lang": "eng" },
|
||||
"VI": { "name": "U.S. Virgin Islands", "code": "VI", "lang": "eng" },
|
||||
"VN": { "name": "Vietnam", "code": "VN", "lang": "vie" },
|
||||
"VU": { "name": "Vanuatu", "code": "VU", "lang": "bis" },
|
||||
"WF": { "name": "Wallis and Futuna", "code": "WF", "lang": "fra" },
|
||||
"WS": { "name": "Samoa", "code": "WS", "lang": "smo" },
|
||||
"XK": { "name": "Kosovo", "code": "XK", "lang": "sqi" },
|
||||
"YE": { "name": "Yemen", "code": "YE", "lang": "ara" },
|
||||
"YT": { "name": "Mayotte", "code": "YT", "lang": "fra" },
|
||||
"ZA": {
|
||||
"name": "South Africa",
|
||||
"code": "ZA",
|
||||
"lang": "afr"
|
||||
},
|
||||
"ZM": { "name": "Zambia", "code": "ZM", "lang": "eng" },
|
||||
"ZW": { "name": "Zimbabwe", "code": "ZW", "lang": "eng" }
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,47 @@
|
|||
const { create: createPlaylist } = require('../core/playlist')
|
||||
const api = require('../core/api')
|
||||
const file = require('../core/file')
|
||||
const _ = require('lodash')
|
||||
|
||||
const PUBLIC_DIR = process.env.PUBLIC_DIR || '.gh-pages'
|
||||
|
||||
module.exports = async function (streams = []) {
|
||||
const logs = []
|
||||
|
||||
await api.categories.load()
|
||||
const categories = await api.categories.all()
|
||||
|
||||
for (const category of categories) {
|
||||
let output = _.filter(streams, { channel: { categories: [category.id] } })
|
||||
output = _.orderBy(
|
||||
output,
|
||||
['channel.name', 'status.level', 'resolution.height'],
|
||||
['asc', 'asc', 'desc']
|
||||
)
|
||||
output = _.uniqBy(output, s => s.channel_id || _.uniqueId())
|
||||
|
||||
const playlist = createPlaylist(output, { public: true })
|
||||
await file.create(`${PUBLIC_DIR}/categories/${category.id}.m3u`, playlist.toString())
|
||||
|
||||
logs.push({ id: category.id, count: output.length })
|
||||
}
|
||||
|
||||
let output = _.filter(streams, s => !s.categories.length)
|
||||
output = _.orderBy(
|
||||
output,
|
||||
['channel.name', 'status.level', 'resolution.height'],
|
||||
['asc', 'asc', 'desc']
|
||||
)
|
||||
output = _.uniqBy(output, s => s.channel_id || _.uniqueId())
|
||||
output = output.map(item => {
|
||||
item.group_title = 'Other'
|
||||
return item
|
||||
})
|
||||
|
||||
const playlist = createPlaylist(output, { public: true })
|
||||
await file.create(`${PUBLIC_DIR}/categories/other.m3u`, playlist.toString())
|
||||
|
||||
logs.push({ id: 'other', count: output.length })
|
||||
|
||||
return logs
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
exports.categories = require('./categories')
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = function () {
|
||||
if (this.group_title) return this.group_title
|
||||
if (this.group_title !== undefined) return this.group_title
|
||||
|
||||
if (Array.isArray(this.categories)) {
|
||||
return this.categories
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
exports.group_title = require('./group_title')
|
||||
exports.display_name = require('./display_name')
|
||||
exports.title = require('./title')
|
||||
exports.tvg_country = require('./tvg_country')
|
||||
exports.tvg_id = require('./tvg_id')
|
||||
exports.tvg_language = require('./tvg_language')
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = function () {
|
||||
let title = this.title
|
||||
let title = this.channel_name
|
||||
|
||||
if (this.resolution.height) {
|
||||
title += ` (${this.resolution.height}p)`
|
|
@ -1,5 +1,3 @@
|
|||
module.exports = function () {
|
||||
if (this.tvg_country) return this.tvg_country
|
||||
|
||||
return Array.isArray(this.countries) ? this.countries.map(i => i.code).join(';') : ''
|
||||
return Array.isArray(this.broadcast_area) ? this.broadcast_area.join(';') : ''
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = function () {
|
||||
return this.id || ''
|
||||
return this.channel_id || ''
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = function () {
|
||||
return this.logo || ''
|
||||
return this.channel && this.channel.logo ? this.channel.logo : ''
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
const { parser } = require('../../core')
|
||||
|
||||
module.exports = function ({ title }) {
|
||||
return parser.parseChannelName(title)
|
||||
return title
|
||||
.trim()
|
||||
.split(' ')
|
||||
.map(s => s.trim())
|
||||
.filter(s => {
|
||||
return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s)
|
||||
})
|
||||
.join(' ')
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml"
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-country="RU" tvg-language="Russian" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General;Legislative",ЛДПР ТВ (1080p)
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-country="RU" tvg-language="Russian" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p)
|
||||
http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml"
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-country="RU" tvg-language="Russian" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General;Legislative",ЛДПР ТВ (1080p)
|
||||
http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8
|
||||
#EXTM3U x-tvg-url=""
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#EXTM3U x-tvg-url=""
|
||||
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-country="UK" tvg-language="English" tvg-logo="https://i.imgur.com/eNPIQ9f.png" group-title="News",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ch/tv.blue.ch.epg.xml,https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml,https://iptv-org.github.io/epg/guides/uk/ontvtonight.com.epg.xml,https://iptv-org.github.io/epg/guides/uk/sky.com.epg.xml"
|
||||
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-country="INT" tvg-language="English" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#EXTM3U x-tvg-url=""
|
||||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ad/andorradifusio.ad.epg.xml"
|
||||
#EXTINF:-1 tvg-id="AndorraTV.ad" tvg-country="AD" tvg-language="Valencian" tvg-logo="" group-title="Other",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="" tvg-country="" tvg-language="" tvg-logo="" group-title="Other",Tastemade
|
||||
https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="" tvg-country="" tvg-language="" tvg-logo="" group-title="Other",Daawah TV
|
||||
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml"
|
||||
#EXTINF:-1 tvg-id="" tvg-country="INT" tvg-language="" tvg-logo="" group-title="Cooking",Tastemade
|
||||
https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-country="RU" tvg-language="Russian" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General;Legislative",ЛДПР ТВ (1080p)
|
||||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ch/tv.blue.ch.epg.xml,https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml,https://iptv-org.github.io/epg/guides/uk/ontvtonight.com.epg.xml,https://iptv-org.github.io/epg/guides/uk/sky.com.epg.xml"
|
||||
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-country="INT" tvg-language="English" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="LDPRTV.ru" tvg-country="RU" tvg-language="Russian" tvg-logo="https://iptvx.one/icn/ldpr-tv.png" group-title="General",ЛДПР ТВ (1080p)
|
||||
http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="VisitXTV.nl" tvg-country="INT" tvg-language="Flemish" tvg-logo="https://i.imgur.com/RJ9wbNF.jpg" group-title="XXX",Visit-X TV
|
||||
https://stream.visit-x.tv/vxtv/ngrp:live_all/playlist.m3u8
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#EXTM3U x-tvg-url=""
|
||||
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-country="UK" tvg-language="English" tvg-logo="https://i.imgur.com/eNPIQ9f.png" group-title="News",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8
|
||||
#EXTINF:-1 tvg-id="" tvg-country="INT" tvg-language="" tvg-logo="" group-title="Cooking",Tastemade
|
||||
https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8
|
||||
#EXTM3U x-tvg-url="https://iptv-org.github.io/epg/guides/ch/tv.blue.ch.epg.xml,https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml,https://iptv-org.github.io/epg/guides/uk/ontvtonight.com.epg.xml,https://iptv-org.github.io/epg/guides/uk/sky.com.epg.xml"
|
||||
#EXTINF:-1 tvg-id="BBCNews.uk" tvg-country="INT" tvg-language="English" tvg-logo="https://raw.githubusercontent.com/Tapiosinn/tv-logos/master/countries/united-kingdom/bbc-news-uk.png" group-title="News",BBC News HD (720p) [Not 24/7]
|
||||
http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="VisitXTV.nl" tvg-country="INT" tvg-language="Flemish" tvg-logo="https://i.imgur.com/RJ9wbNF.jpg" group-title="XXX",Visit-X TV
|
||||
https://stream.visit-x.tv/vxtv/ngrp:live_all/playlist.m3u8
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#EXTM3U x-tvg-url=""
|
||||
#EXTINF:-1 tvg-id="" tvg-country="" tvg-language="" tvg-logo="" group-title="Undefined",Tastemade
|
||||
https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8
|
||||
#EXTINF:-1 tvg-id="" tvg-country="" tvg-language="" tvg-logo="" group-title="Undefined",Daawah TV
|
||||
http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
{"name":"Cooking","slug":"cooking","count":1}
|
||||
{"name":"General","slug":"general","count":1}
|
||||
{"name":"Legislative","slug":"legislative","count":1}
|
||||
{"name":"News","slug":"news","count":1}
|
||||
{"name":"XXX","slug":"xxx","count":1}
|
||||
{"name":"Other","slug":"other","count":1}
|
|
@ -1,4 +0,0 @@
|
|||
{"name":"Andorra","code":"AD","count":1}
|
||||
{"name":"Russia","code":"RU","count":2}
|
||||
{"name":"United Kingdom","code":"UK","count":2}
|
||||
{"name":"Undefined","code":"UNDEFINED","count":1}
|
|
@ -0,0 +1,29 @@
|
|||
{"id":"auto","count":0}
|
||||
{"id":"animation","count":0}
|
||||
{"id":"business","count":0}
|
||||
{"id":"classic","count":0}
|
||||
{"id":"comedy","count":0}
|
||||
{"id":"cooking","count":0}
|
||||
{"id":"culture","count":0}
|
||||
{"id":"documentary","count":0}
|
||||
{"id":"education","count":0}
|
||||
{"id":"entertainment","count":0}
|
||||
{"id":"family","count":0}
|
||||
{"id":"general","count":1}
|
||||
{"id":"kids","count":0}
|
||||
{"id":"legislative","count":0}
|
||||
{"id":"lifestyle","count":0}
|
||||
{"id":"movies","count":0}
|
||||
{"id":"music","count":0}
|
||||
{"id":"news","count":1}
|
||||
{"id":"outdoor","count":0}
|
||||
{"id":"relax","count":0}
|
||||
{"id":"religious","count":0}
|
||||
{"id":"series","count":0}
|
||||
{"id":"science","count":0}
|
||||
{"id":"shop","count":0}
|
||||
{"id":"sports","count":0}
|
||||
{"id":"travel","count":0}
|
||||
{"id":"weather","count":0}
|
||||
{"id":"xxx","count":1}
|
||||
{"id":"other","count":3}
|
|
@ -0,0 +1,251 @@
|
|||
{"name":"Afghanistan","code":"AF","count":2}
|
||||
{"name":"Albania","code":"AL","count":2}
|
||||
{"name":"Algeria","code":"DZ","count":2}
|
||||
{"name":"American Samoa","code":"AS","count":2}
|
||||
{"name":"Andorra","code":"AD","count":3}
|
||||
{"name":"Angola","code":"AO","count":2}
|
||||
{"name":"Anguilla","code":"AI","count":2}
|
||||
{"name":"Antarctica","code":"AQ","count":2}
|
||||
{"name":"Antigua and Barbuda","code":"AG","count":2}
|
||||
{"name":"Argentina","code":"AR","count":2}
|
||||
{"name":"Armenia","code":"AM","count":2}
|
||||
{"name":"Aruba","code":"AW","count":2}
|
||||
{"name":"Australia","code":"AU","count":2}
|
||||
{"name":"Austria","code":"AT","count":2}
|
||||
{"name":"Azerbaijan","code":"AZ","count":2}
|
||||
{"name":"Bahamas","code":"BS","count":2}
|
||||
{"name":"Bahrain","code":"BH","count":2}
|
||||
{"name":"Bangladesh","code":"BD","count":2}
|
||||
{"name":"Barbados","code":"BB","count":2}
|
||||
{"name":"Belarus","code":"BY","count":2}
|
||||
{"name":"Belgium","code":"BE","count":2}
|
||||
{"name":"Belize","code":"BZ","count":2}
|
||||
{"name":"Benin","code":"BJ","count":2}
|
||||
{"name":"Bermuda","code":"BM","count":2}
|
||||
{"name":"Bhutan","code":"BT","count":2}
|
||||
{"name":"Bolivia","code":"BO","count":2}
|
||||
{"name":"Bonaire","code":"BQ","count":2}
|
||||
{"name":"Bosnia and Herzegovina","code":"BA","count":2}
|
||||
{"name":"Botswana","code":"BW","count":2}
|
||||
{"name":"Bouvet Island","code":"BV","count":2}
|
||||
{"name":"Brazil","code":"BR","count":2}
|
||||
{"name":"British Indian Ocean Territory","code":"IO","count":2}
|
||||
{"name":"British Virgin Islands","code":"VG","count":2}
|
||||
{"name":"Brunei","code":"BN","count":2}
|
||||
{"name":"Bulgaria","code":"BG","count":2}
|
||||
{"name":"Burkina Faso","code":"BF","count":2}
|
||||
{"name":"Burundi","code":"BI","count":2}
|
||||
{"name":"Cambodia","code":"KH","count":2}
|
||||
{"name":"Cameroon","code":"CM","count":2}
|
||||
{"name":"Canada","code":"CA","count":2}
|
||||
{"name":"Cape Verde","code":"CV","count":2}
|
||||
{"name":"Cayman Islands","code":"KY","count":2}
|
||||
{"name":"Central African Republic","code":"CF","count":2}
|
||||
{"name":"Chad","code":"TD","count":2}
|
||||
{"name":"Chile","code":"CL","count":2}
|
||||
{"name":"China","code":"CN","count":2}
|
||||
{"name":"Christmas Island","code":"CX","count":2}
|
||||
{"name":"Cocos (Keeling) Islands","code":"CC","count":2}
|
||||
{"name":"Colombia","code":"CO","count":2}
|
||||
{"name":"Comoros","code":"KM","count":2}
|
||||
{"name":"Cook Islands","code":"CK","count":2}
|
||||
{"name":"Costa Rica","code":"CR","count":2}
|
||||
{"name":"Croatia","code":"HR","count":2}
|
||||
{"name":"Cuba","code":"CU","count":2}
|
||||
{"name":"Curacao","code":"CW","count":2}
|
||||
{"name":"Cyprus","code":"CY","count":2}
|
||||
{"name":"Czech Republic","code":"CZ","count":2}
|
||||
{"name":"Democratic Republic of the Congo","code":"CD","count":2}
|
||||
{"name":"Denmark","code":"DK","count":2}
|
||||
{"name":"Djibouti","code":"DJ","count":2}
|
||||
{"name":"Dominica","code":"DM","count":2}
|
||||
{"name":"Dominican Republic","code":"DO","count":2}
|
||||
{"name":"East Timor","code":"TL","count":2}
|
||||
{"name":"Ecuador","code":"EC","count":2}
|
||||
{"name":"Egypt","code":"EG","count":2}
|
||||
{"name":"El Salvador","code":"SV","count":2}
|
||||
{"name":"Equatorial Guinea","code":"GQ","count":2}
|
||||
{"name":"Eritrea","code":"ER","count":2}
|
||||
{"name":"Estonia","code":"EE","count":2}
|
||||
{"name":"Ethiopia","code":"ET","count":2}
|
||||
{"name":"Falkland Islands","code":"FK","count":2}
|
||||
{"name":"Faroe Islands","code":"FO","count":2}
|
||||
{"name":"Fiji","code":"FJ","count":2}
|
||||
{"name":"Finland","code":"FI","count":2}
|
||||
{"name":"France","code":"FR","count":2}
|
||||
{"name":"French Guiana","code":"GF","count":2}
|
||||
{"name":"French Polynesia","code":"PF","count":2}
|
||||
{"name":"French Southern Territories","code":"TF","count":2}
|
||||
{"name":"Gabon","code":"GA","count":2}
|
||||
{"name":"Gambia","code":"GM","count":2}
|
||||
{"name":"Georgia","code":"GE","count":2}
|
||||
{"name":"Germany","code":"DE","count":2}
|
||||
{"name":"Ghana","code":"GH","count":2}
|
||||
{"name":"Gibraltar","code":"GI","count":2}
|
||||
{"name":"Greece","code":"GR","count":2}
|
||||
{"name":"Greenland","code":"GL","count":2}
|
||||
{"name":"Grenada","code":"GD","count":2}
|
||||
{"name":"Guadeloupe","code":"GP","count":2}
|
||||
{"name":"Guam","code":"GU","count":2}
|
||||
{"name":"Guatemala","code":"GT","count":2}
|
||||
{"name":"Guernsey","code":"GG","count":2}
|
||||
{"name":"Guinea","code":"GN","count":2}
|
||||
{"name":"Guinea-Bissau","code":"GW","count":2}
|
||||
{"name":"Guyana","code":"GY","count":2}
|
||||
{"name":"Haiti","code":"HT","count":2}
|
||||
{"name":"Heard Island and McDonald Islands","code":"HM","count":2}
|
||||
{"name":"Honduras","code":"HN","count":2}
|
||||
{"name":"Hong Kong","code":"HK","count":2}
|
||||
{"name":"Hungary","code":"HU","count":2}
|
||||
{"name":"Iceland","code":"IS","count":2}
|
||||
{"name":"India","code":"IN","count":2}
|
||||
{"name":"Indonesia","code":"ID","count":2}
|
||||
{"name":"Iran","code":"IR","count":2}
|
||||
{"name":"Iraq","code":"IQ","count":2}
|
||||
{"name":"Ireland","code":"IE","count":2}
|
||||
{"name":"Isle of Man","code":"IM","count":2}
|
||||
{"name":"Israel","code":"IL","count":2}
|
||||
{"name":"Italy","code":"IT","count":2}
|
||||
{"name":"Ivory Coast","code":"CI","count":2}
|
||||
{"name":"Jamaica","code":"JM","count":2}
|
||||
{"name":"Japan","code":"JP","count":2}
|
||||
{"name":"Jersey","code":"JE","count":2}
|
||||
{"name":"Jordan","code":"JO","count":2}
|
||||
{"name":"Kazakhstan","code":"KZ","count":2}
|
||||
{"name":"Kenya","code":"KE","count":2}
|
||||
{"name":"Kiribati","code":"KI","count":2}
|
||||
{"name":"Kosovo","code":"XK","count":2}
|
||||
{"name":"Kuwait","code":"KW","count":2}
|
||||
{"name":"Kyrgyzstan","code":"KG","count":2}
|
||||
{"name":"Laos","code":"LA","count":2}
|
||||
{"name":"Latvia","code":"LV","count":2}
|
||||
{"name":"Lebanon","code":"LB","count":2}
|
||||
{"name":"Lesotho","code":"LS","count":2}
|
||||
{"name":"Liberia","code":"LR","count":2}
|
||||
{"name":"Libya","code":"LY","count":2}
|
||||
{"name":"Liechtenstein","code":"LI","count":2}
|
||||
{"name":"Lithuania","code":"LT","count":2}
|
||||
{"name":"Luxembourg","code":"LU","count":2}
|
||||
{"name":"Macao","code":"MO","count":2}
|
||||
{"name":"Madagascar","code":"MG","count":2}
|
||||
{"name":"Malawi","code":"MW","count":2}
|
||||
{"name":"Malaysia","code":"MY","count":2}
|
||||
{"name":"Maldives","code":"MV","count":2}
|
||||
{"name":"Mali","code":"ML","count":2}
|
||||
{"name":"Malta","code":"MT","count":2}
|
||||
{"name":"Marshall Islands","code":"MH","count":2}
|
||||
{"name":"Martinique","code":"MQ","count":2}
|
||||
{"name":"Mauritania","code":"MR","count":2}
|
||||
{"name":"Mauritius","code":"MU","count":2}
|
||||
{"name":"Mayotte","code":"YT","count":2}
|
||||
{"name":"Mexico","code":"MX","count":2}
|
||||
{"name":"Micronesia","code":"FM","count":2}
|
||||
{"name":"Moldova","code":"MD","count":2}
|
||||
{"name":"Monaco","code":"MC","count":2}
|
||||
{"name":"Mongolia","code":"MN","count":2}
|
||||
{"name":"Montenegro","code":"ME","count":2}
|
||||
{"name":"Montserrat","code":"MS","count":2}
|
||||
{"name":"Morocco","code":"MA","count":2}
|
||||
{"name":"Mozambique","code":"MZ","count":2}
|
||||
{"name":"Myanmar (Burma)","code":"MM","count":2}
|
||||
{"name":"Namibia","code":"NA","count":2}
|
||||
{"name":"Nauru","code":"NR","count":2}
|
||||
{"name":"Nepal","code":"NP","count":2}
|
||||
{"name":"Netherlands","code":"NL","count":2}
|
||||
{"name":"New Caledonia","code":"NC","count":2}
|
||||
{"name":"New Zealand","code":"NZ","count":2}
|
||||
{"name":"Nicaragua","code":"NI","count":2}
|
||||
{"name":"Niger","code":"NE","count":2}
|
||||
{"name":"Nigeria","code":"NG","count":2}
|
||||
{"name":"Niue","code":"NU","count":2}
|
||||
{"name":"Norfolk Island","code":"NF","count":2}
|
||||
{"name":"North Korea","code":"KP","count":2}
|
||||
{"name":"North Macedonia","code":"MK","count":2}
|
||||
{"name":"Northern Mariana Islands","code":"MP","count":2}
|
||||
{"name":"Norway","code":"NO","count":2}
|
||||
{"name":"Oman","code":"OM","count":2}
|
||||
{"name":"Pakistan","code":"PK","count":2}
|
||||
{"name":"Palau","code":"PW","count":2}
|
||||
{"name":"Palestine","code":"PS","count":2}
|
||||
{"name":"Panama","code":"PA","count":2}
|
||||
{"name":"Papua New Guinea","code":"PG","count":2}
|
||||
{"name":"Paraguay","code":"PY","count":2}
|
||||
{"name":"Peru","code":"PE","count":2}
|
||||
{"name":"Philippines","code":"PH","count":2}
|
||||
{"name":"Pitcairn Islands","code":"PN","count":2}
|
||||
{"name":"Poland","code":"PL","count":2}
|
||||
{"name":"Portugal","code":"PT","count":2}
|
||||
{"name":"Puerto Rico","code":"PR","count":2}
|
||||
{"name":"Qatar","code":"QA","count":2}
|
||||
{"name":"Republic of the Congo","code":"CG","count":2}
|
||||
{"name":"Romania","code":"RO","count":2}
|
||||
{"name":"Russia","code":"RU","count":3}
|
||||
{"name":"Rwanda","code":"RW","count":2}
|
||||
{"name":"Réunion","code":"RE","count":2}
|
||||
{"name":"Saint Barthélemy","code":"BL","count":2}
|
||||
{"name":"Saint Helena","code":"SH","count":2}
|
||||
{"name":"Saint Kitts and Nevis","code":"KN","count":2}
|
||||
{"name":"Saint Lucia","code":"LC","count":2}
|
||||
{"name":"Saint Martin","code":"MF","count":2}
|
||||
{"name":"Saint Pierre and Miquelon","code":"PM","count":2}
|
||||
{"name":"Saint Vincent and the Grenadines","code":"VC","count":2}
|
||||
{"name":"Samoa","code":"WS","count":2}
|
||||
{"name":"San Marino","code":"SM","count":2}
|
||||
{"name":"Saudi Arabia","code":"SA","count":2}
|
||||
{"name":"Senegal","code":"SN","count":2}
|
||||
{"name":"Serbia","code":"RS","count":2}
|
||||
{"name":"Seychelles","code":"SC","count":2}
|
||||
{"name":"Sierra Leone","code":"SL","count":2}
|
||||
{"name":"Singapore","code":"SG","count":2}
|
||||
{"name":"Sint Maarten","code":"SX","count":2}
|
||||
{"name":"Slovakia","code":"SK","count":2}
|
||||
{"name":"Slovenia","code":"SI","count":2}
|
||||
{"name":"Solomon Islands","code":"SB","count":2}
|
||||
{"name":"Somalia","code":"SO","count":2}
|
||||
{"name":"South Africa","code":"ZA","count":2}
|
||||
{"name":"South Georgia and the South Sandwich Islands","code":"GS","count":2}
|
||||
{"name":"South Korea","code":"KR","count":2}
|
||||
{"name":"South Sudan","code":"SS","count":2}
|
||||
{"name":"Spain","code":"ES","count":2}
|
||||
{"name":"Sri Lanka","code":"LK","count":2}
|
||||
{"name":"Sudan","code":"SD","count":2}
|
||||
{"name":"Suriname","code":"SR","count":2}
|
||||
{"name":"Svalbard and Jan Mayen","code":"SJ","count":2}
|
||||
{"name":"Swaziland","code":"SZ","count":2}
|
||||
{"name":"Sweden","code":"SE","count":2}
|
||||
{"name":"Switzerland","code":"CH","count":2}
|
||||
{"name":"Syria","code":"SY","count":2}
|
||||
{"name":"São Tomé and Príncipe","code":"ST","count":2}
|
||||
{"name":"Taiwan","code":"TW","count":2}
|
||||
{"name":"Tajikistan","code":"TJ","count":2}
|
||||
{"name":"Tanzania","code":"TZ","count":2}
|
||||
{"name":"Thailand","code":"TH","count":2}
|
||||
{"name":"Togo","code":"TG","count":2}
|
||||
{"name":"Tokelau","code":"TK","count":2}
|
||||
{"name":"Tonga","code":"TO","count":2}
|
||||
{"name":"Trinidad and Tobago","code":"TT","count":2}
|
||||
{"name":"Tunisia","code":"TN","count":2}
|
||||
{"name":"Turkey","code":"TR","count":2}
|
||||
{"name":"Turkmenistan","code":"TM","count":2}
|
||||
{"name":"Turks and Caicos Islands","code":"TC","count":2}
|
||||
{"name":"Tuvalu","code":"TV","count":2}
|
||||
{"name":"U.S. Minor Outlying Islands","code":"UM","count":2}
|
||||
{"name":"U.S. Virgin Islands","code":"VI","count":2}
|
||||
{"name":"Uganda","code":"UG","count":2}
|
||||
{"name":"Ukraine","code":"UA","count":2}
|
||||
{"name":"United Arab Emirates","code":"AE","count":2}
|
||||
{"name":"United Kingdom","code":"UK","count":2}
|
||||
{"name":"United States","code":"US","count":2}
|
||||
{"name":"Uruguay","code":"UY","count":2}
|
||||
{"name":"Uzbekistan","code":"UZ","count":2}
|
||||
{"name":"Vanuatu","code":"VU","count":2}
|
||||
{"name":"Vatican City","code":"VA","count":2}
|
||||
{"name":"Venezuela","code":"VE","count":2}
|
||||
{"name":"Vietnam","code":"VN","count":2}
|
||||
{"name":"Wallis and Futuna","code":"WF","count":2}
|
||||
{"name":"Western Sahara","code":"EH","count":2}
|
||||
{"name":"Yemen","code":"YE","count":2}
|
||||
{"name":"Zambia","code":"ZM","count":2}
|
||||
{"name":"Zimbabwe","code":"ZW","count":2}
|
||||
{"name":"Åland","code":"AX","count":2}
|
||||
{"name":"Undefined","id":"UNDEFINED","count":2}
|
|
@ -1,6 +1,6 @@
|
|||
{"title":"ЛДПР ТВ","id":"LDPRTV.ru","filepath":"tests/__data__/output/channels/ru.m3u","resolution":{"width":1920,"height":1080},"status":{"label":"","code":"online","level":1},"url":"http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"2ST8btby3mmsgPF0"}
|
||||
{"title":"BBC News HD","id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCIdn"}
|
||||
{"title":"ATV","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"I6cjG2xCBRFFP4sz"}
|
||||
{"title":"BBC News HD","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"WTbieV1ptnZVCIdn"}
|
||||
{"title":"Kayhan TV","id":"KayhanTV.af","filepath":"channels/af.m3u","resolution":{"height":720,"width":null},"status":{"label":"Geo-blocked","code":"geo_blocked","level":2},"url":"http://208.93.117.113/live/Stream1/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":1,"_id":"cFFpFVzSn6xFMUF3"}
|
||||
{"title":"Sharq","id":"Sharq.af","filepath":"channels/af.m3u","resolution":{"height":576,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"http://51.210.199.50/hls/stream.m3u8","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"u7iyA6cjtf1iWWAZ"}
|
||||
{"channel_name":"ЛДПР ТВ","channel_id":"LDPRTV.ru","filepath":"tests/__data__/output/channels/ru.m3u","resolution":{"width":1920,"height":1080},"status":{"label":"","code":"online","level":1},"url":"http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"2ST8btby3mmsgPF0"}
|
||||
{"channel_name":"BBC News HD","channel_id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCIdn"}
|
||||
{"channel_name":"ATV","channel_id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"I6cjG2xCBRFFP4sz"}
|
||||
{"channel_name":"BBC News HD","channel_id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"WTbieV1ptnZVCIdn"}
|
||||
{"channel_name":"Kayhan TV","channel_id":"KayhanTV.af","filepath":"channels/af.m3u","resolution":{"height":720,"width":null},"status":{"label":"Geo-blocked","code":"geo_blocked","level":2},"url":"http://208.93.117.113/live/Stream1/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":1,"_id":"cFFpFVzSn6xFMUF3"}
|
||||
{"channel_name":"Sharq","channel_id":"Sharq.af","filepath":"channels/af.m3u","resolution":{"height":576,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"http://51.210.199.50/hls/stream.m3u8","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"u7iyA6cjtf1iWWAZ"}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{"title":"ATV","id":"ATV.ad","filepath":"tests/__data__/input/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"verufR2ehwdsfou3"}
|
||||
{"title":"Fox Sports 2 Asia (Thai)","id":"FoxSports2AsiaThai.us","filepath":"tests/__data__/input/channels/us_blocked.m3u","resolution":{"height":720,"width":null},"status":{"label":"","code":"online","level":1},"url":"https://example.com/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"sLG04kZhqlEcYc25"}
|
||||
{"id":null,"title":"1A Network","filepath":"tests/__data__/input/channels/unsorted.m3u","resolution":{"height":720,"width":null},"status":{"label":"","code":"online","level":1},"url":"https://simultv.s.llnwi.net/n4s4/2ANetwork/interlink.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"Jruf9KFXRsa5BjYj"}
|
||||
{"channel_name":"ATV","channel_id":"ATV.ad","filepath":"tests/__data__/input/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"verufR2ehwdsfou3"}
|
||||
{"channel_name":"Fox Sports 2 Asia (Thai)","channel_id":"FoxSports2AsiaThai.us","filepath":"tests/__data__/input/channels/us_blocked.m3u","resolution":{"height":720,"width":null},"status":{"label":"","code":"online","level":1},"url":"https://example.com/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"sLG04kZhqlEcYc25"}
|
||||
{"channel_id":null,"channel_name":"1A Network","filepath":"tests/__data__/input/channels/unsorted.m3u","resolution":{"height":720,"width":null},"status":{"label":"","code":"online","level":1},"url":"https://simultv.s.llnwi.net/n4s4/2ANetwork/interlink.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":true,"cluster_id":1,"_id":"Jruf9KFXRsa5BjYj"}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{"name":"ЛДПР ТВ","id":"LDPRTV.ru","filepath":"tests/__data__/output/channels/ru.m3u","src_country":{"name":"Russia","code":"RU","lang":"rus"},"tvg_country":"RU","countries":[{"name":"Russia","code":"RU","lang":"rus"}],"regions":[{"name":"Asia","code":"ASIA"},{"name":"Commonwealth of Independent States","code":"CIS"},{"name":"Europe, the Middle East and Africa","code":"EMEA"},{"name":"Europe","code":"EUR"}],"languages":[{"name":"Russian","code":"rus"}],"categories":[{"name":"General","slug":"general","nsfw":false},{"name":"Legislative","slug":"legislative","nsfw":false}],"tvg_url":"","guides":["https://iptv-org.github.io/epg/guides/ru/tv.yandex.ru.epg.xml"],"logo":"https://iptvx.one/icn/ldpr-tv.png","resolution":{"height":1080,"width":null},"status":{"label":"","code":"online","level":1},"url":"http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPF0"}
|
||||
{"name":"BBC News HD","id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","src_country":{"name":"United Kingdom","code":"UK","lang":"eng"},"tvg_country":"UK","countries":[{"name":"United Kingdom","code":"UK","lang":"eng"}],"regions":[{"name":"Europe, the Middle East and Africa","code":"EMEA"},{"name":"Europe","code":"EUR"}],"languages":[{"name":"English","code":"eng"}],"categories":[{"name":"News","slug":"news","nsfw":false}],"tvg_url":"","guides":[],"logo":"https://i.imgur.com/eNPIQ9f.png","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCIdn"}
|
||||
{"name":"BBC News HD","id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","src_country":{"name":"United Kingdom","code":"UK","lang":"eng"},"tvg_country":"UK","countries":[{"name":"United Kingdom","code":"UK","lang":"eng"}],"regions":[{"name":"Europe, the Middle East and Africa","code":"EMEA"},{"name":"Europe","code":"EUR"}],"languages":[{"name":"English","code":"eng"}],"categories":[{"name":"News","slug":"news","nsfw":false}],"tvg_url":"","guides":[],"logo":"https://i.imgur.com/eNPIQ9f.png","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCId5"}
|
||||
{"name":"ATV","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/ad.m3u","src_country":{"name":"Andorra","code":"AD","lang":"cat"},"tvg_country":"AD","countries":[{"name":"Andorra","code":"AD","lang":"cat"}],"regions":[{"name":"Europe, the Middle East and Africa","code":"EMEA"},{"name":"Europe","code":"EUR"}],"languages":[{"name":"Catalan","code":"cat"}],"categories":[{"name":"General","slug":"general","nsfw":false}],"tvg_url":"","guides":[],"logo":"https://i.imgur.com/kJCjeQ4.png","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":true,"updated":false,"cluster_id":1,"_id":"I6cjG2xCBRFFP4sz"}
|
||||
{"name":"BBC News HD","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/uk.m3u","src_country":{"name":"United Kingdom","code":"UK","lang":"eng"},"tvg_country":"UK","countries":[{"name":"United Kingdom","code":"UK","lang":"eng"}],"regions":[{"name":"Europe, the Middle East and Africa","code":"EMEA"},{"name":"Europe","code":"EUR"}],"languages":[{"name":"English","code":"eng"}],"categories":[{"name":"News","slug":"news","nsfw":false}],"tvg_url":"","guides":[],"logo":"https://i.imgur.com/eNPIQ9f.png","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":3,"_id":"WTbieV1ptnZVCIdn"}
|
||||
{"name":"Visit-X TV","id":"","filepath":"tests/__data__/output/channels/nl.m3u","src_country":{},"tvg_country":"","countries":[],"regions":[],"languages":[],"categories":[{"name":"XXX","slug":"xxx","nsfw":true}],"tvg_url":"","guides":[],"logo":"","resolution":{},"status":{"label":"","code":"online","level":1},"url":"https://stream.visit-x.tv/vxtv/ngrp:live_all/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":true,"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPF5"}
|
||||
{"name":"Tastemade","id":"","filepath":"tests/__data__/output/channels/qa.m3u","src_country":{},"tvg_country":"INT","countries":[{"name":"Andorra","code":"AD","lang":"cat"},{"name":"Russia","code":"RU","lang":"rus"},{"name":"United Kingdom","code":"UK","lang":"eng"}],"regions":[{"name":"Worldwide","code":"INT"}],"languages":[],"categories":[{"name":"Cooking","slug":"cooking","nsfw":false}],"tvg_url":"","guides":[],"logo":"","resolution":{},"status":{"label":"","code":"online","level":1},"url":"https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPAB"}
|
||||
{"name":"Daawah TV","id":"","filepath":"tests/__data__/output/channels/in.m3u","src_country":{},"tvg_country":"","countries":[],"regions":[],"languages":[],"categories":[],"tvg_url":"","guides":[],"logo":"","resolution":{},"status":{"label":"","code":"online","level":1},"url":"http://51.15.246.58:8081/daawahtv/daawahtv2/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_nsfw":false,"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPF9"}
|
|
@ -1,6 +0,0 @@
|
|||
{"title":"ЛДПР ТВ","id":"LDPRTV.ru","filepath":"tests/__data__/output/channels/ru.m3u","resolution":{"height":1080,"width":null},"status":{"label":"","code":"online","level":1},"url":"http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPF0"}
|
||||
{"title":"BBC News HD","id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCIdn"}
|
||||
{"title":"ATV","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"I6cjG2xCBRFFP4sz"}
|
||||
{"title":"BBC News HD","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"WTbieV1ptnZVCIdn"}
|
||||
{"title":"Kayhan TV","id":"KayhanTV.af","filepath":"channels/af.m3u","resolution":{"height":720,"width":null},"status":{"label":"Geo-blocked","code":"geo_blocked","level":2},"url":"http://208.93.117.113/live/Stream1/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":1,"_id":"cFFpFVzSn6xFMUF3"}
|
||||
{"title":"Sharq","id":"Sharq.af","filepath":"channels/af.m3u","resolution":{"height":576,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"http://51.210.199.50/hls/stream.m3u8","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"u7iyA6cjtf1iWWAZ"}
|
|
@ -1,4 +0,0 @@
|
|||
{"title":"ЛДПР ТВ","id":"LDPRTV.ru","filepath":"tests/__data__/output/channels/ru.m3u","resolution":{"height":1080,"width":null},"status":{"label":"","code":"online","level":1},"url":"http://46.46.143.222:1935/live/mp4:ldpr.stream/playlist.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":1,"_id":"2ST8btby3mmsgPF0"}
|
||||
{"title":"BBC News HD","id":"BBCNews.uk","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"https://query-streamlink.herokuapp.com/iptv-query?streaming-ip=https://www.twitch.tv/absliveantigua3/","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"3TbieV1ptnZVCIdn"}
|
||||
{"title":"ATV","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/ad.m3u","resolution":{"height":720,"width":null},"status":{"label":"Offline","code":"offline","level":5},"url":"https://iptv-all.lanesh4d0w.repl.co/andorra/atv","http":{"referrer":"","user-agent":""},"is_broken":true,"updated":false,"cluster_id":1,"_id":"I6cjG2xCBRFFP4sz"}
|
||||
{"title":"Andorra TV","id":"AndorraTV.ad","filepath":"tests/__data__/output/channels/uk.m3u","resolution":{"height":720,"width":null},"status":{"label":"Not 24/7","code":"not_247","level":3},"url":"http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/index.m3u8","http":{"referrer":"","user-agent":""},"is_broken":false,"updated":false,"cluster_id":3,"_id":"WTbieV1ptnZVCIdn"}
|
|
@ -1,160 +1,42 @@
|
|||
const fs = require('fs')
|
||||
const fs = require('fs-extra')
|
||||
const path = require('path')
|
||||
const { execSync } = require('child_process')
|
||||
|
||||
beforeEach(() => {
|
||||
fs.emptyDirSync('tests/__data__/output')
|
||||
fs.emptyDirSync('tests/__data__/temp')
|
||||
fs.copyFileSync(
|
||||
'tests/__data__/input/database/generate-playlists.streams.db',
|
||||
'tests/__data__/temp/streams.db'
|
||||
)
|
||||
|
||||
const stdout = execSync(
|
||||
'DB_DIR=tests/__data__/temp DATA_DIR=tests/__data__/input/data PUBLIC_DIR=tests/__data__/output/.gh-pages LOGS_DIR=tests/__data__/output/logs/generators node --trace-warnings scripts/commands/generate-playlists.js',
|
||||
{ encoding: 'utf8' }
|
||||
)
|
||||
|
||||
console.log(stdout)
|
||||
})
|
||||
|
||||
it.each([
|
||||
'.gh-pages/categories/general.m3u',
|
||||
'.gh-pages/categories/legislative.m3u',
|
||||
'.gh-pages/categories/news.m3u',
|
||||
'.gh-pages/categories/other.m3u',
|
||||
'logs/generators/categories.log'
|
||||
])('can generate %s', filepath => {
|
||||
expect(content(`output/${filepath}`)).toBe(content(`expected/${filepath}`))
|
||||
})
|
||||
|
||||
it.each(['countries/ru.m3u', 'countries/uk.m3u', 'countries/undefined.m3u'])(
|
||||
'can generate %s',
|
||||
filepath => {
|
||||
expect(content(`output/.gh-pages/${filepath}`)).toBe(content(`expected/.gh-pages/${filepath}`))
|
||||
}
|
||||
)
|
||||
|
||||
function content(filepath) {
|
||||
return fs.readFileSync(`tests/__data__/${filepath}`, {
|
||||
encoding: 'utf8'
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
fs.rmdirSync('tests/__data__/output', { recursive: true })
|
||||
fs.copyFileSync('tests/__data__/input/generate-playlists.test.db', 'tests/__data__/temp/test.db')
|
||||
|
||||
execSync(
|
||||
'DB_FILEPATH=tests/__data__/temp/test.db PUBLIC_PATH=tests/__data__/output/.gh-pages LOGS_PATH=tests/__data__/output/logs node scripts/commands/generate-playlists.js',
|
||||
{ encoding: 'utf8' }
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmdirSync('tests/__data__/temp', { recursive: true })
|
||||
fs.mkdirSync('tests/__data__/temp')
|
||||
})
|
||||
|
||||
it('can generate categories', () => {
|
||||
expect(content('output/.gh-pages/categories/general.m3u')).toBe(
|
||||
content('expected/.gh-pages/categories/general.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/categories/legislative.m3u')).toBe(
|
||||
content('expected/.gh-pages/categories/legislative.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/categories/news.m3u')).toBe(
|
||||
content('expected/.gh-pages/categories/news.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/categories/other.m3u')).toBe(
|
||||
content('expected/.gh-pages/categories/other.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate countries', () => {
|
||||
expect(content('output/.gh-pages/countries/ru.m3u')).toBe(
|
||||
content('expected/.gh-pages/countries/ru.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/countries/uk.m3u')).toBe(
|
||||
content('expected/.gh-pages/countries/uk.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/countries/undefined.m3u')).toBe(
|
||||
content('expected/.gh-pages/countries/undefined.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate languages', () => {
|
||||
expect(content('output/.gh-pages/languages/rus.m3u')).toBe(
|
||||
content('expected/.gh-pages/languages/rus.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/languages/eng.m3u')).toBe(
|
||||
content('expected/.gh-pages/languages/eng.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/languages/undefined.m3u')).toBe(
|
||||
content('expected/.gh-pages/languages/undefined.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate regions', () => {
|
||||
expect(content('output/.gh-pages/regions/asia.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/asia.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/regions/cis.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/cis.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/regions/emea.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/emea.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/regions/eur.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/eur.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/regions/int.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/int.m3u')
|
||||
)
|
||||
|
||||
expect(content('output/.gh-pages/regions/undefined.m3u')).toBe(
|
||||
content('expected/.gh-pages/regions/undefined.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate channels.json', () => {
|
||||
expect(content('output/.gh-pages/channels.json')).toBe(
|
||||
content('expected/.gh-pages/channels.json')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate index.category.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.category.m3u')).toBe(
|
||||
content('expected/.gh-pages/index.category.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate index.country.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.country.m3u')).toBe(
|
||||
content('expected/.gh-pages/index.country.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate index.language.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.language.m3u')).toBe(
|
||||
content('expected/.gh-pages/index.language.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate index.region.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.region.m3u')).toBe(
|
||||
content('expected/.gh-pages/index.region.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate index.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.m3u')).toBe(content('expected/.gh-pages/index.m3u'))
|
||||
})
|
||||
|
||||
it('can generate index.nsfw.m3u', () => {
|
||||
expect(content('output/.gh-pages/index.nsfw.m3u')).toBe(
|
||||
content('expected/.gh-pages/index.nsfw.m3u')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate logs categories', () => {
|
||||
expect(content('output/logs/generate-playlists/categories.log')).toBe(
|
||||
content('expected/logs/generate-playlists/categories.log')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate logs countries', () => {
|
||||
expect(content('output/logs/generate-playlists/countries.log')).toBe(
|
||||
content('expected/logs/generate-playlists/countries.log')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate logs languages', () => {
|
||||
expect(content('output/logs/generate-playlists/languages.log')).toBe(
|
||||
content('expected/logs/generate-playlists/languages.log')
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate logs regions', () => {
|
||||
expect(content('output/logs/generate-playlists/regions.log')).toBe(
|
||||
content('expected/logs/generate-playlists/regions.log')
|
||||
)
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue