193 lines
3.6 KiB
Go
193 lines
3.6 KiB
Go
package sharedutil
|
|
|
|
import (
|
|
"math"
|
|
"slices"
|
|
|
|
"github.com/dweymouth/supersonic/backend/mediaprovider"
|
|
)
|
|
|
|
func FilterSlice[T any](ss []T, test func(T) bool) []T {
|
|
if ss == nil {
|
|
return nil
|
|
}
|
|
result := make([]T, 0)
|
|
for _, s := range ss {
|
|
if test(s) {
|
|
result = append(result, s)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func MapSlice[T any, U any](ts []T, f func(T) U) []U {
|
|
if ts == nil {
|
|
return nil
|
|
}
|
|
result := make([]U, len(ts))
|
|
for i, t := range ts {
|
|
result[i] = f(t)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func FilterMapSlice[T any, U any](ts []T, f func(T) (U, bool)) []U {
|
|
if ts == nil {
|
|
return nil
|
|
}
|
|
result := make([]U, 0)
|
|
for _, t := range ts {
|
|
if u, ok := f(t); ok {
|
|
result = append(result, u)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func Reversed[T any](ts []T) []T {
|
|
if ts == nil {
|
|
return nil
|
|
}
|
|
new := make([]T, len(ts))
|
|
j := len(ts) - 1
|
|
for i := range ts {
|
|
new[i] = ts[j]
|
|
j--
|
|
}
|
|
return new
|
|
}
|
|
|
|
func ToSet[T comparable](ts []T) map[T]struct{} {
|
|
set := make(map[T]struct{}, len(ts))
|
|
for _, t := range ts {
|
|
set[t] = struct{}{}
|
|
}
|
|
return set
|
|
}
|
|
|
|
func FindTrackByID(id string, tracks []*mediaprovider.Track) *mediaprovider.Track {
|
|
for _, tr := range tracks {
|
|
if id == tr.ID {
|
|
return tr
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func FindMediaItemByID(id string, items []mediaprovider.MediaItem) mediaprovider.MediaItem {
|
|
for _, tr := range items {
|
|
if id == tr.Metadata().ID {
|
|
return tr
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func MediaItemIDOrEmptyStr(item mediaprovider.MediaItem) string {
|
|
if item == nil {
|
|
return ""
|
|
}
|
|
return item.Metadata().ID
|
|
}
|
|
|
|
func AlbumIDOrEmptyStr(track *mediaprovider.Track) string {
|
|
if track == nil {
|
|
return ""
|
|
}
|
|
return track.AlbumID
|
|
}
|
|
|
|
func TracksToIDs(tracks []*mediaprovider.Track) []string {
|
|
return MapSlice(tracks, func(tr *mediaprovider.Track) string {
|
|
return tr.ID
|
|
})
|
|
}
|
|
|
|
type TrackReorderOp int
|
|
|
|
const (
|
|
MoveToTop TrackReorderOp = iota
|
|
MoveToBottom
|
|
MoveUp
|
|
MoveDown
|
|
)
|
|
|
|
// Reorder tracks and return a new track slice.
|
|
// idxToMove must contain only valid indexes into tracks, and no repeats
|
|
func ReorderTracks(tracks []*mediaprovider.Track, idxToMove []int, op TrackReorderOp) []*mediaprovider.Track {
|
|
newTracks := make([]*mediaprovider.Track, len(tracks))
|
|
switch op {
|
|
case MoveToTop:
|
|
topIdx := 0
|
|
botIdx := len(idxToMove)
|
|
idxToMoveSet := ToSet(idxToMove)
|
|
for i, t := range tracks {
|
|
if _, ok := idxToMoveSet[i]; ok {
|
|
newTracks[topIdx] = t
|
|
topIdx++
|
|
} else {
|
|
newTracks[botIdx] = t
|
|
botIdx++
|
|
}
|
|
}
|
|
case MoveToBottom:
|
|
topIdx := 0
|
|
botIdx := len(tracks) - len(idxToMove)
|
|
idxToMoveSet := ToSet(idxToMove)
|
|
for i, t := range tracks {
|
|
if _, ok := idxToMoveSet[i]; ok {
|
|
newTracks[botIdx] = t
|
|
botIdx++
|
|
} else {
|
|
newTracks[topIdx] = t
|
|
topIdx++
|
|
}
|
|
}
|
|
case MoveUp:
|
|
first := firstIdxCanMoveUp(idxToMove)
|
|
copy(newTracks, tracks)
|
|
for _, i := range idxToMove {
|
|
if i < first {
|
|
continue
|
|
}
|
|
newTracks[i-1], newTracks[i] = newTracks[i], newTracks[i-1]
|
|
}
|
|
case MoveDown:
|
|
last := lastIdxCanMoveDown(idxToMove, len(tracks))
|
|
copy(newTracks, tracks)
|
|
for i := len(idxToMove) - 1; i >= 0; i-- {
|
|
idx := idxToMove[i]
|
|
if idx > last {
|
|
continue
|
|
}
|
|
newTracks[idx+1], newTracks[idx] = newTracks[idx], newTracks[idx+1]
|
|
}
|
|
}
|
|
return newTracks
|
|
}
|
|
|
|
func firstIdxCanMoveUp(idxs []int) int {
|
|
prevIdx := -1
|
|
slices.Sort(idxs)
|
|
for _, idx := range idxs {
|
|
if idx > prevIdx+1 {
|
|
return idx
|
|
}
|
|
prevIdx = idx
|
|
}
|
|
return math.MaxInt
|
|
}
|
|
|
|
func lastIdxCanMoveDown(idxs []int, lenSlice int) int {
|
|
prevIdx := lenSlice
|
|
slices.Sort(idxs)
|
|
for i := len(idxs) - 1; i >= 0; i-- {
|
|
idx := idxs[i]
|
|
if idx < prevIdx-1 {
|
|
return idx
|
|
}
|
|
prevIdx = idx
|
|
}
|
|
return -1
|
|
}
|