diff --git a/scripts/blacklist.json b/scripts/helpers/blacklist.json similarity index 100% rename from scripts/blacklist.json rename to scripts/helpers/blacklist.json diff --git a/scripts/categories.json b/scripts/helpers/categories.json similarity index 100% rename from scripts/categories.json rename to scripts/helpers/categories.json diff --git a/scripts/db.js b/scripts/helpers/db.js similarity index 100% rename from scripts/db.js rename to scripts/helpers/db.js diff --git a/scripts/log.js b/scripts/helpers/log.js similarity index 76% rename from scripts/log.js rename to scripts/helpers/log.js index a4b580cc87..a2e8fe0a2e 100644 --- a/scripts/log.js +++ b/scripts/helpers/log.js @@ -6,11 +6,11 @@ log.print = function (string) { log.start = function () { this.print('Starting...\n') - console.time('\nDone in') + console.time('Done in') } log.finish = function () { - console.timeEnd('\nDone in') + console.timeEnd('Done in') } module.exports = log diff --git a/scripts/helpers/parser.js b/scripts/helpers/parser.js new file mode 100644 index 0000000000..815c819ec0 --- /dev/null +++ b/scripts/helpers/parser.js @@ -0,0 +1,24 @@ +const playlistParser = require('iptv-playlist-parser') +const Playlist = require('./Playlist') +const utils = require('./utils') +const file = require('./file') + +const parser = {} + +parser.parseIndex = function () { + const content = file.read('index.m3u') + const result = playlistParser.parse(content) + + return result.items +} + +parser.parsePlaylist = async function (url) { + const content = file.read(url) + const result = playlistParser.parse(content) + const name = file.getFilename(url) + const country = utils.code2name(name) + + return new Playlist({ header: result.header, items: result.items, url, country, name }) +} + +module.exports = parser diff --git a/scripts/regions.json b/scripts/helpers/regions.json similarity index 100% rename from scripts/regions.json rename to scripts/helpers/regions.json diff --git a/scripts/utils.js b/scripts/helpers/utils.js similarity index 58% rename from scripts/utils.js rename to scripts/helpers/utils.js index 08465b4bda..7a2bae0633 100644 --- a/scripts/utils.js +++ b/scripts/helpers/utils.js @@ -1,21 +1,15 @@ -const fs = require('fs') -const path = require('path') -const axios = require('axios') -const zlib = require('zlib') -const urlParser = require('url') const escapeStringRegexp = require('escape-string-regexp') -const markdownInclude = require('markdown-include') -const iso6393 = require('@freearhey/iso-639-3') const transliteration = require('transliteration') -const regions = require('./regions') +const iso6393 = require('@freearhey/iso-639-3') const categories = require('./categories') +const regions = require('./regions') + +const utils = {} const intlDisplayNames = new Intl.DisplayNames(['en'], { style: 'narrow', type: 'region' }) -const utils = {} - utils.name2id = function (name) { return transliteration .transliterate(name) @@ -77,8 +71,18 @@ utils.sortBy = function (arr, fields) { }) } -utils.getBasename = function (filename) { - return path.basename(filename, path.extname(filename)) +utils.escapeStringRegexp = function (scring) { + return escapeStringRegexp(string) +} + +utils.sleep = function (ms) { + return function (x) { + return new Promise(resolve => setTimeout(() => resolve(x), ms)) + } +} + +utils.removeProtocol = function (string) { + return string.replace(/(^\w+:|^)\/\//, '') } utils.filterPlaylists = function (arr, include = '', exclude = '') { @@ -97,82 +101,4 @@ utils.filterPlaylists = function (arr, include = '', exclude = '') { return arr } -utils.generateTable = function (data, options) { - let output = '\n' - - output += '\t\n\t\t' - for (let column of options.columns) { - output += `` - } - output += '\n\t\n' - - output += '\t\n' - for (let item of data) { - output += '\t\t' - let i = 0 - for (let prop in item) { - const column = options.columns[i] - let nowrap = column.nowrap - let align = column.align - output += `` - i++ - } - output += '\n' - } - output += '\t\n' - - output += '
${column.name}
${item[prop]}
' - - return output -} - -utils.createDir = function (dir) { - if (!fs.existsSync(dir)) { - fs.mkdirSync(dir) - } -} - -utils.readFile = function (filename) { - return fs.readFileSync(path.resolve(__dirname) + `/../${filename}`, { encoding: 'utf8' }) -} - -utils.appendToFile = function (filename, data) { - fs.appendFileSync(path.resolve(__dirname) + '/../' + filename, data) -} - -utils.compileMarkdown = function (filepath) { - return markdownInclude.compileFiles(path.resolve(__dirname, filepath)) -} - -utils.escapeStringRegexp = function (scring) { - return escapeStringRegexp(string) -} - -utils.createFile = function (filename, data = '') { - fs.writeFileSync(path.resolve(__dirname) + '/../' + filename, data) -} - -utils.sleep = function (ms) { - return function (x) { - return new Promise(resolve => setTimeout(() => resolve(x), ms)) - } -} - -utils.removeProtocol = function (string) { - return string.replace(/(^\w+:|^)\/\//, '') -} - -utils.savePlaylist = async function (playlist) { - const original = utils.readFile(playlist.url) - const output = playlist.toString({ raw: true }) - - if (original === output) { - return false - } else { - utils.createFile(playlist.url, output) - } - - return true -} - module.exports = utils diff --git a/scripts/parser.js b/scripts/parser.js deleted file mode 100644 index 0c86a02069..0000000000 --- a/scripts/parser.js +++ /dev/null @@ -1,244 +0,0 @@ -const playlistParser = require('iptv-playlist-parser') -const utils = require('./utils') -const categories = require('./categories') -const path = require('path') - -const sfwCategories = categories.filter(c => !c.nsfw).map(c => c.name) -const nsfwCategories = categories.filter(c => c.nsfw).map(c => c.name) - -const parser = {} - -parser.parseIndex = function () { - const content = utils.readFile('index.m3u') - const result = playlistParser.parse(content) - - return result.items -} - -parser.parsePlaylist = async function (filename) { - const content = utils.readFile(filename) - const result = playlistParser.parse(content) - const name = path.parse(filename).name - const country = utils.code2name(name) - - return new Playlist({ header: result.header, items: result.items, url: filename, country, name }) -} - -class Playlist { - constructor({ header, items, url, name, country }) { - this.url = url - this.name = name - this.country = country - this.header = header - this.channels = items - .map(item => new Channel({ data: item, header, sourceUrl: url })) - .filter(channel => channel.url) - } - - toString(options = {}) { - const config = { raw: false, ...options } - let parts = ['#EXTM3U'] - for (let key in this.header.attrs) { - let value = this.header.attrs[key] - if (value) { - parts.push(`${key}="${value}"`) - } - } - - let output = `${parts.join(' ')}\n` - for (let channel of this.channels) { - output += channel.toString(config.raw) - } - - return output - } -} - -class Channel { - constructor({ data, header, sourceUrl }) { - this.parseData(data) - - this.filename = utils.getBasename(sourceUrl) - if (!this.countries.length) { - const countryName = utils.code2name(this.filename) - this.countries = countryName ? [{ code: this.filename, name: countryName }] : [] - this.tvg.country = this.countries.map(c => c.code.toUpperCase()).join(';') - } - } - - parseData(data) { - const title = this.parseTitle(data.name) - - this.tvg = data.tvg - this.http = data.http - this.url = data.url - this.logo = data.tvg.logo - this.name = title.channelName - this.status = title.streamStatus - this.resolution = title.streamResolution - this.countries = this.parseCountries(data.tvg.country) - this.languages = this.parseLanguages(data.tvg.language) - this.category = this.parseCategory(data.group.title) - this.raw = data.raw - } - - parseCountries(string) { - let arr = string - .split(';') - .reduce((acc, curr) => { - const codes = utils.region2codes(curr) - if (codes.length) { - for (let code of codes) { - if (!acc.includes(code)) { - acc.push(code) - } - } - } else { - acc.push(curr) - } - - return acc - }, []) - .filter(code => code && utils.code2name(code)) - - return arr.map(code => { - return { code: code.toLowerCase(), name: utils.code2name(code) } - }) - } - - parseLanguages(string) { - return string - .split(';') - .map(name => { - const code = name ? utils.language2code(name) : null - if (!code) return null - - return { - code, - name - } - }) - .filter(l => l) - } - - parseCategory(string) { - const category = categories.find(c => c.id === string.toLowerCase()) - - return category ? category.name : '' - } - - parseTitle(title) { - const channelName = title - .trim() - .split(' ') - .map(s => s.trim()) - .filter(s => { - return !/\[|\]/i.test(s) && !/\((\d+)P\)/i.test(s) - }) - .join(' ') - - const streamStatusMatch = title.match(/\[(.*)\]/i) - const streamStatus = streamStatusMatch ? streamStatusMatch[1] : null - - const streamResolutionMatch = title.match(/\((\d+)P\)/i) - const streamResolutionHeight = streamResolutionMatch ? parseInt(streamResolutionMatch[1]) : null - const streamResolution = { width: null, height: streamResolutionHeight } - - return { channelName, streamStatus, streamResolution } - } - - get tvgCountry() { - return this.tvg.country - .split(';') - .map(code => utils.code2name(code)) - .join(';') - } - - get tvgLanguage() { - return this.tvg.language - } - - get tvgUrl() { - return this.tvg.id && this.tvg.url ? this.tvg.url : '' - } - - get tvgId() { - if (this.tvg.id) { - return this.tvg.id - } else if (this.filename !== 'unsorted') { - const id = utils.name2id(this.tvgName) - - return id ? `${id}.${this.filename}` : '' - } - - return '' - } - - get tvgName() { - if (this.tvg.name) { - return this.tvg.name - } else if (this.filename !== 'unsorted') { - return this.name.replace(/\"/gi, '') - } - - return '' - } - - getInfo() { - this.tvg.country = this.tvg.country.toUpperCase() - - let info = `-1 tvg-id="${this.tvgId}" tvg-name="${this.tvgName}" tvg-country="${this.tvg.country}" tvg-language="${this.tvg.language}" tvg-logo="${this.logo}"` - - info += ` group-title="${this.category}",${this.name}` - - if (this.resolution.height) { - info += ` (${this.resolution.height}p)` - } - - if (this.status) { - info += ` [${this.status}]` - } - - if (this.http['referrer']) { - info += `\n#EXTVLCOPT:http-referrer=${this.http['referrer']}` - } - - if (this.http['user-agent']) { - info += `\n#EXTVLCOPT:http-user-agent=${this.http['user-agent']}` - } - - return info - } - - toString(raw = false) { - if (raw) return this.raw + '\n' - - return '#EXTINF:' + this.getInfo() + '\n' + this.url + '\n' - } - - toObject() { - return { - name: this.name, - logo: this.logo || null, - url: this.url, - category: this.category || null, - languages: this.languages, - countries: this.countries, - tvg: { - id: this.tvgId || null, - name: this.tvgName || null, - url: this.tvgUrl || null - } - } - } - - isSFW() { - return sfwCategories.includes(this.category) - } - - isNSFW() { - return nsfwCategories.includes(this.category) - } -} - -module.exports = parser