diff options
author | Florrie <towerofnix@gmail.com> | 2019-05-22 19:29:08 -0300 |
---|---|---|
committer | Florrie <towerofnix@gmail.com> | 2019-05-22 19:29:08 -0300 |
commit | 77bae19bb60e8a2dd6afd4086b2e1a6b06e129e4 (patch) | |
tree | 67dce3323c8c808a2461a98f721d1b5c374143f4 | |
parent | a9f07edbfc6ea3f693ac3149e39616949671d650 (diff) |
Oragnize cached downloads much better!
This means mtui won't be able to access tracks that were downloaded before, but oh well. It also means we get to get rid of js-base64 as a dependency!
-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!) |