This commit is contained in:
Aleksandr Statciuk 2022-02-07 00:04:56 +03:00
parent d604f35ba1
commit 9a4a62fd10
45 changed files with 733 additions and 35060 deletions

View file

@ -16,11 +16,25 @@ class API {
find(query) {
return _.find(this.collection, query)
}
filter(query) {
return _.filter(this.collection, query)
}
all() {
return this.collection
}
}
const api = {}
api.channels = new API(`${DATA_DIR}/channels.json`)
api.countries = new API(`${DATA_DIR}/countries.json`)
api.guides = new API(`${DATA_DIR}/guides.json`)
api.categories = new API(`${DATA_DIR}/categories.json`)
api.languages = new API(`${DATA_DIR}/languages.json`)
api.regions = new API(`${DATA_DIR}/regions.json`)
api.statuses = new API(`${DATA_DIR}/statuses.json`)
api.blocklist = new API(`${DATA_DIR}/blocklist.json`)
module.exports = api

View file

@ -1,61 +1,75 @@
const Database = require('nedb-promises')
const nedb = require('nedb-promises')
const file = require('./file')
const DB_FILEPATH = process.env.DB_FILEPATH || './scripts/channels.db'
const DB_DIR = process.env.DB_DIR || './scripts/database'
const nedb = Database.create({
filename: file.resolve(DB_FILEPATH),
autoload: true,
onload(err) {
if (err) console.error(err)
},
compareStrings: (a, b) => {
a = a.replace(/\s/g, '_')
b = b.replace(/\s/g, '_')
class Database {
constructor(filepath) {
this.filepath = filepath
}
return a.localeCompare(b, undefined, {
sensitivity: 'accent',
numeric: true
load() {
this.db = nedb.create({
filename: file.resolve(this.filepath),
autoload: true,
onload: err => {
if (err) console.error(err)
},
compareStrings: (a, b) => {
a = a.replace(/\s/g, '_')
b = b.replace(/\s/g, '_')
return a.localeCompare(b, undefined, {
sensitivity: 'accent',
numeric: true
})
}
})
}
})
removeIndex(field) {
return this.db.removeIndex(field)
}
addIndex(options) {
return this.db.ensureIndex(options)
}
compact() {
return this.db.persistence.compactDatafile()
}
stopAutocompact() {
return this.db.persistence.stopAutocompaction()
}
reset() {
return file.clear(this.filepath)
}
count(query) {
return this.db.count(query)
}
insert(doc) {
return this.db.insert(doc)
}
update(query, update) {
return this.db.update(query, update)
}
find(query) {
return this.db.find(query)
}
remove(query, options) {
return this.db.remove(query, options)
}
}
const db = {}
db.removeIndex = function (field) {
return nedb.removeIndex(field)
}
db.addIndex = function (options) {
return nedb.ensureIndex(options)
}
db.compact = function () {
return nedb.persistence.compactDatafile()
}
db.reset = function () {
return file.clear(DB_FILEPATH)
}
db.count = function (query) {
return nedb.count(query)
}
db.insert = function (doc) {
return nedb.insert(doc)
}
db.update = function (query, update) {
return nedb.update(query, update)
}
db.find = function (query) {
return nedb.find(query)
}
db.remove = function (query, options) {
return nedb.remove(query, options)
}
db.streams = new Database(`${DB_DIR}/streams.db`)
module.exports = db

View file

@ -1,6 +1,9 @@
const { create: createPlaylist } = require('./playlist')
const store = require('./store')
const path = require('path')
const glob = require('glob')
const fs = require('mz/fs')
const _ = require('lodash')
const file = {}
@ -64,4 +67,45 @@ file.basename = function (filepath) {
return path.basename(filepath)
}
// file.saveAsM3U = async function (filepath, items, options = {}) {
// const playlist = createPlaylist(filepath)
// const header = {}
// if (options.public) {
// let guides = items.map(item => item.guides)
// guides = _.uniq(_.flatten(guides)).sort().join(',')
// header['x-tvg-url'] = guides
// }
// playlist.setHeader(header)
// for (const item of items) {
// const stream = store.create(item)
// let attrs
// if (options.public) {
// attrs = {
// 'tvg-id': stream.get('tvg_id'),
// 'tvg-country': stream.get('tvg_country'),
// 'tvg-language': stream.get('tvg_language'),
// 'tvg-logo': stream.get('tvg_logo'),
// 'user-agent': stream.get('http.user-agent') || undefined,
// 'group-title': stream.get('group_title')
// }
// } else {
// attrs = {
// 'tvg-id': stream.get('tvg_id'),
// 'user-agent': stream.get('http.user-agent') || undefined
// }
// }
// playlist.add(stream.get('url'), stream.get('display_name'), attrs, {
// 'http-referrer': stream.get('http.referrer') || undefined,
// 'http-user-agent': stream.get('http.user-agent') || undefined
// })
// }
// return file.write(filepath, playlist.toString())
// }
module.exports = file

View file

@ -1,119 +1,23 @@
const { create: createPlaylist } = require('./playlist')
const store = require('./store')
const file = require('./file')
const logger = require('./logger')
const db = require('./db')
const _ = require('lodash')
const generators = require('../generators')
const LOGS_DIR = process.env.LOGS_DIR || 'scripts/logs/generators'
const generator = {}
generator.generate = async function (filepath, query = {}, options = {}) {
options = {
...{
format: 'm3u',
saveEmpty: false,
includeNSFW: false,
includeGuides: true,
includeBroken: false,
onLoad: r => r,
uniqBy: item => item.id || _.uniqueId(),
sortBy: null
},
...options
}
query['is_nsfw'] = options.includeNSFW ? { $in: [true, false] } : false
query['is_broken'] = options.includeBroken ? { $in: [true, false] } : false
let items = await db
.find(query)
.sort({ name: 1, 'status.level': 1, 'resolution.height': -1, url: 1 })
items = _.uniqBy(items, 'url')
if (!options.saveEmpty && !items.length) return { filepath, query, options, count: 0 }
if (options.uniqBy) items = _.uniqBy(items, options.uniqBy)
items = options.onLoad(items)
if (options.sortBy) items = _.sortBy(items, options.sortBy)
switch (options.format) {
case 'json':
await saveAsJSON(filepath, items, options)
break
case 'm3u':
default:
await saveAsM3U(filepath, items, options)
break
}
return { filepath, query, options, count: items.length }
}
async function saveAsM3U(filepath, items, options = {}) {
const playlist = await createPlaylist(filepath)
const header = {}
if (options.public) {
let guides = items.map(item => item.guides)
guides = _.uniq(_.flatten(guides)).sort().join(',')
header['x-tvg-url'] = guides
}
await playlist.header(header)
for (const item of items) {
const stream = store.create(item)
let attrs
if (options.public) {
attrs = {
'tvg-id': stream.get('tvg_id'),
'tvg-country': stream.get('tvg_country'),
'tvg-language': stream.get('tvg_language'),
'tvg-logo': stream.get('tvg_logo'),
'user-agent': stream.get('http.user-agent') || undefined,
'group-title': stream.get('group_title')
}
} else {
attrs = {
'tvg-id': stream.get('tvg_id'),
'user-agent': stream.get('http.user-agent') || undefined
}
generator.generate = async function (name, items = []) {
if (typeof generators[name] === 'function') {
try {
const logs = await generators[name].bind()(items)
await file.create(`${LOGS_DIR}/${name}.log`, logs.map(toJSON).join('\n'))
} catch (error) {
logger.error(`generators/${name}.js: ${error.message}`)
}
await playlist.link(stream.get('url'), stream.get('display_name'), attrs, {
'http-referrer': stream.get('http.referrer') || undefined,
'http-user-agent': stream.get('http.user-agent') || undefined
})
}
}
async function saveAsJSON(filepath, items, options) {
const output = items.map(item => {
const stream = store.create(item)
const categories = stream.get('categories').map(c => ({ name: c.name, slug: c.slug }))
const countries = stream.get('countries').map(c => ({ name: c.name, code: c.code }))
return {
name: stream.get('name'),
logo: stream.get('logo'),
url: stream.get('url'),
categories,
countries,
languages: stream.get('languages'),
tvg: {
id: stream.get('tvg_id'),
name: stream.get('name'),
url: stream.get('tvg_url')
}
}
})
await file.create(filepath, JSON.stringify(output))
}
generator.saveAsM3U = saveAsM3U
generator.saveAsJSON = saveAsJSON
module.exports = generator
function toJSON(item) {
return JSON.stringify(item)
}

View file

@ -1,49 +1,92 @@
const file = require('./file')
const store = require('./store')
const _ = require('lodash')
const playlist = {}
playlist.create = async function (filepath) {
playlist.filepath = filepath
const dir = file.dirname(filepath)
file.createDir(dir)
await file.create(filepath, '')
class Playlist {
constructor() {
this.links = []
}
return playlist
setHeader(attrs = {}) {
this.header = attrs
}
add(url, title, attrs, vlcOpts) {
this.links.push({ url, title, attrs, vlcOpts })
}
toString() {
let output = `#EXTM3U`
for (const attr in this.header) {
const value = this.header[attr]
output += ` ${attr}="${value}"`
}
output += `\n`
for (const link of this.links) {
output += `#EXTINF:-1`
for (const name in link.attrs) {
const value = link.attrs[name]
if (value !== undefined) {
output += ` ${name}="${value}"`
}
}
output += `,${link.title}\n`
for (const name in link.vlcOpts) {
const value = link.vlcOpts[name]
if (value !== undefined) {
output += `#EXTVLCOPT:${name}=${value}\n`
}
}
output += `${link.url}\n`
}
return output
}
}
playlist.header = async function (attrs) {
let header = `#EXTM3U`
for (const name in attrs) {
const value = attrs[name]
header += ` ${name}="${value}"`
playlist.create = function (items = [], options = {}) {
const p = new Playlist()
const header = {}
if (options.public) {
let guides = items.map(item => item.guides)
guides = _.uniq(_.flatten(guides)).sort().join(',')
header['x-tvg-url'] = guides
}
header += `\n`
p.setHeader(header)
await file.append(playlist.filepath, header)
for (const item of items) {
const stream = store.create(item)
return playlist
}
playlist.link = async function (url, title, attrs, vlcOpts) {
let link = `#EXTINF:-1`
for (const name in attrs) {
const value = attrs[name]
if (value !== undefined) {
link += ` ${name}="${value}"`
let attrs
if (options.public) {
attrs = {
'tvg-id': stream.get('tvg_id'),
'tvg-country': stream.get('tvg_country'),
'tvg-language': stream.get('tvg_language'),
'tvg-logo': stream.get('tvg_logo'),
'user-agent': stream.get('http.user-agent') || undefined,
'group-title': stream.get('group_title')
}
} else {
attrs = {
'tvg-id': stream.get('tvg_id'),
'user-agent': stream.get('http.user-agent') || undefined
}
}
}
link += `,${title}\n`
for (const name in vlcOpts) {
const value = vlcOpts[name]
if (value !== undefined) {
link += `#EXTVLCOPT:${name}=${value}\n`
}
}
link += `${url}\n`
await file.append(playlist.filepath, link)
p.add(stream.get('url'), stream.get('title'), attrs, {
'http-referrer': stream.get('http.referrer') || undefined,
'http-user-agent': stream.get('http.user-agent') || undefined
})
}
return playlist
return p
}
module.exports = playlist