« get me outta code hell

mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/general-util.js
diff options
context:
space:
mode:
Diffstat (limited to 'general-util.js')
-rw-r--r--general-util.js75
1 files changed, 47 insertions, 28 deletions
diff --git a/general-util.js b/general-util.js
index 85ff8e5..536b3fd 100644
--- a/general-util.js
+++ b/general-util.js
@@ -1,13 +1,11 @@
-const { spawn } = require('child_process')
-const { promisify } = require('util')
-const fetch = require('node-fetch')
-const fs = require('fs')
-const npmCommandExists = require('command-exists')
-const url = require('url')
+import {spawn} from 'node:child_process'
+import {readFile} from 'node:fs/promises'
+import {fileURLToPath, URL} from 'node:url'
 
-const readFile = promisify(fs.readFile)
+import npmCommandExists from 'command-exists'
+import fetch from 'node-fetch'
 
-module.exports.promisifyProcess = function(proc, showLogging = true) {
+export function promisifyProcess(proc, showLogging = true) {
   // Takes a process (from the child_process module) and returns a promise
   // that resolves when the process exits (or rejects, if the exit code is
   // non-zero).
@@ -28,7 +26,7 @@ module.exports.promisifyProcess = function(proc, showLogging = true) {
   })
 }
 
-module.exports.commandExists = async function(command) {
+export async function commandExists(command) {
   // When the command-exists module sees that a given command doesn't exist, it
   // throws an error instead of returning false, which is not what we want.
 
@@ -39,12 +37,12 @@ module.exports.commandExists = async function(command) {
   }
 }
 
-module.exports.killProcess = async function(proc) {
+export async function killProcess(proc) {
   // Windows is stupid and doesn't like it when we try to kill processes.
   // So instead we use taskkill! https://stackoverflow.com/a/28163919/4633828
 
-  if (await module.exports.commandExists('taskkill')) {
-    await module.exports.promisifyProcess(
+  if (await commandExists('taskkill')) {
+    await promisifyProcess(
       spawn('taskkill', ['/pid', proc.pid, '/f', '/t']),
       false
     )
@@ -53,18 +51,18 @@ module.exports.killProcess = async function(proc) {
   }
 }
 
-function downloadPlaylistFromURL(url) {
+export function downloadPlaylistFromURL(url) {
   return fetch(url).then(res => res.text())
 }
 
-function downloadPlaylistFromLocalPath(path) {
+export function downloadPlaylistFromLocalPath(path) {
   return readFile(path).then(buf => buf.toString())
 }
 
-module.exports.downloadPlaylistFromOptionValue = function(arg) {
+export function downloadPlaylistFromOptionValue(arg) {
   let argURL
   try {
-    argURL = new url.URL(arg)
+    argURL = new URL(arg)
   } catch (err) {
     // Definitely not a URL.
   }
@@ -73,14 +71,14 @@ module.exports.downloadPlaylistFromOptionValue = function(arg) {
     if (argURL.protocol === 'http:' || argURL.protocol === 'https:') {
       return downloadPlaylistFromURL(arg)
     } else if (argURL.protocol === 'file:') {
-      return downloadPlaylistFromLocalPath(url.fileURLToPath(argURL))
+      return downloadPlaylistFromLocalPath(fileURLToPath(argURL))
     }
   } else {
     return downloadPlaylistFromLocalPath(arg)
   }
 }
 
-module.exports.shuffleArray = function(array) {
+export function shuffleArray(array) {
   // Shuffles the items in an array. Returns a new array (does not modify the
   // passed array). Super-interesting post on how this algorithm works:
   // https://bost.ocks.org/mike/shuffle/
@@ -103,7 +101,7 @@ module.exports.shuffleArray = function(array) {
   return workingArray
 }
 
-module.exports.throttlePromise = function(maximumAtOneTime = 10) {
+export function throttlePromise(maximumAtOneTime = 10) {
   // Returns a function that takes a callback to create a promise and either
   // runs it now, if there is an available slot, or enqueues it to be run
   // later, if there is not.
@@ -139,7 +137,17 @@ module.exports.throttlePromise = function(maximumAtOneTime = 10) {
   return enqueue
 }
 
-module.exports.getTimeStringsFromSec = function(curSecTotal, lenSecTotal) {
+export function getSecFromTimestamp(timestamp) {
+  const parts = timestamp.split(':').map(n => parseInt(n))
+  switch (parts.length) {
+    case 3: return parts[0] * 3600 + parts[1] * 60 + parts[2]
+    case 2: return parts[0] * 60 + parts[1]
+    case 1: return parts[0]
+    default: return 0
+  }
+}
+
+export function getTimeStringsFromSec(curSecTotal, lenSecTotal, fraction = false) {
   const percentVal = (100 / lenSecTotal) * curSecTotal
   const percentDone = (
     (Math.trunc(percentVal * 100) / 100).toFixed(2) + '%'
@@ -149,29 +157,36 @@ module.exports.getTimeStringsFromSec = function(curSecTotal, lenSecTotal) {
   let leftHour = Math.floor(leftSecTotal / 3600)
   let leftMin = Math.floor((leftSecTotal - leftHour * 3600) / 60)
   let leftSec = Math.floor(leftSecTotal - leftHour * 3600 - leftMin * 60)
+  let leftFrac = lenSecTotal % 1
 
   // Yeah, yeah, duplicate math.
   let curHour = Math.floor(curSecTotal / 3600)
   let curMin = Math.floor((curSecTotal - curHour * 3600) / 60)
   let curSec = Math.floor(curSecTotal - curHour * 3600 - curMin * 60)
+  let curFrac = curSecTotal % 1
 
   // Wee!
   let lenHour = Math.floor(lenSecTotal / 3600)
   let lenMin = Math.floor((lenSecTotal - lenHour * 3600) / 60)
   let lenSec = Math.floor(lenSecTotal - lenHour * 3600 - lenMin * 60)
+  let lenFrac = lenSecTotal % 1
 
   const pad = val => val.toString().padStart(2, '0')
+  const padFrac = val => Math.floor(val * 1000).toString().padEnd(3, '0')
   curMin = pad(curMin)
   curSec = pad(curSec)
   lenMin = pad(lenMin)
   lenSec = pad(lenSec)
   leftMin = pad(leftMin)
   leftSec = pad(leftSec)
+  curFrac = padFrac(curFrac)
+  lenFrac = padFrac(lenFrac)
+  leftFrac = padFrac(leftFrac)
 
   // We don't want to display hour counters if the total length is less
   // than an hour.
   let timeDone, timeLeft, duration
-  if (parseInt(lenHour) > 0) {
+  if (parseInt(lenHour) > 0 || parseInt(curHour) > 0) {
     timeDone = `${curHour}:${curMin}:${curSec}`
     timeLeft = `${leftHour}:${leftMin}:${leftSec}`
     duration = `${lenHour}:${lenMin}:${lenSec}`
@@ -181,19 +196,25 @@ module.exports.getTimeStringsFromSec = function(curSecTotal, lenSecTotal) {
     duration = `${lenMin}:${lenSec}`
   }
 
+  if (fraction) {
+    timeDone += '.' + curFrac
+    timeLeft += '.' + leftFrac
+    duration += '.' + lenFrac
+  }
+
   return {percentDone, timeDone, timeLeft, duration, curSecTotal, lenSecTotal}
 }
 
-module.exports.getTimeStrings = function({curHour, curMin, curSec, lenHour, lenMin, lenSec}) {
+export function getTimeStrings({curHour, curMin, curSec, lenHour, lenMin, lenSec}) {
   // Multiplication casts to numbers; addition prioritizes strings.
   // Thanks, JavaScript!
   const curSecTotal = (3600 * curHour) + (60 * curMin) + (1 * curSec)
   const lenSecTotal = (3600 * lenHour) + (60 * lenMin) + (1 * lenSec)
 
-  return module.exports.getTimeStringsFromSec(curSecTotal, lenSecTotal)
+  return getTimeStringsFromSec(curSecTotal, lenSecTotal)
 }
 
-const parseOptions = async function(options, optionDescriptorMap) {
+export async function parseOptions(options, optionDescriptorMap) {
   // This function is sorely lacking in comments, but the basic usage is
   // as such:
   //
@@ -309,9 +330,7 @@ const parseOptions = async function(options, optionDescriptorMap) {
 
 parseOptions.handleDashless = Symbol()
 
-module.exports.parseOptions = parseOptions
-
-module.exports.silenceEvents = async function(emitter, eventsToSilence, callback) {
+export async function silenceEvents(emitter, eventsToSilence, callback) {
   const oldEmit = emitter.emit
 
   emitter.emit = function(event, ...data) {
@@ -327,7 +346,7 @@ module.exports.silenceEvents = async function(emitter, eventsToSilence, callback
 
 // Kindly stolen from ESDiscuss:
 // https://esdiscuss.org/topic/proposal-add-an-option-to-omit-prototype-of-objects-created-by-json-parse#content-1
-module.exports.parseWithoutPrototype = function(string) {
+export function parseWithoutPrototype(string) {
   return JSON.parse(string, function(k, v) {
     if (v && typeof v === 'object' && !Array.isArray(v)) {
       return Object.assign(Object.create(null), v)