From eb13543e95052b2d1a0c6cd3376c9655579527bc Mon Sep 17 00:00:00 2001 From: Florrie Date: Sat, 22 Dec 2018 13:19:30 -0400 Subject: Make mtui work in webpack --- downloaders.js | 178 ++------------------------------------------------------- 1 file changed, 6 insertions(+), 172 deletions(-) (limited to 'downloaders.js') diff --git a/downloaders.js b/downloaders.js index 4b4750c..f5df4d2 100644 --- a/downloaders.js +++ b/downloaders.js @@ -1,95 +1,3 @@ -const { promisifyProcess } = require('./general-util') -const { promisify } = require('util') -const { spawn } = require('child_process') -const { Base64 } = require('js-base64') -const mkdirp = promisify(require('mkdirp')) -const fs = require('fs') -const fetch = require('node-fetch') -const tempy = require('tempy') -const path = require('path') -const sanitize = require('sanitize-filename') - -const writeFile = promisify(fs.writeFile) -const rename = promisify(fs.rename) -const stat = promisify(fs.stat) -const readdir = promisify(fs.readdir) -const symlink = promisify(fs.symlink) - -const copyFile = (source, target) => { - // Stolen from https://stackoverflow.com/a/30405105/4633828 - const rd = fs.createReadStream(source) - const wr = fs.createWriteStream(target) - return new Promise((resolve, reject) => { - rd.on('error', reject) - wr.on('error', reject) - wr.on('finish', resolve) - rd.pipe(wr) - }).catch(function(error) { - rd.destroy() - wr.end() - throw error - }) -} - -const cachify = (identifier, baseFunction) => { - return async arg => { - // If there was no argument passed (or it aws empty), nothing will work.. - if (!arg) { - throw new TypeError('Expected a downloader argument') - } - - // Determine where the final file will end up. This is just a directory - - // the file's own name is determined by the downloader. - const cacheDir = downloaders.rootCacheDir + '/' + identifier - const finalDirectory = cacheDir + '/' + Base64.encode(arg) - - // Check if that directory only exists. If it does, return the file in it, - // because it being there means we've already downloaded it at some point - // in the past. - let exists - try { - await stat(finalDirectory) - exists = true - } catch (error) { - // ENOENT means the folder doesn't exist, which is one of the potential - // expected outputs, so do nothing and let the download continue. - if (error.code === 'ENOENT') { - exists = false - } - // Otherwise, there was some unexpected error, so throw it: - else { - throw error - } - } - - // If the directory exists, return the file in it. Downloaders always - // return only one file, so it's expected that the directory will only - // contain a single file. We ignore any other files. Note we also allow - // the download to continue if there aren't any files in the directory - - // that would mean that the file (but not the directory) was unexpectedly - // deleted. - if (exists) { - const files = await readdir(finalDirectory) - if (files.length >= 1) { - return finalDirectory + '/' + files[0] - } - } - - // The "temporary" output, aka the download location. Generally in a - // temporary location as returned by tempy. - const tempFile = await baseFunction(arg) - - // Then move the download to the final location. First we need to make the - // folder exist, then we move the file. - const finalFile = finalDirectory + '/' + path.basename(tempFile) - await mkdirp(finalDirectory) - await rename(tempFile, finalFile) - - // And return. - return finalFile - } -} - const removeFileProtocol = arg => { const fileProto = 'file://' if (arg.startsWith(fileProto)) { @@ -100,91 +8,17 @@ const removeFileProtocol = arg => { } const downloaders = { - extension: 'mp3', // Generally target file extension, used by youtube-dl - - // TODO: Cross-platform stuff - rootCacheDir: process.env.HOME + '/.mtui/downloads', - - http: cachify('http', arg => { - const out = ( - tempy.directory() + '/' + - sanitize(decodeURIComponent(path.basename(arg)))) - + fetch: arg => { return fetch(arg) - .then(response => response.buffer()) - .then(buffer => writeFile(out, buffer)) - .then(() => out) - }), - - youtubedl: cachify('youtubedl', arg => { - const outDir = tempy.directory() - const outFile = outDir + '/%(id)s-%(uploader)s-%(title)s.%(ext)s' - - const opts = [ - '--quiet', - '--no-warnings', - '--extract-audio', - '--audio-format', downloaders.extension, - '--output', outFile, - arg - ] - - return promisifyProcess(spawn('youtube-dl', opts)) - .then(() => readdir(outDir)) - .then(files => outDir + '/' + files[0]) - }), - - local: cachify('local', arg => { - // Usually we'd just return the given argument in a local - // downloader, which is efficient, since there's no need to - // copy a file from one place on the hard drive to another. - // But reading from a separate drive (e.g. a USB stick or a - // CD) can take a lot longer than reading directly from the - // computer's own drive, so this downloader copies the file - // to a temporary file on the computer's drive. - // Ideally, we'd be able to check whether a file is on the - // computer's main drive mount or not before going through - // the steps to copy, but I'm not sure if there's a way to - // do that (and it's even less likely there'd be a cross- - // platform way). - - // It's possible the downloader argument starts with the "file://" - // protocol string; in that case we'll want to snip it off and URL- - // decode the string. - arg = removeFileProtocol(arg) - - // TODO: Is it necessary to sanitize here? - // Haha, the answer to "should I sanitize" is probably always YES.. - const base = path.basename(arg, path.extname(arg)) - const out = tempy.directory() + '/' + sanitize(base) + path.extname(arg) - - return copyFile(arg, out) - .then(() => out) - }), - - locallink: cachify('locallink', arg => { - // Like the local downloader, but creates a symbolic link to the argument. - - arg = removeFileProtocol(arg) - const base = path.basename(arg, path.extname(arg)) - const out = tempy.directory() + '/' + sanitize(base) + path.extname(arg) - - return symlink(path.resolve(arg), out) - .then(() => out) - }), - - echo: arg => arg, + .then(response => response.blob()) + .then(blob => URL.createObjectURL(blob)) + }, getDownloaderFor: arg => { if (arg.startsWith('http://') || arg.startsWith('https://')) { - if (arg.includes('youtube.com')) { - return downloaders.youtubedl - } else { - return downloaders.http - } + return downloaders.http } else { - // return downloaders.local - return downloaders.locallink + return null } } } -- cgit 1.3.0-6-gf8a5