Merge branch 'master' of https://github.com/airsonic/airsonic into transifex_update
This commit is contained in:
@@ -189,6 +189,7 @@ public class Application extends SpringBootServletInitializer implements Embedde
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainer container) {
|
||||
LOG.trace("Servlet container is {}", container.getClass().getCanonicalName());
|
||||
// Yes, there is a good reason we do this.
|
||||
// We cannot count on the tomcat classes being on the classpath which will
|
||||
// happen if the war is deployed to another app server like Jetty. So, we
|
||||
@@ -197,6 +198,7 @@ public class Application extends SpringBootServletInitializer implements Embedde
|
||||
try {
|
||||
Class<?> tomcatESCF = Class.forName("org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory");
|
||||
if(tomcatESCF.isInstance(container)) {
|
||||
LOG.info("Detected Tomcat web server");
|
||||
LOG.debug("Attempting to optimize tomcat");
|
||||
Object tomcatESCFInstance = tomcatESCF.cast(container);
|
||||
Class<?> tomcatApplicationClass = Class.forName("org.airsonic.player.TomcatApplication");
|
||||
@@ -207,10 +209,19 @@ public class Application extends SpringBootServletInitializer implements Embedde
|
||||
LOG.debug("Skipping tomcat optimization as we are not running on tomcat");
|
||||
}
|
||||
} catch (NoClassDefFoundError | ClassNotFoundException e) {
|
||||
LOG.debug("Skipping tomcat optimization as the tomcat classes are not available");
|
||||
LOG.debug("No tomcat classes found");
|
||||
} catch (Exception e) {
|
||||
LOG.warn("An error happened while trying to optimize tomcat", e);
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> jettyESCF = Class.forName("org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory");
|
||||
if(jettyESCF.isInstance(container)) {
|
||||
LOG.warn("Detected Jetty web server. Here there be dragons.");
|
||||
}
|
||||
} catch (NoClassDefFoundError | ClassNotFoundException e) {
|
||||
LOG.debug("No jetty classes found");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.airsonic.player;
|
||||
import org.apache.catalina.Container;
|
||||
import org.apache.catalina.Wrapper;
|
||||
import org.apache.catalina.webresources.StandardRoot;
|
||||
import org.apache.tomcat.util.scan.StandardJarScanFilter;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
|
||||
@@ -12,6 +13,11 @@ public class TomcatApplication {
|
||||
|
||||
tomcatFactory.addContextCustomizers((TomcatContextCustomizer) context -> {
|
||||
|
||||
StandardJarScanFilter standardJarScanFilter = new StandardJarScanFilter();
|
||||
standardJarScanFilter.setTldScan("dwr-*.jar,jstl-*.jar,spring-security-taglibs-*.jar,spring-web-*.jar,spring-webmvc-*.jar,string-*.jar,taglibs-standard-impl-*.jar,tomcat-annotations-api-*.jar,tomcat-embed-jasper-*.jar");
|
||||
standardJarScanFilter.setTldSkip("*");
|
||||
context.getJarScanner().setJarScanFilter(standardJarScanFilter);
|
||||
|
||||
boolean development = (System.getProperty("airsonic.development") != null);
|
||||
|
||||
// Increase the size and time before eviction of the Tomcat
|
||||
|
||||
+2
-2
@@ -1274,7 +1274,7 @@ public class SubsonicRESTController {
|
||||
child.setSuffix(suffix);
|
||||
child.setContentType(StringUtil.getMimeType(suffix));
|
||||
child.setIsVideo(mediaFile.isVideo());
|
||||
child.setPath(getRelativePath(mediaFile));
|
||||
child.setPath(getRelativePath(mediaFile, settingsService));
|
||||
|
||||
org.airsonic.player.domain.Bookmark bookmark = bookmarkCache.get(new BookmarkKey(username, mediaFile.getId()));
|
||||
if (bookmark != null) {
|
||||
@@ -1329,7 +1329,7 @@ public class SubsonicRESTController {
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getRelativePath(MediaFile musicFile) {
|
||||
public static String getRelativePath(MediaFile musicFile, SettingsService settingsService) {
|
||||
|
||||
String filePath = musicFile.getPath();
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ package org.airsonic.player.dao;
|
||||
import org.airsonic.player.domain.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -43,6 +44,9 @@ public class PlayerDao extends AbstractDao {
|
||||
"last_seen, cover_art_scheme, transcode_scheme, dynamic_ip, technology, client_id, mixer";
|
||||
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
|
||||
|
||||
@Autowired
|
||||
private PlayerDaoPlayQueueFactory playerDaoPlayQueueFactory;
|
||||
|
||||
private PlayerRowMapper rowMapper = new PlayerRowMapper();
|
||||
private Map<Integer, PlayQueue> playlists = Collections.synchronizedMap(new HashMap<Integer, PlayQueue>());
|
||||
|
||||
@@ -166,7 +170,7 @@ public class PlayerDao extends AbstractDao {
|
||||
private void addPlaylist(Player player) {
|
||||
PlayQueue playQueue = playlists.get(player.getId());
|
||||
if (playQueue == null) {
|
||||
playQueue = new PlayQueue();
|
||||
playQueue = playerDaoPlayQueueFactory.createPlayQueue();
|
||||
playlists.put(player.getId(), playQueue);
|
||||
}
|
||||
player.setPlayQueue(playQueue);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.airsonic.player.dao;
|
||||
|
||||
import org.airsonic.player.domain.PlayQueue;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class PlayerDaoPlayQueueFactory {
|
||||
|
||||
public PlayQueue createPlayQueue() {
|
||||
return new PlayQueue();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.airsonic.player.service;
|
||||
|
||||
import com.github.biconou.AudioPlayer.JavaPlayer;
|
||||
import com.github.biconou.AudioPlayer.api.*;
|
||||
import com.github.biconou.AudioPlayer.api.PlayList;
|
||||
import com.github.biconou.AudioPlayer.api.PlayerListener;
|
||||
import org.airsonic.player.domain.*;
|
||||
import org.airsonic.player.domain.Player;
|
||||
import org.airsonic.player.service.jukebox.JavaPlayerFactory;
|
||||
import org.airsonic.player.util.FileUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -19,23 +19,25 @@ import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author R??mi Cocula
|
||||
* @author Rémi Cocula
|
||||
*/
|
||||
@Service
|
||||
public class JukeboxJavaService {
|
||||
|
||||
private static final org.slf4j.Logger log = LoggerFactory.getLogger(JukeboxJavaService.class);
|
||||
|
||||
private static final float DEFAULT_GAIN = 0.75f;
|
||||
|
||||
@Autowired
|
||||
private AudioScrobblerService audioScrobblerService;
|
||||
@Autowired
|
||||
private StatusService statusService;
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
@Autowired
|
||||
private SecurityService securityService;
|
||||
@Autowired
|
||||
private MediaFileService mediaFileService;
|
||||
@Autowired
|
||||
private JavaPlayerFactory javaPlayerFactory;
|
||||
|
||||
|
||||
private TransferStatus status;
|
||||
@@ -45,31 +47,32 @@ public class JukeboxJavaService {
|
||||
|
||||
/**
|
||||
* Finds the corresponding active audio player for a given airsonic player.
|
||||
* If no player exists we create one.
|
||||
* The JukeboxJavaService references all active audio players in a map indexed by airsonic player id.
|
||||
*
|
||||
* @param airsonicPlayer a given airsonic player.
|
||||
* @return the corresponding active audio player of null if none exists.
|
||||
* @return the corresponding active audio player.
|
||||
*/
|
||||
private com.github.biconou.AudioPlayer.api.Player retrieveAudioPlayerForAirsonicPlayer(Player airsonicPlayer) {
|
||||
com.github.biconou.AudioPlayer.api.Player foundPlayer = activeAudioPlayers.get(airsonicPlayer.getId());
|
||||
if (foundPlayer == null) {
|
||||
synchronized (activeAudioPlayers) {
|
||||
foundPlayer = initAudioPlayer(airsonicPlayer);
|
||||
if (foundPlayer == null) {
|
||||
com.github.biconou.AudioPlayer.api.Player newPlayer = initAudioPlayer(airsonicPlayer);
|
||||
if (newPlayer == null) {
|
||||
throw new RuntimeException("Did not initialized a player");
|
||||
} else {
|
||||
activeAudioPlayers.put(airsonicPlayer.getId(), foundPlayer);
|
||||
String mixer = airsonicPlayer.getJavaJukeboxMixer();
|
||||
if (StringUtils.isBlank(mixer)) {
|
||||
mixer = DEFAULT_MIXER_ENTRY_KEY;
|
||||
}
|
||||
List<com.github.biconou.AudioPlayer.api.Player> playersForMixer = activeAudioPlayersPerMixer.get(mixer);
|
||||
if (playersForMixer == null) {
|
||||
playersForMixer = new ArrayList<>();
|
||||
activeAudioPlayersPerMixer.put(mixer, playersForMixer);
|
||||
}
|
||||
playersForMixer.add(foundPlayer);
|
||||
}
|
||||
activeAudioPlayers.put(airsonicPlayer.getId(), newPlayer);
|
||||
String mixer = airsonicPlayer.getJavaJukeboxMixer();
|
||||
if (StringUtils.isBlank(mixer)) {
|
||||
mixer = DEFAULT_MIXER_ENTRY_KEY;
|
||||
}
|
||||
List<com.github.biconou.AudioPlayer.api.Player> playersForMixer = activeAudioPlayersPerMixer.get(mixer);
|
||||
if (playersForMixer == null) {
|
||||
playersForMixer = new ArrayList<>();
|
||||
activeAudioPlayersPerMixer.put(mixer, playersForMixer);
|
||||
}
|
||||
playersForMixer.add(newPlayer);
|
||||
foundPlayer = newPlayer;
|
||||
}
|
||||
}
|
||||
return foundPlayer;
|
||||
@@ -88,11 +91,12 @@ public class JukeboxJavaService {
|
||||
|
||||
if (StringUtils.isNotBlank(airsonicPlayer.getJavaJukeboxMixer())) {
|
||||
log.info("use mixer : {}", airsonicPlayer.getJavaJukeboxMixer());
|
||||
audioPlayer = new JavaPlayer(airsonicPlayer.getJavaJukeboxMixer());
|
||||
audioPlayer = javaPlayerFactory.createJavaPlayer(airsonicPlayer.getJavaJukeboxMixer());
|
||||
} else {
|
||||
log.info("use default mixer");
|
||||
audioPlayer = new JavaPlayer();
|
||||
audioPlayer = javaPlayerFactory.createJavaPlayer();
|
||||
}
|
||||
audioPlayer.setGain(DEFAULT_GAIN);
|
||||
if (audioPlayer != null) {
|
||||
audioPlayer.registerListener(new PlayerListener() {
|
||||
@Override
|
||||
@@ -159,10 +163,7 @@ public class JukeboxJavaService {
|
||||
throw new RuntimeException("The player " + airsonicPlayer.getName() + " is not a java jukebox player");
|
||||
}
|
||||
com.github.biconou.AudioPlayer.api.Player audioPlayer = retrieveAudioPlayerForAirsonicPlayer(airsonicPlayer);
|
||||
if (audioPlayer != null) {
|
||||
return audioPlayer.getGain();
|
||||
}
|
||||
return 0.5f;
|
||||
return audioPlayer.getGain();
|
||||
}
|
||||
|
||||
public void setGain(final Player airsonicPlayer, final float gain) {
|
||||
@@ -271,20 +272,8 @@ public class JukeboxJavaService {
|
||||
}
|
||||
}
|
||||
|
||||
public void start(Player airsonicPlayer) throws Exception {
|
||||
log.debug("begin start jukebox : player = id:{};name:{}", airsonicPlayer.getId(), airsonicPlayer.getName());
|
||||
|
||||
com.github.biconou.AudioPlayer.api.Player audioPlayer = retrieveAudioPlayerForAirsonicPlayer(airsonicPlayer);
|
||||
|
||||
// Control user authorizations
|
||||
User user = securityService.getUserByName(airsonicPlayer.getUsername());
|
||||
if (!user.isJukeboxRole()) {
|
||||
log.warn("{} is not authorized for jukebox playback.", user.getUsername());
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("PlayQueue.Status is {}", airsonicPlayer.getPlayQueue().getStatus());
|
||||
audioPlayer.play();
|
||||
public void start(Player airsonicPlayer) {
|
||||
play(airsonicPlayer);
|
||||
}
|
||||
|
||||
public void stop(Player airsonicPlayer) throws Exception {
|
||||
@@ -332,7 +321,6 @@ public class JukeboxJavaService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setAudioScrobblerService(AudioScrobblerService audioScrobblerService) {
|
||||
this.audioScrobblerService = audioScrobblerService;
|
||||
}
|
||||
@@ -341,10 +329,6 @@ public class JukeboxJavaService {
|
||||
this.statusService = statusService;
|
||||
}
|
||||
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
|
||||
+4
-1
@@ -21,6 +21,7 @@ package org.airsonic.player.service;
|
||||
|
||||
import org.airsonic.player.domain.*;
|
||||
import org.airsonic.player.service.jukebox.AudioPlayer;
|
||||
import org.airsonic.player.service.jukebox.AudioPlayerFactory;
|
||||
import org.airsonic.player.util.FileUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -52,6 +53,8 @@ public class JukeboxLegacySubsonicService implements AudioPlayer.Listener {
|
||||
private SecurityService securityService;
|
||||
@Autowired
|
||||
private MediaFileService mediaFileService;
|
||||
@Autowired
|
||||
private AudioPlayerFactory audioPlayerFactory;
|
||||
|
||||
private AudioPlayer audioPlayer;
|
||||
private Player player;
|
||||
@@ -111,7 +114,7 @@ public class JukeboxLegacySubsonicService implements AudioPlayer.Listener {
|
||||
String command = settingsService.getJukeboxCommand();
|
||||
parameters.setTranscoding(new Transcoding(null, null, null, null, command, null, null, false));
|
||||
in = transcodingService.getTranscodedInputStream(parameters);
|
||||
audioPlayer = new AudioPlayer(in, this);
|
||||
audioPlayer = audioPlayerFactory.createAudioPlayer(in, this);
|
||||
audioPlayer.setGain(gain);
|
||||
audioPlayer.play();
|
||||
onSongStart(file);
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
@@ -61,6 +62,7 @@ public class TranscodingService {
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
@Autowired
|
||||
@Lazy // used to deal with circular dependencies between PlayerService and TranscodingService
|
||||
private PlayerService playerService;
|
||||
|
||||
/**
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package org.airsonic.player.service.jukebox;
|
||||
|
||||
import org.airsonic.player.service.JukeboxLegacySubsonicService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
@Component
|
||||
public class AudioPlayerFactory {
|
||||
|
||||
public AudioPlayer createAudioPlayer(InputStream in, JukeboxLegacySubsonicService jukeboxLegacySubsonicService) throws Exception {
|
||||
return new AudioPlayer(in, jukeboxLegacySubsonicService);
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package org.airsonic.player.service.jukebox;
|
||||
|
||||
import com.github.biconou.AudioPlayer.JavaPlayer;
|
||||
import com.github.biconou.AudioPlayer.api.Player;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class JavaPlayerFactory {
|
||||
|
||||
public Player createJavaPlayer() {
|
||||
return new JavaPlayer();
|
||||
}
|
||||
|
||||
public Player createJavaPlayer(String mixerName) {
|
||||
return new JavaPlayer(mixerName);
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,6 @@ logging.level.org.airsonic=INFO
|
||||
logging.level.liquibase=INFO
|
||||
logging.pattern.console=%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p){green} %clr(---){faint} %clr(%-40.40logger{32}){blue} %clr(:){faint} %m%n%wEx
|
||||
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} %5p --- %-40.40logger{32} : %m%n%wEx
|
||||
|
||||
# Helpful to debug which jars are scanned
|
||||
#logging.level.org.apache.tomcat.util.scan=TRACE
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ help.homepage.title=Homepage
|
||||
help.forum.title=Forum
|
||||
help.shop.title=Merchandise
|
||||
help.contact.title=Contact
|
||||
help.contact.text=Airsonic is a community project. You can find us in <a href="irc://chat.freenode.net/airsonic">#airsonic on Freenode</a>. Technical issues can be submitted to the <a href="https://github.com/airsonic/airsonic/issues">issue tracker on GitHub</a>.
|
||||
help.contact.text=Airsonic is a community project. You can find us in <a href="irc://chat.freenode.net/airsonic">#airsonic on Freenode</a>. Technical issues can be submitted to the <a href="https://github.com/airsonic/airsonic/issues" target="_blank">issue tracker on GitHub</a>.
|
||||
help.log=Log
|
||||
help.logfile=The complete log is saved in {0}.
|
||||
# settingsHeader.jsp
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ help.homepage.title=\u9996\u9801
|
||||
help.forum.title=\u8AD6\u58C7
|
||||
help.shop.title=\u5546\u54C1
|
||||
help.contact.title=\u806F\u7E6B
|
||||
help.contact.text=Airsonic \u662F\u793E\u7FA4\u5C08\u6848\u3002 \u60A8\u53EF\u4EE5\u5728 <a href="irc://chat.freenode.net/airsonic">Freenode \u4E0A\u7684 #airsonic</a> \u627E\u5230\u6211\u5011\u3002\u6280\u8853\u554F\u984C\u53EF\u4EE5\u63D0\u4EA4\u5230 <a href="https://github.com/airsonic/airsonic/issues">Github \u4E0A\u7684\u554F\u984C\u8FFD\u8E64\u5668</a>\u3002
|
||||
help.contact.text=Airsonic \u662F\u793E\u7FA4\u5C08\u6848\u3002 \u60A8\u53EF\u4EE5\u5728 <a href="irc://chat.freenode.net/airsonic">Freenode \u4E0A\u7684 #airsonic</a> \u627E\u5230\u6211\u5011\u3002\u6280\u8853\u554F\u984C\u53EF\u4EE5\u63D0\u4EA4\u5230 <a href="https://github.com/airsonic/airsonic/issues" target="_blank">Github \u4E0A\u7684\u554F\u984C\u8FFD\u8E64\u5668</a>\u3002
|
||||
help.log=\u8A18\u9304
|
||||
help.logfile=\u5B8C\u6574\u7684\u7D00\u9304\u5B58\u653E\u5728 {0}\u3002
|
||||
# settingsHeader.jsp
|
||||
|
||||
Reference in New Issue
Block a user