Create /helpers directory
This commit is contained in:
parent
f164c948ed
commit
16f409856e
|
@ -6,11 +6,11 @@ log.print = function (string) {
|
||||||
|
|
||||||
log.start = function () {
|
log.start = function () {
|
||||||
this.print('Starting...\n')
|
this.print('Starting...\n')
|
||||||
console.time('\nDone in')
|
console.time('Done in')
|
||||||
}
|
}
|
||||||
|
|
||||||
log.finish = function () {
|
log.finish = function () {
|
||||||
console.timeEnd('\nDone in')
|
console.timeEnd('Done in')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = log
|
module.exports = log
|
|
@ -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
|
|
@ -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 escapeStringRegexp = require('escape-string-regexp')
|
||||||
const markdownInclude = require('markdown-include')
|
|
||||||
const iso6393 = require('@freearhey/iso-639-3')
|
|
||||||
const transliteration = require('transliteration')
|
const transliteration = require('transliteration')
|
||||||
const regions = require('./regions')
|
const iso6393 = require('@freearhey/iso-639-3')
|
||||||
const categories = require('./categories')
|
const categories = require('./categories')
|
||||||
|
const regions = require('./regions')
|
||||||
|
|
||||||
|
const utils = {}
|
||||||
const intlDisplayNames = new Intl.DisplayNames(['en'], {
|
const intlDisplayNames = new Intl.DisplayNames(['en'], {
|
||||||
style: 'narrow',
|
style: 'narrow',
|
||||||
type: 'region'
|
type: 'region'
|
||||||
})
|
})
|
||||||
|
|
||||||
const utils = {}
|
|
||||||
|
|
||||||
utils.name2id = function (name) {
|
utils.name2id = function (name) {
|
||||||
return transliteration
|
return transliteration
|
||||||
.transliterate(name)
|
.transliterate(name)
|
||||||
|
@ -77,8 +71,18 @@ utils.sortBy = function (arr, fields) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.getBasename = function (filename) {
|
utils.escapeStringRegexp = function (scring) {
|
||||||
return path.basename(filename, path.extname(filename))
|
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 = '') {
|
utils.filterPlaylists = function (arr, include = '', exclude = '') {
|
||||||
|
@ -97,82 +101,4 @@ utils.filterPlaylists = function (arr, include = '', exclude = '') {
|
||||||
return arr
|
return arr
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.generateTable = function (data, options) {
|
|
||||||
let output = '<table>\n'
|
|
||||||
|
|
||||||
output += '\t<thead>\n\t\t<tr>'
|
|
||||||
for (let column of options.columns) {
|
|
||||||
output += `<th align="${column.align}">${column.name}</th>`
|
|
||||||
}
|
|
||||||
output += '</tr>\n\t</thead>\n'
|
|
||||||
|
|
||||||
output += '\t<tbody>\n'
|
|
||||||
for (let item of data) {
|
|
||||||
output += '\t\t<tr>'
|
|
||||||
let i = 0
|
|
||||||
for (let prop in item) {
|
|
||||||
const column = options.columns[i]
|
|
||||||
let nowrap = column.nowrap
|
|
||||||
let align = column.align
|
|
||||||
output += `<td align="${align}"${nowrap ? ' nowrap' : ''}>${item[prop]}</td>`
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
output += '</tr>\n'
|
|
||||||
}
|
|
||||||
output += '\t</tbody>\n'
|
|
||||||
|
|
||||||
output += '</table>'
|
|
||||||
|
|
||||||
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
|
module.exports = utils
|
|
@ -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
|
|
Loading…
Reference in New Issue