91 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			91 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const { file, parser, logger, checker, m3u } = require('../../core')
 | |
| const { program } = require('commander')
 | |
| 
 | |
| 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)
 | |
| 
 | |
| const options = program.opts()
 | |
| 
 | |
| 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
 | |
|     logger.info(`${filepath}`)
 | |
|     const playlist = await parser.parsePlaylist(filepath)
 | |
|     const before = playlist.items.length
 | |
|     for (const stream of playlist.items) {
 | |
|       if (options.debug) logger.info(stream.url)
 | |
|       const [_, status] = stream.raw.match(/status="([a-z]+)"/) || [null, null]
 | |
|       stream.status = status
 | |
|       if (status === 'error' && /^(http|https)/.test(stream.url)) {
 | |
|         const result = await checkStream(stream)
 | |
|         const newStatus = parseStatus(result.error)
 | |
|         if (status === newStatus) {
 | |
|           stream.remove = true
 | |
|           logger.info(`removed "${stream.name}"`)
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     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
 | |
|         }
 | |
|       }))
 | |
| 
 | |
|     if (before !== items.length) {
 | |
|       const output = m3u.create(items)
 | |
|       await file.create(filepath, output)
 | |
|       logger.info(`saved`)
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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'
 | |
|   }
 | |
| }
 |