@@ -263,6 +263,9 @@ func (s *Server) setupRoutes() *http.ServeMux {
263263 mux .HandleFunc ("/admin/delete-webhook" , s .handleDeleteWebhook )
264264 mux .HandleFunc ("/admin/player/toggle" , s .handlePlayerToggle )
265265 mux .HandleFunc ("/admin/player/scan" , s .handlePlayerScan )
266+ mux .HandleFunc ("/admin/autodj/add" , s .handleAddAutoDJ )
267+ mux .HandleFunc ("/admin/autodj/delete" , s .handleDeleteAutoDJ )
268+ mux .HandleFunc ("/admin/autodj/toggle" , s .handleToggleAutoDJ )
266269 mux .HandleFunc ("/admin/add-relay" , s .handleAddRelay )
267270 mux .HandleFunc ("/admin/toggle-relay" , s .handleToggleRelay )
268271 mux .HandleFunc ("/admin/restart-relay" , s .handleRestartRelay )
@@ -383,9 +386,23 @@ func (s *Server) Start() error {
383386 }
384387 }
385388
386- if s .Config .MusicDir != "" {
389+ // Start configured AutoDJs
390+ for _ , adj := range s .Config .AutoDJs {
391+ if adj .Enabled {
392+ streamer , err := s .StreamerM .StartStreamer (adj .Name , adj .Mount , adj .MusicDir , adj .Loop )
393+ if err != nil {
394+ logrus .WithError (err ).Errorf ("Failed to start AutoDJ %s" , adj .Name )
395+ } else {
396+ logrus .Infof ("AutoDJ %s started on %s" , adj .Name , adj .Mount )
397+ streamer .ScanMusicDir () // Initial scan
398+ }
399+ }
400+ }
401+
402+ // Legacy/Default AutoDJ (backward compatibility)
403+ if s .Config .MusicDir != "" && s .StreamerM .GetStreamer ("/autodj" ) == nil {
387404 // Start a default streamer for /autodj
388- streamer , err := s .StreamerM .StartStreamer ("AutoDJ" , "/autodj" , s .Config .MusicDir )
405+ streamer , err := s .StreamerM .StartStreamer ("AutoDJ" , "/autodj" , s .Config .MusicDir , true )
389406 if err == nil {
390407 if s .Config .MPDEnabled {
391408 port := s .Config .MPDPort
@@ -399,6 +416,7 @@ func (s *Server) Start() error {
399416 logrus .Infof ("MPD Server listening on port %s" , port )
400417 }
401418 }
419+ streamer .ScanMusicDir ()
402420 } else {
403421 logrus .WithError (err ).Error ("Failed to start default streamer" )
404422 }
@@ -2128,6 +2146,104 @@ func (s *Server) handlePlayerScan(w http.ResponseWriter, r *http.Request) {
21282146 http .Redirect (w , r , "/admin#tab-streamer" , http .StatusSeeOther )
21292147}
21302148
2149+ func (s * Server ) handleAddAutoDJ (w http.ResponseWriter , r * http.Request ) {
2150+ if ! s .isCSRFSafe (r ) {
2151+ http .Error (w , "Forbidden" , http .StatusForbidden )
2152+ return
2153+ }
2154+ if _ , ok := s .checkAuth (r ); ! ok {
2155+ return
2156+ }
2157+
2158+ name := r .FormValue ("name" )
2159+ mount := r .FormValue ("mount" )
2160+ musicDir := r .FormValue ("music_dir" )
2161+ loop := r .FormValue ("loop" ) == "on"
2162+
2163+ if name == "" || mount == "" || musicDir == "" {
2164+ http .Error (w , "Name, mount, and music directory are required" , http .StatusBadRequest )
2165+ return
2166+ }
2167+
2168+ if mount [0 ] != '/' {
2169+ mount = "/" + mount
2170+ }
2171+
2172+ adj := & config.AutoDJConfig {
2173+ Name : name ,
2174+ Mount : mount ,
2175+ MusicDir : musicDir ,
2176+ Enabled : true ,
2177+ Loop : loop ,
2178+ }
2179+
2180+ s .Config .AutoDJs = append (s .Config .AutoDJs , adj )
2181+ s .Config .SaveConfig ()
2182+
2183+ // Start it immediately
2184+ streamer , err := s .StreamerM .StartStreamer (adj .Name , adj .Mount , adj .MusicDir , adj .Loop )
2185+ if err == nil {
2186+ streamer .ScanMusicDir ()
2187+ streamer .Play ()
2188+ }
2189+
2190+ http .Redirect (w , r , "/admin#tab-streamer" , http .StatusSeeOther )
2191+ }
2192+
2193+ func (s * Server ) handleDeleteAutoDJ (w http.ResponseWriter , r * http.Request ) {
2194+ if ! s .isCSRFSafe (r ) {
2195+ http .Error (w , "Forbidden" , http .StatusForbidden )
2196+ return
2197+ }
2198+ if _ , ok := s .checkAuth (r ); ! ok {
2199+ return
2200+ }
2201+
2202+ mount := r .FormValue ("mount" )
2203+ newADJs := []* config.AutoDJConfig {}
2204+ for _ , adj := range s .Config .AutoDJs {
2205+ if adj .Mount != mount {
2206+ newADJs = append (newADJs , adj )
2207+ } else {
2208+ s .StreamerM .StopStreamer (mount )
2209+ }
2210+ }
2211+ s .Config .AutoDJs = newADJs
2212+ s .Config .SaveConfig ()
2213+
2214+ http .Redirect (w , r , "/admin#tab-streamer" , http .StatusSeeOther )
2215+ }
2216+
2217+ func (s * Server ) handleToggleAutoDJ (w http.ResponseWriter , r * http.Request ) {
2218+ if ! s .isCSRFSafe (r ) {
2219+ http .Error (w , "Forbidden" , http .StatusForbidden )
2220+ return
2221+ }
2222+ if _ , ok := s .checkAuth (r ); ! ok {
2223+ return
2224+ }
2225+
2226+ mount := r .FormValue ("mount" )
2227+ for _ , adj := range s .Config .AutoDJs {
2228+ if adj .Mount == mount {
2229+ adj .Enabled = ! adj .Enabled
2230+ if adj .Enabled {
2231+ streamer , err := s .StreamerM .StartStreamer (adj .Name , adj .Mount , adj .MusicDir , adj .Loop )
2232+ if err == nil {
2233+ streamer .ScanMusicDir ()
2234+ streamer .Play ()
2235+ }
2236+ } else {
2237+ s .StreamerM .StopStreamer (mount )
2238+ }
2239+ break
2240+ }
2241+ }
2242+ s .Config .SaveConfig ()
2243+
2244+ http .Redirect (w , r , "/admin#tab-streamer" , http .StatusSeeOther )
2245+ }
2246+
21312247func (s * Server ) handleGetStats (w http.ResponseWriter , r * http.Request ) {
21322248 if _ , ok := s .checkAuth (r ); ! ok {
21332249 http .Error (w , "Unauthorized" , http .StatusUnauthorized )
0 commit comments