wip
This commit is contained in:
		
							
								
								
									
										9
									
								
								.github/workflows/auto-update.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/auto-update.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,6 +8,15 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v2 |       - 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 |       - uses: actions/setup-node@v2 | ||||||
|         if: ${{ !env.ACT }} |         if: ${{ !env.ACT }} | ||||||
|         with: |         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') | 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() { | async function main() { | ||||||
|   await setUp() |   const streams = await loadStreams() | ||||||
|  |  | ||||||
|   await generateCategories() |   logger.info(`generating categories/...`) | ||||||
|   await generateCountries() |   await generator.generate('categories', streams) | ||||||
|   await generateLanguages() |  | ||||||
|   await generateRegions() |  | ||||||
|   await generateIndex() |  | ||||||
|   await generateIndexNSFW() |  | ||||||
|   await generateIndexCategory() |  | ||||||
|   await generateIndexCountry() |  | ||||||
|   await generateIndexLanguage() |  | ||||||
|   await generateIndexRegion() |  | ||||||
|  |  | ||||||
|   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() | main() | ||||||
|  |  | ||||||
| async function generateCategories() { | async function generateCountries(streams) { | ||||||
|   logger.info(`Generating categories/...`) |   logger.info(`generating countries/...`) | ||||||
|  |   const countries = await loadCountries() | ||||||
|   for (const category of categories) { |   const regions = await loadRegions() | ||||||
|     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/...`) |  | ||||||
|  |  | ||||||
|   for (const country of countries) { |   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`, |       `${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, |       name: country.name, | ||||||
|       code: country.code, |       code: country.code, | ||||||
|       count |       count | ||||||
|     }) |     }) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const { count: undefinedCount } = await generator.generate( |   const { count } = await generator.generate(`${PUBLIC_PATH}/countries/undefined.m3u`, streams, { | ||||||
|     `${PUBLIC_PATH}/countries/undefined.m3u`, |     public: true, | ||||||
|     { |     filter: s => !s.broadcast_area.length, | ||||||
|       countries: { $size: 0 } |     onLoad: items => { | ||||||
|     }, |       return items.map(item => { | ||||||
|     { |         item.group_title = 'Undefined' | ||||||
|       onLoad: function (items) { |         return item | ||||||
|         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() { | async function generateLanguages() { | ||||||
| @@ -395,47 +376,44 @@ async function generateIndexRegion() { | |||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
| async function generateChannelsJson() { | async function loadStreams() { | ||||||
|   logger.info('Generating channels.json...') |   await api.channels.load() | ||||||
|  |   let channels = await api.channels.all() | ||||||
|  |   channels = _.keyBy(channels, 'id') | ||||||
|  |  | ||||||
|   await generator.generate( |   await api.countries.load() | ||||||
|     `${PUBLIC_PATH}/channels.json`, |   let countries = await api.countries.all() | ||||||
|     {}, |   countries = _.keyBy(countries, 'code') | ||||||
|     { format: 'json', includeNSFW: true, uniqBy: null } |  | ||||||
|   ) |   await api.categories.load() | ||||||
| } |   let categories = await api.categories.all() | ||||||
|  |   categories = _.keyBy(categories, 'id') | ||||||
| async function setUp() { |  | ||||||
|   logger.info(`Loading database...`) |   await api.languages.load() | ||||||
|   const items = await db.find({}) |   let languages = await api.languages.all() | ||||||
|   categories = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.categories)), 'slug'), ['name']).filter( |   languages = _.keyBy(languages, 'code') | ||||||
|     i => i |  | ||||||
|   ) |   await api.guides.load() | ||||||
|   countries = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.countries)), 'code'), ['name']).filter( |   let guides = await api.guides.all() | ||||||
|     i => i |   guides = _.groupBy(guides, 'channel') | ||||||
|   ) |  | ||||||
|   languages = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.languages)), 'code'), ['name']).filter( |   await db.streams.load() | ||||||
|     i => i |   let streams = await db.streams.find({}) | ||||||
|   ) |  | ||||||
|   regions = _.sortBy(_.uniqBy(_.flatten(items.map(i => i.regions)), 'code'), ['name']).filter( |   return streams.map(stream => { | ||||||
|     i => i |     const channel = channels[stream.channel_id] || null | ||||||
|   ) |  | ||||||
|  |     stream.channel = channel | ||||||
|   const categoriesLog = `${LOGS_PATH}/generate-playlists/categories.log` |     stream.broadcast_area = channel | ||||||
|   const countriesLog = `${LOGS_PATH}/generate-playlists/countries.log` |       ? channel.broadcast_area.map(item => { | ||||||
|   const languagesLog = `${LOGS_PATH}/generate-playlists/languages.log` |           const [_, code] = item.split('/') | ||||||
|   const regionsLog = `${LOGS_PATH}/generate-playlists/regions.log` |           return code | ||||||
|  |         }) | ||||||
|   logger.info(`Creating '${categoriesLog}'...`) |       : [] | ||||||
|   await file.create(categoriesLog) |     stream.categories = channel ? channel.categories.map(id => categories[id]) : [] | ||||||
|   logger.info(`Creating '${countriesLog}'...`) |     stream.languages = channel ? channel.languages.map(code => languages[code]) : [] | ||||||
|   await file.create(countriesLog) |     stream.guides = guides[stream.channel_id] ? guides[stream.channel_id].map(g => g.url) : [] | ||||||
|   logger.info(`Creating '${languagesLog}'...`) |  | ||||||
|   await file.create(languagesLog) |     return stream | ||||||
|   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') |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -16,11 +16,25 @@ class API { | |||||||
|   find(query) { |   find(query) { | ||||||
|     return _.find(this.collection, query) |     return _.find(this.collection, query) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   filter(query) { | ||||||
|  |     return _.filter(this.collection, query) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   all() { | ||||||
|  |     return this.collection | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| const api = {} | const api = {} | ||||||
|  |  | ||||||
| api.channels = new API(`${DATA_DIR}/channels.json`) | api.channels = new API(`${DATA_DIR}/channels.json`) | ||||||
| api.countries = new API(`${DATA_DIR}/countries.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 | module.exports = api | ||||||
|   | |||||||
| @@ -1,61 +1,75 @@ | |||||||
| const Database = require('nedb-promises') | const nedb = require('nedb-promises') | ||||||
| const file = require('./file') | 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({ | class Database { | ||||||
|   filename: file.resolve(DB_FILEPATH), |   constructor(filepath) { | ||||||
|   autoload: true, |     this.filepath = filepath | ||||||
|   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, { |   load() { | ||||||
|       sensitivity: 'accent', |     this.db = nedb.create({ | ||||||
|       numeric: true |       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 = {} | const db = {} | ||||||
|  |  | ||||||
| db.removeIndex = function (field) { | db.streams = new Database(`${DB_DIR}/streams.db`) | ||||||
|   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) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = db | module.exports = db | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
|  | const { create: createPlaylist } = require('./playlist') | ||||||
|  | const store = require('./store') | ||||||
| const path = require('path') | const path = require('path') | ||||||
| const glob = require('glob') | const glob = require('glob') | ||||||
| const fs = require('mz/fs') | const fs = require('mz/fs') | ||||||
|  | const _ = require('lodash') | ||||||
|  |  | ||||||
| const file = {} | const file = {} | ||||||
|  |  | ||||||
| @@ -64,4 +67,45 @@ file.basename = function (filepath) { | |||||||
|   return path.basename(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 | module.exports = file | ||||||
|   | |||||||
| @@ -1,119 +1,23 @@ | |||||||
| const { create: createPlaylist } = require('./playlist') |  | ||||||
| const store = require('./store') |  | ||||||
| const file = require('./file') | const file = require('./file') | ||||||
| const logger = require('./logger') | const generators = require('../generators') | ||||||
| const db = require('./db') |  | ||||||
| const _ = require('lodash') | const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs/generators' | ||||||
|  |  | ||||||
| const generator = {} | const generator = {} | ||||||
|  |  | ||||||
| generator.generate = async function (filepath, query = {}, options = {}) { | generator.generate = async function (name, items = []) { | ||||||
|   options = { |   if (typeof generators[name] === 'function') { | ||||||
|     ...{ |     try { | ||||||
|       format: 'm3u', |       const logs = await generators[name].bind()(items) | ||||||
|       saveEmpty: false, |       await file.create(`${LOGS_DIR}/${name}.log`, logs.map(toJSON).join('\n')) | ||||||
|       includeNSFW: false, |     } catch (error) { | ||||||
|       includeGuides: true, |       logger.error(`generators/${name}.js: ${error.message}`) | ||||||
|       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 |  | ||||||
|       } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     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 | 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 = {} | const playlist = {} | ||||||
|  |  | ||||||
| playlist.create = async function (filepath) { | class Playlist { | ||||||
|   playlist.filepath = filepath |   constructor() { | ||||||
|   const dir = file.dirname(filepath) |     this.links = [] | ||||||
|   file.createDir(dir) |   } | ||||||
|   await file.create(filepath, '') |  | ||||||
|  |  | ||||||
|   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) { | playlist.create = function (items = [], options = {}) { | ||||||
|   let header = `#EXTM3U` |   const p = new Playlist() | ||||||
|   for (const name in attrs) { |  | ||||||
|     const value = attrs[name] |   const header = {} | ||||||
|     header += ` ${name}="${value}"` |   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 |     let attrs | ||||||
| } |     if (options.public) { | ||||||
|  |       attrs = { | ||||||
| playlist.link = async function (url, title, attrs, vlcOpts) { |         'tvg-id': stream.get('tvg_id'), | ||||||
|   let link = `#EXTINF:-1` |         'tvg-country': stream.get('tvg_country'), | ||||||
|   for (const name in attrs) { |         'tvg-language': stream.get('tvg_language'), | ||||||
|     const value = attrs[name] |         'tvg-logo': stream.get('tvg_logo'), | ||||||
|     if (value !== undefined) { |         'user-agent': stream.get('http.user-agent') || undefined, | ||||||
|       link += ` ${name}="${value}"` |         '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 | module.exports = playlist | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								scripts/data/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								scripts/data/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -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
											
										
									
								
							
							
								
								
									
										47
									
								
								scripts/generators/categories.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								scripts/generators/categories.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								scripts/generators/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								scripts/generators/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | exports.categories = require('./categories') | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| module.exports = function () { | 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)) { |   if (Array.isArray(this.categories)) { | ||||||
|     return this.categories |     return this.categories | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| exports.group_title = require('./group_title') | exports.group_title = require('./group_title') | ||||||
| exports.display_name = require('./display_name') | exports.title = require('./title') | ||||||
| exports.tvg_country = require('./tvg_country') | exports.tvg_country = require('./tvg_country') | ||||||
| exports.tvg_id = require('./tvg_id') | exports.tvg_id = require('./tvg_id') | ||||||
| exports.tvg_language = require('./tvg_language') | exports.tvg_language = require('./tvg_language') | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| module.exports = function () { | module.exports = function () { | ||||||
|   let title = this.title |   let title = this.channel_name | ||||||
| 
 | 
 | ||||||
|   if (this.resolution.height) { |   if (this.resolution.height) { | ||||||
|     title += ` (${this.resolution.height}p)` |     title += ` (${this.resolution.height}p)` | ||||||
| @@ -1,5 +1,3 @@ | |||||||
| module.exports = function () { | module.exports = function () { | ||||||
|   if (this.tvg_country) return this.tvg_country |   return Array.isArray(this.broadcast_area) ? this.broadcast_area.join(';') : '' | ||||||
|  |  | ||||||
|   return Array.isArray(this.countries) ? this.countries.map(i => i.code).join(';') : '' |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,3 @@ | |||||||
| module.exports = function () { | module.exports = function () { | ||||||
|   return this.id || '' |   return this.channel_id || '' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,3 +1,3 @@ | |||||||
| module.exports = function () { | 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 }) { | 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" | #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 | 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" | #EXTM3U x-tvg-url="" | ||||||
| #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 |  | ||||||
|   | |||||||
| @@ -1,3 +1,3 @@ | |||||||
| #EXTM3U x-tvg-url="" | #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="UK" tvg-language="English" tvg-logo="https://i.imgur.com/eNPIQ9f.png" group-title="News",BBC News HD (720p) [Not 24/7] | #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/index.m3u8 | 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 | #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 | 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" | #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="" tvg-country="INT" tvg-language="" tvg-logo="" group-title="Cooking",Tastemade | #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] | ||||||
| https://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8 | 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;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 | 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="" | #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="UK" tvg-language="English" tvg-logo="https://i.imgur.com/eNPIQ9f.png" group-title="News",BBC News HD (720p) [Not 24/7] | #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/index.m3u8 | http://1111296894.rsc.cdn77.org/LS-ATL-54548-6/playlist.m3u8 | ||||||
| #EXTINF:-1 tvg-id="" tvg-country="INT" tvg-language="" tvg-logo="" group-title="Cooking",Tastemade | #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://tastemade-freetv16min-plex.amagi.tv/hls/amagi_hls_data_tastemade-tastemadefreetv16-plex/CDN/playlist.m3u8 | https://stream.visit-x.tv/vxtv/ngrp:live_all/playlist.m3u8 | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
| #EXTM3U x-tvg-url="" | #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 | #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 | 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} |  | ||||||
							
								
								
									
										29
									
								
								tests/__data__/expected/logs/generators/categories.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/__data__/expected/logs/generators/categories.log
									
									
									
									
									
										Normal file
									
								
							| @@ -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} | ||||||
							
								
								
									
										251
									
								
								tests/__data__/expected/logs/generators/countries.log
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								tests/__data__/expected/logs/generators/countries.log
									
									
									
									
									
										Normal file
									
								
							| @@ -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"} | {"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"} | ||||||
| {"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"} | {"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"} | ||||||
| {"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"} | {"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"} | ||||||
| {"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"} | {"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"} | ||||||
| {"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"} | {"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"} | ||||||
| {"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":"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"} | {"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"} | ||||||
| {"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"} | {"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"} | ||||||
| {"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_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 path = require('path') | ||||||
| const { execSync } = require('child_process') | 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) { | function content(filepath) { | ||||||
|   return fs.readFileSync(`tests/__data__/${filepath}`, { |   return fs.readFileSync(`tests/__data__/${filepath}`, { | ||||||
|     encoding: 'utf8' |     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') |  | ||||||
|   ) |  | ||||||
| }) |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user