@@ -405,19 +405,30 @@ <h3 style="margin: 0; display: flex; align-items: center; gap: 0.5rem;">
405405 {{if .MPDServer}}MPD: < span style ="color: var(--success); font-weight: bold; "> PORT {{.MPDServer.Port}}</ span > |{{end}}
406406 Dir: < span style ="font-family: monospace; font-size: 0.75rem; "> {{.MusicDir}}</ span >
407407 </ div > </ div >
408- < div style ="display: flex; gap: 0.5rem; ">
409- < form action ="/admin/player/shuffle " method ="POST ">
410- < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
411- < input type ="hidden " name ="mount " value ="{{.OutputMount}} ">
412- < button type ="submit " class ="btn {{if .Shuffle}}btn-primary{{else}}btn-outline{{end}} btn-sm " title ="Toggle Shuffle ">
413- < i data-lucide ="shuffle " style ="width: 14px; height: 14px; "> </ i >
414- </ button >
415- </ form >
416- {{if eq .State 1}}
417- < a href ="/player{{.OutputMount}} " target ="_blank " class ="btn btn-outline btn-sm "> < i data-lucide ="headphones " style ="width: 14px; height: 14px; margin-right: 4px; "> </ i > MONITOR</ a >
418- {{end}}
419- < form action ="/admin/autodj/toggle " method ="POST ">
420- < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
408+ < div style ="display: flex; gap: 0.5rem; align-items: center; ">
409+ < div class ="btn-group " style ="display: flex; background: rgba(0,0,0,0.2); border-radius: 8px; padding: 2px; ">
410+ < form action ="/admin/player/shuffle " method ="POST " style ="margin:0; ">
411+ < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
412+ < input type ="hidden " name ="mount " value ="{{.OutputMount}} ">
413+ < button type ="submit " class ="btn {{if .Shuffle}}btn-primary{{else}}btn-outline{{end}} btn-sm " style ="border:none; background:transparent; " title ="Toggle Shuffle ">
414+ < i data-lucide ="shuffle " style ="width: 14px; height: 14px; "> </ i >
415+ </ button >
416+ </ form >
417+ {{if eq .State 1}}
418+ < form action ="/admin/player/next " method ="POST " style ="margin:0; ">
419+ < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
420+ < input type ="hidden " name ="mount " value ="{{.OutputMount}} ">
421+ < button type ="submit " class ="btn btn-outline btn-sm " style ="border:none; background:transparent; " title ="Skip to Next ">
422+ < i data-lucide ="skip-forward " style ="width: 14px; height: 14px; "> </ i >
423+ </ button >
424+ </ form >
425+ {{end}}
426+ </ div >
427+
428+ {{if eq .State 1}}
429+ < a href ="/player{{.OutputMount}} " target ="_blank " class ="btn btn-outline btn-sm "> < i data-lucide ="headphones " style ="width: 14px; height: 14px; margin-right: 4px; "> </ i > MONITOR</ a >
430+ {{end}}
431+ < form action ="/admin/autodj/toggle " method ="POST "> < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
421432 < input type ="hidden " name ="mount " value ="{{.OutputMount}} ">
422433 < button type ="submit " class ="btn {{if eq .State 1}}btn-warning{{else}}btn-primary{{end}} btn-sm ">
423434 {{if eq .State 1}}STOP{{else}}START{{end}}
@@ -431,25 +442,35 @@ <h3 style="margin: 0; display: flex; align-items: center; gap: 0.5rem;">
431442 </ div >
432443 </ div >
433444
434- {{if eq .State 1}}
435- < div style ="background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 8px; border: 1px solid var(--border); ">
436- < div style ="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; ">
437- < div class ="label " style ="font-size: 0.7rem; color: var(--text-dim); "> NOW PLAYING</ div >
438- < div style ="font-size: 0.7rem; color: var(--primary); font-family: monospace; ">
439- Playlist: {{.CurrentPos}} / {{len .Playlist}}
440- </ div >
441- </ div >
442- < div class ="song-title " style ="font-size: 1rem; color: var(--text); margin-bottom: 0.75rem; ">
443- {{if .CurrentFile}}{{.CurrentFile}}{{else}}Preparing...{{end}}
444- </ div >
445- < div style ="height: 4px; background: var(--bg); border-radius: 2px; overflow: hidden; position: relative; ">
446- < div class ="progress-bar-adj " data-start ="{{.CurrentFileTime.Unix}} " data-duration ="{{.CurrentFileDuration.Seconds}} " style ="height: 100%; background: var(--primary); width: 0%; transition: width 1s linear; "> </ div >
445+ {{if eq .State 1}}
446+ < div style ="background: rgba(0,0,0,0.2); padding: 1rem; border-radius: 8px; border: 1px solid var(--border); ">
447+ < div style ="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem; ">
448+ < div >
449+ < div class ="label " style ="font-size: 0.7rem; color: var(--text-dim); "> NOW PLAYING</ div >
450+ < div class ="song-title " style ="font-size: 1rem; color: var(--text); margin-bottom: 0.75rem; ">
451+ {{if .CurrentFile}}{{.CurrentFile}}{{else}}Preparing...{{end}}
452+ </ div >
447453 </ div >
448- < div style ="display: flex; justify-content: space-between; margin-top: 0.4rem; font-size: 0.7rem; color: var(--text-dim); font-family: monospace; ">
449- < span class ="progress-time-current "> 00:00</ span >
450- < span class ="progress-time-total " id ="duration-{{.OutputMount}} "> 00:00</ span >
451- </ div > </ div >
452- {{end}}
454+ < div style ="display: flex; gap: 0.4rem; ">
455+ < form action ="/admin/player/toggle " method ="POST " style ="margin:0; ">
456+ < input type ="hidden " name ="csrf " value ="{{$.CSRFToken}} ">
457+ < input type ="hidden " name ="mount " value ="{{.OutputMount}} ">
458+ < button type ="submit " class ="btn btn-outline btn-sm " style ="padding: 0.3rem; " title ="Pause/Stop Streamer "> < i data-lucide ="square " style ="width: 12px; height: 12px; fill: currentColor; "> </ i > </ button >
459+ </ form >
460+ </ div >
461+ </ div >
462+ < div style ="height: 4px; background: var(--bg); border-radius: 2px; overflow: hidden; position: relative; ">
463+ < div class ="progress-bar-adj " data-start ="{{.CurrentFileTime.Unix}} " data-duration ="{{.CurrentFileDuration.Seconds}} " style ="height: 100%; background: var(--primary); width: 0%; transition: width 1s linear; "> </ div >
464+ </ div >
465+ < div style ="display: flex; justify-content: space-between; margin-top: 0.4rem; font-size: 0.7rem; color: var(--text-dim); font-family: monospace; ">
466+ < span class ="progress-time-current "> 00:00</ span >
467+ < div style ="font-size: 0.7rem; color: var(--primary); font-family: monospace; ">
468+ Playlist: {{.CurrentPos}} / {{len .Playlist}}
469+ </ div >
470+ < span class ="progress-time-total " id ="duration-{{.OutputMount}} "> 00:00</ span >
471+ </ div >
472+ </ div >
473+ {{end}}
453474
454475 < div style ="margin-top: 1.5rem; ">
455476 {{if .Queue}}
@@ -666,7 +687,8 @@ <h2>Top Connection Scanners (404s)</h2>
666687 handle : '.lucide-grip-vertical' ,
667688 onEnd : function ( evt ) {
668689 const mount = el . dataset . mount ;
669- const from = evt . oldIndex ;
690+ // Use dataset.index to get the ORIGINAL index from the backend
691+ const from = parseInt ( evt . item . dataset . index ) ;
670692 const to = evt . newIndex ;
671693 if ( from === to ) return ;
672694
@@ -681,6 +703,11 @@ <h2>Top Connection Scanners (404s)</h2>
681703 body : formData
682704 } ) . then ( r => {
683705 if ( ! r . ok ) alert ( 'Failed to reorder playlist' ) ;
706+ // We don't reload here to avoid losing scroll position,
707+ // but ideally we should refresh the data-index attributes
708+ // if we want multiple moves without a reload.
709+ // For simplicity, let's just reload for now to keep indices in sync.
710+ window . location . reload ( ) ;
684711 } ) ;
685712 }
686713 } ) ;
0 commit comments