« get me outta code hell

Oragnize cached downloads much better! - mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/downloaders.js
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2019-05-22 19:29:08 -0300
committerFlorrie <towerofnix@gmail.com>2019-05-22 19:29:08 -0300
commit77bae19bb60e8a2dd6afd4086b2e1a6b06e129e4 (patch)
tree67dce3323c8c808a2461a98f721d1b5c374143f4 /downloaders.js
parenta9f07edbfc6ea3f693ac3149e39616949671d650 (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!
Diffstat (limited to 'downloaders.js')
-rw-r--r--downloaders.js153
1 files changed, 83 insertions, 70 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,