« get me outta code hell

Add --start-at option and 'track version' concept - http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorrie <towerofnix@gmail.com>2017-10-12 23:04:47 -0300
committerFlorrie <towerofnix@gmail.com>2017-10-12 23:04:47 -0300
commitaa9a8404f426562ce049171fa97b5714305b1459 (patch)
tree472858df27cac402836a983fc7afbb2f0942d8ea
parentd16f7efae8f7b9a142ca9514bc98b41eb4ee16c8 (diff)
Add --start-at option and 'track version' concept
-rw-r--r--src/loop-play.js7
-rw-r--r--src/pickers.js17
-rwxr-xr-xsrc/play.js32
-rw-r--r--src/playlist-utils.js32
4 files changed, 81 insertions, 7 deletions
diff --git a/src/loop-play.js b/src/loop-play.js
index 5d0048b..9724f7c 100644
--- a/src/loop-play.js
+++ b/src/loop-play.js
@@ -537,7 +537,8 @@ module.exports = async function startLoopPlay(
   playlist, {
     pickerOptions, playerCommand, converterCommand,
     useConverterOptions = true,
-    disablePlaybackStatus = false
+    disablePlaybackStatus = false,
+    startTrack = null
   }
 ) {
   // Looping play function. Takes a playlist and an object containing general
@@ -576,6 +577,10 @@ module.exports = async function startLoopPlay(
     playlist, generalPicker, pickerOptions
   )
 
+  if (startTrack) {
+    historyController.timeline.push(startTrack)
+  }
+
   const playController = new PlayController(
     player, playlist, historyController, downloadController
   )
diff --git a/src/pickers.js b/src/pickers.js
index 245e16f..1afa0c2 100644
--- a/src/pickers.js
+++ b/src/pickers.js
@@ -15,7 +15,7 @@ const _seedRandom = require('seed-random')
 // Uncertain on how to handle serialization of tracks.. some tracks may appear twice in the same playlist (or two tracks of the same name appear); in this case the serialized path to the two track appearances is the same, when they really refer to two separate instances of the track within the playlist. Could track serialization instead be index-based (rather than name-based)..?
 
 const {
-  flattenGrouplike, isGroup, updatePlaylistFormat
+  flattenGrouplike, isGroup, updatePlaylistFormat, isSameTrack
 } = require('./playlist-utils')
 
 class HistoryController {
@@ -212,7 +212,20 @@ function generalPicker(sourcePlaylist, lastTrack, options) {
     // console.log('\x1b[1K\rDone indexing.')
   }
 
-  const index = playlist.items.indexOf(lastTrack)
+  let index
+
+  if (lastTrack !== null) {
+    // The "current" version of the last track (that is, the object
+    // representing this track which appears in the flattened/updated/cached
+    // playlist).
+    const currentLastTrack = playlist.items.find(
+      t => isSameTrack(t, lastTrack)
+    )
+
+    index = playlist.items.indexOf(currentLastTrack)
+  } else {
+    index = -1
+  }
 
   if (index === -1) {
     return playlist.items[0]
diff --git a/src/play.js b/src/play.js
index 6adbb9c..6d4456d 100755
--- a/src/play.js
+++ b/src/play.js
@@ -14,7 +14,7 @@ const processSmartPlaylist = require('./smart-playlist')
 
 const {
   filterPlaylistByPathString, removeGroupByPathString, getPlaylistTreeString,
-  updatePlaylistFormat, collapseGrouplike, filterGrouplikeByProperty
+  updatePlaylistFormat, collapseGrouplike, filterGrouplikeByProperty, isTrack
 } = require('./playlist-utils')
 
 const readFile = promisify(fs.readFile)
@@ -72,6 +72,7 @@ async function main(args) {
   let pickerSortMode = 'shuffle'
   let pickerLoopMode = 'loop-regenerate'
   let shuffleSeed
+  let startTrack
   let playerCommand = await determineDefaultPlayer()
   let converterCommand = await determineDefaultConverter()
 
@@ -416,6 +417,32 @@ async function main(args) {
 
     '-loop': util => util.alias('-loop-mode'),
 
+    '-start': function(util) {
+      // --start <track path>  (alias: -s, --start-track, --start-[on|at])
+      // Sets the first track to be played.
+      // This is especially useful when using an ordered sort; this option
+      // could be used to start a long album part way through.
+      const pathString = util.nextArg()
+      const track = filterPlaylistByPathString(activePlaylist, pathString)
+      if (isTrack(track)) {
+        startTrack = track
+        console.log('Starting on track', pathString)
+      } else {
+        console.warn(
+          'A starting track path was given, but there is no track at ' +
+          'that path?'
+        )
+      }
+    },
+
+    '-start-track': util => util.alias('-start'),
+    '-start-on': util => util.alias('-start'),
+    '-start-at': util => util.alias('-start'),
+    '-starting-track': util => util.alias('-start'),
+    '-starting-on': util => util.alias('-start'),
+    '-starting-at': util => util.alias('-start'),
+    's': util => util.alias('-start'),
+
     '-player': function(util) {
       // --player <player>
       // Sets the shell command by which audio is played.
@@ -519,7 +546,8 @@ async function main(args) {
       useConverterOptions: willUseConverterOptions || (
         willUseConverterOptions === null && shouldUseConverterOptions
       ),
-      disablePlaybackStatus
+      disablePlaybackStatus,
+      startTrack
     })
 
     // We're looking to gather standard input one keystroke at a time.
diff --git a/src/playlist-utils.js b/src/playlist-utils.js
index 4abcfab..a75008a 100644
--- a/src/playlist-utils.js
+++ b/src/playlist-utils.js
@@ -7,6 +7,7 @@ const { promisify } = require('util')
 const unlink = promisify(fs.unlink)
 
 const parentSymbol = Symbol('Parent group')
+const oldSymbol = Symbol('Old track or group reference')
 
 function updatePlaylistFormat(playlist) {
   const defaultPlaylist = {
@@ -44,7 +45,8 @@ function updatePlaylistFormat(playlist) {
 function updateGroupFormat(group) {
   const defaultGroup = {
     name: '',
-    items: []
+    items: [],
+    [oldSymbol]: group
   }
 
   let groupObj = {}
@@ -86,7 +88,8 @@ function updateGroupFormat(group) {
 function updateTrackFormat(track) {
   const defaultTrack = {
     name: '',
-    downloaderArg: ''
+    downloaderArg: '',
+    [oldSymbol]: track
   }
 
   let trackObj = {}
@@ -381,6 +384,30 @@ function parsePathString(pathString) {
   return pathParts
 }
 
+function isSameTrack(track1, track2) {
+  // Compares the two old-version chains of the given tracks. If there's any
+  // overlap, return true, as they are simply different versions of the same
+  // track; otherwise, return false.
+
+  // HAHAHA. You cannot convince me this isn't a good usage of generators.
+  const chain = function*(track) {
+    let oldTrack = track
+    while (oldTrack[oldSymbol]) {
+      yield (oldTrack = oldTrack[oldSymbol])
+    }
+  }
+
+  const track2Chain = Array.from(chain(track2))
+
+  for (const oldTrack1 of chain(track1)) {
+    if (track2Chain.includes(oldTrack1)) {
+      return true
+    }
+  }
+
+  return false
+}
+
 function isGroup(obj) {
   return !!(obj && obj.items)
 
@@ -439,6 +466,7 @@ module.exports = {
   getPlaylistTreeString,
   getItemPathString,
   parsePathString,
+  isSameTrack,
   isGroup, isTrack,
   safeUnlink
 }