Merge Pull Request #137 into develop

Conflicts:
	libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
master
Eugene E. Kashpureff Jr 8 years ago
commit c50060884d
  1. 194
      libresonic-main/src/main/java/org/libresonic/player/controller/RandomPlayQueueController.java
  2. 112
      libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java
  3. 96
      libresonic-main/src/main/java/org/libresonic/player/domain/RandomSearchCriteria.java
  4. 20
      libresonic-main/src/main/java/org/libresonic/player/service/MediaFileService.java
  5. 23
      libresonic-main/src/main/resources/org/libresonic/player/i18n/ResourceBundle_en.properties
  6. 158
      libresonic-main/src/main/webapp/WEB-INF/jsp/more.jsp
  7. 2
      libresonic-main/src/main/webapp/WEB-INF/libresonic-servlet.xml

@ -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,33 @@ 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");
if (StringUtils.equalsIgnoreCase("any", genre)) {
genre = null;
}
String genre;
Integer fromYear = null;
Integer toYear = null;
Integer minAlbumRating = null;
Integer maxAlbumRating = null;
Integer minPlayCount = null;
Integer maxPlayCount = 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 +77,151 @@ 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 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;
// Handle the music folder filter
List<MusicFolder> 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,
genre,
fromYear,
toYear,
musicFolders,
minLastPlayedDate,
maxLastPlayedDate,
minAlbumRating,
maxAlbumRating,
minPlayCount,
maxPlayCount,
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(shouldAddToPlayList, mediaFileService.getRandomSongs(criteria, user.getUsername()));
if (request.getParameter("autoRandom") != null) {
playQueue.setRandomSearchCriteria(criteria);
@ -110,10 +252,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 +259,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,114 @@ 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("minPlayCount", criteria.getMinPlayCount());
put("maxPlayCount", criteria.getMaxPlayCount());
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.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";
}
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;

@ -19,6 +19,7 @@
*/
package org.libresonic.player.domain;
import java.util.Date;
import java.util.List;
/**
@ -34,6 +35,15 @@ public class RandomSearchCriteria {
private final Integer fromYear;
private final Integer toYear;
private final List<MusicFolder> musicFolders;
private final Date minLastPlayedDate;
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;
/**
* Creates a new instance.
@ -45,11 +55,61 @@ public class RandomSearchCriteria {
* @param musicFolders Only return songs from these music folder. May NOT be <code>null</code>.
*/
public RandomSearchCriteria(int count, String genre, Integer fromYear, Integer toYear, List<MusicFolder> musicFolders) {
this(
count, genre, fromYear, toYear, musicFolders,
null, null, 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 <code>null</code>.
* @param fromYear Only return songs released after (or in) this year. May be <code>null</code>.
* @param toYear Only return songs released before (or in) this year. May be <code>null</code>.
* @param musicFolders Only return songs from these music folder. May NOT be <code>null</code>.
* @param minLastPlayedDate Only return songs last played after this date. May be <code>null</code>.
* @param maxLastPlayedDate Only return songs last played before this date. May be <code>null</code>.
* @param minAlbumRating Only return songs rated more or equalt to this value. May be <code>null</code>.
* @param maxAlbumRating Only return songs rated less or equal to this value. May be <code>null</code>.
* @param minPlayCount Only return songs whose play count is more or equal to this value. May be <code>null</code>.
* @param maxPlayCount Only return songs whose play count is less or equal to this value. May be <code>null</code>.
* @param showStarredSongs Show starred songs. May NOT be <code>null</code>.
* @param showUnstarredSongs Show unstarred songs. May NOT be <code>null</code>.
* @param format Only return songs whose file format is equal to this value. May be <code>null</code>.
*/
public RandomSearchCriteria(
int count,
String genre,
Integer fromYear,
Integer toYear,
List<MusicFolder> musicFolders,
Date minLastPlayedDate,
Date maxLastPlayedDate,
Integer minAlbumRating,
Integer maxAlbumRating,
Integer minPlayCount,
Integer maxPlayCount,
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.minPlayCount = minPlayCount;
this.maxPlayCount = maxPlayCount;
this.showStarredSongs = showStarredSongs;
this.showUnstarredSongs = showUnstarredSongs;
this.format = format;
}
public int getCount() {
@ -71,4 +131,40 @@ public class RandomSearchCriteria {
public List<MusicFolder> 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;
}
public Integer getMinPlayCount() {
return minPlayCount;
}
public Integer getMaxPlayCount() {
return maxPlayCount;
}
}

@ -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.
*/

@ -233,12 +233,29 @@ 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.addtoplaylist = Add to current playlist
more.random.any = Any
more.random.format = Format
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.playcount = Play count
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 = <p>Check out the steadily growing list of <a href="http://libresonic.org/pages/apps.jsp" target="_blank">Libresonic apps</a>. \
These provide fun and alternative ways to enjoy your media collection - no matter where you are. \

@ -2,9 +2,11 @@
<html><head>
<%@ include file="head.jsp" %>
<%@ include file="jquery.jsp" %>
<style type="text/css">
#progressBar {width: 350px; height: 10px; border: 1px solid black; display:none;}
#progressBarContent {width: 0; height: 10px; background: url("<c:url value="/icons/default_light/progress.png"/>") repeat;}
#randomPlayQueue td { padding: 0 5px; }
</style>
<script type="text/javascript" src="<c:url value="/dwr/interface/transferService.js"/>"></script>
<script type="text/javascript" src="<c:url value="/dwr/engine.js"/>"></script>
@ -36,6 +38,58 @@
window.setTimeout("refreshProgress()", 5000);
}
}
// From Modernizr
// See: https://modernizr.com/
function isLocalStorageEnabled() {
var mod = 'modernizr';
try {
localStorage.setItem(mod, mod);
localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
}
// Load previously used shuffle parameters
function loadShuffleForm() {
if (!isLocalStorageEnabled()) return;
var form = document.getElementById("randomPlayQueue");
try {
var data = JSON.parse(localStorage.getItem("randomPlayQueue"));
} catch(e) { return; }
elements = form.getElementsByTagName("input");
for (var i = 0; i < elements.length; i++) {
if (elements[i].type == "submit") continue;
if (data[elements[i].name]) elements[i].value = data[elements[i].name];
}
elements = form.getElementsByTagName("select");
for (var i = 0; i < elements.length; i++) {
if (data[elements[i].name]) elements[i].value = data[elements[i].name];
}
}
// Save shuffle parameters
function saveShuffleForm() {
if (!isLocalStorageEnabled()) return;
var form = document.getElementById("randomPlayQueue");
var data = {}
var elements = [];
elements = form.getElementsByTagName("input");
for (var i = 0; i < elements.length; i++) data[elements[i].name] = elements[i].value;
elements = form.getElementsByTagName("select");
for (var i = 0; i < elements.length; i++) data[elements[i].name] = elements[i].value;
localStorage.setItem("randomPlayQueue", JSON.stringify(data));
}
$(function() {
${model.user.uploadRole ? "refreshProgress();" : ""}
$("#randomPlayQueue").on("submit", saveShuffleForm);
loadShuffleForm();
});
</script>
<style type="text/css">
@ -45,7 +99,7 @@
</style>
</head>
<body class="mainframe bgcolor1" onload="${model.user.uploadRole ? "refreshProgress()" : ""}">
<body class="mainframe bgcolor1">
<h1>
<img src="<spring:theme code="moreImage"/>" alt=""/>
@ -58,31 +112,23 @@
<span style="vertical-align: middle"><fmt:message key="more.random.title"/></span>
</h2>
<form method="post" action="randomPlayQueue.view?">
<form id="randomPlayQueue" method="post" action="randomPlayQueue.view?">
<table>
<tr>
<td><fmt:message key="more.random.text"/></td>
<td>
<select name="size">
<select style="width: 100%;" name="size">
<option value="10"><fmt:message key="more.random.songs"><fmt:param value="10"/></fmt:message></option>
<option value="20" selected><fmt:message key="more.random.songs"><fmt:param value="20"/></fmt:message></option>
<option value="30"><fmt:message key="more.random.songs"><fmt:param value="30"/></fmt:message></option>
<option value="40"><fmt:message key="more.random.songs"><fmt:param value="40"/></fmt:message></option>
<option value="50"><fmt:message key="more.random.songs"><fmt:param value="50"/></fmt:message></option>
</select>
</td>
<td><fmt:message key="more.random.genre"/></td>
<td>
<select name="genre">
<option value="any"><fmt:message key="more.random.anygenre"/></option>
<c:forEach items="${model.genres}" var="genre">
<option value="${genre.name}"><str:truncateNicely upper="20">${genre.name} (${genre.songCount})</str:truncateNicely></option>
</c:forEach>
<option value="100"><fmt:message key="more.random.songs"><fmt:param value="100"/></fmt:message></option>
</select>
</td>
<td><fmt:message key="more.random.year"/></td>
<td>
<select name="year">
<select style="width: 100%;" name="year">
<option value="any"><fmt:message key="more.random.anyyear"/></option>
<c:forEach begin="0" end="${model.currentYear - 2010}" var="yearOffset">
@ -101,17 +147,99 @@
<option value="0 1949">&lt; 1950</option>
</select>
</td>
</tr>
<tr>
<td><fmt:message key="more.random.genre"/></td>
<td>
<select style="width: 100%;" name="genre">
<option value="any"><fmt:message key="more.random.anygenre"/></option>
<c:forEach items="${model.genres}" var="genre">
<option value="${genre.name}"><str:truncateNicely upper="20">${genre.name} (${genre.songCount})</str:truncateNicely></option>
</c:forEach>
</select>
</td>
<td><fmt:message key="more.random.albumrating"/></td>
<td>
<select name="albumRatingComp">
<option value="lt">&lt;</option>
<option value="le">&le;</option>
<option value="eq">=</option>
<option value="ge" selected="selected">&ge;</option>
<option value="gt">&gt;</option>
</select>
<select name="albumRatingValue">
<option value="any" selected="selected"><fmt:message key="more.random.any"/></option>
<option value="0">0 <fmt:message key="more.random.stars"/></option>
<option value="1">1 <fmt:message key="more.random.star"/></option>
<option value="2">2 <fmt:message key="more.random.stars"/></option>
<option value="3">3 <fmt:message key="more.random.stars"/></option>
<option value="4">4 <fmt:message key="more.random.stars"/></option>
<option value="5">5 <fmt:message key="more.random.stars"/></option>
</select>
</td>
</tr>
<tr>
<td><fmt:message key="more.random.songrating"/></td>
<td>
<select style="width: 100%;" name="songRating">
<option value="any" selected="selected"><fmt:message key="more.random.any"/></option>
<option value="starred"><fmt:message key="more.random.starred"/></option>
<option value="unstarred"><fmt:message key="more.random.unstarred"/></option>
</select>
</td>
<td><fmt:message key="more.random.lastplayed"/></td>
<td>
<select name="lastPlayedComp">
<option value="lt" selected="selected">&lt;</option>
<option value="gt">&gt;</option>
</select>
<select name="lastPlayedValue">
<option value="any" selected="selected"><fmt:message key="more.random.any"/></option>
<option value="1day"><fmt:message key="more.random.1day"/></option>
<option value="1week"><fmt:message key="more.random.1week"/></option>
<option value="1month"><fmt:message key="more.random.1month"/></option>
<option value="3months"><fmt:message key="more.random.3months"/></option>
<option value="6months"><fmt:message key="more.random.6months"/></option>
<option value="1year"><fmt:message key="more.random.1year"/></option>
</select>
</td>
</tr>
<tr>
<td><fmt:message key="more.random.folder"/></td>
<td>
<select name="musicFolderId">
<select style="width: 100%;" name="musicFolderId">
<option value="-1"><fmt:message key="more.random.anyfolder"/></option>
<c:forEach items="${model.musicFolders}" var="musicFolder">
<option value="${musicFolder.id}">${musicFolder.name}</option>
</c:forEach>
</select>
</td>
<td><fmt:message key="more.random.playcount"/></td>
<td>
<select name="playCountComp">
<option value="lt" selected="selected">&lt;</option>
<option value="gt">&gt;</option>
</select>
<input type="number" name="playCountValue"/> times
</td>
</tr>
<tr>
<td><fmt:message key="more.random.format"/></td>
<td>
<select style="width: 100%;" name="format">
<option value="any" selected="selected"><fmt:message key="more.random.any"/></option>
<option value="flac">FLAC</option>
<option value="mp3">MP3</option>
</select>
</td>
<td><fmt:message key="more.random.addtoplaylist"/></td>
<td>
<input type="submit" value="<fmt:message key="more.random.ok"/>">
<input name="addToPlaylist" value="true" type="checkbox"/>
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="<fmt:message key="more.random.ok"/>">
</td>
</tr>
<c:if test="${not model.clientSidePlaylist}">

@ -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">

Loading…
Cancel
Save