Handle additional shuffle filters
This commit also replaces Lucene with an SQL query when looking for random songs. This should improve performance quite a bit and allows us to use more complex filters on columns that are not indexed by Lucene.
This commit is contained in:
+131
-26
@@ -19,30 +19,21 @@
|
||||
*/
|
||||
package org.libresonic.player.controller;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.apache.taglibs.standard.lang.jstl.IntegerDivideOperator;
|
||||
import org.libresonic.player.domain.*;
|
||||
import org.libresonic.player.service.*;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
import org.springframework.web.bind.ServletRequestUtils;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
import org.springframework.web.servlet.mvc.ParameterizableViewController;
|
||||
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.PlayQueue;
|
||||
import org.libresonic.player.domain.Player;
|
||||
import org.libresonic.player.domain.RandomSearchCriteria;
|
||||
import org.libresonic.player.service.PlayerService;
|
||||
import org.libresonic.player.service.SearchService;
|
||||
import org.libresonic.player.service.SecurityService;
|
||||
import org.libresonic.player.service.SettingsService;
|
||||
|
||||
/**
|
||||
* Controller for the creating a random play queue.
|
||||
*
|
||||
@@ -52,21 +43,31 @@ public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
|
||||
private PlayerService playerService;
|
||||
private List<ReloadFrame> reloadFrames;
|
||||
private SearchService searchService;
|
||||
private MediaFileService mediaFileService;
|
||||
private SecurityService securityService;
|
||||
private SettingsService settingsService;
|
||||
|
||||
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
|
||||
int size = ServletRequestUtils.getRequiredIntParameter(request, "size");
|
||||
String genre = request.getParameter("genre");
|
||||
|
||||
String genre;
|
||||
Integer fromYear = null;
|
||||
Integer toYear = null;
|
||||
Integer minAlbumRating = null;
|
||||
Integer maxAlbumRating = null;
|
||||
Date minLastPlayedDate = null;
|
||||
Date maxLastPlayedDate = null;
|
||||
boolean doesShowStarredSongs = false;
|
||||
boolean doesShowUnstarredSongs = false;
|
||||
|
||||
// Handle the genre filter
|
||||
genre = request.getParameter("genre");
|
||||
if (StringUtils.equalsIgnoreCase("any", genre)) {
|
||||
genre = null;
|
||||
}
|
||||
|
||||
Integer fromYear = null;
|
||||
Integer toYear = null;
|
||||
|
||||
// Handle the release year filter
|
||||
String year = request.getParameter("year");
|
||||
if (!StringUtils.equalsIgnoreCase("any", year)) {
|
||||
String[] tmp = StringUtils.split(year);
|
||||
@@ -74,12 +75,116 @@ public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
toYear = Integer.parseInt(tmp[1]);
|
||||
}
|
||||
|
||||
// Handle the song rating filter
|
||||
String songRating = request.getParameter("songRating");
|
||||
if (StringUtils.equalsIgnoreCase("any", songRating)) {
|
||||
doesShowStarredSongs = true;
|
||||
doesShowUnstarredSongs = true;
|
||||
} else if (StringUtils.equalsIgnoreCase("starred", songRating)) {
|
||||
doesShowStarredSongs = true;
|
||||
doesShowUnstarredSongs = false;
|
||||
} else if (StringUtils.equalsIgnoreCase("unstarred", songRating)) {
|
||||
doesShowStarredSongs = false;
|
||||
doesShowUnstarredSongs = true;
|
||||
}
|
||||
|
||||
// Handle the last played date filter
|
||||
String lastPlayedValue = request.getParameter("lastPlayedValue");
|
||||
String lastPlayedComp = request.getParameter("lastPlayedComp");
|
||||
Calendar lastPlayed = Calendar.getInstance();
|
||||
lastPlayed.setTime(new Date());
|
||||
switch (lastPlayedValue) {
|
||||
case "any":
|
||||
lastPlayed = null;
|
||||
break;
|
||||
case "1day":
|
||||
lastPlayed.add(Calendar.DAY_OF_YEAR, -1);
|
||||
break;
|
||||
case "1week":
|
||||
lastPlayed.add(Calendar.WEEK_OF_YEAR, -1);
|
||||
break;
|
||||
case "1month":
|
||||
lastPlayed.add(Calendar.MONTH, -1);
|
||||
break;
|
||||
case "3months":
|
||||
lastPlayed.add(Calendar.MONTH, -3);
|
||||
break;
|
||||
case "6months":
|
||||
lastPlayed.add(Calendar.MONTH, -6);
|
||||
break;
|
||||
case "1year":
|
||||
lastPlayed.add(Calendar.YEAR, -1);
|
||||
break;
|
||||
}
|
||||
if (lastPlayed != null) {
|
||||
switch (lastPlayedComp) {
|
||||
case "lt":
|
||||
minLastPlayedDate = null;
|
||||
maxLastPlayedDate = lastPlayed.getTime();
|
||||
break;
|
||||
case "gt":
|
||||
minLastPlayedDate = lastPlayed.getTime();
|
||||
maxLastPlayedDate = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the album rating filter
|
||||
Integer albumRatingValue = null;
|
||||
try { albumRatingValue = Integer.parseInt(request.getParameter("albumRatingValue")); }
|
||||
catch (NumberFormatException e) { }
|
||||
String albumRatingComp = request.getParameter("albumRatingComp");
|
||||
if (albumRatingValue != null) {
|
||||
switch (albumRatingComp) {
|
||||
case "lt":
|
||||
minAlbumRating = null;
|
||||
maxAlbumRating = albumRatingValue - 1;
|
||||
break;
|
||||
case "gt":
|
||||
minAlbumRating = albumRatingValue + 1;
|
||||
maxAlbumRating = null;
|
||||
break;
|
||||
case "le":
|
||||
minAlbumRating = null;
|
||||
maxAlbumRating = albumRatingValue;
|
||||
break;
|
||||
case "ge":
|
||||
minAlbumRating = albumRatingValue;
|
||||
maxAlbumRating = null;
|
||||
break;
|
||||
case "eq":
|
||||
minAlbumRating = albumRatingValue;
|
||||
maxAlbumRating = albumRatingValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the format filter
|
||||
String format = request.getParameter("format");
|
||||
if (StringUtils.equalsIgnoreCase(format, "any")) format = null;
|
||||
|
||||
// Handle the music folder filter
|
||||
List<MusicFolder> musicFolders = getMusicFolders(request);
|
||||
|
||||
// Search the database using these criteria
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(
|
||||
size,
|
||||
genre,
|
||||
fromYear,
|
||||
toYear,
|
||||
musicFolders,
|
||||
minLastPlayedDate,
|
||||
maxLastPlayedDate,
|
||||
minAlbumRating,
|
||||
maxAlbumRating,
|
||||
doesShowStarredSongs,
|
||||
doesShowUnstarredSongs,
|
||||
format
|
||||
);
|
||||
User user = securityService.getCurrentUser(request);
|
||||
Player player = playerService.getPlayer(request, response);
|
||||
PlayQueue playQueue = player.getPlayQueue();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(size, genre, fromYear, toYear, musicFolders);
|
||||
playQueue.addFiles(false, searchService.getRandomSongs(criteria));
|
||||
playQueue.addFiles(false, mediaFileService.getRandomSongs(criteria, user.getUsername()));
|
||||
|
||||
if (request.getParameter("autoRandom") != null) {
|
||||
playQueue.setRandomSearchCriteria(criteria);
|
||||
@@ -110,10 +215,6 @@ public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
this.reloadFrames = reloadFrames;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService) {
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public void setSecurityService(SecurityService securityService) {
|
||||
this.securityService = securityService;
|
||||
}
|
||||
@@ -121,4 +222,8 @@ public class RandomPlayQueueController extends ParameterizableViewController {
|
||||
public void setSettingsService(SettingsService settingsService) {
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
public void setMediaFileService(MediaFileService mediaFileService) {
|
||||
this.mediaFileService = mediaFileService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,10 +20,12 @@
|
||||
package org.libresonic.player.dao;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.libresonic.player.domain.RandomSearchCriteria;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import org.libresonic.player.domain.Genre;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -480,6 +482,100 @@ public class MediaFileDao extends AbstractDao {
|
||||
rowMapper, args);
|
||||
}
|
||||
|
||||
public List<MediaFile> getRandomSongs(RandomSearchCriteria criteria, final String username) {
|
||||
if (criteria.getMusicFolders().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
Map<String, Object> args = new HashMap<String, Object>() {{
|
||||
put("folders", MusicFolder.toPathList(criteria.getMusicFolders()));
|
||||
put("username", username);
|
||||
put("count", criteria.getCount());
|
||||
put("fromYear", criteria.getFromYear());
|
||||
put("toYear", criteria.getToYear());
|
||||
put("genre", criteria.getGenre());
|
||||
put("minLastPlayed", criteria.getMinLastPlayedDate());
|
||||
put("maxLastPlayed", criteria.getMaxLastPlayedDate());
|
||||
put("minAlbumRating", criteria.getMinAlbumRating());
|
||||
put("maxAlbumRating", criteria.getMaxAlbumRating());
|
||||
put("starred", criteria.isShowStarredSongs());
|
||||
put("unstarred", criteria.isShowUnstarredSongs());
|
||||
put("format", criteria.getFormat());
|
||||
}};
|
||||
|
||||
boolean joinAlbumRating = (criteria.getMinAlbumRating() != null || criteria.getMaxAlbumRating() != null);
|
||||
boolean joinStarred = (criteria.isShowStarredSongs() ^ criteria.isShowUnstarredSongs());
|
||||
|
||||
String query = "select top :count " + prefix(COLUMNS, "media_file") + " from media_file ";
|
||||
|
||||
if (joinStarred) {
|
||||
query += "left outer join starred_media_file on media_file.id = starred_media_file.media_file_id and starred_media_file.username = :username ";
|
||||
}
|
||||
|
||||
if (joinAlbumRating) {
|
||||
query += "left outer join media_file media_album on media_album.type = 'ALBUM' and media_album.album = media_file.album and media_album.artist = media_file.artist ";
|
||||
query += "left outer join user_rating on user_rating.path = media_album.path and user_rating.username = :username ";
|
||||
}
|
||||
|
||||
query += " where media_file.present and media_file.type = 'MUSIC'";
|
||||
|
||||
if (!criteria.getMusicFolders().isEmpty()) {
|
||||
query += " and media_file.folder in (:folders)";
|
||||
}
|
||||
|
||||
if (criteria.getGenre() != null) {
|
||||
query += " and media_file.genre = :genre";
|
||||
}
|
||||
|
||||
if (criteria.getFormat() != null) {
|
||||
query += " and media_file.format = :format";
|
||||
}
|
||||
|
||||
if (criteria.getFromYear() != null) {
|
||||
query += " and media_file.year >= :fromYear";
|
||||
}
|
||||
|
||||
if (criteria.getToYear() != null) {
|
||||
query += " and media_file.year <= :toYear";
|
||||
}
|
||||
|
||||
if (criteria.getMinLastPlayedDate() != null) {
|
||||
query += " and media_file.last_played >= :minLastPlayed";
|
||||
}
|
||||
|
||||
if (criteria.getMaxLastPlayedDate() != null) {
|
||||
if (criteria.getMinLastPlayedDate() == null) {
|
||||
query += " and (media_file.last_played is null or media_file.last_played <= :maxLastPlayed)";
|
||||
} else {
|
||||
query += " and media_file.last_played <= :maxLastPlayed";
|
||||
}
|
||||
}
|
||||
|
||||
if (criteria.getMinAlbumRating() != null) {
|
||||
query += " and user_rating.rating >= :minAlbumRating";
|
||||
}
|
||||
|
||||
if (criteria.getMaxAlbumRating() != null) {
|
||||
if (criteria.getMinAlbumRating() == null) {
|
||||
query += " and (user_rating.rating is null or user_rating.rating <= :maxAlbumRating)";
|
||||
} else {
|
||||
query += " and user_rating.rating <= :maxAlbumRating";
|
||||
}
|
||||
}
|
||||
|
||||
if (criteria.isShowStarredSongs() && !criteria.isShowUnstarredSongs()) {
|
||||
query += " and starred_media_file.id is not null";
|
||||
}
|
||||
|
||||
if (criteria.isShowUnstarredSongs() && !criteria.isShowStarredSongs()) {
|
||||
query += " and starred_media_file.id is null";
|
||||
}
|
||||
|
||||
query += " order by rand()";
|
||||
|
||||
return namedQuery(query, rowMapper, args);
|
||||
}
|
||||
|
||||
public int getAlbumCount(final List<MusicFolder> musicFolders) {
|
||||
if (musicFolders.isEmpty()) {
|
||||
return 0;
|
||||
|
||||
@@ -41,11 +41,7 @@ import net.sf.ehcache.Element;
|
||||
import org.libresonic.player.Logger;
|
||||
import org.libresonic.player.dao.AlbumDao;
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.domain.Album;
|
||||
import org.libresonic.player.domain.Genre;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.MediaFileComparator;
|
||||
import org.libresonic.player.domain.MusicFolder;
|
||||
import org.libresonic.player.domain.*;
|
||||
import org.libresonic.player.service.metadata.JaudiotaggerParser;
|
||||
import org.libresonic.player.service.metadata.MetaData;
|
||||
import org.libresonic.player.service.metadata.MetaDataParser;
|
||||
@@ -331,7 +327,7 @@ public class MediaFileService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random songs for the give parent.
|
||||
* Returns random songs for the given parent.
|
||||
*
|
||||
* @param parent The parent.
|
||||
* @param count Max number of songs to return.
|
||||
@@ -348,6 +344,18 @@ public class MediaFileService {
|
||||
return children.subList(0, Math.min(count, children.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns random songs matching search criteria.
|
||||
*
|
||||
* @param criteria Random search criteria.
|
||||
* @param count Max number of songs to return.
|
||||
* @return Random songs
|
||||
* @see SearchService.getRandomSongs
|
||||
*/
|
||||
public List<MediaFile> getRandomSongs(RandomSearchCriteria criteria, String username) {
|
||||
return mediaFileDao.getRandomSongs(criteria, username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes video files from the given list.
|
||||
*/
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<bean id="randomPlayQueueController" class="org.libresonic.player.controller.RandomPlayQueueController">
|
||||
<property name="viewName" value="reload"/>
|
||||
<property name="playerService" ref="playerService"/>
|
||||
<property name="searchService" ref="searchService"/>
|
||||
<property name="mediaFileService" ref="mediaFileService"/>
|
||||
<property name="securityService" ref="securityService"/>
|
||||
<property name="settingsService" ref="settingsService"/>
|
||||
<property name="reloadFrames">
|
||||
|
||||
Reference in New Issue
Block a user