« get me outta code hell

manual tempdir creation & handling - mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/tempdir.js
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2024-05-10 21:56:29 -0300
committer(quasar) nebula <qznebula@protonmail.com>2024-05-10 21:56:29 -0300
commit27af62f38d2f0a99af5c34963d27197467fb0141 (patch)
treebb09cddc836b16db57242f6d785478bab17e343b /tempdir.js
parenta36e372ba88b59e08fa938f76b261fdc2797bef2 (diff)
manual tempdir creation & handling
This is mostly for compatibility with devices where the home
directory isn't on the same device as the system temporary
directory, so locallink and other symlink-based operations
get trolled (fail!).

This doesn't address the more general issue of e.g. playing
music off of an external drive probably fails(!!) - but in
those cases locallink isn't appropriate anyway, so they're
outta scope of this commit.
Diffstat (limited to 'tempdir.js')
-rw-r--r--tempdir.js86
1 files changed, 86 insertions, 0 deletions
diff --git a/tempdir.js b/tempdir.js
new file mode 100644
index 0000000..9264ffd
--- /dev/null
+++ b/tempdir.js
@@ -0,0 +1,86 @@
+import {mkdirSync, readdirSync, statSync} from 'node:fs'
+import * as os from 'node:os'
+import * as path from 'node:path'
+
+import {nanoid} from 'nanoid'
+import {rimrafSync} from 'rimraf'
+
+// Invariably obliterate contents of the rootDirectory upon
+// mtui startup if their mtime is older than this duration
+// before the current date.
+const ancient = 7 * 24 * 60 * 60 * 1000
+
+const ourTemporaryDirectories = []
+
+const rootDirectory = path.join(os.homedir(), '.mtui', 'tmp')
+
+function obliterateTemporaryDirectory(tempdir) {
+  const rel = path.relative(rootDirectory, tempdir)
+  if (rel.startsWith('/') || rel.startsWith('.')) {
+    console.trace()
+    console.error(`Ostensible tempdir located here:`)
+    console.error(tempdir)
+    console.error(`Doesn't appear to be located in tempdir root:`)
+    console.error(rootDirectory)
+    console.error(`This is a programming error, and possibly dangerous.`)
+    console.error(`So, exiting now. This should be investigated.`)
+    process.exit(1)
+  }
+
+  rimrafSync(tempdir)
+}
+
+function cleanupAncient() {
+  const fsOp = (fn, ...args) => {
+    try {
+      return fn(...args)
+    } catch (error) {
+      console.error(error)
+      console.error(`There was an error preparing the temporary file directory.`)
+      console.error(`You may be able to resolve this by deleting or moving away`)
+      console.error(`this path:`)
+      console.error(rootDirectory)
+      process.exit(1)
+    }
+  }
+
+  fsOp(() =>
+    mkdirSync(rootDirectory, {recursive: true}))
+
+  const tempdirs =
+    (fsOp(readdirSync, rootDirectory)
+      .map(dir => path.join(rootDirectory, dir)))
+
+  let first = true
+  for (const tempdir of tempdirs) {
+    const stats = fsOp(statSync, tempdir)
+
+    if (Date.now() - stats.mtimeMs > ancient) {
+      if (first) {
+        console.log(`One or more tempdirs haven't been modified in a while, removing:`)
+        first = false
+      }
+      console.log(tempdir)
+      fsOp(obliterateTemporaryDirectory, tempdir)
+    }
+  }
+}
+
+// This has to be a sync function, since it'll run on
+// the process' 'exit' event.
+function cleanupOurs() {
+  for (const tempdir of ourTemporaryDirectories) {
+    obliterateTemporaryDirectory(tempdir)
+  }
+}
+
+cleanupAncient()
+process.on('exit', cleanupOurs)
+
+export default function temporaryDirectory() {
+  const name = nanoid()
+  const dir = path.join(rootDirectory, name)
+  ourTemporaryDirectories.push(dir)
+  mkdirSync(dir)
+  return dir
+}