iptv-checker
Version:
Node.js CLI tool for checking links in IPTV playlists
169 lines (150 loc) • 4.59 kB
JavaScript
const curlirize = require('axios-curlirize')
const axios = require('axios')
const https = require('https')
const errors = require('./errors')
const CancelToken = axios.CancelToken
module.exports.loadPlaylist = loadPlaylist
module.exports.loadStream = loadStream
const playlistClient = axios.create({
method: 'GET',
timeout: 60000, // 60 second timeout
responseType: 'text',
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
})
playlistClient.interceptors.response.use(
response => {
const { 'content-type': contentType = '' } = response.headers
if (!/mpegurl/.test(contentType)) {
throw new Error('URL is not an M3U playlist file')
}
return response.data
},
() => {
return Promise.reject('Error fetching playlist')
}
)
const streamClient = axios.create({
method: 'GET',
timeout: 60000,
maxContentLength: 100 * 1024,
httpsAgent: new https.Agent({
rejectUnauthorized: false,
}),
validateStatus: function (status) {
return (status >= 200 && status < 400) || status === 405
},
})
curlirize(streamClient, result => {
const { command } = result
console.log(`CURL: "${command}"`)
})
function loadPlaylist(url) {
return playlistClient(url)
}
function loadStream(item, config, logger) {
if (!/^(http|https)/.test(item.url)) return Promise.resolve()
const userAgent =
item.http && item.http['user-agent']
? item.http['user-agent']
: config.userAgent
const referer =
item.http && item.http.referrer ? item.http.referrer : config.httpReferer
const timeout = item.timeout || config.timeout
const headers = {}
if (userAgent) {
headers['User-Agent'] = userAgent
}
if (referer) {
headers['Referer'] = referer
}
let source = CancelToken.source()
setTimeout(() => {
source.cancel('timeout')
}, timeout)
return streamClient(item.url, {
timeout,
headers,
cancelToken: source.token,
curlirize: config.debug,
})
.then(() => Promise.resolve())
.catch(err => {
const code = parseError(err, config, logger)
if (code === 'HTTP_MAX_CONTENT_LENGTH_EXCEEDED') {
return Promise.resolve()
}
return Promise.reject({
ok: false,
code,
message: errors[code],
})
})
}
function parseError(err, config, logger) {
if (err.response) {
return parseResponseStatus(err.response.status)
} else if (err.message && err.message.startsWith('timeout')) {
return 'HTTP_REQUEST_TIMEOUT'
} else if (err.message && err.message.includes('ECONNREFUSED')) {
return 'HTTP_INTERNAL_SERVER_ERROR'
} else if (err.message && err.message.startsWith('maxContentLength')) {
return 'HTTP_MAX_CONTENT_LENGTH_EXCEEDED'
} else if (err.code === 'EPROTO') {
return 'HTTP_PROTOCOL_ERROR'
} else if (err.code === 'ENETUNREACH') {
return 'HTTP_NETWORK_UNREACHABLE'
} else if (err.code === 'ENOTFOUND') {
return 'HTTP_NOT_FOUND'
} else if (err.code === 'ECONNRESET') {
return 'HTTP_ECONNRESET'
} else if (err.code.startsWith('HPE')) {
return 'HTTP_PARSE_ERROR'
}
logger.debug('HTTP_UNDEFINED')
logger.debug(err)
return 'HTTP_UNDEFINED'
}
function parseResponseStatus(status) {
const codes = {
400: 'HTTP_BAD_REQUEST',
401: 'HTTP_UNAUTHORIZED',
402: 'HTTP_PAYMENT_REQUIRED',
403: 'HTTP_FORBIDDEN',
404: 'HTTP_NOT_FOUND',
405: 'HTTP_METHOD_NOT_ALLOWED',
406: 'HTTP_NOT_ACCEPTABLE',
407: 'HTTP_PROXY_AUTHENTICATION_REQUIRED',
408: 'HTTP_REQUEST_TIMEOUT',
409: 'HTTP_CONFLICT',
410: 'HTTP_GONE',
411: 'HTTP_LENGTH_REQUIRED',
412: 'HTTP_PRECONDITION_FAILED',
413: 'HTTP_REQUEST_TOO_LONG',
414: 'HTTP_REQUEST_URI_TOO_LONG',
415: 'HTTP_UNSUPPORTED_MEDIA_TYPE',
416: 'HTTP_REQUESTED_RANGE_NOT_SATISFIABLE',
417: 'HTTP_EXPECTATION_FAILED',
418: 'HTTP_IM_A_TEAPOT',
419: 'HTTP_INSUFFICIENT_SPACE_ON_RESOURCE',
420: 'HTTP_METHOD_FAILURE',
421: 'HTTP_MISDIRECTED_REQUEST',
422: 'HTTP_UNPROCESSABLE_ENTITY',
423: 'HTTP_LOCKED',
424: 'HTTP_FAILED_DEPENDENCY',
428: 'HTTP_PRECONDITION_REQUIRED',
429: 'HTTP_TOO_MANY_REQUESTS',
431: 'HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE',
451: 'HTTP_UNAVAILABLE_FOR_LEGAL_REASONS',
500: 'HTTP_INTERNAL_SERVER_ERROR',
501: 'HTTP_NOT_IMPLEMENTED',
502: 'HTTP_BAD_GATEWAY',
503: 'HTTP_SERVICE_UNAVAILABLE',
504: 'HTTP_GATEWAY_TIMEOUT',
505: 'HTTP_HTTP_VERSION_NOT_SUPPORTED',
507: 'HTTP_INSUFFICIENT_STORAGE',
511: 'HTTP_NETWORK_AUTHENTICATION_REQUIRED',
}
return codes[status]
}