@@ -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 )
0 commit comments