@@ -411,6 +411,26 @@ const routes = {
411411 } ,
412412 } ,
413413 } ) ,
414+ nextSongInfo : createRoute ( {
415+ method : 'get' ,
416+ path : `/api/${ API_VERSION } /queue/next` ,
417+ summary : 'get next song info' ,
418+ description :
419+ 'Get information about the next song in the queue (relative index +1)' ,
420+ responses : {
421+ 200 : {
422+ description : 'Success' ,
423+ content : {
424+ 'application/json' : {
425+ schema : SongInfoSchema ,
426+ } ,
427+ } ,
428+ } ,
429+ 204 : {
430+ description : 'No next song in queue' ,
431+ } ,
432+ } ,
433+ } ) ,
414434 queueInfo : createRoute ( {
415435 method : 'get' ,
416436 path : `/api/${ API_VERSION } /queue` ,
@@ -748,6 +768,63 @@ export const register = (
748768 app . openapi ( routes . oldQueueInfo , queueInfo ) ;
749769 app . openapi ( routes . queueInfo , queueInfo ) ;
750770
771+ app . openapi ( routes . nextSongInfo , async ( ctx ) => {
772+ const queueResponsePromise = new Promise < QueueResponse > ( ( resolve ) => {
773+ ipcMain . once ( 'peard:get-queue-response' , ( _ , queue : QueueResponse ) => {
774+ return resolve ( queue ) ;
775+ } ) ;
776+
777+ controller . requestQueueInformation ( ) ;
778+ } ) ;
779+
780+ const queue = await queueResponsePromise ;
781+
782+ if ( ! queue ?. items || queue . items . length === 0 ) {
783+ ctx . status ( 204 ) ;
784+ return ctx . body ( null ) ;
785+ }
786+
787+ // Find the currently selected song
788+ const currentIndex = queue . items . findIndex ( ( item ) => {
789+ const renderer =
790+ item . playlistPanelVideoRenderer ||
791+ item . playlistPanelVideoWrapperRenderer ?. primaryRenderer
792+ ?. playlistPanelVideoRenderer ;
793+ return renderer ?. selected === true ;
794+ } ) ;
795+
796+ // Get the next song (currentIndex + 1)
797+ const nextIndex = currentIndex + 1 ;
798+ if ( nextIndex >= queue . items . length ) {
799+ // No next song available
800+ ctx . status ( 204 ) ;
801+ return ctx . body ( null ) ;
802+ }
803+
804+ const nextItem = queue . items [ nextIndex ] ;
805+ const nextRenderer =
806+ nextItem . playlistPanelVideoRenderer ||
807+ nextItem . playlistPanelVideoWrapperRenderer ?. primaryRenderer
808+ ?. playlistPanelVideoRenderer ;
809+
810+ if ( ! nextRenderer ) {
811+ ctx . status ( 204 ) ;
812+ return ctx . body ( null ) ;
813+ }
814+
815+ // Extract relevant information similar to SongInfo format
816+ const nextSongInfo = {
817+ title : nextRenderer . title ?. runs ?. [ 0 ] ?. text ,
818+ videoId : nextRenderer . videoId ,
819+ thumbnail : nextRenderer . thumbnail ,
820+ lengthText : nextRenderer . lengthText ,
821+ shortBylineText : nextRenderer . shortBylineText ,
822+ } ;
823+
824+ ctx . status ( 200 ) ;
825+ return ctx . json ( nextSongInfo ) ;
826+ } ) ;
827+
751828 app . openapi ( routes . addSongToQueue , ( ctx ) => {
752829 const { videoId, insertPosition } = ctx . req . valid ( 'json' ) ;
753830 controller . addSongToQueue ( videoId , insertPosition ) ;
0 commit comments