2019-01-02 16:11:22 +01:00
|
|
|
package playlist
|
|
|
|
|
|
|
|
|
|
import (
|
2019-01-02 19:06:45 +01:00
|
|
|
"log"
|
2019-01-02 16:11:22 +01:00
|
|
|
"time"
|
|
|
|
|
"errors"
|
|
|
|
|
"strconv"
|
|
|
|
|
"math/rand"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"../archive"
|
|
|
|
|
"../config"
|
|
|
|
|
)
|
|
|
|
|
|
2019-01-02 19:06:45 +01:00
|
|
|
var pln = log.Println
|
2019-01-02 16:11:22 +01:00
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
type Pop func(*Playlist)
|
|
|
|
|
|
2019-01-02 16:11:22 +01:00
|
|
|
type Playlist struct {
|
|
|
|
|
NAME string
|
|
|
|
|
CTRACK config.Track_t
|
|
|
|
|
CALBUM config.Album_t
|
|
|
|
|
LIST []uint32
|
|
|
|
|
MAX int
|
2019-01-03 14:21:24 +01:00
|
|
|
CALLB Pop
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type PrettyPlaylist struct {
|
|
|
|
|
NAME string
|
|
|
|
|
CTRACK config.Track_t
|
|
|
|
|
CALBUM config.Album_t
|
|
|
|
|
LIST []config.Track_t
|
|
|
|
|
MAX int
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
type MinimalTrack struct {
|
|
|
|
|
ID string `json:"id"`
|
|
|
|
|
NAME string `json:"name"`
|
|
|
|
|
MAKER string `json:"maker"`
|
|
|
|
|
ALBUM string `json:"album"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type MinimalPlaylist struct {
|
|
|
|
|
NAME string `json:"name"`
|
|
|
|
|
CTRACK string `json:"ctrack"`
|
|
|
|
|
CALBUM string `json:"calbum"`
|
|
|
|
|
LIST []MinimalTrack `json:"list"`
|
|
|
|
|
}
|
2019-01-02 16:11:22 +01:00
|
|
|
|
|
|
|
|
func MakeRandom(name string, max int) (*Playlist, error) {
|
|
|
|
|
err := archive.Build()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := &Playlist{NAME: name, MAX: max}
|
|
|
|
|
p.LIST = make([]uint32, max)
|
|
|
|
|
|
|
|
|
|
for i := 0; i < max; i++ {
|
|
|
|
|
r, _ := Random()
|
|
|
|
|
p.LIST[i] = r // might duplicate
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p, nil;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-04 09:59:01 +01:00
|
|
|
func (mp *MinimalPlaylist) Encode() []byte {
|
|
|
|
|
res, err := json.Marshal(mp)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
return res
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Decode(jsonstr string) (*Playlist, error) {
|
|
|
|
|
|
|
|
|
|
pp := &PrettyPlaylist{}
|
|
|
|
|
|
|
|
|
|
if err := json.Unmarshal([]byte(jsonstr), pp); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p := pp.Unpretty()
|
|
|
|
|
return p, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *Playlist) Pretty() *PrettyPlaylist {
|
|
|
|
|
pp := &PrettyPlaylist{NAME: p.NAME, CTRACK: p.CTRACK, CALBUM: p.CALBUM, MAX: p.MAX}
|
|
|
|
|
pp.LIST = make([]config.Track_t, len(p.LIST))
|
|
|
|
|
for i := 0; i < len(p.LIST); i++ {
|
|
|
|
|
pp.LIST[i] = archive.Archive_map[p.LIST[i]]
|
|
|
|
|
}
|
|
|
|
|
return pp
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (pp *PrettyPlaylist) Unpretty() *Playlist {
|
|
|
|
|
p := &Playlist{NAME: pp.NAME, CTRACK: pp.CTRACK, CALBUM: pp.CALBUM, MAX: pp.MAX}
|
|
|
|
|
p.LIST = make([]uint32, len(pp.LIST))
|
|
|
|
|
for i := 0; i < len(pp.LIST); i++ {
|
2019-01-03 14:21:24 +01:00
|
|
|
p.LIST[i] = pp.LIST[i].ID
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
return p
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
func (p *Playlist) Minimal() *MinimalPlaylist {
|
|
|
|
|
mp := &MinimalPlaylist{NAME: p.NAME, CTRACK: p.CTRACK.NAME}
|
|
|
|
|
mp.CALBUM = p.CALBUM.MAKER + " - " + p.CALBUM.NAME
|
|
|
|
|
mp.LIST = make([]MinimalTrack, len(p.LIST))
|
|
|
|
|
for i := 0; i < len(p.LIST); i++ {
|
|
|
|
|
track := archive.Archive_map[p.LIST[i]]
|
|
|
|
|
strid := strconv.FormatUint(uint64(track.ID), 10)
|
|
|
|
|
mp.LIST[i] = MinimalTrack{ID: strid, NAME: track.NAME, MAKER: track.MAKER, ALBUM: track.ALBUM}
|
|
|
|
|
}
|
|
|
|
|
return mp
|
|
|
|
|
}
|
2019-01-02 16:11:22 +01:00
|
|
|
|
|
|
|
|
func (pp *PrettyPlaylist) Print() {
|
|
|
|
|
|
|
|
|
|
pln("Name: " + pp.NAME)
|
|
|
|
|
pln("CTRACK: " + pp.CTRACK.NAME)
|
|
|
|
|
pln("CALBUM: " + pp.CALBUM.NAME)
|
|
|
|
|
pln("Next:")
|
|
|
|
|
for i, r := range pp.LIST {
|
|
|
|
|
pln(" " + strconv.Itoa(i) + " - " + r.NAME)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-02 19:06:45 +01:00
|
|
|
func print(slice []uint32) {
|
|
|
|
|
for i, r := range slice {
|
|
|
|
|
pln(" " + strconv.Itoa(i) + " - " + archive.Archive_map[r].NAME)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://github.com/golang/go/wiki/SliceTricks
|
2019-01-02 16:11:22 +01:00
|
|
|
// https://stackoverflow.com/questions/33834742/remove-and-adding-elements-to-array-in-go-lang
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
func (p *Playlist) Pop() (string, error) {
|
2019-01-02 16:11:22 +01:00
|
|
|
if len(p.LIST) == 0 {
|
2019-01-03 14:21:24 +01:00
|
|
|
return "", errors.New("Playlist is empty")
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
t := p.LIST[0]
|
|
|
|
|
if len(p.LIST) < 2 {
|
|
|
|
|
p.LIST = make([]uint32, 0)
|
|
|
|
|
} else {
|
|
|
|
|
p.LIST = p.LIST[1:]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.CTRACK = archive.Archive_map[t]
|
|
|
|
|
p.CALBUM = config.Xcfg.Archive.ALBUMS[p.CTRACK.AID]
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
if p.CALLB != nil {
|
|
|
|
|
p.CALLB(p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return p.CTRACK.PATH, nil
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *Playlist) Push(track_id uint32) error {
|
|
|
|
|
if len(p.LIST) == p.MAX {
|
|
|
|
|
return errors.New("Playlist is full")
|
|
|
|
|
}
|
|
|
|
|
p.LIST = append(p.LIST, track_id)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *Playlist) PushFront(track_id uint32) error {
|
|
|
|
|
if len(p.LIST) == p.MAX {
|
|
|
|
|
return errors.New("Playlist is full")
|
|
|
|
|
}
|
|
|
|
|
p.LIST = append([]uint32{ track_id }, p.LIST...) // don't forget '...''
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (p *Playlist) Insert(track_id uint32, at_index int) error {
|
|
|
|
|
if at_index > p.MAX {
|
|
|
|
|
return errors.New("Invalid insert index")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p.LIST = append(p.LIST, 0)
|
|
|
|
|
copy(p.LIST[at_index+1:], p.LIST[at_index:])
|
|
|
|
|
p.LIST[at_index] = track_id
|
|
|
|
|
|
|
|
|
|
if len(p.LIST) > p.MAX {
|
|
|
|
|
p.LIST = p.LIST[:p.MAX]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-02 19:06:45 +01:00
|
|
|
func (p *Playlist) index(track_id uint32) int {
|
2019-01-02 16:11:22 +01:00
|
|
|
k := -1
|
2019-01-02 19:06:45 +01:00
|
|
|
for i := 0; i < len(p.LIST); i++ {
|
2019-01-02 16:11:22 +01:00
|
|
|
if p.LIST[i] == track_id { k = i }
|
|
|
|
|
}
|
2019-01-02 19:06:45 +01:00
|
|
|
return k
|
|
|
|
|
}
|
2019-01-02 16:11:22 +01:00
|
|
|
|
2019-01-02 19:06:45 +01:00
|
|
|
func (p *Playlist) Delete(track_id uint32) error {
|
|
|
|
|
k := p.index(track_id);
|
2019-01-02 16:11:22 +01:00
|
|
|
if k < 0 {
|
|
|
|
|
return errors.New("Invalid track ID")
|
|
|
|
|
}
|
|
|
|
|
p.LIST = append(p.LIST[:k-1], p.LIST[k+1:]...)
|
|
|
|
|
return nil
|
2019-01-02 19:06:45 +01:00
|
|
|
}
|
2019-01-02 16:11:22 +01:00
|
|
|
|
2019-01-02 19:06:45 +01:00
|
|
|
func (p *Playlist) Move(track_id uint32, at_index int) error {
|
|
|
|
|
if at_index > p.MAX {
|
|
|
|
|
return errors.New("Invalid insert index")
|
|
|
|
|
}
|
|
|
|
|
k := p.index(track_id);
|
|
|
|
|
if k < 0 {
|
|
|
|
|
return errors.New("Invalid track ID")
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
// pln("k: " + strconv.Itoa(k))
|
|
|
|
|
// pln("at_index: " + strconv.Itoa(at_index))
|
|
|
|
|
// pln("------")
|
|
|
|
|
|
|
|
|
|
var slice_head, slice_move, slice_tail []uint32
|
|
|
|
|
if k < at_index {
|
|
|
|
|
slice_head = make([]uint32, len(p.LIST[:k]))
|
|
|
|
|
copy(slice_head, p.LIST[:k])
|
|
|
|
|
// slice_head = p.LIST[:k]
|
|
|
|
|
slice_move = make([]uint32, len(p.LIST[k+1:at_index+1]))
|
|
|
|
|
copy(slice_move, p.LIST[k+1:at_index+1])
|
|
|
|
|
// slice_move = p.LIST[k:at_index]
|
|
|
|
|
slice_tail = make([]uint32, len(p.LIST[at_index+1:]))
|
|
|
|
|
copy(slice_tail, p.LIST[at_index+1:])
|
|
|
|
|
p.LIST = append(slice_head, append(append(slice_move, track_id), slice_tail...)...)
|
|
|
|
|
} else if k > at_index {
|
|
|
|
|
slice_head = make([]uint32, len(p.LIST[:at_index]))
|
|
|
|
|
copy(slice_head, p.LIST[:at_index])
|
|
|
|
|
// slice_head = p.LIST[:at_index]
|
|
|
|
|
slice_move = make([]uint32, len(p.LIST[at_index:k]))
|
|
|
|
|
copy(slice_move, p.LIST[at_index:k])
|
|
|
|
|
// slice_tail = p.LIST[k+1:]
|
|
|
|
|
slice_tail = make([]uint32, len(p.LIST[k+1:]))
|
|
|
|
|
copy(slice_tail, p.LIST[k+1:])
|
|
|
|
|
p.LIST = append(append(append(slice_head, track_id), slice_move...), slice_tail...)
|
|
|
|
|
}
|
2019-01-02 19:06:45 +01:00
|
|
|
|
|
|
|
|
|
2019-01-03 14:21:24 +01:00
|
|
|
// pln("slice head: ")
|
|
|
|
|
// print(slice_head)
|
|
|
|
|
// pln("slice move: ")
|
|
|
|
|
// print(slice_move)
|
|
|
|
|
// pln("** ")
|
|
|
|
|
// pln("slice tail: ")
|
|
|
|
|
// print(slice_tail)
|
|
|
|
|
// pln("...........................")
|
2019-01-02 19:06:45 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
2019-01-02 16:11:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func Random() (uint32, string) {
|
|
|
|
|
rand.Seed(time.Now().UTC().UnixNano())
|
|
|
|
|
index := archive.Archive_map_keys[rand.Intn(len(archive.Archive_map_keys) - 1)]
|
|
|
|
|
return archive.Archive_map[index].ID, archive.Archive_map[index].NAME
|
|
|
|
|
}
|