2022-03-11 16:32:53 +01:00
|
|
|
const { file, parser, logger, checker, m3u } = require('../../core')
|
|
|
|
const { program } = require('commander')
|
|
|
|
|
|
|
|
const options = program
|
|
|
|
.argument('[filepath]', 'Path to file to validate')
|
|
|
|
.option('-t, --timeout <timeout>', 'Set timeout for each request', parser.parseNumber, 60000)
|
|
|
|
.option('-d, --delay <delay>', 'Set delay for each request', parser.parseNumber, 0)
|
|
|
|
.option('--debug', 'Enable debug mode')
|
|
|
|
.parse(process.argv)
|
|
|
|
|
|
|
|
async function main() {
|
|
|
|
const files = program.args.length ? program.args : await file.list('streams/*.m3u')
|
|
|
|
|
|
|
|
for (const filepath of files) {
|
|
|
|
if (!filepath.endsWith('.m3u')) continue
|
2022-03-11 16:38:30 +01:00
|
|
|
logger.info(`${filepath}`)
|
2022-03-11 16:32:53 +01:00
|
|
|
const playlist = await parser.parsePlaylist(filepath)
|
2022-03-11 16:38:30 +01:00
|
|
|
const before = playlist.items.length
|
2022-03-11 16:32:53 +01:00
|
|
|
for (const stream of playlist.items) {
|
|
|
|
const [_, status] = stream.raw.match(/status="([a-z]+)"/) || [null, null]
|
|
|
|
stream.status = status
|
2022-03-11 17:03:30 +01:00
|
|
|
if (status === 'error' && /^(http|https)/.test(stream.url) && !/\[.*\]$/.test(stream.name)) {
|
2022-03-11 16:32:53 +01:00
|
|
|
const result = await checkStream(stream)
|
|
|
|
const newStatus = parseStatus(result.error)
|
|
|
|
if (status === newStatus) {
|
|
|
|
stream.remove = true
|
2022-03-11 16:38:30 +01:00
|
|
|
logger.info(`removed "${stream.name}"`)
|
2022-03-11 16:32:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const items = playlist.items
|
|
|
|
.filter(i => !i.remove)
|
|
|
|
.map(item => ({
|
|
|
|
attrs: {
|
|
|
|
'tvg-id': item.tvg.id,
|
|
|
|
status: item.status,
|
|
|
|
'user-agent': item.http['user-agent'] || undefined
|
|
|
|
},
|
|
|
|
title: item.name,
|
|
|
|
url: item.url,
|
|
|
|
vlcOpts: {
|
|
|
|
'http-referrer': item.http.referrer || undefined,
|
|
|
|
'http-user-agent': item.http['user-agent'] || undefined
|
|
|
|
}
|
|
|
|
}))
|
|
|
|
|
2022-03-11 16:38:30 +01:00
|
|
|
if (before !== items.length) {
|
|
|
|
const output = m3u.create(items)
|
|
|
|
await file.create(filepath, output)
|
|
|
|
logger.info(`saved`)
|
|
|
|
}
|
2022-03-11 16:32:53 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
main()
|
|
|
|
|
|
|
|
async function checkStream(item) {
|
|
|
|
const config = {
|
|
|
|
timeout: options.timeout,
|
|
|
|
delay: options.delay,
|
|
|
|
debug: options.debug
|
|
|
|
}
|
|
|
|
|
|
|
|
const request = {
|
|
|
|
url: item.url,
|
|
|
|
http: {
|
|
|
|
referrer: item.http.referrer,
|
|
|
|
'user-agent': item.http['user-agent']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return checker.check(request, config)
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseStatus(error) {
|
|
|
|
if (!error) return 'online'
|
|
|
|
|
|
|
|
switch (error) {
|
|
|
|
case 'Operation timed out':
|
|
|
|
return 'timeout'
|
|
|
|
case 'Server returned 403 Forbidden (access denied)':
|
|
|
|
return 'blocked'
|
|
|
|
default:
|
|
|
|
return 'error'
|
|
|
|
}
|
|
|
|
}
|