@ -19,8 +19,11 @@
* /
package org.airsonic.player.ajax ;
import chameleon.playlist.* ;
import chameleon.playlist.Playlist ;
import com.google.common.base.Function ;
import com.google.common.collect.Lists ;
import org.airsonic.player.dao.InternetRadioDao ;
import org.airsonic.player.dao.MediaFileDao ;
import org.airsonic.player.dao.PlayQueueDao ;
import org.airsonic.player.domain.* ;
@ -28,6 +31,8 @@ import org.airsonic.player.service.*;
import org.airsonic.player.service.PlaylistService ;
import org.airsonic.player.util.StringUtil ;
import org.directwebremoting.WebContextFactory ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import org.springframework.beans.factory.annotation.Autowired ;
import org.springframework.stereotype.Service ;
import org.springframework.web.servlet.support.RequestContextUtils ;
@ -35,6 +40,7 @@ import org.springframework.web.servlet.support.RequestContextUtils;
import javax.servlet.http.HttpServletRequest ;
import javax.servlet.http.HttpServletResponse ;
import java.net.URL ;
import java.util.* ;
/ * *
@ -74,8 +80,12 @@ public class PlayQueueService {
@Autowired
private PlayQueueDao playQueueDao ;
@Autowired
private InternetRadioDao internetRadioDao ;
@Autowired
private JWTSecurityService jwtSecurityService ;
private static final Logger LOG = LoggerFactory . getLogger ( PlayQueueService . class ) ;
/ * *
* Returns the play queue for the player of the current user .
*
@ -143,6 +153,7 @@ public class PlayQueueService {
String username = securityService . getCurrentUsername ( request ) ;
Player player = getCurrentPlayer ( request , response ) ;
PlayQueue playQueue = player . getPlayQueue ( ) ;
playQueue . setInternetRadio ( null ) ;
if ( playQueue . getRandomSearchCriteria ( ) ! = null ) {
playQueue . addFiles ( true , mediaFileService . getRandomSongs ( playQueue . getRandomSearchCriteria ( ) , username ) ) ;
}
@ -232,6 +243,19 @@ public class PlayQueueService {
return doPlay ( request , player , songs ) . setStartPlayerAt ( 0 ) ;
}
/ * *
* @param index Start playing at this index , or play whole radio playlist if { @code null } .
* /
public PlayQueueInfo playInternetRadio ( int id , Integer index ) throws Exception {
HttpServletRequest request = WebContextFactory . get ( ) . getHttpServletRequest ( ) ;
InternetRadio radio = internetRadioDao . getInternetRadioById ( id ) ;
if ( ! radio . isEnabled ( ) ) { throw new Exception ( "Radio is not enabled" ) ; }
Player player = resolvePlayer ( ) ;
return doPlayInternetRadio ( request , player , radio ) . setStartPlayerAt ( 0 ) ;
}
/ * *
* @param index Start playing at this index , or play whole playlist if { @code null } .
* /
@ -418,6 +442,17 @@ public class PlayQueueService {
}
player . getPlayQueue ( ) . addFiles ( false , files ) ;
player . getPlayQueue ( ) . setRandomSearchCriteria ( null ) ;
player . getPlayQueue ( ) . setInternetRadio ( null ) ;
if ( player . isJukebox ( ) ) {
jukeboxService . play ( player ) ;
}
return convert ( request , player , true ) ;
}
private PlayQueueInfo doPlayInternetRadio ( HttpServletRequest request , Player player , InternetRadio radio ) throws Exception {
player . getPlayQueue ( ) . clear ( ) ;
player . getPlayQueue ( ) . setRandomSearchCriteria ( null ) ;
player . getPlayQueue ( ) . setInternetRadio ( radio ) ;
if ( player . isJukebox ( ) ) {
jukeboxService . play ( player ) ;
}
@ -433,6 +468,7 @@ public class PlayQueueService {
Player player = getCurrentPlayer ( request , response ) ;
player . getPlayQueue ( ) . addFiles ( false , randomFiles ) ;
player . getPlayQueue ( ) . setRandomSearchCriteria ( null ) ;
player . getPlayQueue ( ) . setInternetRadio ( null ) ;
return convert ( request , player , true ) . setStartPlayerAt ( 0 ) ;
}
@ -445,6 +481,8 @@ public class PlayQueueService {
List < MediaFile > similarSongs = lastFmService . getSimilarSongs ( artist , count , musicFolders ) ;
Player player = getCurrentPlayer ( request , response ) ;
player . getPlayQueue ( ) . addFiles ( false , similarSongs ) ;
player . getPlayQueue ( ) . setRandomSearchCriteria ( null ) ;
player . getPlayQueue ( ) . setInternetRadio ( null ) ;
return convert ( request , player , true ) . setStartPlayerAt ( 0 ) ;
}
@ -478,6 +516,7 @@ public class PlayQueueService {
playQueue . addFiles ( true , files ) ;
}
playQueue . setRandomSearchCriteria ( null ) ;
playQueue . setInternetRadio ( null ) ;
return playQueue ;
}
@ -589,7 +628,7 @@ public class PlayQueueService {
HttpServletResponse response = WebContextFactory . get ( ) . getHttpServletResponse ( ) ;
Player player = getCurrentPlayer ( request , response ) ;
PlayQueue playQueue = player . getPlayQueue ( ) ;
if ( playQueue . isRadioEnabled ( ) ) {
if ( playQueue . isShuffle RadioEnabled ( ) ) {
playQueue . setRandomSearchCriteria ( null ) ;
playQueue . setRepeatEnabled ( false ) ;
} else {
@ -636,20 +675,43 @@ public class PlayQueueService {
}
private PlayQueueInfo convert ( HttpServletRequest request , Player player , boolean serverSidePlaylist , int offset ) throws Exception {
String url = NetworkService . getBaseUrl ( request ) ;
Locale locale = RequestContextUtils . getLocale ( request ) ;
PlayQueue playQueue = player . getPlayQueue ( ) ;
/ * if ( serverSidePlaylist & & player . isJukebox ( ) ) {
updateJukebox ( player , offset ) ;
} * /
boolean isCurrentPlayer = player . getIpAddress ( ) ! = null & & player . getIpAddress ( ) . equals ( request . getRemoteAddr ( ) ) ;
List < PlayQueueInfo . Entry > entries ;
if ( playQueue . isInternetRadioEnabled ( ) ) {
entries = convertInternetRadio ( request , player ) ;
} else {
entries = convertMediaFileList ( request , player ) ;
}
boolean isCurrentPlayer = player . getIpAddress ( ) ! = null & & player . getIpAddress ( ) . equals ( request . getRemoteAddr ( ) ) ;
boolean isStopEnabled = playQueue . getStatus ( ) = = PlayQueue . Status . PLAYING & & ! player . isExternalWithPlaylist ( ) ;
boolean m3uSupported = player . isExternal ( ) | | player . isExternalWithPlaylist ( ) ;
serverSidePlaylist = player . isAutoControlEnabled ( ) & & m3uSupported & & isCurrentPlayer & & serverSidePlaylist ;
Locale locale = RequestContextUtils . getLocale ( request ) ;
List < PlayQueueInfo . Entry > entries = new ArrayList < PlayQueueInfo . Entry > ( ) ;
float gain = jukeboxService . getGain ( player ) ;
return new PlayQueueInfo (
entries ,
isStopEnabled ,
playQueue . isRepeatEnabled ( ) ,
playQueue . isShuffleRadioEnabled ( ) ,
playQueue . isInternetRadioEnabled ( ) ,
serverSidePlaylist ,
gain
) ;
}
private List < PlayQueueInfo . Entry > convertMediaFileList ( HttpServletRequest request , Player player ) {
String url = NetworkService . getBaseUrl ( request ) ;
Locale locale = RequestContextUtils . getLocale ( request ) ;
PlayQueue playQueue = player . getPlayQueue ( ) ;
List < PlayQueueInfo . Entry > entries = new ArrayList < PlayQueueInfo . Entry > ( ) ;
for ( MediaFile file : playQueue . getFiles ( ) ) {
String albumUrl = url + "main.view?id=" + file . getId ( ) ;
@ -668,12 +730,114 @@ public class PlayQueueService {
formatFileSize ( file . getFileSize ( ) , locale ) , starred , albumUrl , streamUrl , remoteStreamUrl ,
coverArtUrl , remoteCoverArtUrl ) ) ;
}
boolean isStopEnabled = playQueue . getStatus ( ) = = PlayQueue . Status . PLAYING & & ! player . isExternalWithPlaylist ( ) ;
float gain = 0 . 0f ;
gain = jukeboxService . getGain ( player ) ;
return entries ;
}
private List < PlayQueueInfo . Entry > convertInternetRadio ( HttpServletRequest request , Player player ) throws Exception {
// Retrieve radio playlist and parse it
PlayQueue playQueue = player . getPlayQueue ( ) ;
InternetRadio radio = playQueue . getInternetRadio ( ) ;
URL playlistUrl = new URL ( radio . getStreamUrl ( ) ) ;
SpecificPlaylist inputPlaylist = null ;
try {
LOG . info ( "Parsing playlist at {}..." , playlistUrl . toString ( ) ) ;
inputPlaylist = SpecificPlaylistFactory . getInstance ( ) . readFrom ( playlistUrl ) ;
} catch ( Exception e ) {
LOG . error ( "Unable to parse playlist: {}" , playlistUrl . toString ( ) , e ) ;
throw e ;
}
if ( inputPlaylist = = null ) {
LOG . error ( "Unsupported playlist format: {}" , playlistUrl . toString ( ) ) ;
throw new Exception ( "Unsupported playlist format " + playlistUrl . toString ( ) ) ;
}
// Retrieve stream URLs
List < PlayQueueInfo . Entry > entries = new ArrayList < > ( ) ;
final String radioHomepageUrl = radio . getHomepageUrl ( ) ;
final String radioName = radio . getName ( ) ;
inputPlaylist . toPlaylist ( ) . acceptDown ( new PlaylistVisitor ( ) {
@Override
public void beginVisitPlaylist ( Playlist playlist ) throws Exception {
}
@Override
public void endVisitPlaylist ( Playlist playlist ) throws Exception {
}
@Override
public void beginVisitParallel ( Parallel parallel ) throws Exception {
}
@Override
public void endVisitParallel ( Parallel parallel ) throws Exception {
}
@Override
public void beginVisitSequence ( Sequence sequence ) throws Exception {
}
@Override
public void endVisitSequence ( Sequence sequence ) throws Exception {
}
@Override
public void beginVisitMedia ( Media media ) throws Exception {
// Retrieve stream URL
String streamUrl = media . getSource ( ) . getURI ( ) . toString ( ) ;
// Fake stream title using the URL
String streamTitle = streamUrl ;
String streamAlbum = radioName ;
String streamGenre = "Internet Radio" ;
// Fake entry id so that the source can be selected
Integer streamId = - ( 1 + entries . size ( ) ) ;
Integer streamTrackNumber = entries . size ( ) ;
Integer streamYear = 0 ;
LOG . info ( "Got source media at {}..." , streamUrl ) ;
entries . add ( new PlayQueueInfo . Entry (
streamId , // Entry id
streamTrackNumber , // Track number
streamTitle , // Use URL as stream title
"" ,
streamAlbum , // Album name
streamGenre ,
streamYear ,
"" ,
0 ,
"" ,
"" ,
"" ,
"" ,
false ,
radioHomepageUrl , // Album URL
streamUrl , // Stream URL
streamUrl , // Remote stream URL
null ,
null
) ) ;
}
return new PlayQueueInfo ( entries , isStopEnabled , playQueue . isRepeatEnabled ( ) , playQueue . isRadioEnabled ( ) , serverSidePlaylist , gain ) ;
@Override
public void endVisitMedia ( Media media ) throws Exception {
}
} ) ;
if ( entries . isEmpty ( ) ) {
LOG . error ( "Cannot fetch stream URLs from radio source {}" , playlistUrl . toString ( ) ) ;
}
return entries ;
}
private String formatFileSize ( Long fileSize , Locale locale ) {
@ -797,4 +961,8 @@ public class PlayQueueService {
public void setJwtSecurityService ( JWTSecurityService jwtSecurityService ) {
this . jwtSecurityService = jwtSecurityService ;
}
public void setInternetRadioDao ( InternetRadioDao internetRadioDao ) {
this . internetRadioDao = internetRadioDao ;
}
}