Skip to content

Commit ff980c8

Browse files
author
Datanoise
committed
fix: resolve JS syntax error in SSE handler and finalize lifecycle fixes
1 parent b41eb72 commit ff980c8

4 files changed

Lines changed: 105 additions & 95 deletions

File tree

relay/streamer.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -272,32 +272,35 @@ func (s *Streamer) LoadPlaylist(filename string) error {
272272
if err != nil {
273273
if os.IsNotExist(err) {
274274
if filename == s.Name+".pls" {
275-
return s.SavePlaylist() // Create default empty
275+
return s.SavePlaylist()
276276
}
277-
return nil // Just ignore if custom playlist doesn't exist yet
277+
return nil
278278
}
279279
return err
280280
}
281281
defer f.Close()
282282

283-
s.mu.Lock()
284-
s.Playlist = []string{}
285-
s.LastPlaylist = filename
286-
s.mu.Unlock()
287-
288-
// Simple PLS parser
283+
var newPlaylist []string
289284
scanner := bufio.NewScanner(f)
290-
s.mu.Lock()
291-
defer s.mu.Unlock()
292285
for scanner.Scan() {
293-
line := scanner.Text()
294-
if strings.HasPrefix(line, "File") {
286+
line := strings.TrimSpace(scanner.Text())
287+
if line == "" || strings.HasPrefix(line, "[") || strings.HasPrefix(line, "#") {
288+
continue
289+
}
290+
if strings.HasPrefix(strings.ToLower(line), "file") {
295291
parts := strings.SplitN(line, "=", 2)
296292
if len(parts) == 2 {
297-
s.Playlist = append(s.Playlist, parts[1])
293+
newPlaylist = append(newPlaylist, strings.TrimSpace(parts[1]))
298294
}
299295
}
300296
}
297+
298+
if len(newPlaylist) > 0 {
299+
s.mu.Lock()
300+
s.Playlist = newPlaylist
301+
s.LastPlaylist = filename
302+
s.mu.Unlock()
303+
}
301304
return nil
302305
}
303306

@@ -321,6 +324,12 @@ func (s *Streamer) SetPlaylist(p []string) {
321324
s.Playlist = p
322325
}
323326

327+
func (s *Streamer) SetLastPlaylist(name string) {
328+
s.mu.Lock()
329+
defer s.mu.Unlock()
330+
s.LastPlaylist = name
331+
}
332+
324333
func (s *Streamer) AddToPlaylist(path string) {
325334
s.mu.Lock()
326335
defer s.mu.Unlock()
@@ -455,7 +464,8 @@ func (sm *StreamerManager) StopStreamer(mount string) {
455464
s.MPDServer.Stop()
456465
}
457466
s.Stop()
458-
delete(sm.instances, mount)
467+
// We DON'T delete it from sm.instances anymore,
468+
// so it remains manageable via UI even when stopped.
459469
}
460470
}
461471

server/server.go

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -436,13 +436,14 @@ func (s *Server) Start() error {
436436
}
437437
// Start configured AutoDJs
438438
for _, adj := range s.Config.AutoDJs {
439-
if adj.Enabled {
440-
absMusicDir, _ := filepath.Abs(adj.MusicDir)
441-
streamer, err := s.StreamerM.StartStreamer(adj.Name, adj.Mount, absMusicDir, adj.Loop, adj.Format, adj.Bitrate, adj.InjectMetadata, adj.Playlist, adj.MPDEnabled, adj.MPDPort, adj.MPDPassword, adj.Visible, adj.LastPlaylist)
442-
if err != nil {
443-
logrus.WithError(err).Errorf("Failed to start AutoDJ %s", adj.Name)
444-
} else {
445-
logrus.Infof("AutoDJ %s started on %s", adj.Name, adj.Mount)
439+
absMusicDir, _ := filepath.Abs(adj.MusicDir)
440+
streamer, err := s.StreamerM.StartStreamer(adj.Name, adj.Mount, absMusicDir, adj.Loop, adj.Format, adj.Bitrate, adj.InjectMetadata, adj.Playlist, adj.MPDEnabled, adj.MPDPort, adj.MPDPassword, adj.Visible, adj.LastPlaylist)
441+
if err == nil {
442+
// If we have a last playlist file, it's the source of truth
443+
if adj.LastPlaylist != "" {
444+
streamer.LoadPlaylist(adj.LastPlaylist)
445+
}
446+
if adj.Enabled {
446447
if adj.LastPlaylist != "" {
447448
streamer.LoadPlaylist(adj.LastPlaylist)
448449
}
@@ -454,7 +455,10 @@ func (s *Server) Start() error {
454455
if len(adj.Playlist) == 0 {
455456
streamer.ScanMusicDir()
456457
}
458+
streamer.Play()
457459
}
460+
} else {
461+
logrus.WithError(err).Errorf("Failed to initialize AutoDJ %s", adj.Name)
458462
}
459463
}
460464

@@ -2609,18 +2613,8 @@ func (s *Server) handlePlayerQueue(w http.ResponseWriter, r *http.Request) {
26092613
}
26102614

26112615
if action == "add" {
2612-
musicDir := streamer.GetMusicDir()
2613-
fullPath := path
2614-
if !filepath.IsAbs(path) {
2615-
fullPath = filepath.Join(musicDir, path)
2616-
}
2617-
// Security: Ensure fullPath is still within musicDir
2618-
if !strings.HasPrefix(fullPath, musicDir) {
2619-
http.Error(w, "Forbidden", http.StatusForbidden)
2620-
return
2621-
}
2622-
abs, _ := filepath.Abs(fullPath)
2623-
streamer.PushToQueue(abs)
2616+
streamer.PushToQueue(path)
2617+
logrus.Debugf("AutoDJ %s: Queued song %s", mount, path)
26242618
} else if action == "remove" {
26252619
var index int
26262620
fmt.Sscanf(r.FormValue("index"), "%d", &index)
@@ -2728,11 +2722,12 @@ func (s *Server) handlePlayerFiles(w http.ResponseWriter, r *http.Request) {
27282722
}
27292723

27302724
type fileEntry struct {
2731-
Name string `json:"name"`
2732-
Title string `json:"title"`
2733-
IsDir bool `json:"is_dir"`
2734-
Path string `json:"path"`
2735-
IsPLS bool `json:"is_pls"`
2725+
Name string `json:"name"`
2726+
Title string `json:"title"`
2727+
IsDir bool `json:"is_dir"`
2728+
Path string `json:"path"`
2729+
AbsPath string `json:"abs_path"`
2730+
IsPLS bool `json:"is_pls"`
27362731
}
27372732
var res []fileEntry
27382733

@@ -2741,12 +2736,14 @@ func (s *Server) handlePlayerFiles(w http.ResponseWriter, r *http.Request) {
27412736
if plsEntries, err := os.ReadDir("playlists"); err == nil {
27422737
for _, f := range plsEntries {
27432738
if !f.IsDir() && strings.HasSuffix(f.Name(), ".pls") {
2739+
abs, _ := filepath.Abs(filepath.Join("playlists", f.Name()))
27442740
res = append(res, fileEntry{
2745-
Name: f.Name(),
2746-
Title: "Playlist: " + f.Name(),
2747-
IsDir: false,
2748-
Path: f.Name(),
2749-
IsPLS: true,
2741+
Name: f.Name(),
2742+
Title: "Playlist: " + f.Name(),
2743+
IsDir: false,
2744+
Path: f.Name(),
2745+
AbsPath: abs,
2746+
IsPLS: true,
27502747
})
27512748
}
27522749
}
@@ -2757,15 +2754,18 @@ func (s *Server) handlePlayerFiles(w http.ResponseWriter, r *http.Request) {
27572754
ext := strings.ToLower(filepath.Ext(f.Name()))
27582755
if f.IsDir() || ext == ".mp3" {
27592756
title := f.Name()
2757+
full := filepath.Join(fullPath, f.Name())
27602758
if !f.IsDir() {
2761-
title = streamer.GetSongTitle(filepath.Join(fullPath, f.Name()))
2759+
title = streamer.GetSongTitle(full)
27622760
}
2761+
abs, _ := filepath.Abs(full)
27632762
res = append(res, fileEntry{
2764-
Name: f.Name(),
2765-
Title: title,
2766-
IsDir: f.IsDir(),
2767-
Path: filepath.Join(subDir, f.Name()),
2768-
IsPLS: false,
2763+
Name: f.Name(),
2764+
Title: title,
2765+
IsDir: f.IsDir(),
2766+
Path: filepath.Join(subDir, f.Name()),
2767+
AbsPath: abs,
2768+
IsPLS: false,
27692769
})
27702770
}
27712771
}
@@ -2794,29 +2794,21 @@ func (s *Server) handlePlayerPlaylistAction(w http.ResponseWriter, r *http.Reque
27942794
}
27952795

27962796
if action == "add" {
2797-
musicDir := streamer.GetMusicDir()
2798-
fullPath := filepath.Join(musicDir, relPath)
2799-
2800-
// Security: Ensure fullPath is still within musicDir
2801-
if !strings.HasPrefix(fullPath, musicDir) {
2802-
http.Error(w, "Forbidden", http.StatusForbidden)
2803-
return
2804-
}
2797+
fullPath := relPath // Already absolute from frontend now
2798+
logrus.Debugf("AutoDJ %s: Adding song %s to playlist", mount, fullPath)
28052799

28062800
info, err := os.Stat(fullPath)
28072801
if err == nil {
28082802
if info.IsDir() {
28092803
// Add folder recursively
28102804
filepath.Walk(fullPath, func(p string, i os.FileInfo, e error) error {
28112805
if e == nil && !i.IsDir() && strings.ToLower(filepath.Ext(p)) == ".mp3" {
2812-
abs, _ := filepath.Abs(p)
2813-
streamer.AddToPlaylist(abs)
2806+
streamer.AddToPlaylist(p)
28142807
}
28152808
return nil
28162809
})
28172810
} else {
2818-
abs, _ := filepath.Abs(fullPath)
2819-
streamer.AddToPlaylist(abs)
2811+
streamer.AddToPlaylist(fullPath)
28202812
}
28212813
}
28222814
} else if action == "remove" {
@@ -2825,12 +2817,21 @@ func (s *Server) handlePlayerPlaylistAction(w http.ResponseWriter, r *http.Reque
28252817
streamer.RemoveFromPlaylist(idx)
28262818
}
28272819
playlistCopy := streamer.GetPlaylist()
2820+
2821+
// If we added a song and haven't saved a playlist yet, set the default name
2822+
lastPl := streamer.GetStats().LastPlaylist
2823+
if action == "add" && lastPl == "" {
2824+
lastPl = streamer.Name + ".pls"
2825+
streamer.SetLastPlaylist(lastPl)
2826+
}
2827+
28282828
streamer.SavePlaylist()
28292829

28302830
// Persist
28312831
for _, adj := range s.Config.AutoDJs {
28322832
if adj.Mount == mount {
28332833
adj.Playlist = playlistCopy
2834+
adj.LastPlaylist = lastPl
28342835
s.Config.SaveConfig()
28352836
break
28362837
}
@@ -2891,9 +2892,9 @@ func (s *Server) handleUpdateAutoDJ(w http.ResponseWriter, r *http.Request) {
28912892

28922893
// Restart streamer
28932894
s.StreamerM.StopStreamer(oldMount)
2894-
if adj.Enabled {
2895-
streamer, err := s.StreamerM.StartStreamer(adj.Name, adj.Mount, adj.MusicDir, adj.Loop, adj.Format, adj.Bitrate, adj.InjectMetadata, adj.Playlist, adj.MPDEnabled, adj.MPDPort, adj.MPDPassword, adj.Visible, adj.LastPlaylist)
2896-
if err == nil {
2895+
streamer, err := s.StreamerM.StartStreamer(adj.Name, adj.Mount, absMusicDir, adj.Loop, adj.Format, adj.Bitrate, adj.InjectMetadata, adj.Playlist, adj.MPDEnabled, adj.MPDPort, adj.MPDPassword, adj.Visible, adj.LastPlaylist)
2896+
if err == nil {
2897+
if adj.Enabled {
28972898
if adj.InjectMetadata {
28982899
if st, ok := s.Relay.GetStream(adj.Mount); ok {
28992900
st.SetVisible(adj.Visible)

server/templates/admin.html

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -913,14 +913,14 @@ <h2>Edit AutoDJ</h2>
913913
<input type="hidden" name="csrf" value="${csrfToken}">
914914
<input type="hidden" name="mount" value="${mount}">
915915
<input type="hidden" name="action" value="add">
916-
<input type="hidden" name="path" value="${e.path}">
916+
<input type="hidden" name="path" value="${e.abs_path}">
917917
<button type="submit" class="btn btn-outline btn-sm" style="padding:0.1rem 0.3rem; font-size:0.7rem;" title="Queue Next">QUEUE</button>
918918
</form>` : ''}
919919
<form action="${actionURL}" method="POST" onsubmit="${confirmMsg ? `if(confirm('${confirmMsg}')) ` : ''}return submitForm(event, true)" style="margin:0;">
920920
<input type="hidden" name="csrf" value="${csrfToken}">
921921
<input type="hidden" name="mount" value="${mount}">
922922
<input type="hidden" name="action" value="add">
923-
<input type="hidden" name="file" value="${e.path}">
923+
<input type="hidden" name="file" value="${e.abs_path}">
924924
<button type="submit" class="btn ${e.is_pls ? 'btn-warning' : 'btn-primary'} btn-sm" style="padding:0.1rem 0.3rem; font-size:0.7rem;" title="${e.is_dir ? 'Add folder recursively' : (e.is_pls ? 'Load playlist file' : 'Add to Playlist')}">${actionLabel}</button>
925925
</form>
926926
</div>
@@ -1331,42 +1331,42 @@ <h2>Edit AutoDJ</h2>
13311331
if (adjDetails) adjDetails.style.display = 'block';
13321332
if (songTitle) songTitle.textContent = st.song || 'Preparing...';
13331333
if (progressBar) {
1334-
// IMPORTANT: Update these to reset the timeline
13351334
progressBar.dataset.start = st.start_time;
13361335
progressBar.dataset.duration = st.duration;
13371336
}
1338-
if (adjPos) adjPos.textContent = st.playlist_pos;
1339-
if (adjLen) adjLen.textContent = st.playlist_len;
1340-
1341-
// Update Queue
1342-
if (queueContainer && queueList) {
1343-
if (st.queue && st.queue.length > 0) {
1344-
queueContainer.style.display = 'block';
1345-
let qHtml = '';
1346-
st.queue.forEach((qitem, qidx) => {
1347-
qHtml += `
1348-
<div style="display: flex; justify-content: space-between; align-items: center; background: rgba(56, 189, 248, 0.05); padding: 0.5rem; border-radius: 4px; border: 1px dashed var(--primary-glow);">
1349-
<span style="font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${qitem.Path}">${qitem.Title}</span>
1350-
<form action="/admin/player/queue" method="POST" onsubmit="return submitForm(event, false)" style="margin:0;">
1351-
<input type="hidden" name="csrf" value="${csrfToken}">
1352-
<input type="hidden" name="mount" value="${st.mount}">
1353-
<input type="hidden" name="action" value="remove">
1354-
<input type="hidden" name="index" value="${qidx}">
1355-
<button type="submit" style="background:none; border:none; color:var(--danger); cursor:pointer;"><i data-lucide="x-circle" style="width:14px; height:14px;"></i></button>
1356-
</form>
1357-
</div>`;
1358-
});
1359-
queueList.innerHTML = qHtml;
1360-
lucide.createIcons();
1361-
} else {
1362-
queueContainer.style.display = 'none';
1363-
}
1364-
}
13651337
} else {
13661338
statusPill.classList.add('inactive');
13671339
statusText.textContent = 'IDLE';
13681340
if (adjDetails) adjDetails.style.display = 'none';
13691341
}
1342+
1343+
if (adjPos) adjPos.textContent = st.playlist_pos;
1344+
if (adjLen) adjLen.textContent = st.playlist_len;
1345+
1346+
// Update Queue
1347+
if (queueContainer && queueList) {
1348+
if (st.queue && st.queue.length > 0) {
1349+
queueContainer.style.display = 'block';
1350+
let qHtml = '';
1351+
st.queue.forEach((qitem, qidx) => {
1352+
qHtml += `
1353+
<div style="display: flex; justify-content: space-between; align-items: center; background: rgba(56, 189, 248, 0.05); padding: 0.5rem; border-radius: 4px; border: 1px dashed var(--primary-glow);">
1354+
<span style="font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="${qitem.Path}">${qitem.Title}</span>
1355+
<form action="/admin/player/queue" method="POST" onsubmit="return submitForm(event, false)" style="margin:0;">
1356+
<input type="hidden" name="csrf" value="${csrfToken}">
1357+
<input type="hidden" name="mount" value="${st.mount}">
1358+
<input type="hidden" name="action" value="remove">
1359+
<input type="hidden" name="index" value="${qidx}">
1360+
<button type="submit" style="background:none; border:none; color:var(--danger); cursor:pointer;"><i data-lucide="x-circle" style="width:14px; height:14px;"></i></button>
1361+
</form>
1362+
</div>`;
1363+
});
1364+
queueList.innerHTML = qHtml;
1365+
lucide.createIcons();
1366+
} else {
1367+
queueContainer.style.display = 'none';
1368+
}
1369+
}
13701370
});
13711371
}
13721372

tinyice.pid

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)