node-m3u8-downloader
Version:
Downloads an m3u8 playlist to specified output
90 lines (69 loc) • 3.27 kB
JavaScript
const { Parser } = require('m3u8-parser')
const { URL } = require('node:url')
const fs = require('fs')
const fsp = require('fs/promises')
const path = require('path')
const dl = require('./download')
const error = require('./error')
module.exports = function hlshandle(streamUrl, quality = 'highest', cache = path.join(require('os').tmpdir(), 'm3u8dl')) {
return new Promise(async(resolve) => {
if(fs.existsSync(cache)) await fsp.rm(cache, { recursive: true, force: true })
fs.mkdirSync(cache)
let mainParser = new Parser()
let secondParser = new Parser()
let main = path.join(cache, '.master.m3u8')
let dl_r1 = await dl.basicDL(streamUrl, main)
if(dl_r1 !== 100) return resolve(dl_r1)
mainParser.push(fs.readFileSync(main).toString())
mainParser.end()
let mainParsed = mainParser.manifest
if(mainParsed.segments && mainParsed.segments.length > 0) {
return resolve(mainParsed.segments.map(v => {
let uri = v.uri || v.url
if(isUrl(uri)) return v
v.uri = new URL(uri, streamUrl).href
return v
}))
} else if(mainParsed.playlists && Array.isArray(mainParsed.playlists) && mainParsed.playlists.length > 0) {
let withAttributes = mainParsed.playlists.filter(v => v && v.attributes)
let sorted = withAttributes.sort((a, b) => a.attributes.BANDWIDTH - b.attributes.BANDWIDTH)
let all_q = ['highest', 'medium', 'lowest']
let fetched = filter(sorted, quality)
let msg_fetched = all_q.find(v => v === quality) ? 'BAD SOURCE' : `QUALITY CHOSEN NOT in [${all_q.join(', ')}]`
if(!fetched) return resolve(new error(`BAD QUALITY [${quality}]: ${msg_fetched}`))
if(fs.existsSync(main)) await fsp.rm(main, { recursive: true, force: true })
let uri = new URL(fetched.uri, streamUrl).href
let dl_r2 = await dl.basicDL(uri, main)
if(dl_r2 !== 100) return resolve(dl_r2)
secondParser.push(fs.readFileSync(main).toString())
secondParser.end()
let fetchedParsed = secondParser.manifest
if(fetchedParsed.segments && fetchedParsed.segments.length > 0) {
return resolve(fetchedParsed.segments)
} else if(fetchedParsed.playlists && Array.isArray(fetchedParsed.playlists) && fetchedParsed.playlists.length > 0) {
return resolve(await hlshandle(uri, quality, cache))
} else {
return resolve(new error('BAD STREAM'))
}
} else {
return resolve(new error('NO STREAMS FOUND'))
}
})
}
function filter(all, quality) {
if (quality === 'highest') {
return all[all.length - 1]
} else if (quality === 'lowest') {
return all[0]
} else if (quality === 'medium') {
return all[Math.floor(all.length / 2)]
} else return null
}
function isUrl(input) {
try {
new URL(input)
return true
} catch (_) {
return false
}
}