diff options
-rw-r--r-- | downloaders.js | 153 | ||||
-rw-r--r-- | list-cache.js | 33 | ||||
-rw-r--r-- | package-lock.json | 9 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | todo.txt | 1 |
5 files changed, 86 insertions, 111 deletions
diff --git a/downloaders.js b/downloaders.js index 4b4750c..4c1ce38 100644 --- a/downloaders.js +++ b/downloaders.js @@ -1,7 +1,7 @@ const { promisifyProcess } = require('./general-util') const { promisify } = require('util') const { spawn } = require('child_process') -const { Base64 } = require('js-base64') +const { URL } = require('url') const mkdirp = promisify(require('mkdirp')) const fs = require('fs') const fetch = require('node-fetch') @@ -31,7 +31,9 @@ const copyFile = (source, target) => { }) } -const cachify = (identifier, baseFunction) => { +const disableBackResolving = arg => arg.split('/').map(str => str.replace(/^\../, '_..')).join('/') + +const cachify = (identifier, keyFunction, baseFunction) => { return async arg => { // If there was no argument passed (or it aws empty), nothing will work.. if (!arg) { @@ -41,7 +43,7 @@ const cachify = (identifier, baseFunction) => { // 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) + const finalDirectory = cacheDir + '/' + disableBackResolving(keyFunction(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 @@ -105,73 +107,84 @@ const downloaders = { // TODO: Cross-platform stuff rootCacheDir: process.env.HOME + '/.mtui/downloads', - http: cachify('http', arg => { - const out = ( - tempy.directory() + '/' + - sanitize(decodeURIComponent(path.basename(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) - }), + http: cachify('http', + arg => { + const url = new URL(arg) + return url.hostname + url.pathname + }, + arg => { + const out = ( + tempy.directory() + '/' + + sanitize(decodeURIComponent(path.basename(arg)))) + + return fetch(arg) + .then(response => response.buffer()) + .then(buffer => writeFile(out, buffer)) + .then(() => out) + }), + + youtubedl: cachify('youtubedl', + arg => (arg.match(/watch\?v=(.*)/) || ['', arg])[1], + 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 => arg, + 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 => arg, + 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, diff --git a/list-cache.js b/list-cache.js deleted file mode 100644 index 71dbbcc..0000000 --- a/list-cache.js +++ /dev/null @@ -1,33 +0,0 @@ -const { Base64 } = require('js-base64') -const fs = require('fs') -const util = require('util') -const downloaders = require('./downloaders') -const ansi = require('./tui-lib/util/ansi') - -const readdir = util.promisify(fs.readdir) - -async function main(args) { - const dlPath = downloaders.rootCacheDir - const type = args[0] || null - - async function list(dir) { - console.log(await readdir(dir)) - } - - if (!type) { - console.log('No type specified. Pass one of the following as an argument:', (await readdir(dlPath)).join(', ')) - return - } - - console.log(`${ansi.setAttributes([ansi.A_BRIGHT])}Downloads of type "${type}"${ansi.resetAttributes()}`) - for (const entry of await readdir(dlPath + '/' + type)) { - const files = await readdir(`${dlPath}/${type}/${entry}`) - console.log(` ${Base64.decode(entry)}: ${ansi.setAttributes([ansi.A_BRIGHT])}${files.join(', ')} ${ansi.resetAttributes()}${ansi.setAttributes([ansi.A_DIM])}(${type}/${entry})${ansi.resetAttributes()}`) - } -} - -module.exports = main - -if (require.main === module) { - main(process.argv.slice(2)).catch(err => console.error(err)) -} diff --git a/package-lock.json b/package-lock.json index efb723d..b2f1ec5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "music-ui", - "version": "1.0.0", + "name": "mtui", + "version": "0.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -32,11 +32,6 @@ "es6-error": "^3.0.1" } }, - "js-base64": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.5.tgz", - "integrity": "sha512-aUnNwqMOXw3yvErjMPSQu6qIIzUmT1e5KcU1OZxRDU1g/am6mzBvcrmLAYwzmB59BHPrh5/tKaiF4OPhqRWESQ==" - }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", diff --git a/package.json b/package.json index dae7b6b..0c5975a 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "command-exists": "^1.2.6", "expand-home-dir": "0.0.3", "fifo-js": "^2.1.0", - "js-base64": "^2.4.5", "mkdirp": "^0.5.1", "node-fetch": "^2.1.2", "node-natural-sort": "^0.8.6", diff --git a/todo.txt b/todo.txt index 2907a02..0e2f6d9 100644 --- a/todo.txt +++ b/todo.txt @@ -92,6 +92,7 @@ TODO: Investigate performance issues with very very long ListScrollForms. TODO: At some point, putting mtui downloads in ~/.mtui - but preferrably with a more humanish structure - would be quite nice..! + (Done! Yeeeeeeees!!!!!!!!) TODO: Press "M" to show a context menu. (Done!) |