From 8ff762ed0febf9788a84b3915d07cff6b8d2ce89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Tue, 1 Nov 2016 12:09:30 +0100 Subject: [PATCH 1/5] Add more filters to the shuffle page --- .../player/domain/RandomSearchCriteria.java | 80 +++++++++++++++++++ .../player/i18n/ResourceBundle_en.properties | 24 +++++- .../src/main/webapp/WEB-INF/jsp/more.jsp | 73 ++++++++++++++++- 3 files changed, 172 insertions(+), 5 deletions(-) diff --git a/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java b/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java index a192350e..3e746316 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java +++ b/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java @@ -19,6 +19,7 @@ */ package org.libresonic.player.domain; +import java.util.Date; import java.util.List; /** @@ -34,6 +35,13 @@ public class RandomSearchCriteria { private final Integer fromYear; private final Integer toYear; private final List musicFolders; + private final Date minLastPlayedDate; + private final Date maxLastPlayedDate; + private final Integer minAlbumRating; + private final Integer maxAlbumRating; + private final boolean showStarredSongs; + private final boolean showUnstarredSongs; + private final String format; /** * Creates a new instance. @@ -45,11 +53,55 @@ public class RandomSearchCriteria { * @param musicFolders Only return songs from these music folder. May NOT be null. */ public RandomSearchCriteria(int count, String genre, Integer fromYear, Integer toYear, List musicFolders) { + this( + count, genre, fromYear, toYear, musicFolders, + null, null, null, null, true, true, null + ); + } + + /** + * Creates a new instance. + * + * @param count Maximum number of songs to return. + * @param genre Only return songs of the given genre. May be null. + * @param fromYear Only return songs released after (or in) this year. May be null. + * @param toYear Only return songs released before (or in) this year. May be null. + * @param musicFolders Only return songs from these music folder. May NOT be null. + * @param minLastPlayedDate Only return songs last played after this date. May be null. + * @param maxLastPlayedDate Only return songs last played before this date. May be null. + * @param minAlbumRating Only return songs rated more or equalt to this value. May be null. + * @param maxAlbumRating Only return songs rated less or equal to this value. May be null. + * @param showStarredSongs Show starred songs. May NOT be null. + * @param showUnstarredSongs Show unstarred songs. May NOT be null. + * @param format Only return songs whose file format is equal to this value. May be null. + */ + public RandomSearchCriteria( + int count, + String genre, + Integer fromYear, + Integer toYear, + List musicFolders, + Date minLastPlayedDate, + Date maxLastPlayedDate, + Integer minAlbumRating, + Integer maxAlbumRating, + boolean showStarredSongs, + boolean showUnstarredSongs, + String format + ) { + this.count = count; this.genre = genre; this.fromYear = fromYear; this.toYear = toYear; this.musicFolders = musicFolders; + this.minLastPlayedDate = minLastPlayedDate; + this.maxLastPlayedDate = maxLastPlayedDate; + this.minAlbumRating = minAlbumRating; + this.maxAlbumRating = maxAlbumRating; + this.showStarredSongs = showStarredSongs; + this.showUnstarredSongs = showUnstarredSongs; + this.format = format; } public int getCount() { @@ -71,4 +123,32 @@ public class RandomSearchCriteria { public List getMusicFolders() { return musicFolders; } + + public Date getMinLastPlayedDate() { + return minLastPlayedDate; + } + + public Date getMaxLastPlayedDate() { + return maxLastPlayedDate; + } + + public Integer getMinAlbumRating() { + return minAlbumRating; + } + + public Integer getMaxAlbumRating() { + return maxAlbumRating; + } + + public boolean isShowStarredSongs() { + return showStarredSongs; + } + + public boolean isShowUnstarredSongs() { + return showUnstarredSongs; + } + + public String getFormat() { + return format; + } } diff --git a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties index a71e11bb..66ad6030 100644 --- a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties +++ b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties @@ -233,12 +233,30 @@ more.random.text = Shuffle play more.random.songs = {0} songs more.random.auto = Play more random songs when end of play queue is reached. more.random.ok = OK -more.random.genre = from genre +more.random.add = Add +more.random.any = Any +more.random.format = Format +more.random.before = Before +more.random.after = After +more.random.genre = Genre more.random.anygenre = Any -more.random.year = and year +more.random.year = Year more.random.anyyear = Any -more.random.folder = in folder +more.random.folder = Folder more.random.anyfolder = Any +more.random.star = star +more.random.stars = stars +more.random.starred = Starred +more.random.unstarred = Unstarred +more.random.songrating = Song rating +more.random.albumrating = Album rating +more.random.lastplayed = Last played +more.random.1day = 1 day ago +more.random.1week = 1 week ago +more.random.1month = 1 month ago +more.random.3months = 3 months ago +more.random.6months = 6 months ago +more.random.1year = 1 year ago more.apps.title = Libresonic Apps more.apps.text =

Check out the steadily growing list of Libresonic apps. \ These provide fun and alternative ways to enjoy your media collection - no matter where you are. \ diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp index 1f0e016c..0648f112 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp @@ -63,8 +63,11 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - "> + "> + "> @@ -187,4 +256,4 @@ - \ No newline at end of file + From 71b17a858825572e6ab0b43ae1b24ea9ecbc6de2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Sun, 6 Nov 2016 13:10:17 +0100 Subject: [PATCH 2/5] 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. --- .../controller/RandomPlayQueueController.java | 157 +++++++++++++++--- .../libresonic/player/dao/MediaFileDao.java | 98 ++++++++++- .../player/service/MediaFileService.java | 20 ++- .../webapp/WEB-INF/libresonic-servlet.xml | 2 +- 4 files changed, 243 insertions(+), 34 deletions(-) diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java index 6c813199..42e2687b 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java +++ b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java @@ -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 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"); - if (StringUtils.equalsIgnoreCase("any", genre)) { - genre = null; - } + 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; + } + // 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 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; + } } diff --git a/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java b/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java index 78dd0b47..d6b9dd7d 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java +++ b/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java @@ -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 getRandomSongs(RandomSearchCriteria criteria, final String username) { + if (criteria.getMusicFolders().isEmpty()) { + return Collections.emptyList(); + } + + Map args = new HashMap() {{ + 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 musicFolders) { if (musicFolders.isEmpty()) { return 0; diff --git a/libresonic-main/src/main/java/org/libresonic/player/service/MediaFileService.java b/libresonic-main/src/main/java/org/libresonic/player/service/MediaFileService.java index 27e755b9..5e59832c 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/service/MediaFileService.java +++ b/libresonic-main/src/main/java/org/libresonic/player/service/MediaFileService.java @@ -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 getRandomSongs(RandomSearchCriteria criteria, String username) { + return mediaFileDao.getRandomSongs(criteria, username); + } + /** * Removes video files from the given list. */ diff --git a/libresonic-main/src/main/webapp/WEB-INF/libresonic-servlet.xml b/libresonic-main/src/main/webapp/WEB-INF/libresonic-servlet.xml index 52b0f556..edece234 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/libresonic-servlet.xml +++ b/libresonic-main/src/main/webapp/WEB-INF/libresonic-servlet.xml @@ -117,7 +117,7 @@ - + From 1311e0220a2be72d17d46e4397cbca33615c59b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Sun, 6 Nov 2016 14:26:28 +0100 Subject: [PATCH 3/5] Add to playlist feature in the 'shuffle' section --- .../player/controller/RandomPlayQueueController.java | 5 ++++- .../org/libresonic/player/i18n/ResourceBundle_en.properties | 2 +- libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java index 42e2687b..9759ead5 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java +++ b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java @@ -166,6 +166,9 @@ public class RandomPlayQueueController extends ParameterizableViewController { // Handle the music folder filter List musicFolders = getMusicFolders(request); + // Do we add to the current playlist or do we replace it? + boolean shouldAddToPlayList = ServletRequestUtils.getBooleanParameter(request, "addToPlaylist", false); + // Search the database using these criteria RandomSearchCriteria criteria = new RandomSearchCriteria( size, @@ -184,7 +187,7 @@ public class RandomPlayQueueController extends ParameterizableViewController { User user = securityService.getCurrentUser(request); Player player = playerService.getPlayer(request, response); PlayQueue playQueue = player.getPlayQueue(); - playQueue.addFiles(false, mediaFileService.getRandomSongs(criteria, user.getUsername())); + playQueue.addFiles(shouldAddToPlayList, mediaFileService.getRandomSongs(criteria, user.getUsername())); if (request.getParameter("autoRandom") != null) { playQueue.setRandomSearchCriteria(criteria); diff --git a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties index 66ad6030..cfb7dcc2 100644 --- a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties +++ b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties @@ -233,7 +233,7 @@ more.random.text = Shuffle play more.random.songs = {0} songs more.random.auto = Play more random songs when end of play queue is reached. more.random.ok = OK -more.random.add = Add +more.random.add = Add to current playlist more.random.any = Any more.random.format = Format more.random.before = Before diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp index 0648f112..0d5271ad 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp @@ -172,9 +172,9 @@ - - "> - "> + + "> + From 76f42dfc1f307d4b55fdcd7740015748f2acd400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Fri, 25 Nov 2016 21:29:39 +0100 Subject: [PATCH 4/5] Add play count filter --- .../controller/RandomPlayQueueController.java | 34 ++++++++ .../libresonic/player/dao/MediaFileDao.java | 14 ++++ .../player/domain/RandomSearchCriteria.java | 18 ++++- .../player/i18n/ResourceBundle_en.properties | 5 +- .../src/main/webapp/WEB-INF/jsp/more.jsp | 78 ++++++++++--------- 5 files changed, 109 insertions(+), 40 deletions(-) diff --git a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java index 9759ead5..e741635d 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java +++ b/libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java @@ -56,6 +56,8 @@ public class RandomPlayQueueController extends ParameterizableViewController { Integer toYear = null; Integer minAlbumRating = null; Integer maxAlbumRating = null; + Integer minPlayCount = null; + Integer maxPlayCount = null; Date minLastPlayedDate = null; Date maxLastPlayedDate = null; boolean doesShowStarredSongs = false; @@ -159,6 +161,36 @@ public class RandomPlayQueueController extends ParameterizableViewController { } } + // Handle the play count filter + Integer playCountValue = null; + try { playCountValue = Integer.parseInt(request.getParameter("playCountValue")); } + catch (NumberFormatException e) { } + String playCountComp = request.getParameter("playCountComp"); + if (playCountValue != null) { + switch (playCountComp) { + case "lt": + minPlayCount = null; + maxPlayCount = playCountValue - 1; + break; + case "gt": + minPlayCount = playCountValue + 1; + maxPlayCount = null; + break; + case "le": + minPlayCount = null; + maxPlayCount = playCountValue; + break; + case "ge": + minPlayCount = playCountValue; + maxPlayCount = null; + break; + case "eq": + minPlayCount = playCountValue; + maxPlayCount = playCountValue; + break; + } + } + // Handle the format filter String format = request.getParameter("format"); if (StringUtils.equalsIgnoreCase(format, "any")) format = null; @@ -180,6 +212,8 @@ public class RandomPlayQueueController extends ParameterizableViewController { maxLastPlayedDate, minAlbumRating, maxAlbumRating, + minPlayCount, + maxPlayCount, doesShowStarredSongs, doesShowUnstarredSongs, format diff --git a/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java b/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java index d6b9dd7d..3d964a4b 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java +++ b/libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java @@ -498,6 +498,8 @@ public class MediaFileDao extends AbstractDao { put("maxLastPlayed", criteria.getMaxLastPlayedDate()); put("minAlbumRating", criteria.getMinAlbumRating()); put("maxAlbumRating", criteria.getMaxAlbumRating()); + put("minPlayCount", criteria.getMinPlayCount()); + put("maxPlayCount", criteria.getMaxPlayCount()); put("starred", criteria.isShowStarredSongs()); put("unstarred", criteria.isShowUnstarredSongs()); put("format", criteria.getFormat()); @@ -563,6 +565,18 @@ public class MediaFileDao extends AbstractDao { } } + if (criteria.getMinPlayCount() != null) { + query += " and media_file.play_count >= :minPlayCount"; + } + + if (criteria.getMaxPlayCount() != null) { + if (criteria.getMinPlayCount() == null) { + query += " and (media_file.play_count is null or media_file.play_count <= :maxPlayCount)"; + } else { + query += " and media_file.play_count <= :maxPlayCount"; + } + } + if (criteria.isShowStarredSongs() && !criteria.isShowUnstarredSongs()) { query += " and starred_media_file.id is not null"; } diff --git a/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java b/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java index 3e746316..535cbe8d 100644 --- a/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java +++ b/libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java @@ -39,6 +39,8 @@ public class RandomSearchCriteria { private final Date maxLastPlayedDate; private final Integer minAlbumRating; private final Integer maxAlbumRating; + private final Integer minPlayCount; + private final Integer maxPlayCount; private final boolean showStarredSongs; private final boolean showUnstarredSongs; private final String format; @@ -55,7 +57,7 @@ public class RandomSearchCriteria { public RandomSearchCriteria(int count, String genre, Integer fromYear, Integer toYear, List musicFolders) { this( count, genre, fromYear, toYear, musicFolders, - null, null, null, null, true, true, null + null, null, null, null, null, null, true, true, null ); } @@ -71,6 +73,8 @@ public class RandomSearchCriteria { * @param maxLastPlayedDate Only return songs last played before this date. May be null. * @param minAlbumRating Only return songs rated more or equalt to this value. May be null. * @param maxAlbumRating Only return songs rated less or equal to this value. May be null. + * @param minPlayCount Only return songs whose play count is more or equal to this value. May be null. + * @param maxPlayCount Only return songs whose play count is less or equal to this value. May be null. * @param showStarredSongs Show starred songs. May NOT be null. * @param showUnstarredSongs Show unstarred songs. May NOT be null. * @param format Only return songs whose file format is equal to this value. May be null. @@ -85,6 +89,8 @@ public class RandomSearchCriteria { Date maxLastPlayedDate, Integer minAlbumRating, Integer maxAlbumRating, + Integer minPlayCount, + Integer maxPlayCount, boolean showStarredSongs, boolean showUnstarredSongs, String format @@ -99,6 +105,8 @@ public class RandomSearchCriteria { this.maxLastPlayedDate = maxLastPlayedDate; this.minAlbumRating = minAlbumRating; this.maxAlbumRating = maxAlbumRating; + this.minPlayCount = minPlayCount; + this.maxPlayCount = maxPlayCount; this.showStarredSongs = showStarredSongs; this.showUnstarredSongs = showUnstarredSongs; this.format = format; @@ -151,4 +159,12 @@ public class RandomSearchCriteria { public String getFormat() { return format; } + + public Integer getMinPlayCount() { + return minPlayCount; + } + + public Integer getMaxPlayCount() { + return maxPlayCount; + } } diff --git a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties index cfb7dcc2..1691ec04 100644 --- a/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties +++ b/libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties @@ -233,11 +233,9 @@ more.random.text = Shuffle play more.random.songs = {0} songs more.random.auto = Play more random songs when end of play queue is reached. more.random.ok = OK -more.random.add = Add to current playlist +more.random.addtoplaylist = Add to current playlist more.random.any = Any more.random.format = Format -more.random.before = Before -more.random.after = After more.random.genre = Genre more.random.anygenre = Any more.random.year = Year @@ -251,6 +249,7 @@ more.random.unstarred = Unstarred more.random.songrating = Song rating more.random.albumrating = Album rating more.random.lastplayed = Last played +more.random.playcount = Play count more.random.1day = 1 day ago more.random.1week = 1 week ago more.random.1month = 1 month ago diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp index 0d5271ad..ce337650 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp @@ -5,6 +5,7 @@ @@ -52,12 +53,12 @@ -

+ - - - - - - - + - - + + + + + + + + - - - + From ad94e9a9d34a6bae28f73dda09e4ca68301f72b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Thomas?= Date: Fri, 25 Nov 2016 22:13:43 +0100 Subject: [PATCH 5/5] Store previously used shuffle filters --- .../src/main/webapp/WEB-INF/jsp/more.jsp | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp index ce337650..cee7e6d1 100644 --- a/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp +++ b/libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp @@ -2,6 +2,7 @@ <%@ include file="head.jsp" %> + <%@ include file="jquery.jsp" %>
- @@ -66,22 +67,9 @@
- -
- @@ -102,16 +90,15 @@
- + + + +
+ +
+ + + + times +
-
- +
"> -