Skip to content

Commit 393ac24

Browse files
author
Datanoise
committed
feat: add animated action feedback overlays for all AJAX dashboard actions
1 parent 7072c11 commit 393ac24

5 files changed

Lines changed: 320 additions & 47 deletions

File tree

playlists/abce.pls

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
[playlist]
2+
NumberOfEntries=71
3+
File1=/Users/syso/dev/tinyice/music/(Verse 1)(1).mp3
4+
Title1=(Verse 1)(1).mp3
5+
File2=/Users/syso/dev/tinyice/music/DEVASTATION - I Feel Love (Acapella) (Donna Summer Cover)-PN6sPWmZ1o4.mp3
6+
Title2=DEVASTATION - I Feel Love (Acapella) (Donna Summer Cover)-PN6sPWmZ1o4.mp3
7+
File3=/Users/syso/dev/tinyice/music/Der Zauberhut 1v2. Hörbuch von Terry Pratchett [_glTWbUIVEo].mp3
8+
Title3=Der Zauberhut 1v2. Hörbuch von Terry Pratchett [_glTWbUIVEo].mp3
9+
File4=/Users/syso/dev/tinyice/music/Lexyyysy42l.mp3
10+
Title4=Lexyyysy42l.mp3
11+
File5=/Users/syso/dev/tinyice/music/Lexyyysyl.mp3
12+
Title5=Lexyyysyl.mp3
13+
File6=/Users/syso/dev/tinyice/music/Lexyyysyl3.mp3
14+
Title6=Lexyyysyl3.mp3
15+
File7=/Users/syso/dev/tinyice/music/Linda Di Franco - TV Scene [261953612].mp3
16+
Title7=Linda Di Franco - TV Scene [261953612].mp3
17+
File8=/Users/syso/dev/tinyice/music/Live from Nuits sonores: Helena Hauff b2b DJ MELL G [2200736951].mp3
18+
Title8=Live from Nuits sonores: Helena Hauff b2b DJ MELL G [2200736951].mp3
19+
File9=/Users/syso/dev/tinyice/music/Madness [DvVP_nOgxQo].mp3
20+
Title9=Madness [DvVP_nOgxQo].mp3
21+
File10=/Users/syso/dev/tinyice/music/Oldskool Jungle Drum n Bass Mix 92-97 [VHXvfSmyjj4].mp3
22+
Title10=Oldskool Jungle Drum n Bass Mix 92-97 [VHXvfSmyjj4].mp3
23+
File11=/Users/syso/dev/tinyice/music/Omega Nebula - Dubplate - Little Lion Sound - Hero [CycPQZDEHtM].mp3
24+
Title11=Omega Nebula - Dubplate - Little Lion Sound - Hero [CycPQZDEHtM].mp3
25+
File12=/Users/syso/dev/tinyice/music/That sound your old pc speaker does when you receive a phone call [FYjs7vsaSEw].mp3
26+
Title12=That sound your old pc speaker does when you receive a phone call [FYjs7vsaSEw].mp3
27+
File13=/Users/syso/dev/tinyice/music/The Door in the Wall by H.G. Wells | Short Story Audiobook [Huam5sSRjwc].mp3
28+
Title13=The Door in the Wall by H.G. Wells | Short Story Audiobook [Huam5sSRjwc].mp3
29+
File14=/Users/syso/dev/tinyice/music/Video by songsthatshouldnotwork [DRXcvzJjrE-].mp3
30+
Title14=Video by songsthatshouldnotwork [DRXcvzJjrE-].mp3
31+
File15=/Users/syso/dev/tinyice/music/You're so precious - Ver 2 Different Take.mp3
32+
Title15=You're so precious - Ver 2 Different Take.mp3
33+
File16=/Users/syso/dev/tinyice/music/_tech2_crazy.mp3
34+
Title16=_tech2_crazy.mp3
35+
File17=/Users/syso/dev/tinyice/music/a_d.mp3
36+
Title17=a_d.mp3
37+
File18=/Users/syso/dev/tinyice/music/a_d_full.mp3
38+
Title18=a_d_full.mp3
39+
File19=/Users/syso/dev/tinyice/music/a_d_full_b.mp3
40+
Title19=a_d_full_b.mp3
41+
File20=/Users/syso/dev/tinyice/music/a_d_full_b_fixed.mp3
42+
Title20=a_d_full_b_fixed.mp3
43+
File21=/Users/syso/dev/tinyice/music/a_dwwe#s.mp3
44+
Title21=a_dwwe#s.mp3
45+
File22=/Users/syso/dev/tinyice/music/a_dwwe#sss4344ws5few3s.mp3
46+
Title22=a_dwwe#sss4344ws5few3s.mp3
47+
File23=/Users/syso/dev/tinyice/music/a_dwwe#sss4344ws_420.mp3
48+
Title23=a_dwwe#sss4344ws_420.mp3
49+
File24=/Users/syso/dev/tinyice/music/abc.mp3
50+
Title24=abc.mp3
51+
File25=/Users/syso/dev/tinyice/music/abedi.mp3
52+
Title25=abedi.mp3
53+
File26=/Users/syso/dev/tinyice/music/acapella.mp3
54+
Title26=acapella.mp3
55+
File27=/Users/syso/dev/tinyice/music/allie_cover_.mp3
56+
Title27=allie_cover_.mp3
57+
File28=/Users/syso/dev/tinyice/music/allie_cvr.mp3
58+
Title28=allie_cvr.mp3
59+
File29=/Users/syso/dev/tinyice/music/ambi1a.mp3
60+
Title29=ambi1a.mp3
61+
File30=/Users/syso/dev/tinyice/music/aos.mp3
62+
Title30=aos.mp3
63+
File31=/Users/syso/dev/tinyice/music/asd.mp3
64+
Title31=asd.mp3
65+
File32=/Users/syso/dev/tinyice/music/bis.mp3
66+
Title32=bis.mp3
67+
File33=/Users/syso/dev/tinyice/music/bjean.mp3
68+
Title33=bjean.mp3
69+
File34=/Users/syso/dev/tinyice/music/bla123.mp3
70+
Title34=bla123.mp3
71+
File35=/Users/syso/dev/tinyice/music/cabs.mp3
72+
Title35=cabs.mp3
73+
File36=/Users/syso/dev/tinyice/music/disco.mp3
74+
Title36=disco.mp3
75+
File37=/Users/syso/dev/tinyice/music/dnbass.mp3
76+
Title37=dnbass.mp3
77+
File38=/Users/syso/dev/tinyice/music/drbrk.mp3
78+
Title38=drbrk.mp3
79+
File39=/Users/syso/dev/tinyice/music/ero.mp3
80+
Title39=ero.mp3
81+
File40=/Users/syso/dev/tinyice/music/fmbeat2.mp3
82+
Title40=fmbeat2.mp3
83+
File41=/Users/syso/dev/tinyice/music/fuckdat.mp3
84+
Title41=fuckdat.mp3
85+
File42=/Users/syso/dev/tinyice/music/funkyshit.mp3
86+
Title42=funkyshit.mp3
87+
File43=/Users/syso/dev/tinyice/music/geilo3.mp3
88+
Title43=geilo3.mp3
89+
File44=/Users/syso/dev/tinyice/music/geilo32.mp3
90+
Title44=geilo32.mp3
91+
File45=/Users/syso/dev/tinyice/music/give.mp3
92+
Title45=give.mp3
93+
File46=/Users/syso/dev/tinyice/music/house2.mp3
94+
Title46=house2.mp3
95+
File47=/Users/syso/dev/tinyice/music/ladida2 2.mp3
96+
Title47=ladida2 2.mp3
97+
File48=/Users/syso/dev/tinyice/music/loop_hqhdho.mp3
98+
Title48=loop_hqhdho.mp3
99+
File49=/Users/syso/dev/tinyice/music/lup.mp3
100+
Title49=lup.mp3
101+
File50=/Users/syso/dev/tinyice/music/modem.mp3
102+
Title50=modem.mp3
103+
File51=/Users/syso/dev/tinyice/music/osasd.mp3
104+
Title51=osasd.mp3
105+
File52=/Users/syso/dev/tinyice/music/precious3.mp3
106+
Title52=precious3.mp3
107+
File53=/Users/syso/dev/tinyice/music/precious4.mp3
108+
Title53=precious4.mp3
109+
File54=/Users/syso/dev/tinyice/music/saliut.mp3
110+
Title54=saliut.mp3
111+
File55=/Users/syso/dev/tinyice/music/salut2.mp3
112+
Title55=salut2.mp3
113+
File56=/Users/syso/dev/tinyice/music/sd2.mp3
114+
Title56=sd2.mp3
115+
File57=/Users/syso/dev/tinyice/music/sess6_bef_edit46.mp3
116+
Title57=sess6_bef_edit46.mp3
117+
File58=/Users/syso/dev/tinyice/music/sess6_bef_edit463.mp3
118+
Title58=sess6_bef_edit463.mp3
119+
File59=/Users/syso/dev/tinyice/music/sess6_bef_edit49.mp3
120+
Title59=sess6_bef_edit49.mp3
121+
File60=/Users/syso/dev/tinyice/music/short.mp3
122+
Title60=short.mp3
123+
File61=/Users/syso/dev/tinyice/music/shtuff123.mp3
124+
Title61=shtuff123.mp3
125+
File62=/Users/syso/dev/tinyice/music/sj.mp3
126+
Title62=sj.mp3
127+
File63=/Users/syso/dev/tinyice/music/snip.mp3
128+
Title63=snip.mp3
129+
File64=/Users/syso/dev/tinyice/music/untitled1.mp3
130+
Title64=untitled1.mp3
131+
File65=/Users/syso/dev/tinyice/music/untitled12.mp3
132+
Title65=untitled12.mp3
133+
File66=/Users/syso/dev/tinyice/music/untitled13.mp3
134+
Title66=untitled13.mp3
135+
File67=/Users/syso/dev/tinyice/music/x82.mp3
136+
Title67=x82.mp3
137+
File68=/Users/syso/dev/tinyice/music/xaea_3.mp3
138+
Title68=xaea_3.mp3
139+
File69=/Users/syso/dev/tinyice/music/xbd.mp3
140+
Title69=xbd.mp3
141+
File70=/Users/syso/dev/tinyice/music/xfh.mp3
142+
Title70=xfh.mp3
143+
File71=/Users/syso/dev/tinyice/music/young.mp3
144+
Title71=young.mp3
145+
Version=2

relay/streamer.go

Lines changed: 57 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package relay
22

33
import (
4+
"bufio"
45
"context"
56
"fmt"
67
"math/rand"
@@ -218,33 +219,66 @@ func (s *Streamer) ScanMusicDir() error {
218219
return fmt.Errorf("music directory not configured")
219220
}
220221

222+
// We don't automatically populate s.Playlist anymore
223+
// Just clear cache if we rescanned
221224
s.titleCache = make(map[string]string)
222-
s.Playlist = []string{}
223-
err := filepath.Walk(s.MusicDir, func(path string, info os.FileInfo, err error) error {
224-
if err != nil {
225-
return err
226-
}
227-
if !info.IsDir() {
228-
ext := strings.ToLower(filepath.Ext(path))
229-
if ext == ".mp3" {
230-
absPath, _ := filepath.Abs(path)
231-
s.Playlist = append(s.Playlist, absPath)
232-
}
225+
return nil
226+
}
227+
228+
func (s *Streamer) SavePlaylist() error {
229+
s.mu.RLock()
230+
defer s.mu.RUnlock()
231+
232+
if err := os.MkdirAll("playlists", 0755); err != nil {
233+
return err
234+
}
235+
236+
path := filepath.Join("playlists", s.Name+".pls")
237+
f, err := os.Create(path)
238+
if err != nil {
239+
return err
240+
}
241+
defer f.Close()
242+
243+
fmt.Fprintf(f, "[playlist]\nNumberOfEntries=%d\n", len(s.Playlist))
244+
for i, p := range s.Playlist {
245+
fmt.Fprintf(f, "File%d=%s\n", i+1, p)
246+
fmt.Fprintf(f, "Title%d=%s\n", i+1, s.GetSongTitle(p))
247+
}
248+
fmt.Fprintf(f, "Version=2\n")
249+
return nil
250+
}
251+
252+
func (s *Streamer) LoadPlaylist() error {
253+
path := filepath.Join("playlists", s.Name+".pls")
254+
f, err := os.Open(path)
255+
if err != nil {
256+
if os.IsNotExist(err) {
257+
return s.SavePlaylist() // Create empty
233258
}
234-
return nil
235-
})
259+
return err
260+
}
261+
defer f.Close()
236262

237-
if err == nil {
238-
// Populate cache in background
239-
playlistCopy := make([]string, len(s.Playlist))
240-
copy(playlistCopy, s.Playlist)
241-
go func() {
242-
for _, p := range playlistCopy {
243-
s.fetchTitleAndCache(p)
263+
s.mu.Lock()
264+
defer s.mu.Unlock()
265+
s.Playlist = []string{}
266+
267+
var entries int
268+
fmt.Fscanf(f, "[playlist]\nNumberOfEntries=%d\n", &entries)
269+
270+
// Simple PLS parser
271+
scanner := bufio.NewScanner(f)
272+
for scanner.Scan() {
273+
line := scanner.Text()
274+
if strings.HasPrefix(line, "File") {
275+
parts := strings.SplitN(line, "=", 2)
276+
if len(parts) == 2 {
277+
s.Playlist = append(s.Playlist, parts[1])
244278
}
245-
}()
279+
}
246280
}
247-
return err
281+
return nil
248282
}
249283

250284
func (s *Streamer) GetMusicDir() string {
@@ -376,6 +410,7 @@ func (sm *StreamerManager) StartStreamer(name, mount, musicDir string, loop bool
376410
}
377411

378412
sm.instances[mount] = s
413+
s.LoadPlaylist()
379414

380415
go sm.runStreamerLoop(ctx, s)
381416
return s, nil

server/server.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ func (s *Server) setupRoutes() *http.ServeMux {
301301
mux.HandleFunc("/admin/delete-webhook", s.handleDeleteWebhook)
302302
mux.HandleFunc("/admin/player/toggle", s.handlePlayerToggle)
303303
mux.HandleFunc("/admin/player/scan", s.handlePlayerScan)
304+
mux.HandleFunc("/admin/player/save-playlist", s.handlePlayerSavePlaylist)
304305
mux.HandleFunc("/admin/player/reorder", s.handlePlayerReorder)
305306
mux.HandleFunc("/admin/player/queue", s.handlePlayerQueue)
306307
mux.HandleFunc("/admin/player/shuffle", s.handlePlayerShuffle)
@@ -2324,17 +2325,31 @@ func (s *Server) handlePlayerScan(w http.ResponseWriter, r *http.Request) {
23242325
logrus.WithError(err).Error("Failed to scan music directory")
23252326
}
23262327

2327-
// Persist
2328-
playlistCopy := streamer.GetPlaylist()
2329-
for _, adj := range s.Config.AutoDJs {
2330-
if adj.Mount == mount {
2331-
adj.Playlist = playlistCopy
2332-
s.Config.SaveConfig()
2333-
break
2334-
}
2328+
w.WriteHeader(http.StatusOK)
2329+
}
2330+
2331+
func (s *Server) handlePlayerSavePlaylist(w http.ResponseWriter, r *http.Request) {
2332+
if !s.isCSRFSafe(r) {
2333+
http.Error(w, "Forbidden", http.StatusForbidden)
2334+
return
2335+
}
2336+
if _, ok := s.checkAuth(r); !ok {
2337+
return
23352338
}
23362339

2337-
http.Redirect(w, r, "/admin#tab-streamer", http.StatusSeeOther)
2340+
mount := r.FormValue("mount")
2341+
streamer := s.StreamerM.GetStreamer(mount)
2342+
if streamer == nil {
2343+
http.Error(w, "Streamer not found", http.StatusNotFound)
2344+
return
2345+
}
2346+
2347+
if err := streamer.SavePlaylist(); err != nil {
2348+
http.Error(w, err.Error(), http.StatusInternalServerError)
2349+
return
2350+
}
2351+
2352+
w.WriteHeader(http.StatusOK)
23382353
}
23392354

23402355
func (s *Server) handleAddAutoDJ(w http.ResponseWriter, r *http.Request) {

0 commit comments

Comments
 (0)