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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
'use strict'
function flattenPlaylist(playlist) {
// Flattens a playlist, taking all of the non-group items (tracks) at all
// levels in the playlist tree and returns them as a single-level array of
// tracks.
const groups = playlist.filter(x => isGroup(x))
const nonGroups = playlist.filter(x => !isGroup(x))
return groups.map(g => flattenPlaylist(getGroupContents(g)))
.reduce((a, b) => a.concat(b), nonGroups)
}
function filterPlaylistByPathString(playlist, pathString) {
// Calls filterPlaylistByPath, taking a path string, rather than a parsed
// path.
return filterPlaylistByPath(playlist, parsePathString(pathString))
}
function filterPlaylistByPath(playlist, pathParts) {
// Finds a group by following the given group path and returns it. If the
// function encounters an item in the group path that is not found, it logs
// a warning message and returns the group found up to that point.
const titleMatch = (group, caseInsensitive = false) => {
let a = getGroupTitle(group)
let b = cur
if (caseInsensitive) {
a = a.toLowerCase()
b = b.toLowerCase()
}
return a === b || a === b + '/'
}
const cur = pathParts[0]
let match = playlist.find(g => titleMatch(g, false))
if (!match) {
match = playlist.find(g => titleMatch(g, true))
}
if (match) {
if (pathParts.length > 1) {
const rest = pathParts.slice(1)
return filterPlaylistByPath(getGroupContents(match), rest)
} else {
return match
}
} else {
console.warn(`Not found: "${cur}"`)
return playlist
}
}
function removeGroupByPathString(playlist, pathString) {
// Calls removeGroupByPath, taking a path string, rather than a parsed path.
return removeGroupByPath(playlist, parsePathString(pathString))
}
function removeGroupByPath(playlist, pathParts) {
// Removes the group at the given path from the given playlist.
const groupToRemove = filterPlaylistByPath(playlist, pathParts)
const parentPath = pathParts.slice(0, pathParts.length - 1)
let parent
if (parentPath.length === 0) {
parent = playlist
} else {
parent = getGroupContents(filterPlaylistByPath(playlist, parentPath))
}
const index = parent.indexOf(groupToRemove)
if (index >= 0) {
parent.splice(index, 1)
} else {
console.error(
`Group ${pathParts.join('/')} doesn't exist, so we can't explicitly ` +
"ignore it."
)
}
}
function getPlaylistTreeString(playlist, showTracks = false) {
function recursive(group) {
const groups = group.filter(x => isGroup(x))
const nonGroups = group.filter(x => !isGroup(x))
const childrenString = groups.map(group => {
const title = getGroupTitle(group)
const groupString = recursive(getGroupContents(group))
if (groupString) {
const indented = groupString.split('\n').map(l => '| ' + l).join('\n')
return '\n' + title + '\n' + indented
} else {
return title
}
}).join('\n')
let tracksString = ''
if (showTracks) {
tracksString = nonGroups.map(g => getGroupTitle(g)).join('\n')
}
if (tracksString && childrenString) {
return tracksString + '\n' + childrenString
} else if (childrenString) {
return childrenString
} else if (tracksString) {
return tracksString
} else {
return ''
}
}
return recursive(playlist)
}
function parsePathString(pathString) {
const pathParts = pathString.split('/')
return pathParts
}
function getGroupTitle(group) {
return group[0]
}
function getGroupContents(group) {
return group[1]
}
function isGroup(array) {
return Array.isArray(array[1])
}
module.exports = {
flattenPlaylist,
filterPlaylistByPathString, filterPlaylistByPath,
removeGroupByPathString, removeGroupByPath,
getPlaylistTreeString,
parsePathString,
getGroupTitle, getGroupContents
}
|