« get me outta code hell

pickers.js « src - http-music - Command-line music player + utils (not a server!)
about summary refs log tree commit diff
path: root/src/pickers.js
blob: 6a30c378397ae243ca47a0e7e5148613d9117569 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict'

const { flattenGrouplike } = require('./playlist-utils')

function makePicker(grouplike, sort, loop) {
  // Options to take into consideration:
  // - How should the top-level be sorted?
  //   (e.g. "order", "shuffle", "shuffle-groups")
  // - How should looping be handled?
  //   (e.g. "loop", "no-loop")
  // - Also keep in mind aliases for all of the above.
  //   (e.g. "ordered", "shuffled", "noloop")
  //
  // What about a shuffle-mode that should simply pick a random track every
  // time?
  //
  // What about a shuffle-mode that re-shuffles the list every time a loop
  // happens?
  //
  // Both of those options could probably be handled via the 'loop' option.

  const topLevel = {items: []}

  let generateTopLevel = () => {
    if (sort === 'order') {
      topLevel.items = flattenGrouplike(grouplike).items
    }

    if (sort === 'shuffle') {
      topLevel.items = shuffleArray(flattenGrouplike(grouplike).items)
    }

    if (sort === 'shuffle-top-level' || sort === 'shuffle-groups') {
      topLevel.items = flattenGrouplike(shuffleArray(grouplike.items))
    }

    console.log(topLevel.items.map(require('./playlist-utils').getItemPathString).join('\n'))
  }

  generateTopLevel()

  let index = 0

  return function() {
    if (index === topLevel.items.length) {
      if (loop === 'loop-same-order' || loop === 'loop') {
        index = 0
      }

      if (loop === 'loop-regenerate') {
        generateTopLevel()
        index = 0
      }

      if (loop === 'no-loop' || loop === 'no') {
        // Returning null means the picker is done picking.
        // (In theory, we could use an ES2015 generator intead, but this works
        // well enough.)
        return null
      }
    }

    if (index > topLevel.items.length) {
      throw new Error(
        "Picker index is greater than total item count?" +
        `(${index} > ${topLevel.items.length}`
      )
    }

    if (index < topLevel.items.length) {
      // Pick-random is a special exception - in this case we don't actually
      // care about the value of the index variable; instead we just pick a
      // random track from the generated top level.
      //
      // Loop=pick-random is different from sort=shuffle. Sort=shuffle always
      // ensures the same song doesn't play twice in a single shuffle. It's
      // like how when you shuffle a deck of cards, you'll still never pick
      // the same card twice, until you go all the way through the deck and
      // re-shuffle the deck!
      //
      // Loop=pick-random instead picks a random track every time the picker
      // is called. It's more like you reshuffle the complete deck every time
      // you pick something.
      //
      // Now, how should pick-random work when dealing with groups, such as
      // when using sort=shuffle-groups? (If I can't find a solution, I'd say
      // that's alright.)
      if (loop === 'pick-random') {
        const pickedIndex = Math.floor(Math.random() * topLevel.items.length)
        return topLevel.items[pickedIndex]
      }

      // If we're using any other mode, we just want to get the current item
      // in the playlist, and increment the index variable by one (note i++
      // and not ++i; i++ increments AFTER getting i so it gets us the range
      // 0..length-1, whereas ++i increments BEFORE, which gets us the range
      // 1..length.
      return topLevel.items[index++]
    }
  }
}

function shuffleArray(array) {
  // Shuffles the items in an array. Super-interesting post on how it works:
  // https://bost.ocks.org/mike/shuffle/

  const workingArray = array.slice(0)

  let m = array.length

  while (m) {
    let i = Math.floor(Math.random() * m)
    m--

    // Stupid lol; avoids the need of a temporary variable!
    Object.assign(workingArray, {
      [m]: workingArray[i],
      [i]: workingArray[m]
    })
  }

  return workingArray
}


module.exports = makePicker