UNPKG

oimi-helper

Version:

this is a helper for ffandown

228 lines (225 loc) 7.51 kB
const childProcess = require('child_process') const fse = require('fs-extra') const path = require('path') const os = require('os') const ffmpeg = require('fluent-ffmpeg') const download = require('download') const Helper = { version: '4.4.1', registryUrl: 'https://pic.kblue.site', registryMap: { github: 'https://nn.oimi.space/https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1', selfCdn: 'https://pic.kblue.site', }, decencyList: ['ffmpeg', 'ffprobe'], /** * @description change ffmpeg dependency download registry * @param {string} nameOrBaseUrl */ changeRegistry (nameOrBaseUrl) { if (!nameOrBaseUrl) throw new Error('nameOrBaseUrl cant be none') const isInnerRegistry = Object.keys(this.registryMap).includes(nameOrBaseUrl) if (isInnerRegistry) { this.registryUrl = this.registryMap[nameOrBaseUrl] } else { this.registryUrl = nameOrBaseUrl } }, /** * @description: judge input path is a directory * @param {string} pathDir path * @return {boolean} true: path is a file */ isExist (pathDir) { return fse.pathExistsSync(pathDir) }, /** * @description exec command * @param {string} cmd * @returns {*} */ execCmd (cmd) { return new Promise((resolve, reject) => { childProcess.exec( cmd, (error) => { if (error) { reject(error) } else { resolve() } }, ) }) }, async chmod (file) { const supported = ['linux', 'darwin'] return new Promise((resolve, reject) => { if (supported.indexOf(process.platform) === -1) { reject(new Error('the platform not support auto chmod, please do it by yourself')) } else { const cmd = `chmod +x ${file}` this.execCmd(cmd).then(() => { resolve('chmod success') }).catch((e) => reject(e)) } }) }, /** * @description: get lib path * @param {string} componentName path * @return {string} lib path */ getLibPath (componentName = 'ffmpeg') { const executableFileSuffix = os.platform().startsWith('win') ? `${componentName}.exe` : componentName return path.join(process.cwd(), `lib/${executableFileSuffix}`) }, /** * @description get current device platform * @returns {string|null} platform */ detectPlatform () { let type = (os.type()).toLowerCase() let arch = (os.arch()).toLowerCase() if (type === 'darwin') { return 'osx-64' } if (type === 'windows_nt') { return arch === 'x64' ? 'windows-64' : 'windows-32' } if (type === 'linux') { if (arch === 'arm' || arch === 'arm64') { return 'linux-armel' } return arch === 'x64' ? 'linux-64' : 'linux-32' } return null }, /** * get binaries download urls * @param {String} component ffmpeg ffprobe * @returns {String} download url */ getDownloadUrl (component, version = '4.4.1') { return `${this.registryUrl}/${component}-${version}-${this.detectPlatform()}.zip` }, setEnv (type, path) { if (type === 'ffmpeg') { ffmpeg.setFfmpegPath(path) } if (type === 'ffprobe') { ffmpeg.setFfprobePath(path) } console.log(`\x1b[32m[ffandown] ${type}: env variable is set successfully\x1b[0m`) }, /** * is need download * @param {String} type ffmpeg ffprobe * @returns {boolean} true: need to download ffmpeg */ needDownload (type) { const libPath = this.getLibPath(type) const isExist = this.isExist(libPath) if (isExist) { this.setEnv(type, libPath) return false } return true }, /** * @param {ffmpeg|ffprobe} type * @returns */ getLibsStatus (type) { return { type, libPath: this.getLibPath(type), downloadURL: this.getDownloadUrl(type, '4.4.1'), download: this.needDownload(type), } }, /** * @description * @param {string} url download url * @param {string} libPath lib path * @param {string} type 类型 ffmpeg/ffprobe * @return {void} */ async downloadAndSetEnv (url, libPath, type) { try { // download ffmpeg or ffprobe dependency const ORA = await import('ora') const ora = ORA?.default const spinner = ora(`downloading ${type}...`).start() try { await download(url, 'lib', { extract: true }) spinner.succeed(`downloaded ${type}`) } catch (error) { spinner.fail(`download ${type} failed`) throw error } this.setEnv(type, libPath) await this.chmod(libPath) } catch (e) { console.warn('download and set env failed:' + String(e).trim()) } }, /** * download ffbinary * @param {Array} libs * @returns */ downloadDependency (libs = this.decencyList) { return new Promise((resolve, reject) => { const arr = libs .map(i => this.getLibsStatus(i)) .filter(i => i.download) .map(i => this.downloadAndSetEnv(i.downloadURL, i.libPath, i.type)) Promise.allSettled(arr).then(results => { const isFailed = results.filter(item => item.status === 'rejected') if (isFailed.length > 0) { reject(isFailed.map(i => i.error).join('/n')) } else { resolve('download binaries success') } }) }) }, /** * @description: make sure directory exists * @param {string} _path configuration path * @return {string} real download directory */ ensurePath (_path) { if (_path.startsWith('@')) { const relPath = _path.replace('@', '') fse.ensureDirSync(relPath) return relPath } const relPath = path.join(process.cwd(), _path) fse.ensureDirSync(relPath) return relPath }, ensureMediaDir: (_path) => fse.ensureDirSync(_path), getUrlFileExt (url) { const parsedUrl = new URL(`http://${url}`) return parsedUrl.pathname.split('.').pop() }, removeFile (path) { fse.removeSync(path) }, checkM3uFile (url, USER_AGENT) { return new Promise((resolve, reject) => { // prefetch media need carry User-Agent fetch(url, { headers: { 'User-Agent': USER_AGENT || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36' }, }).then(async (res) => { const data = await res.text() if (data.startsWith('#EXTM3U')) { resolve(data) } else { reject(new Error(`${url}: this is not a m3u url`)) } }).catch(e => reject(e)) }) }, } module.exports = Helper