Merge Pull Request #172 into develop. #yolo

master
Eugene E. Kashpureff Jr 8 years ago
commit 752704c15d
  1. 69
      documentation/DATABASE.md
  2. 26
      libresonic-main/pom.xml
  3. 19
      libresonic-main/src/main/java/org/libresonic/player/dao/AbstractDao.java
  4. 38
      libresonic-main/src/main/java/org/libresonic/player/dao/AlbumDao.java
  5. 19
      libresonic-main/src/main/java/org/libresonic/player/dao/ArtistDao.java
  6. 14
      libresonic-main/src/main/java/org/libresonic/player/dao/AvatarDao.java
  7. 9
      libresonic-main/src/main/java/org/libresonic/player/dao/BookmarkDao.java
  8. 2
      libresonic-main/src/main/java/org/libresonic/player/dao/DaoHelper.java
  9. 31
      libresonic-main/src/main/java/org/libresonic/player/dao/DaoHelperFactory.java
  10. 37
      libresonic-main/src/main/java/org/libresonic/player/dao/GenericDaoHelper.java
  11. 133
      libresonic-main/src/main/java/org/libresonic/player/dao/HsqlDaoHelper.java
  12. 7
      libresonic-main/src/main/java/org/libresonic/player/dao/InternetRadioDao.java
  13. 66
      libresonic-main/src/main/java/org/libresonic/player/dao/MediaFileDao.java
  14. 9
      libresonic-main/src/main/java/org/libresonic/player/dao/MusicFolderDao.java
  15. 9
      libresonic-main/src/main/java/org/libresonic/player/dao/PlayQueueDao.java
  16. 15
      libresonic-main/src/main/java/org/libresonic/player/dao/PlayerDao.java
  17. 17
      libresonic-main/src/main/java/org/libresonic/player/dao/PlaylistDao.java
  18. 29
      libresonic-main/src/main/java/org/libresonic/player/dao/PodcastDao.java
  19. 13
      libresonic-main/src/main/java/org/libresonic/player/dao/ShareDao.java
  20. 9
      libresonic-main/src/main/java/org/libresonic/player/dao/TranscodingDao.java
  21. 22
      libresonic-main/src/main/java/org/libresonic/player/dao/UserDao.java
  22. 77
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/Schema.java
  23. 88
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema25.java
  24. 112
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema26.java
  25. 57
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema27.java
  26. 113
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema28.java
  27. 58
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema29.java
  28. 58
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema30.java
  29. 55
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema31.java
  30. 96
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema32.java
  31. 50
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema33.java
  32. 56
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema34.java
  33. 154
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema35.java
  34. 51
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema36.java
  35. 80
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema37.java
  36. 57
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema38.java
  37. 49
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema40.java
  38. 67
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema43.java
  39. 79
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema45.java
  40. 89
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema46.java
  41. 263
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema47.java
  42. 72
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema49.java
  43. 66
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema50.java
  44. 62
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema51.java
  45. 87
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema52.java
  46. 83
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema53.java
  47. 54
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema61.java
  48. 48
      libresonic-main/src/main/java/org/libresonic/player/dao/schema/hsql/Schema62.java
  49. 103
      libresonic-main/src/main/java/org/libresonic/player/service/ApacheCommonsConfigurationService.java
  50. 258
      libresonic-main/src/main/java/org/libresonic/player/service/SettingsService.java
  51. 22
      libresonic-main/src/main/java/org/libresonic/player/spring/AdditionalPropertySourceConfigurer.java
  52. 19
      libresonic-main/src/main/java/org/libresonic/player/spring/CommonsConfigurationPropertySource.java
  53. 7
      libresonic-main/src/main/java/org/libresonic/player/spring/DataSourceConfigType.java
  54. 40
      libresonic-main/src/main/java/org/libresonic/player/spring/DatasourceProfileActivatorPropertySource.java
  55. 77
      libresonic-main/src/main/java/org/libresonic/player/spring/DbmsVersionPrecondition.java
  56. 19
      libresonic-main/src/main/java/org/libresonic/player/spring/HsqlDatabase.java
  57. 75
      libresonic-main/src/main/java/org/libresonic/player/spring/SpringLiquibase.java
  58. 11
      libresonic-main/src/main/resources/liquibase/db-changelog.xml
  59. 31
      libresonic-main/src/main/resources/liquibase/legacy/legacy-changelog.xml
  60. 131
      libresonic-main/src/main/resources/liquibase/legacy/schema25.xml
  61. 163
      libresonic-main/src/main/resources/liquibase/legacy/schema26.xml
  62. 37
      libresonic-main/src/main/resources/liquibase/legacy/schema27.xml
  63. 232
      libresonic-main/src/main/resources/liquibase/legacy/schema28.xml
  64. 44
      libresonic-main/src/main/resources/liquibase/legacy/schema29.xml
  65. 44
      libresonic-main/src/main/resources/liquibase/legacy/schema30.xml
  66. 42
      libresonic-main/src/main/resources/liquibase/legacy/schema31.xml
  67. 121
      libresonic-main/src/main/resources/liquibase/legacy/schema32.xml
  68. 30
      libresonic-main/src/main/resources/liquibase/legacy/schema33.xml
  69. 42
      libresonic-main/src/main/resources/liquibase/legacy/schema34.xml
  70. 945
      libresonic-main/src/main/resources/liquibase/legacy/schema35.xml
  71. 30
      libresonic-main/src/main/resources/liquibase/legacy/schema36.xml
  72. 81
      libresonic-main/src/main/resources/liquibase/legacy/schema37.xml
  73. 40
      libresonic-main/src/main/resources/liquibase/legacy/schema38.xml
  74. 22
      libresonic-main/src/main/resources/liquibase/legacy/schema40.xml
  75. 130
      libresonic-main/src/main/resources/liquibase/legacy/schema43.xml
  76. 84
      libresonic-main/src/main/resources/liquibase/legacy/schema45.xml
  77. 94
      libresonic-main/src/main/resources/liquibase/legacy/schema46.xml
  78. 472
      libresonic-main/src/main/resources/liquibase/legacy/schema47.xml
  79. 65
      libresonic-main/src/main/resources/liquibase/legacy/schema49.xml
  80. 64
      libresonic-main/src/main/resources/liquibase/legacy/schema50.xml
  81. 54
      libresonic-main/src/main/resources/liquibase/legacy/schema51.xml
  82. 96
      libresonic-main/src/main/resources/liquibase/legacy/schema52.xml
  83. 105
      libresonic-main/src/main/resources/liquibase/legacy/schema53.xml
  84. 42
      libresonic-main/src/main/resources/liquibase/legacy/schema61.xml
  85. 30
      libresonic-main/src/main/resources/liquibase/legacy/schema62.xml
  86. 4
      libresonic-main/src/main/resources/log4j.properties
  87. 17
      libresonic-main/src/main/webapp/WEB-INF/applicationContext-db-embed.xml
  88. 12
      libresonic-main/src/main/webapp/WEB-INF/applicationContext-db-jndi.xml
  89. 18
      libresonic-main/src/main/webapp/WEB-INF/applicationContext-db-legacy.xml
  90. 35
      libresonic-main/src/main/webapp/WEB-INF/applicationContext-db.xml
  91. 14
      libresonic-main/src/main/webapp/WEB-INF/applicationContext-service.xml
  92. 5
      libresonic-main/src/main/webapp/WEB-INF/web.xml
  93. 43
      libresonic-main/src/test/java/org/libresonic/player/dao/DaoTestCaseBase.java
  94. 10
      libresonic-main/src/test/java/org/libresonic/player/service/SettingsServiceTestCase.java
  95. 32
      libresonic-main/src/test/java/org/libresonic/player/service/StartupTestCase.java
  96. 123
      libresonic-main/src/test/java/org/libresonic/player/util/FileUtils.java
  97. BIN
      libresonic-main/src/test/resources/db/pre-liquibase/db/libresonic.data
  98. BIN
      libresonic-main/src/test/resources/db/pre-liquibase/db/libresonic.lck
  99. 2884
      libresonic-main/src/test/resources/db/pre-liquibase/db/libresonic.log
  100. 17
      libresonic-main/src/test/resources/db/pre-liquibase/db/libresonic.properties
  101. Some files were not shown because too many files have changed in this diff Show More

@ -0,0 +1,69 @@
# Database Configuration
*Before doing anything, make sure your database is properly backed up. Ensure your server is shutdown*
Libresonic has the capability to override the database settings. If you wish to
continue using the current hsql 1.8 database driver, no action is needed. When
upgrading to a new version of Libresonic powered by liquibase you may see some
liquibase logging to double check old migrations, but on subsequent startups it
will not execute them again.
For those that wish to change their database, instructions differ based on
whether you wish for your database connection to be managed by your container (tomcat),
or whether you wish Libresonic to manage it for you. The former may offer some performance
gains in the case of many concurrent users with connection pooling while the latter is easiest.
We will refer to container managed configuration as jndi and libresonic managed configuration as embedded.
## Embedded
*Before doing anything, make sure your database is properly backed up. Ensure your server is shutdown*
In your libresonic.properties file, you will need to add the following settings (this is just an example):
```
database.config.type=embed
database.config.embed.driver=org.hsqldb.jdbcDriver
database.config.embed.url=jdbc:hsqldb:file:/tmp/libre/db/libresonic
database.config.embed.username=sa
database.config.embed.password=
```
In addition, you will need to ensure that a jdbc driver suitable for your
database is on the
[classpath](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/classpath.html)
## JNDI
*Before doing anything, make sure your database is properly backed up. Ensure your server is shutdown*
In your libresonic.properties file, you will need to add the following settings (this is just an example):
```
database.config.type=jndi
database.config.jndi.name=jdbc/libresonicDB
```
Then in your context.xml in your tomcat directory, add the jndi config:
```
<Resource name="jdbc/libresonicDB" auth="Container"
type="javax.sql.DataSource"
maxActive="20"
maxIdle="30"
maxWait="10000"
username="libresonic"
password="REDACTED"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://hostname/libresonic?sessionVariables=sql_mode=ANSI_QUOTES"/>
```
Finally, copy the jdbc driver from the database vendor website to the `lib` directory in your tomcat folder.
## Database Vendor Specific Notes
### PostgreSQL
`stringtype=unspecified` on your jdbc url string is necessary.
You will also need to add `database.usertable.quote=\"` to your properties
file. This is due to the fact that our `user` table is a keyword for postgres.

@ -125,6 +125,12 @@
<version>2.1</version> <version>2.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
@ -353,6 +359,26 @@
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-configuration2</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.2</version>
<!-- commons-configuration2 requires during runtime -->
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<!-- Cannot use 3.5.3/2 due to https://liquibase.jira.com/browse/CORE-2944 -->
<version>3.5.1</version>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>

@ -19,15 +19,23 @@
*/ */
package org.libresonic.player.dao; package org.libresonic.player.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.*; import org.springframework.jdbc.core.*;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.libresonic.player.Logger; import org.libresonic.player.Logger;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.Assert;
/** /**
* Abstract superclass for all DAO's. * Abstract superclass for all DAO's.
@ -107,6 +115,16 @@ public class AbstractDao {
return result; return result;
} }
protected <T> List<T> namedQueryWithLimit(String sql, RowMapper<T> rowMapper, Map<String, Object> args, int limit) {
long t = System.nanoTime();
JdbcTemplate jdbcTemplate = new JdbcTemplate(daoHelper.getDataSource());
jdbcTemplate.setMaxRows(limit);
NamedParameterJdbcTemplate namedTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
List<T> result = namedTemplate.query(sql, args, rowMapper);
log(sql, t);
return result;
}
protected List<String> queryForStrings(String sql, Object... args) { protected List<String> queryForStrings(String sql, Object... args) {
long t = System.nanoTime(); long t = System.nanoTime();
List<String> result = getJdbcTemplate().queryForList(sql, args, String.class); List<String> result = getJdbcTemplate().queryForList(sql, args, String.class);
@ -173,4 +191,5 @@ public class AbstractDao {
public void setDaoHelper(DaoHelper daoHelper) { public void setDaoHelper(DaoHelper daoHelper) {
this.daoHelper = daoHelper; this.daoHelper = daoHelper;
} }
} }

@ -41,10 +41,11 @@ import org.libresonic.player.util.FileUtil;
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class AlbumDao extends AbstractDao { public class AlbumDao extends AbstractDao {
private static final String INSERT_COLUMNS = "path, name, artist, song_count, duration_seconds, cover_art_path, " +
private static final String COLUMNS = "id, path, name, artist, song_count, duration_seconds, cover_art_path, " +
"year, genre, play_count, last_played, comment, created, last_scanned, present, folder_id"; "year, genre, play_count, last_played, comment, created, last_scanned, present, folder_id";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final RowMapper rowMapper = new AlbumMapper(); private final RowMapper rowMapper = new AlbumMapper();
/** /**
@ -55,7 +56,7 @@ public class AlbumDao extends AbstractDao {
* @return The album or null. * @return The album or null.
*/ */
public Album getAlbum(String artistName, String albumName) { public Album getAlbum(String artistName, String albumName) {
return queryOne("select " + COLUMNS + " from album where artist=? and name=?", rowMapper, artistName, albumName); return queryOne("select " + QUERY_COLUMNS + " from album where artist=? and name=?", rowMapper, artistName, albumName);
} }
/** /**
@ -67,7 +68,7 @@ public class AlbumDao extends AbstractDao {
public Album getAlbumForFile(MediaFile file) { public Album getAlbumForFile(MediaFile file) {
// First, get all albums with the correct album name (irrespective of artist). // First, get all albums with the correct album name (irrespective of artist).
List<Album> candidates = query("select " + COLUMNS + " from album where name=?", rowMapper, file.getAlbumName()); List<Album> candidates = query("select " + QUERY_COLUMNS + " from album where name=?", rowMapper, file.getAlbumName());
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
return null; return null;
} }
@ -91,7 +92,7 @@ public class AlbumDao extends AbstractDao {
} }
public Album getAlbum(int id) { public Album getAlbum(int id) {
return queryOne("select " + COLUMNS + " from album where id=?", rowMapper, id); return queryOne("select " + QUERY_COLUMNS + " from album where id=?", rowMapper, id);
} }
public List<Album> getAlbumsForArtist(final String artist, final List<MusicFolder> musicFolders) { public List<Album> getAlbumsForArtist(final String artist, final List<MusicFolder> musicFolders) {
@ -102,7 +103,8 @@ public class AlbumDao extends AbstractDao {
put("artist", artist); put("artist", artist);
put("folders", MusicFolder.toIdList(musicFolders)); put("folders", MusicFolder.toIdList(musicFolders));
}}; }};
return namedQuery("select " + COLUMNS + " from album where artist = :artist and present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS
+ " from album where artist = :artist and present and folder_id in (:folders) " +
"order by name", "order by name",
rowMapper, args); rowMapper, args);
} }
@ -135,7 +137,7 @@ public class AlbumDao extends AbstractDao {
if (n == 0) { if (n == 0) {
update("insert into album (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")", null, album.getPath(), update("insert into album (" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")", album.getPath(),
album.getName(), album.getArtist(), album.getSongCount(), album.getDurationSeconds(), album.getName(), album.getArtist(), album.getSongCount(), album.getDurationSeconds(),
album.getCoverArtPath(), album.getYear(), album.getGenre(), album.getPlayCount(), album.getLastPlayed(), album.getCoverArtPath(), album.getYear(), album.getGenre(), album.getPlayCount(), album.getLastPlayed(),
album.getComment(), album.getCreated(), album.getLastScanned(), album.isPresent(), album.getFolderId()); album.getComment(), album.getCreated(), album.getLastScanned(), album.isPresent(), album.getFolderId());
@ -164,7 +166,7 @@ public class AlbumDao extends AbstractDao {
put("offset", offset); put("offset", offset);
}}; }};
String orderBy = byArtist ? "artist, name" : "name"; String orderBy = byArtist ? "artist, name" : "name";
return namedQuery("select " + COLUMNS + " from album where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from album where present and folder_id in (:folders) " +
"order by " + orderBy + " limit :count offset :offset", rowMapper, args); "order by " + orderBy + " limit :count offset :offset", rowMapper, args);
} }
@ -185,7 +187,8 @@ public class AlbumDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from album where play_count > 0 and present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS
+ " from album where play_count > 0 and present and folder_id in (:folders) " +
"order by play_count desc limit :count offset :offset", rowMapper, args); "order by play_count desc limit :count offset :offset", rowMapper, args);
} }
@ -206,7 +209,8 @@ public class AlbumDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from album where last_played is not null and present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS
+ " from album where last_played is not null and present and folder_id in (:folders) " +
"order by last_played desc limit :count offset :offset", rowMapper, args); "order by last_played desc limit :count offset :offset", rowMapper, args);
} }
@ -227,7 +231,7 @@ public class AlbumDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from album where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from album where present and folder_id in (:folders) " +
"order by created desc limit :count offset :offset", rowMapper, args); "order by created desc limit :count offset :offset", rowMapper, args);
} }
@ -250,7 +254,7 @@ public class AlbumDao extends AbstractDao {
put("offset", offset); put("offset", offset);
put("username", username); put("username", username);
}}; }};
return namedQuery("select " + prefix(COLUMNS, "album") + " from starred_album, album where album.id = starred_album.album_id and " + return namedQuery("select " + prefix(QUERY_COLUMNS, "album") + " from starred_album, album where album.id = starred_album.album_id and " +
"album.present and album.folder_id in (:folders) and starred_album.username = :username " + "album.present and album.folder_id in (:folders) and starred_album.username = :username " +
"order by starred_album.created desc limit :count offset :offset", "order by starred_album.created desc limit :count offset :offset",
rowMapper, args); rowMapper, args);
@ -275,7 +279,7 @@ public class AlbumDao extends AbstractDao {
put("offset", offset); put("offset", offset);
put("genre", genre); put("genre", genre);
}}; }};
return namedQuery("select " + COLUMNS + " from album where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from album where present and folder_id in (:folders) " +
"and genre = :genre limit :count offset :offset", rowMapper, args); "and genre = :genre limit :count offset :offset", rowMapper, args);
} }
@ -302,18 +306,18 @@ public class AlbumDao extends AbstractDao {
put("toYear", toYear); put("toYear", toYear);
}}; }};
if (fromYear <= toYear) { if (fromYear <= toYear) {
return namedQuery("select " + COLUMNS + " from album where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from album where present and folder_id in (:folders) " +
"and year between :fromYear and :toYear order by year limit :count offset :offset", "and year between :fromYear and :toYear order by year limit :count offset :offset",
rowMapper, args); rowMapper, args);
} else { } else {
return namedQuery("select " + COLUMNS + " from album where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from album where present and folder_id in (:folders) " +
"and year between :toYear and :fromYear order by year desc limit :count offset :offset", "and year between :toYear and :fromYear order by year desc limit :count offset :offset",
rowMapper, args); rowMapper, args);
} }
} }
public void markNonPresent(Date lastScanned) { public void markNonPresent(Date lastScanned) {
int minId = queryForInt("select top 1 id from album where last_scanned != ? and present", 0, lastScanned); int minId = queryForInt("select min(id) from album where last_scanned != ? and present", 0, lastScanned);
int maxId = queryForInt("select max(id) from album where last_scanned != ? and present", 0, lastScanned); int maxId = queryForInt("select max(id) from album where last_scanned != ? and present", 0, lastScanned);
final int batchSize = 1000; final int batchSize = 1000;
@ -323,7 +327,7 @@ public class AlbumDao extends AbstractDao {
} }
public void expunge() { public void expunge() {
int minId = queryForInt("select top 1 id from album where not present", 0); int minId = queryForInt("select min(id) from album where not present", 0);
int maxId = queryForInt("select max(id) from album where not present", 0); int maxId = queryForInt("select max(id) from album where not present", 0);
final int batchSize = 1000; final int batchSize = 1000;

@ -41,7 +41,8 @@ import java.util.Map;
public class ArtistDao extends AbstractDao { public class ArtistDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(ArtistDao.class); private static final Logger LOG = Logger.getLogger(ArtistDao.class);
private static final String COLUMNS = "id, name, cover_art_path, album_count, last_scanned, present, folder_id"; private static final String INSERT_COLUMNS = "name, cover_art_path, album_count, last_scanned, present, folder_id";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final RowMapper rowMapper = new ArtistMapper(); private final RowMapper rowMapper = new ArtistMapper();
@ -52,7 +53,7 @@ public class ArtistDao extends AbstractDao {
* @return The artist or null. * @return The artist or null.
*/ */
public Artist getArtist(String artistName) { public Artist getArtist(String artistName) {
return queryOne("select " + COLUMNS + " from artist where name=?", rowMapper, artistName); return queryOne("select " + QUERY_COLUMNS + " from artist where name=?", rowMapper, artistName);
} }
/** /**
@ -71,7 +72,7 @@ public class ArtistDao extends AbstractDao {
put("folders", MusicFolder.toIdList(musicFolders)); put("folders", MusicFolder.toIdList(musicFolders));
}}; }};
return namedQueryOne("select " + COLUMNS + " from artist where name = :name and folder_id in (:folders)", return namedQueryOne("select " + QUERY_COLUMNS + " from artist where name = :name and folder_id in (:folders)",
rowMapper, args); rowMapper, args);
} }
@ -82,7 +83,7 @@ public class ArtistDao extends AbstractDao {
* @return The artist or null. * @return The artist or null.
*/ */
public Artist getArtist(int id) { public Artist getArtist(int id) {
return queryOne("select " + COLUMNS + " from artist where id=?", rowMapper, id); return queryOne("select " + QUERY_COLUMNS + " from artist where id=?", rowMapper, id);
} }
/** /**
@ -102,7 +103,7 @@ public class ArtistDao extends AbstractDao {
int n = update(sql, artist.getCoverArtPath(), artist.getAlbumCount(), artist.getLastScanned(), artist.isPresent(), artist.getFolderId(), artist.getName()); int n = update(sql, artist.getCoverArtPath(), artist.getAlbumCount(), artist.getLastScanned(), artist.isPresent(), artist.getFolderId(), artist.getName());
if (n == 0) { if (n == 0) {
update("insert into artist (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")", null, update("insert into artist (" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")",
artist.getName(), artist.getCoverArtPath(), artist.getAlbumCount(), artist.getLastScanned(), artist.isPresent(), artist.getFolderId()); artist.getName(), artist.getCoverArtPath(), artist.getAlbumCount(), artist.getLastScanned(), artist.isPresent(), artist.getFolderId());
} }
@ -128,7 +129,7 @@ public class ArtistDao extends AbstractDao {
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from artist where present and folder_id in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from artist where present and folder_id in (:folders) " +
"order by name limit :count offset :offset", rowMapper, args); "order by name limit :count offset :offset", rowMapper, args);
} }
@ -153,7 +154,7 @@ public class ArtistDao extends AbstractDao {
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + prefix(COLUMNS, "artist") + " from starred_artist, artist " + return namedQuery("select " + prefix(QUERY_COLUMNS, "artist") + " from starred_artist, artist " +
"where artist.id = starred_artist.artist_id and " + "where artist.id = starred_artist.artist_id and " +
"artist.present and starred_artist.username = :username and " + "artist.present and starred_artist.username = :username and " +
"artist.folder_id in (:folders) " + "artist.folder_id in (:folders) " +
@ -166,7 +167,7 @@ public class ArtistDao extends AbstractDao {
} }
public void markNonPresent(Date lastScanned) { public void markNonPresent(Date lastScanned) {
int minId = queryForInt("select top 1 id from artist where last_scanned != ? and present", 0, lastScanned); int minId = queryForInt("select min(id) from artist where last_scanned != ? and present", 0, lastScanned);
int maxId = queryForInt("select max(id) from artist where last_scanned != ? and present", 0, lastScanned); int maxId = queryForInt("select max(id) from artist where last_scanned != ? and present", 0, lastScanned);
final int batchSize = 1000; final int batchSize = 1000;
@ -176,7 +177,7 @@ public class ArtistDao extends AbstractDao {
} }
public void expunge() { public void expunge() {
int minId = queryForInt("select top 1 id from artist where not present", 0); int minId = queryForInt("select min(id) from artist where not present", 0);
int maxId = queryForInt("select max(id) from artist where not present", 0); int maxId = queryForInt("select max(id) from artist where not present", 0);
final int batchSize = 1000; final int batchSize = 1000;

@ -33,7 +33,8 @@ import java.util.List;
*/ */
public class AvatarDao extends AbstractDao { public class AvatarDao extends AbstractDao {
private static final String COLUMNS = "id, name, created_date, mime_type, width, height, data"; private static final String INSERT_COLUMNS = "name, created_date, mime_type, width, height, data";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final AvatarRowMapper rowMapper = new AvatarRowMapper(); private final AvatarRowMapper rowMapper = new AvatarRowMapper();
/** /**
@ -42,7 +43,7 @@ public class AvatarDao extends AbstractDao {
* @return All system avatars. * @return All system avatars.
*/ */
public List<Avatar> getAllSystemAvatars() { public List<Avatar> getAllSystemAvatars() {
String sql = "select " + COLUMNS + " from system_avatar"; String sql = "select " + QUERY_COLUMNS + " from system_avatar";
return query(sql, rowMapper); return query(sql, rowMapper);
} }
@ -53,7 +54,7 @@ public class AvatarDao extends AbstractDao {
* @return The avatar or <code>null</code> if not found. * @return The avatar or <code>null</code> if not found.
*/ */
public Avatar getSystemAvatar(int id) { public Avatar getSystemAvatar(int id) {
String sql = "select " + COLUMNS + " from system_avatar where id=" + id; String sql = "select " + QUERY_COLUMNS + " from system_avatar where id=" + id;
return queryOne(sql, rowMapper); return queryOne(sql, rowMapper);
} }
@ -64,7 +65,7 @@ public class AvatarDao extends AbstractDao {
* @return The avatar or <code>null</code> if not found. * @return The avatar or <code>null</code> if not found.
*/ */
public Avatar getCustomAvatar(String username) { public Avatar getCustomAvatar(String username) {
String sql = "select " + COLUMNS + " from custom_avatar where username=?"; String sql = "select " + QUERY_COLUMNS + " from custom_avatar where username=?";
return queryOne(sql, rowMapper, username); return queryOne(sql, rowMapper, username);
} }
@ -79,8 +80,9 @@ public class AvatarDao extends AbstractDao {
update(sql, username); update(sql, username);
if (avatar != null) { if (avatar != null) {
update("insert into custom_avatar(" + COLUMNS + ", username) values(" + questionMarks(COLUMNS) + ", ?)", update("insert into custom_avatar(" + INSERT_COLUMNS
null, avatar.getName(), avatar.getCreatedDate(), avatar.getMimeType(), + ", username) values(" + questionMarks(INSERT_COLUMNS) + ", ?)",
avatar.getName(), avatar.getCreatedDate(), avatar.getMimeType(),
avatar.getWidth(), avatar.getHeight(), avatar.getData(), username); avatar.getWidth(), avatar.getHeight(), avatar.getData(), username);
} }
} }

@ -34,7 +34,8 @@ import org.libresonic.player.domain.Bookmark;
*/ */
public class BookmarkDao extends AbstractDao { public class BookmarkDao extends AbstractDao {
private static final String COLUMNS = "id, media_file_id, position_millis, username, comment, created, changed"; private static final String INSERT_COLUMNS = "media_file_id, position_millis, username, comment, created, changed";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private BookmarkRowMapper bookmarkRowMapper = new BookmarkRowMapper(); private BookmarkRowMapper bookmarkRowMapper = new BookmarkRowMapper();
@ -44,7 +45,7 @@ public class BookmarkDao extends AbstractDao {
* @return Possibly empty list of all bookmarks. * @return Possibly empty list of all bookmarks.
*/ */
public List<Bookmark> getBookmarks() { public List<Bookmark> getBookmarks() {
String sql = "select " + COLUMNS + " from bookmark"; String sql = "select " + QUERY_COLUMNS + " from bookmark";
return query(sql, bookmarkRowMapper); return query(sql, bookmarkRowMapper);
} }
@ -54,7 +55,7 @@ public class BookmarkDao extends AbstractDao {
* @return Possibly empty list of all bookmarks for the user. * @return Possibly empty list of all bookmarks for the user.
*/ */
public List<Bookmark> getBookmarks(String username) { public List<Bookmark> getBookmarks(String username) {
String sql = "select " + COLUMNS + " from bookmark where username=?"; String sql = "select " + QUERY_COLUMNS + " from bookmark where username=?";
return query(sql, bookmarkRowMapper, username); return query(sql, bookmarkRowMapper, username);
} }
@ -66,7 +67,7 @@ public class BookmarkDao extends AbstractDao {
bookmark.getPositionMillis(), bookmark.getComment(), bookmark.getChanged(), bookmark.getMediaFileId(), bookmark.getUsername()); bookmark.getPositionMillis(), bookmark.getComment(), bookmark.getChanged(), bookmark.getMediaFileId(), bookmark.getUsername());
if (n == 0) { if (n == 0) {
update("insert into bookmark (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")", null, update("insert into bookmark (" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")",
bookmark.getMediaFileId(), bookmark.getPositionMillis(), bookmark.getUsername(), bookmark.getComment(), bookmark.getMediaFileId(), bookmark.getPositionMillis(), bookmark.getUsername(), bookmark.getComment(),
bookmark.getCreated(), bookmark.getChanged()); bookmark.getCreated(), bookmark.getChanged());
int id = queryForInt("select id from bookmark where media_file_id=? and username=?", 0, bookmark.getMediaFileId(), bookmark.getUsername()); int id = queryForInt("select id from bookmark where media_file_id=? and username=?", 0, bookmark.getMediaFileId(), bookmark.getUsername());

@ -19,6 +19,7 @@
*/ */
package org.libresonic.player.dao; package org.libresonic.player.dao;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@ -43,4 +44,5 @@ public interface DaoHelper {
*/ */
NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(); NamedParameterJdbcTemplate getNamedParameterJdbcTemplate();
DataSource getDataSource();
} }

@ -1,31 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 (C) Sindre Mehus
*/
package org.libresonic.player.dao;
/**
* @author Sindre Mehus
* @version $Id$
*/
public class DaoHelperFactory {
public static DaoHelper create() {
return new HsqlDaoHelper();
}
}

@ -0,0 +1,37 @@
package org.libresonic.player.dao;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
public class GenericDaoHelper implements DaoHelper {
final JdbcTemplate jdbcTemplate;
final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
final DataSource dataSource;
public GenericDaoHelper(
DataSource dataSource
) {
this.dataSource = dataSource;
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}
@Override
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
@Override
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return namedParameterJdbcTemplate;
}
@Override
public DataSource getDataSource() {
return dataSource;
}
}

@ -1,133 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 (C) Sindre Mehus
*/
package org.libresonic.player.dao;
import java.io.File;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.libresonic.player.dao.schema.hsql.Schema25;
import org.libresonic.player.dao.schema.hsql.Schema26;
import org.libresonic.player.dao.schema.hsql.Schema27;
import org.libresonic.player.dao.schema.hsql.Schema28;
import org.libresonic.player.dao.schema.hsql.Schema29;
import org.libresonic.player.dao.schema.hsql.Schema30;
import org.libresonic.player.dao.schema.hsql.Schema31;
import org.libresonic.player.dao.schema.hsql.Schema32;
import org.libresonic.player.dao.schema.hsql.Schema33;
import org.libresonic.player.dao.schema.hsql.Schema34;
import org.libresonic.player.dao.schema.hsql.Schema35;
import org.libresonic.player.dao.schema.hsql.Schema36;
import org.libresonic.player.dao.schema.hsql.Schema37;
import org.libresonic.player.dao.schema.hsql.Schema38;
import org.libresonic.player.dao.schema.hsql.Schema40;
import org.libresonic.player.dao.schema.hsql.Schema43;
import org.libresonic.player.dao.schema.hsql.Schema45;
import org.libresonic.player.dao.schema.hsql.Schema46;
import org.libresonic.player.dao.schema.hsql.Schema47;
import org.libresonic.player.dao.schema.hsql.Schema49;
import org.libresonic.player.dao.schema.hsql.Schema50;
import org.libresonic.player.dao.schema.hsql.Schema51;
import org.libresonic.player.dao.schema.hsql.Schema52;
import org.libresonic.player.dao.schema.hsql.Schema53;
import org.libresonic.player.dao.schema.hsql.Schema61;
import org.libresonic.player.dao.schema.hsql.Schema62;
import org.libresonic.player.service.SettingsService;
/**
* DAO helper class which creates the data source, and updates the database schema.
*
* @author Sindre Mehus
*/
public class HsqlDaoHelper implements DaoHelper {
private static final Logger LOG = Logger.getLogger(HsqlDaoHelper.class);
private Schema[] schemas = {new Schema25(), new Schema26(), new Schema27(), new Schema28(), new Schema29(),
new Schema30(), new Schema31(), new Schema32(), new Schema33(), new Schema34(),
new Schema35(), new Schema36(), new Schema37(), new Schema38(), new Schema40(),
new Schema43(), new Schema45(), new Schema46(), new Schema47(), new Schema49(),
new Schema50(), new Schema51(), new Schema52(), new Schema53(), new Schema61(),
new Schema62()};
private DataSource dataSource;
private static boolean shutdownHookAdded;
public HsqlDaoHelper() {
dataSource = createDataSource();
checkDatabase();
addShutdownHook();
}
private void addShutdownHook() {
if (shutdownHookAdded) {
return;
}
shutdownHookAdded = true;
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("Shutting down database...");
getJdbcTemplate().execute("shutdown");
System.err.println("Shutting down database - Done!");
}
});
}
/**
* Returns a JDBC template for performing database operations.
*
* @return A JDBC template.
*/
public JdbcTemplate getJdbcTemplate() {
return new JdbcTemplate(dataSource);
}
public NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource);
}
private DataSource createDataSource() {
File libresonicHome = SettingsService.getLibresonicHome();
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUrl("jdbc:hsqldb:file:" + libresonicHome.getPath() + "/db/libresonic");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}
private void checkDatabase() {
LOG.info("Checking database schema.");
try {
for (Schema schema : schemas) {
schema.execute(getJdbcTemplate());
}
LOG.info("Done checking database schema.");
} catch (Exception x) {
LOG.error("Failed to initialize database.", x);
}
}
}

@ -36,7 +36,8 @@ import org.libresonic.player.domain.InternetRadio;
public class InternetRadioDao extends AbstractDao { public class InternetRadioDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(InternetRadioDao.class); private static final Logger LOG = Logger.getLogger(InternetRadioDao.class);
private static final String COLUMNS = "id, name, stream_url, homepage_url, enabled, changed"; private static final String INSERT_COLUMNS = "name, stream_url, homepage_url, enabled, changed";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final InternetRadioRowMapper rowMapper = new InternetRadioRowMapper(); private final InternetRadioRowMapper rowMapper = new InternetRadioRowMapper();
/** /**
@ -45,7 +46,7 @@ public class InternetRadioDao extends AbstractDao {
* @return Possibly empty list of all internet radio stations. * @return Possibly empty list of all internet radio stations.
*/ */
public List<InternetRadio> getAllInternetRadios() { public List<InternetRadio> getAllInternetRadios() {
String sql = "select " + COLUMNS + " from internet_radio"; String sql = "select " + QUERY_COLUMNS + " from internet_radio";
return query(sql, rowMapper); return query(sql, rowMapper);
} }
@ -55,7 +56,7 @@ public class InternetRadioDao extends AbstractDao {
* @param radio The internet radio station to create. * @param radio The internet radio station to create.
*/ */
public void createInternetRadio(InternetRadio radio) { public void createInternetRadio(InternetRadio radio) {
String sql = "insert into internet_radio (" + COLUMNS + ") values (null, ?, ?, ?, ?, ?)"; String sql = "insert into internet_radio (" + INSERT_COLUMNS + ") values (?, ?, ?, ?, ?)";
update(sql, radio.getName(), radio.getStreamUrl(), radio.getHomepageUrl(), radio.isEnabled(), radio.getChanged()); update(sql, radio.getName(), radio.getStreamUrl(), radio.getHomepageUrl(), radio.isEnabled(), radio.getChanged());
LOG.info("Created internet radio station " + radio.getName()); LOG.info("Created internet radio station " + radio.getName());
} }

@ -40,15 +40,16 @@ import static org.libresonic.player.domain.MediaFile.MediaType.*;
* @author Sindre Mehus * @author Sindre Mehus
*/ */
public class MediaFileDao extends AbstractDao { public class MediaFileDao extends AbstractDao {
private static final String INSERT_COLUMNS = "path, folder, type, format, title, album, artist, album_artist, disc_number, " +
private static final String COLUMNS = "id, path, folder, type, format, title, album, artist, album_artist, disc_number, " +
"track_number, year, genre, bit_rate, variable_bit_rate, duration_seconds, file_size, width, height, cover_art_path, " + "track_number, year, genre, bit_rate, variable_bit_rate, duration_seconds, file_size, width, height, cover_art_path, " +
"parent_path, play_count, last_played, comment, created, changed, last_scanned, children_last_updated, present, version"; "parent_path, play_count, last_played, comment, created, changed, last_scanned, children_last_updated, present, version";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private static final String GENRE_COLUMNS = "name, song_count, album_count"; private static final String GENRE_COLUMNS = "name, song_count, album_count";
public static final int VERSION = 4; public static final int VERSION = 4;
private final RowMapper rowMapper = new MediaFileMapper(); private final RowMapper<MediaFile> rowMapper = new MediaFileMapper();
private final RowMapper musicFileInfoRowMapper = new MusicFileInfoMapper(); private final RowMapper musicFileInfoRowMapper = new MusicFileInfoMapper();
private final RowMapper genreRowMapper = new GenreMapper(); private final RowMapper genreRowMapper = new GenreMapper();
@ -59,7 +60,7 @@ public class MediaFileDao extends AbstractDao {
* @return The media file or null. * @return The media file or null.
*/ */
public MediaFile getMediaFile(String path) { public MediaFile getMediaFile(String path) {
return queryOne("select " + COLUMNS + " from media_file where path=?", rowMapper, path); return queryOne("select " + QUERY_COLUMNS + " from media_file where path=?", rowMapper, path);
} }
/** /**
@ -69,7 +70,7 @@ public class MediaFileDao extends AbstractDao {
* @return The media file or null. * @return The media file or null.
*/ */
public MediaFile getMediaFile(int id) { public MediaFile getMediaFile(int id) {
return queryOne("select " + COLUMNS + " from media_file where id=?", rowMapper, id); return queryOne("select " + QUERY_COLUMNS + " from media_file where id=?", rowMapper, id);
} }
/** /**
@ -79,18 +80,18 @@ public class MediaFileDao extends AbstractDao {
* @return The list of children. * @return The list of children.
*/ */
public List<MediaFile> getChildrenOf(String path) { public List<MediaFile> getChildrenOf(String path) {
return query("select " + COLUMNS + " from media_file where parent_path=? and present", rowMapper, path); return query("select " + QUERY_COLUMNS + " from media_file where parent_path=? and present", rowMapper, path);
} }
public List<MediaFile> getFilesInPlaylist(int playlistId) { public List<MediaFile> getFilesInPlaylist(int playlistId) {
return query("select " + prefix(COLUMNS, "media_file") + " from playlist_file, media_file where " + return query("select " + prefix(QUERY_COLUMNS, "media_file") + " from playlist_file, media_file where " +
"media_file.id = playlist_file.media_file_id and " + "media_file.id = playlist_file.media_file_id and " +
"playlist_file.playlist_id = ? " + "playlist_file.playlist_id = ? " +
"order by playlist_file.id", rowMapper, playlistId); "order by playlist_file.id", rowMapper, playlistId);
} }
public List<MediaFile> getSongsForAlbum(String artist, String album) { public List<MediaFile> getSongsForAlbum(String artist, String album) {
return query("select " + COLUMNS + " from media_file where album_artist=? and album=? and present " + return query("select " + QUERY_COLUMNS + " from media_file where album_artist=? and album=? and present " +
"and type in (?,?,?) order by disc_number, track_number", rowMapper, "and type in (?,?,?) order by disc_number, track_number", rowMapper,
artist, album, MUSIC.name(), AUDIOBOOK.name(), PODCAST.name()); artist, album, MUSIC.name(), AUDIOBOOK.name(), PODCAST.name());
} }
@ -105,7 +106,8 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type = :type and present and folder in (:folders) " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and present and folder in (:folders) " +
"order by title limit :count offset :offset", rowMapper, args); "order by title limit :count offset :offset", rowMapper, args);
} }
@ -118,7 +120,7 @@ public class MediaFileDao extends AbstractDao {
put("name", name); put("name", name);
put("folders", MusicFolder.toPathList(musicFolders)); put("folders", MusicFolder.toPathList(musicFolders));
}}; }};
return namedQueryOne("select " + COLUMNS + " from media_file where type = :type and artist = :name " + return namedQueryOne("select " + QUERY_COLUMNS + " from media_file where type = :type and artist = :name " +
"and present and folder in (:folders)", rowMapper, args); "and present and folder in (:folders)", rowMapper, args);
} }
@ -175,7 +177,7 @@ public class MediaFileDao extends AbstractDao {
file.setPlayCount(musicFileInfo.getPlayCount()); file.setPlayCount(musicFileInfo.getPlayCount());
} }
update("insert into media_file (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")", null, update("insert into media_file (" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")",
file.getPath(), file.getFolder(), file.getMediaType().name(), file.getFormat(), file.getTitle(), file.getAlbumName(), file.getArtist(), file.getPath(), file.getFolder(), file.getMediaType().name(), file.getFormat(), file.getTitle(), file.getAlbumName(), file.getArtist(),
file.getAlbumArtist(), file.getDiscNumber(), file.getTrackNumber(), file.getYear(), file.getGenre(), file.getBitRate(), file.getAlbumArtist(), file.getDiscNumber(), file.getTrackNumber(), file.getYear(), file.getGenre(), file.getBitRate(),
file.isVariableBitRate(), file.getDurationSeconds(), file.getFileSize(), file.getWidth(), file.getHeight(), file.isVariableBitRate(), file.getDurationSeconds(), file.getFileSize(), file.getWidth(), file.getHeight(),
@ -228,7 +230,8 @@ public class MediaFileDao extends AbstractDao {
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type = :type and play_count > 0 and present and folder in (:folders) " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and play_count > 0 and present and folder in (:folders) " +
"order by play_count desc limit :count offset :offset", rowMapper, args); "order by play_count desc limit :count offset :offset", rowMapper, args);
} }
@ -250,7 +253,8 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type = :type and last_played is not null and present " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and last_played is not null and present " +
"and folder in (:folders) order by last_played desc limit :count offset :offset", rowMapper, args); "and folder in (:folders) order by last_played desc limit :count offset :offset", rowMapper, args);
} }
@ -273,7 +277,8 @@ public class MediaFileDao extends AbstractDao {
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type = :type and folder in (:folders) and present " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and folder in (:folders) and present " +
"order by created desc limit :count offset :offset", rowMapper, args); "order by created desc limit :count offset :offset", rowMapper, args);
} }
@ -298,7 +303,8 @@ public class MediaFileDao extends AbstractDao {
}}; }};
String orderBy = byArtist ? "artist, album" : "album"; String orderBy = byArtist ? "artist, album" : "album";
return namedQuery("select " + COLUMNS + " from media_file where type = :type and folder in (:folders) and present " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and folder in (:folders) and present " +
"order by " + orderBy + " limit :count offset :offset", rowMapper, args); "order by " + orderBy + " limit :count offset :offset", rowMapper, args);
} }
@ -327,11 +333,13 @@ public class MediaFileDao extends AbstractDao {
}}; }};
if (fromYear <= toYear) { if (fromYear <= toYear) {
return namedQuery("select " + COLUMNS + " from media_file where type = :type and folder in (:folders) and present " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and folder in (:folders) and present " +
"and year between :fromYear and :toYear order by year limit :count offset :offset", "and year between :fromYear and :toYear order by year limit :count offset :offset",
rowMapper, args); rowMapper, args);
} else { } else {
return namedQuery("select " + COLUMNS + " from media_file where type = :type and folder in (:folders) and present " + return namedQuery("select " + QUERY_COLUMNS
+ " from media_file where type = :type and folder in (:folders) and present " +
"and year between :toYear and :fromYear order by year desc limit :count offset :offset", "and year between :toYear and :fromYear order by year desc limit :count offset :offset",
rowMapper, args); rowMapper, args);
} }
@ -358,7 +366,7 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type = :type and folder in (:folders) " + return namedQuery("select " + QUERY_COLUMNS + " from media_file where type = :type and folder in (:folders) " +
"and present and genre = :genre limit :count offset :offset", rowMapper, args); "and present and genre = :genre limit :count offset :offset", rowMapper, args);
} }
@ -373,13 +381,14 @@ public class MediaFileDao extends AbstractDao {
put("offset", offset); put("offset", offset);
put("folders", MusicFolder.toPathList(musicFolders)); put("folders", MusicFolder.toPathList(musicFolders));
}}; }};
return namedQuery("select " + COLUMNS + " from media_file where type in (:types) and genre = :genre " + return namedQuery("select " + QUERY_COLUMNS + " from media_file where type in (:types) and genre = :genre " +
"and present and folder in (:folders) limit :count offset :offset", "and present and folder in (:folders) limit :count offset :offset",
rowMapper, args); rowMapper, args);
} }
public List<MediaFile> getSongsByArtist(String artist, int offset, int count) { public List<MediaFile> getSongsByArtist(String artist, int offset, int count) {
return query("select " + COLUMNS + " from media_file where type in (?,?,?) and artist=? and present limit ? offset ?", return query("select " + QUERY_COLUMNS
+ " from media_file where type in (?,?,?) and artist=? and present limit ? offset ?",
rowMapper, MUSIC.name(), PODCAST.name(), AUDIOBOOK.name(), artist, count, offset); rowMapper, MUSIC.name(), PODCAST.name(), AUDIOBOOK.name(), artist, count, offset);
} }
@ -393,7 +402,7 @@ public class MediaFileDao extends AbstractDao {
put("type", MUSIC.name()); put("type", MUSIC.name());
put("folders", MusicFolder.toPathList(musicFolders)); put("folders", MusicFolder.toPathList(musicFolders));
}}; }};
return namedQueryOne("select " + COLUMNS + " from media_file where artist = :artist " + return namedQueryOne("select " + QUERY_COLUMNS + " from media_file where artist = :artist " +
"and title = :title and type = :type and present and folder in (:folders)" , "and title = :title and type = :type and present and folder in (:folders)" ,
rowMapper, args); rowMapper, args);
} }
@ -419,7 +428,7 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + prefix(COLUMNS, "media_file") + " from starred_media_file, media_file where media_file.id = starred_media_file.media_file_id and " + return namedQuery("select " + prefix(QUERY_COLUMNS, "media_file") + " from starred_media_file, media_file where media_file.id = starred_media_file.media_file_id and " +
"media_file.present and media_file.type = :type and media_file.folder in (:folders) and starred_media_file.username = :username " + "media_file.present and media_file.type = :type and media_file.folder in (:folders) and starred_media_file.username = :username " +
"order by starred_media_file.created desc limit :count offset :offset", "order by starred_media_file.created desc limit :count offset :offset",
rowMapper, args); rowMapper, args);
@ -446,7 +455,7 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + prefix(COLUMNS, "media_file") + " from starred_media_file, media_file " + return namedQuery("select " + prefix(QUERY_COLUMNS, "media_file") + " from starred_media_file, media_file " +
"where media_file.id = starred_media_file.media_file_id and " + "where media_file.id = starred_media_file.media_file_id and " +
"media_file.present and media_file.type = :type and starred_media_file.username = :username and " + "media_file.present and media_file.type = :type and starred_media_file.username = :username and " +
"media_file.folder in (:folders) " + "media_file.folder in (:folders) " +
@ -475,7 +484,7 @@ public class MediaFileDao extends AbstractDao {
put("count", count); put("count", count);
put("offset", offset); put("offset", offset);
}}; }};
return namedQuery("select " + prefix(COLUMNS, "media_file") + " from starred_media_file, media_file where media_file.id = starred_media_file.media_file_id and " + return namedQuery("select " + prefix(QUERY_COLUMNS, "media_file") + " from starred_media_file, media_file where media_file.id = starred_media_file.media_file_id and " +
"media_file.present and media_file.type in (:types) and starred_media_file.username = :username and " + "media_file.present and media_file.type in (:types) and starred_media_file.username = :username and " +
"media_file.folder in (:folders) " + "media_file.folder in (:folders) " +
"order by starred_media_file.created desc limit :count offset :offset", "order by starred_media_file.created desc limit :count offset :offset",
@ -490,7 +499,6 @@ public class MediaFileDao extends AbstractDao {
Map<String, Object> args = new HashMap<String, Object>() {{ Map<String, Object> args = new HashMap<String, Object>() {{
put("folders", MusicFolder.toPathList(criteria.getMusicFolders())); put("folders", MusicFolder.toPathList(criteria.getMusicFolders()));
put("username", username); put("username", username);
put("count", criteria.getCount());
put("fromYear", criteria.getFromYear()); put("fromYear", criteria.getFromYear());
put("toYear", criteria.getToYear()); put("toYear", criteria.getToYear());
put("genre", criteria.getGenre()); put("genre", criteria.getGenre());
@ -508,7 +516,7 @@ public class MediaFileDao extends AbstractDao {
boolean joinAlbumRating = (criteria.getMinAlbumRating() != null || criteria.getMaxAlbumRating() != null); boolean joinAlbumRating = (criteria.getMinAlbumRating() != null || criteria.getMaxAlbumRating() != null);
boolean joinStarred = (criteria.isShowStarredSongs() ^ criteria.isShowUnstarredSongs()); boolean joinStarred = (criteria.isShowStarredSongs() ^ criteria.isShowUnstarredSongs());
String query = "select top :count " + prefix(COLUMNS, "media_file") + " from media_file "; String query = "select " + prefix(QUERY_COLUMNS, "media_file") + " from media_file ";
if (joinStarred) { 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 "; query += "left outer join starred_media_file on media_file.id = starred_media_file.media_file_id and starred_media_file.username = :username ";
@ -587,7 +595,7 @@ public class MediaFileDao extends AbstractDao {
query += " order by rand()"; query += " order by rand()";
return namedQuery(query, rowMapper, args); return namedQueryWithLimit(query, rowMapper, args, criteria.getCount());
} }
public int getAlbumCount(final List<MusicFolder> musicFolders) { public int getAlbumCount(final List<MusicFolder> musicFolders) {
@ -649,7 +657,7 @@ public class MediaFileDao extends AbstractDao {
} }
public void markNonPresent(Date lastScanned) { public void markNonPresent(Date lastScanned) {
int minId = queryForInt("select top 1 id from media_file where last_scanned != ? and present", 0, lastScanned); int minId = queryForInt("select min(id) from media_file where last_scanned != ? and present", 0, lastScanned);
int maxId = queryForInt("select max(id) from media_file where last_scanned != ? and present", 0, lastScanned); int maxId = queryForInt("select max(id) from media_file where last_scanned != ? and present", 0, lastScanned);
final int batchSize = 1000; final int batchSize = 1000;
@ -661,7 +669,7 @@ public class MediaFileDao extends AbstractDao {
} }
public void expunge() { public void expunge() {
int minId = queryForInt("select top 1 id from media_file where not present", 0); int minId = queryForInt("select min(id) from media_file where not present", 0);
int maxId = queryForInt("select max(id) from media_file where not present", 0); int maxId = queryForInt("select max(id) from media_file where not present", 0);
final int batchSize = 1000; final int batchSize = 1000;

@ -36,7 +36,8 @@ import java.util.List;
public class MusicFolderDao extends AbstractDao { public class MusicFolderDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(MusicFolderDao.class); private static final Logger LOG = Logger.getLogger(MusicFolderDao.class);
private static final String COLUMNS = "id, path, name, enabled, changed"; private static final String INSERT_COLUMNS = "path, name, enabled, changed";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final MusicFolderRowMapper rowMapper = new MusicFolderRowMapper(); private final MusicFolderRowMapper rowMapper = new MusicFolderRowMapper();
/** /**
@ -45,7 +46,7 @@ public class MusicFolderDao extends AbstractDao {
* @return Possibly empty list of all music folders. * @return Possibly empty list of all music folders.
*/ */
public List<MusicFolder> getAllMusicFolders() { public List<MusicFolder> getAllMusicFolders() {
String sql = "select " + COLUMNS + " from music_folder"; String sql = "select " + QUERY_COLUMNS + " from music_folder";
return query(sql, rowMapper); return query(sql, rowMapper);
} }
@ -55,7 +56,7 @@ public class MusicFolderDao extends AbstractDao {
* @param musicFolder The music folder to create. * @param musicFolder The music folder to create.
*/ */
public void createMusicFolder(MusicFolder musicFolder) { public void createMusicFolder(MusicFolder musicFolder) {
String sql = "insert into music_folder (" + COLUMNS + ") values (null, ?, ?, ?, ?)"; String sql = "insert into music_folder (" + INSERT_COLUMNS + ") values (?, ?, ?, ?)";
update(sql, musicFolder.getPath(), musicFolder.getName(), musicFolder.isEnabled(), musicFolder.getChanged()); update(sql, musicFolder.getPath(), musicFolder.getName(), musicFolder.isEnabled(), musicFolder.getChanged());
Integer id = queryForInt("select max(id) from music_folder", 0); Integer id = queryForInt("select max(id) from music_folder", 0);
@ -86,7 +87,7 @@ public class MusicFolderDao extends AbstractDao {
} }
public List<MusicFolder> getMusicFoldersForUser(String username) { public List<MusicFolder> getMusicFoldersForUser(String username) {
String sql = "select " + prefix(COLUMNS, "music_folder") + " from music_folder, music_folder_user " + String sql = "select " + prefix(QUERY_COLUMNS, "music_folder") + " from music_folder, music_folder_user " +
"where music_folder.id = music_folder_user.music_folder_id and music_folder_user.username = ?"; "where music_folder.id = music_folder_user.music_folder_id and music_folder_user.username = ?";
return query(sql, rowMapper, username); return query(sql, rowMapper, username);
} }

@ -33,11 +33,12 @@ import org.libresonic.player.domain.SavedPlayQueue;
*/ */
public class PlayQueueDao extends AbstractDao { public class PlayQueueDao extends AbstractDao {
private static final String COLUMNS = "id, username, current, position_millis, changed, changed_by"; private static final String INSERT_COLUMNS = "username, current, position_millis, changed, changed_by";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final RowMapper rowMapper = new PlayQueueMapper(); private final RowMapper rowMapper = new PlayQueueMapper();
public synchronized SavedPlayQueue getPlayQueue(String username) { public synchronized SavedPlayQueue getPlayQueue(String username) {
SavedPlayQueue playQueue = queryOne("select " + COLUMNS + " from play_queue where username=?", rowMapper, username); SavedPlayQueue playQueue = queryOne("select " + QUERY_COLUMNS + " from play_queue where username=?", rowMapper, username);
if (playQueue == null) { if (playQueue == null) {
return null; return null;
} }
@ -48,8 +49,8 @@ public class PlayQueueDao extends AbstractDao {
public synchronized void savePlayQueue(SavedPlayQueue playQueue) { public synchronized void savePlayQueue(SavedPlayQueue playQueue) {
update("delete from play_queue where username=?", playQueue.getUsername()); update("delete from play_queue where username=?", playQueue.getUsername());
update("insert into play_queue(" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")", update("insert into play_queue(" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")",
null, playQueue.getUsername(), playQueue.getCurrentMediaFileId(), playQueue.getPositionMillis(), playQueue.getUsername(), playQueue.getCurrentMediaFileId(), playQueue.getPositionMillis(),
playQueue.getChanged(), playQueue.getChangedBy()); playQueue.getChanged(), playQueue.getChangedBy());
int id = queryForInt("select max(id) from play_queue", 0); int id = queryForInt("select max(id) from play_queue", 0);
playQueue.setId(id); playQueue.setId(id);

@ -35,8 +35,9 @@ import java.util.*;
public class PlayerDao extends AbstractDao { public class PlayerDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(PlayerDao.class); private static final Logger LOG = Logger.getLogger(PlayerDao.class);
private static final String COLUMNS = "id, name, type, username, ip_address, auto_control_enabled, m3u_bom_enabled, " + private static final String INSERT_COLUMNS = "name, type, username, ip_address, auto_control_enabled, m3u_bom_enabled, " +
"last_seen, cover_art_scheme, transcode_scheme, dynamic_ip, technology, client_id"; "last_seen, cover_art_scheme, transcode_scheme, dynamic_ip, technology, client_id";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private PlayerRowMapper rowMapper = new PlayerRowMapper(); private PlayerRowMapper rowMapper = new PlayerRowMapper();
private Map<String, PlayQueue> playlists = Collections.synchronizedMap(new HashMap<String, PlayQueue>()); private Map<String, PlayQueue> playlists = Collections.synchronizedMap(new HashMap<String, PlayQueue>());
@ -47,7 +48,7 @@ public class PlayerDao extends AbstractDao {
* @return Possibly empty list of all users. * @return Possibly empty list of all users.
*/ */
public List<Player> getAllPlayers() { public List<Player> getAllPlayers() {
String sql = "select " + COLUMNS + " from player"; String sql = "select " + QUERY_COLUMNS + " from player";
return query(sql, rowMapper); return query(sql, rowMapper);
} }
@ -61,10 +62,10 @@ public class PlayerDao extends AbstractDao {
*/ */
public List<Player> getPlayersForUserAndClientId(String username, String clientId) { public List<Player> getPlayersForUserAndClientId(String username, String clientId) {
if (clientId != null) { if (clientId != null) {
String sql = "select " + COLUMNS + " from player where username=? and client_id=?"; String sql = "select " + QUERY_COLUMNS + " from player where username=? and client_id=?";
return query(sql, rowMapper, username, clientId); return query(sql, rowMapper, username, clientId);
} else { } else {
String sql = "select " + COLUMNS + " from player where username=? and client_id is null"; String sql = "select " + QUERY_COLUMNS + " from player where username=? and client_id is null";
return query(sql, rowMapper, username); return query(sql, rowMapper, username);
} }
} }
@ -76,7 +77,7 @@ public class PlayerDao extends AbstractDao {
* @return The player with the given ID, or <code>null</code> if no such player exists. * @return The player with the given ID, or <code>null</code> if no such player exists.
*/ */
public Player getPlayerById(String id) { public Player getPlayerById(String id) {
String sql = "select " + COLUMNS + " from player where id=?"; String sql = "select " + QUERY_COLUMNS + " from player where id=?";
return queryOne(sql, rowMapper, id); return queryOne(sql, rowMapper, id);
} }
@ -92,7 +93,7 @@ public class PlayerDao extends AbstractDao {
} }
int id = existingMax + 1; int id = existingMax + 1;
player.setId(String.valueOf(id)); player.setId(String.valueOf(id));
String sql = "insert into player (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")"; String sql = "insert into player (" + QUERY_COLUMNS + ") values (" + questionMarks(QUERY_COLUMNS) + ")";
update(sql, player.getId(), player.getName(), player.getType(), player.getUsername(), update(sql, player.getId(), player.getName(), player.getType(), player.getUsername(),
player.getIpAddress(), player.isAutoControlEnabled(), player.isM3uBomEnabled(), player.getIpAddress(), player.isAutoControlEnabled(), player.isM3uBomEnabled(),
player.getLastSeen(), CoverArtScheme.MEDIUM.name(), player.getLastSeen(), CoverArtScheme.MEDIUM.name(),
@ -153,7 +154,7 @@ public class PlayerDao extends AbstractDao {
update(sql, player.getName(), player.getType(), player.getUsername(), update(sql, player.getName(), player.getType(), player.getUsername(),
player.getIpAddress(), player.isAutoControlEnabled(), player.isM3uBomEnabled(), player.getIpAddress(), player.isAutoControlEnabled(), player.isM3uBomEnabled(),
player.getLastSeen(), player.getTranscodeScheme().name(), player.isDynamicIp(), player.getLastSeen(), player.getTranscodeScheme().name(), player.isDynamicIp(),
player.getTechnology(), player.getClientId(), player.getId()); player.getTechnology().name(), player.getClientId(), player.getId());
} }
private void addPlaylist(Player player) { private void addPlaylist(Player player) {

@ -40,15 +40,16 @@ import java.util.TreeMap;
public class PlaylistDao extends AbstractDao { public class PlaylistDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(PlaylistDao.class); private static final Logger LOG = Logger.getLogger(PlaylistDao.class);
private static final String COLUMNS = "id, username, is_public, name, comment, file_count, duration_seconds, " + private static final String INSERT_COLUMNS = "username, is_public, name, comment, file_count, duration_seconds, " +
"created, changed, imported_from"; "created, changed, imported_from";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private final RowMapper rowMapper = new PlaylistMapper(); private final RowMapper rowMapper = new PlaylistMapper();
public List<Playlist> getReadablePlaylistsForUser(String username) { public List<Playlist> getReadablePlaylistsForUser(String username) {
List<Playlist> result1 = getWritablePlaylistsForUser(username); List<Playlist> result1 = getWritablePlaylistsForUser(username);
List<Playlist> result2 = query("select " + COLUMNS + " from playlist where is_public", rowMapper); List<Playlist> result2 = query("select " + QUERY_COLUMNS + " from playlist where is_public", rowMapper);
List<Playlist> result3 = query("select " + prefix(COLUMNS, "playlist") + " from playlist, playlist_user where " + List<Playlist> result3 = query("select " + prefix(QUERY_COLUMNS, "playlist") + " from playlist, playlist_user where " +
"playlist.id = playlist_user.playlist_id and " + "playlist.id = playlist_user.playlist_id and " +
"playlist.username != ? and " + "playlist.username != ? and " +
"playlist_user.username = ?", rowMapper, username, username); "playlist_user.username = ?", rowMapper, username, username);
@ -68,20 +69,20 @@ public class PlaylistDao extends AbstractDao {
} }
public List<Playlist> getWritablePlaylistsForUser(String username) { public List<Playlist> getWritablePlaylistsForUser(String username) {
return query("select " + COLUMNS + " from playlist where username=?", rowMapper, username); return query("select " + QUERY_COLUMNS + " from playlist where username=?", rowMapper, username);
} }
public Playlist getPlaylist(int id) { public Playlist getPlaylist(int id) {
return queryOne("select " + COLUMNS + " from playlist where id=?", rowMapper, id); return queryOne("select " + QUERY_COLUMNS + " from playlist where id=?", rowMapper, id);
} }
public List<Playlist> getAllPlaylists() { public List<Playlist> getAllPlaylists() {
return query("select " + COLUMNS + " from playlist", rowMapper); return query("select " + QUERY_COLUMNS + " from playlist", rowMapper);
} }
public synchronized void createPlaylist(Playlist playlist) { public synchronized void createPlaylist(Playlist playlist) {
update("insert into playlist(" + COLUMNS + ") values(" + questionMarks(COLUMNS) + ")", update("insert into playlist(" + INSERT_COLUMNS + ") values(" + questionMarks(INSERT_COLUMNS) + ")",
null, playlist.getUsername(), playlist.isShared(), playlist.getName(), playlist.getComment(), playlist.getUsername(), playlist.isShared(), playlist.getName(), playlist.getComment(),
0, 0, playlist.getCreated(), playlist.getChanged(), playlist.getImportedFrom()); 0, 0, playlist.getCreated(), playlist.getChanged(), playlist.getImportedFrom());
int id = queryForInt("select max(id) from playlist", 0); int id = queryForInt("select max(id) from playlist", 0);

@ -36,9 +36,11 @@ import org.libresonic.player.domain.PodcastStatus;
*/ */
public class PodcastDao extends AbstractDao { public class PodcastDao extends AbstractDao {
private static final String CHANNEL_COLUMNS = "id, url, title, description, image_url, status, error_message"; private static final String CHANNEL_INSERT_COLUMNS = "url, title, description, image_url, status, error_message";
private static final String EPISODE_COLUMNS = "id, channel_id, url, path, title, description, publish_date, " + private static final String CHANNEL_QUERY_COLUMNS = "id, " + CHANNEL_INSERT_COLUMNS;
private static final String EPISODE_INSERT_COLUMNS = "channel_id, url, path, title, description, publish_date, " +
"duration, bytes_total, bytes_downloaded, status, error_message"; "duration, bytes_total, bytes_downloaded, status, error_message";
private static final String EPISODE_QUERY_COLUMNS = "id, " + EPISODE_INSERT_COLUMNS;
private PodcastChannelRowMapper channelRowMapper = new PodcastChannelRowMapper(); private PodcastChannelRowMapper channelRowMapper = new PodcastChannelRowMapper();
private PodcastEpisodeRowMapper episodeRowMapper = new PodcastEpisodeRowMapper(); private PodcastEpisodeRowMapper episodeRowMapper = new PodcastEpisodeRowMapper();
@ -50,8 +52,9 @@ public class PodcastDao extends AbstractDao {
* @return The ID of the newly created channel. * @return The ID of the newly created channel.
*/ */
public synchronized int createChannel(PodcastChannel channel) { public synchronized int createChannel(PodcastChannel channel) {
String sql = "insert into podcast_channel (" + CHANNEL_COLUMNS + ") values (" + questionMarks(CHANNEL_COLUMNS) + ")"; String sql = "insert into podcast_channel (" + CHANNEL_INSERT_COLUMNS + ") values (" + questionMarks(
update(sql, null, channel.getUrl(), channel.getTitle(), channel.getDescription(), channel.getImageUrl(), CHANNEL_INSERT_COLUMNS) + ")";
update(sql, channel.getUrl(), channel.getTitle(), channel.getDescription(), channel.getImageUrl(),
channel.getStatus().name(), channel.getErrorMessage()); channel.getStatus().name(), channel.getErrorMessage());
return getJdbcTemplate().queryForObject("select max(id) from podcast_channel", Integer.class); return getJdbcTemplate().queryForObject("select max(id) from podcast_channel", Integer.class);
@ -63,7 +66,7 @@ public class PodcastDao extends AbstractDao {
* @return Possibly empty list of all Podcast channels. * @return Possibly empty list of all Podcast channels.
*/ */
public List<PodcastChannel> getAllChannels() { public List<PodcastChannel> getAllChannels() {
String sql = "select " + CHANNEL_COLUMNS + " from podcast_channel"; String sql = "select " + CHANNEL_QUERY_COLUMNS + " from podcast_channel";
return query(sql, channelRowMapper); return query(sql, channelRowMapper);
} }
@ -71,7 +74,7 @@ public class PodcastDao extends AbstractDao {
* Returns a single Podcast channel. * Returns a single Podcast channel.
*/ */
public PodcastChannel getChannel(int channelId) { public PodcastChannel getChannel(int channelId) {
String sql = "select " + CHANNEL_COLUMNS + " from podcast_channel where id=?"; String sql = "select " + CHANNEL_QUERY_COLUMNS + " from podcast_channel where id=?";
return queryOne(sql, channelRowMapper, channelId); return queryOne(sql, channelRowMapper, channelId);
} }
@ -102,8 +105,9 @@ public class PodcastDao extends AbstractDao {
* @param episode The Podcast episode to create. * @param episode The Podcast episode to create.
*/ */
public void createEpisode(PodcastEpisode episode) { public void createEpisode(PodcastEpisode episode) {
String sql = "insert into podcast_episode (" + EPISODE_COLUMNS + ") values (" + questionMarks(EPISODE_COLUMNS) + ")"; String sql = "insert into podcast_episode (" + EPISODE_INSERT_COLUMNS + ") values (" + questionMarks(
update(sql, null, episode.getChannelId(), episode.getUrl(), episode.getPath(), EPISODE_INSERT_COLUMNS) + ")";
update(sql, episode.getChannelId(), episode.getUrl(), episode.getPath(),
episode.getTitle(), episode.getDescription(), episode.getPublishDate(), episode.getTitle(), episode.getDescription(), episode.getPublishDate(),
episode.getDuration(), episode.getBytesTotal(), episode.getBytesDownloaded(), episode.getDuration(), episode.getBytesTotal(), episode.getBytesDownloaded(),
episode.getStatus().name(), episode.getErrorMessage()); episode.getStatus().name(), episode.getErrorMessage());
@ -116,7 +120,7 @@ public class PodcastDao extends AbstractDao {
* reverse chronological order (newest episode first). * reverse chronological order (newest episode first).
*/ */
public List<PodcastEpisode> getEpisodes(int channelId) { public List<PodcastEpisode> getEpisodes(int channelId) {
String sql = "select " + EPISODE_COLUMNS + " from podcast_episode where channel_id = ? " + String sql = "select " + EPISODE_QUERY_COLUMNS + " from podcast_episode where channel_id = ? " +
"and status != ? order by publish_date desc"; "and status != ? order by publish_date desc";
return query(sql, episodeRowMapper, channelId, PodcastStatus.DELETED); return query(sql, episodeRowMapper, channelId, PodcastStatus.DELETED);
} }
@ -128,7 +132,8 @@ public class PodcastDao extends AbstractDao {
* reverse chronological order (newest episode first). * reverse chronological order (newest episode first).
*/ */
public List<PodcastEpisode> getNewestEpisodes(int count) { public List<PodcastEpisode> getNewestEpisodes(int count) {
String sql = "select " + EPISODE_COLUMNS + " from podcast_episode where status = ? and publish_date is not null " + String sql = "select " + EPISODE_QUERY_COLUMNS
+ " from podcast_episode where status = ? and publish_date is not null " +
"order by publish_date desc limit ?"; "order by publish_date desc limit ?";
return query(sql, episodeRowMapper, PodcastStatus.COMPLETED, count); return query(sql, episodeRowMapper, PodcastStatus.COMPLETED, count);
} }
@ -140,12 +145,12 @@ public class PodcastDao extends AbstractDao {
* @return The episode or <code>null</code> if not found. * @return The episode or <code>null</code> if not found.
*/ */
public PodcastEpisode getEpisode(int episodeId) { public PodcastEpisode getEpisode(int episodeId) {
String sql = "select " + EPISODE_COLUMNS + " from podcast_episode where id=?"; String sql = "select " + EPISODE_QUERY_COLUMNS + " from podcast_episode where id=?";
return queryOne(sql, episodeRowMapper, episodeId); return queryOne(sql, episodeRowMapper, episodeId);
} }
public PodcastEpisode getEpisodeByUrl(String url) { public PodcastEpisode getEpisodeByUrl(String url) {
String sql = "select " + EPISODE_COLUMNS + " from podcast_episode where url=?"; String sql = "select " + EPISODE_QUERY_COLUMNS + " from podcast_episode where url=?";
return queryOne(sql, episodeRowMapper, url); return queryOne(sql, episodeRowMapper, url);
} }

@ -38,7 +38,8 @@ import org.libresonic.player.domain.Share;
*/ */
public class ShareDao extends AbstractDao { public class ShareDao extends AbstractDao {
private static final String COLUMNS = "id, name, description, username, created, expires, last_visited, visit_count"; private static final String INSERT_COLUMNS = "name, description, username, created, expires, last_visited, visit_count";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private ShareRowMapper shareRowMapper = new ShareRowMapper(); private ShareRowMapper shareRowMapper = new ShareRowMapper();
private ShareFileRowMapper shareFileRowMapper = new ShareFileRowMapper(); private ShareFileRowMapper shareFileRowMapper = new ShareFileRowMapper();
@ -49,8 +50,8 @@ public class ShareDao extends AbstractDao {
* @param share The share to create. The ID of the share will be set by this method. * @param share The share to create. The ID of the share will be set by this method.
*/ */
public synchronized void createShare(Share share) { public synchronized void createShare(Share share) {
String sql = "insert into share (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")"; String sql = "insert into share (" + INSERT_COLUMNS + ") values (" + questionMarks(INSERT_COLUMNS) + ")";
update(sql, null, share.getName(), share.getDescription(), share.getUsername(), share.getCreated(), update(sql, share.getName(), share.getDescription(), share.getUsername(), share.getCreated(),
share.getExpires(), share.getLastVisited(), share.getVisitCount()); share.getExpires(), share.getLastVisited(), share.getVisitCount());
int id = getJdbcTemplate().queryForObject("select max(id) from share", Integer.class); int id = getJdbcTemplate().queryForObject("select max(id) from share", Integer.class);
@ -63,17 +64,17 @@ public class ShareDao extends AbstractDao {
* @return Possibly empty list of all shares. * @return Possibly empty list of all shares.
*/ */
public List<Share> getAllShares() { public List<Share> getAllShares() {
String sql = "select " + COLUMNS + " from share"; String sql = "select " + QUERY_COLUMNS + " from share";
return query(sql, shareRowMapper); return query(sql, shareRowMapper);
} }
public Share getShareByName(String shareName) { public Share getShareByName(String shareName) {
String sql = "select " + COLUMNS + " from share where name=?"; String sql = "select " + QUERY_COLUMNS + " from share where name=?";
return queryOne(sql, shareRowMapper, shareName); return queryOne(sql, shareRowMapper, shareName);
} }
public Share getShareById(int id) { public Share getShareById(int id) {
String sql = "select " + COLUMNS + " from share where id=?"; String sql = "select " + QUERY_COLUMNS + " from share where id=?";
return queryOne(sql, shareRowMapper, id); return queryOne(sql, shareRowMapper, id);
} }

@ -36,7 +36,8 @@ import org.libresonic.player.domain.Transcoding;
public class TranscodingDao extends AbstractDao { public class TranscodingDao extends AbstractDao {
private static final Logger LOG = Logger.getLogger(TranscodingDao.class); private static final Logger LOG = Logger.getLogger(TranscodingDao.class);
private static final String COLUMNS = "id, name, source_formats, target_format, step1, step2, step3, default_active"; private static final String INSERT_COLUMNS = "name, source_formats, target_format, step1, step2, step3, default_active";
private static final String QUERY_COLUMNS = "id, " + INSERT_COLUMNS;
private TranscodingRowMapper rowMapper = new TranscodingRowMapper(); private TranscodingRowMapper rowMapper = new TranscodingRowMapper();
/** /**
@ -45,7 +46,7 @@ public class TranscodingDao extends AbstractDao {
* @return Possibly empty list of all transcodings. * @return Possibly empty list of all transcodings.
*/ */
public List<Transcoding> getAllTranscodings() { public List<Transcoding> getAllTranscodings() {
String sql = "select " + COLUMNS + " from transcoding2"; String sql = "select " + QUERY_COLUMNS + " from transcoding2";
return query(sql, rowMapper); return query(sql, rowMapper);
} }
@ -56,7 +57,7 @@ public class TranscodingDao extends AbstractDao {
* @return All active transcodings for the player. * @return All active transcodings for the player.
*/ */
public List<Transcoding> getTranscodingsForPlayer(String playerId) { public List<Transcoding> getTranscodingsForPlayer(String playerId) {
String sql = "select " + COLUMNS + " from transcoding2, player_transcoding2 " + String sql = "select " + QUERY_COLUMNS + " from transcoding2, player_transcoding2 " +
"where player_transcoding2.player_id = ? " + "where player_transcoding2.player_id = ? " +
"and player_transcoding2.transcoding_id = transcoding2.id"; "and player_transcoding2.transcoding_id = transcoding2.id";
return query(sql, rowMapper, playerId); return query(sql, rowMapper, playerId);
@ -87,7 +88,7 @@ public class TranscodingDao extends AbstractDao {
existingMax = 0; existingMax = 0;
} }
transcoding.setId(existingMax + 1); transcoding.setId(existingMax + 1);
String sql = "insert into transcoding2 (" + COLUMNS + ") values (" + questionMarks(COLUMNS) + ")"; String sql = "insert into transcoding2 (" + QUERY_COLUMNS + ") values (" + questionMarks(QUERY_COLUMNS) + ")";
update(sql, transcoding.getId(), transcoding.getName(), transcoding.getSourceFormats(), update(sql, transcoding.getId(), transcoding.getName(), transcoding.getSourceFormats(),
transcoding.getTargetFormat(), transcoding.getStep1(), transcoding.getTargetFormat(), transcoding.getStep1(),
transcoding.getStep2(), transcoding.getStep3(), transcoding.isDefaultActive()); transcoding.getStep2(), transcoding.getStep3(), transcoding.isDefaultActive());

@ -61,6 +61,8 @@ public class UserDao extends AbstractDao {
private UserRowMapper userRowMapper = new UserRowMapper(); private UserRowMapper userRowMapper = new UserRowMapper();
private UserSettingsRowMapper userSettingsRowMapper = new UserSettingsRowMapper(); private UserSettingsRowMapper userSettingsRowMapper = new UserSettingsRowMapper();
String userTableQuote = "";
/** /**
* Returns the user with the given username. * Returns the user with the given username.
* *
@ -68,7 +70,7 @@ public class UserDao extends AbstractDao {
* @return The user, or <code>null</code> if not found. * @return The user, or <code>null</code> if not found.
*/ */
public User getUserByName(String username) { public User getUserByName(String username) {
String sql = "select " + USER_COLUMNS + " from user where username=?"; String sql = "select " + USER_COLUMNS + " from " + getUserTable() + " where username=?";
return queryOne(sql, userRowMapper, username); return queryOne(sql, userRowMapper, username);
} }
@ -79,7 +81,7 @@ public class UserDao extends AbstractDao {
* @return The user, or <code>null</code> if not found. * @return The user, or <code>null</code> if not found.
*/ */
public User getUserByEmail(String email) { public User getUserByEmail(String email) {
String sql = "select " + USER_COLUMNS + " from user where email=?"; String sql = "select " + USER_COLUMNS + " from " + getUserTable() + " where email=?";
return queryOne(sql, userRowMapper, email); return queryOne(sql, userRowMapper, email);
} }
@ -89,7 +91,7 @@ public class UserDao extends AbstractDao {
* @return Possibly empty array of all users. * @return Possibly empty array of all users.
*/ */
public List<User> getAllUsers() { public List<User> getAllUsers() {
String sql = "select " + USER_COLUMNS + " from user"; String sql = "select " + USER_COLUMNS + " from " + getUserTable();
return query(sql, userRowMapper); return query(sql, userRowMapper);
} }
@ -99,7 +101,7 @@ public class UserDao extends AbstractDao {
* @param user The user to create. * @param user The user to create.
*/ */
public void createUser(User user) { public void createUser(User user) {
String sql = "insert into user (" + USER_COLUMNS + ") values (" + questionMarks(USER_COLUMNS) + ')'; String sql = "insert into " + getUserTable() + " (" + USER_COLUMNS + ") values (" + questionMarks(USER_COLUMNS) + ')';
update(sql, user.getUsername(), encrypt(user.getPassword()), user.getEmail(), user.isLdapAuthenticated(), update(sql, user.getUsername(), encrypt(user.getPassword()), user.getEmail(), user.isLdapAuthenticated(),
user.getBytesStreamed(), user.getBytesDownloaded(), user.getBytesUploaded()); user.getBytesStreamed(), user.getBytesDownloaded(), user.getBytesUploaded());
writeRoles(user); writeRoles(user);
@ -117,7 +119,7 @@ public class UserDao extends AbstractDao {
update("delete from user_role where username=?", username); update("delete from user_role where username=?", username);
update("delete from player where username=?", username); update("delete from player where username=?", username);
update("delete from user where username=?", username); update("delete from " + getUserTable() + " where username=?", username);
} }
/** /**
@ -126,7 +128,7 @@ public class UserDao extends AbstractDao {
* @param user The user to update. * @param user The user to update.
*/ */
public void updateUser(User user) { public void updateUser(User user) {
String sql = "update user set password=?, email=?, ldap_authenticated=?, bytes_streamed=?, bytes_downloaded=?, bytes_uploaded=? " + String sql = "update " + getUserTable() + " set password=?, email=?, ldap_authenticated=?, bytes_streamed=?, bytes_downloaded=?, bytes_uploaded=? " +
"where username=?"; "where username=?";
getJdbcTemplate().update(sql, new Object[]{encrypt(user.getPassword()), user.getEmail(), user.isLdapAuthenticated(), getJdbcTemplate().update(sql, new Object[]{encrypt(user.getPassword()), user.getEmail(), user.isLdapAuthenticated(),
user.getBytesStreamed(), user.getBytesDownloaded(), user.getBytesUploaded(), user.getBytesStreamed(), user.getBytesDownloaded(), user.getBytesUploaded(),
@ -355,4 +357,12 @@ public class UserDao extends AbstractDao {
return settings; return settings;
} }
} }
private String getUserTable() {
return userTableQuote + "user" + userTableQuote;
}
public void setUserTableQuote(String userTableQuote) {
this.userTableQuote = userTableQuote;
}
} }

@ -1,77 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
*
* @author Sindre Mehus
*/
public abstract class Schema {
/**
* Executes this schema.
* @param template The JDBC template to use.
*/
public abstract void execute(JdbcTemplate template);
/**
* Returns whether the given table exists.
* @param template The JDBC template to use.
* @param table The table in question.
* @return Whether the table exists.
*/
protected boolean tableExists(JdbcTemplate template, String table) {
try {
template.execute("select 1 from " + table);
} catch (Exception x) {
return false;
}
return true;
}
/**
* Returns whether the given column in the given table exists.
* @param template The JDBC template to use.
* @param column The column in question.
* @param table The table in question.
* @return Whether the column exists.
*/
protected boolean columnExists(JdbcTemplate template, String column, String table) {
try {
template.execute("select " + column + " from " + table + " where 1 = 0");
} catch (Exception x) {
return false;
}
return true;
}
protected boolean rowExists(JdbcTemplate template, String whereClause, String table) {
try {
int rowCount = template.queryForObject("select count(*) from " + table + " where " + whereClause,Integer.class);
return rowCount > 0;
} catch (Exception x) {
return false;
}
}
}

@ -1,88 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 2.5.
*
* @author Sindre Mehus
*/
public class Schema25 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema25.class);
public void execute(JdbcTemplate template) {
if (!tableExists(template, "version")) {
// Increase data file limit. See http://www.hsqldb.org/doc/guide/ch04.html
template.execute("set property \"hsqldb.cache_file_scale\" 8");
LOG.info("Database table 'version' not found. Creating it.");
template.execute("create table version (version int not null)");
template.execute("insert into version values (1)");
LOG.info("Database table 'version' was created successfully.");
}
if (!tableExists(template, "role")) {
LOG.info("Database table 'role' not found. Creating it.");
template.execute("create table role (" +
"id int not null," +
"name varchar not null," +
"primary key (id))");
template.execute("insert into role values (1, 'admin')");
template.execute("insert into role values (2, 'download')");
template.execute("insert into role values (3, 'upload')");
template.execute("insert into role values (4, 'playlist')");
template.execute("insert into role values (5, 'coverart')");
LOG.info("Database table 'role' was created successfully.");
}
if (!tableExists(template, "user")) {
LOG.info("Database table 'user' not found. Creating it.");
template.execute("create table user (" +
"username varchar not null," +
"password varchar not null," +
"primary key (username))");
template.execute("insert into user values ('admin', 'admin')");
LOG.info("Database table 'user' was created successfully.");
}
if (!tableExists(template, "user_role")) {
LOG.info("Database table 'user_role' not found. Creating it.");
template.execute("create table user_role (" +
"username varchar not null," +
"role_id int not null," +
"primary key (username, role_id)," +
"foreign key (username) references user(username)," +
"foreign key (role_id) references role(id))");
template.execute("insert into user_role values ('admin', 1)");
template.execute("insert into user_role values ('admin', 2)");
template.execute("insert into user_role values ('admin', 3)");
template.execute("insert into user_role values ('admin', 4)");
template.execute("insert into user_role values ('admin', 5)");
LOG.info("Database table 'user_role' was created successfully.");
}
}
}

@ -1,112 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.*;
import org.libresonic.player.dao.schema.Schema;
import org.libresonic.player.util.Util;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 2.6.
*
* @author Sindre Mehus
*/
public class Schema26 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema26.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 2",Integer.class) == 0) {
LOG.info("Updating database schema to version 2.");
template.execute("insert into version values (2)");
}
if (!tableExists(template, "music_folder")) {
LOG.info("Database table 'music_folder' not found. Creating it.");
template.execute("create table music_folder (" +
"id identity," +
"path varchar not null," +
"name varchar not null," +
"enabled boolean not null)");
template.execute("insert into music_folder values (null, '" + Util.getDefaultMusicFolder() + "', 'Music', true)");
LOG.info("Database table 'music_folder' was created successfully.");
}
if (!tableExists(template, "music_file_info")) {
LOG.info("Database table 'music_file_info' not found. Creating it.");
template.execute("create cached table music_file_info (" +
"id identity," +
"path varchar not null," +
"rating int," +
"comment varchar," +
"play_count int," +
"last_played datetime)");
template.execute("create index idx_music_file_info_path on music_file_info(path)");
LOG.info("Database table 'music_file_info' was created successfully.");
}
if (!tableExists(template, "internet_radio")) {
LOG.info("Database table 'internet_radio' not found. Creating it.");
template.execute("create table internet_radio (" +
"id identity," +
"name varchar not null," +
"stream_url varchar not null," +
"homepage_url varchar," +
"enabled boolean not null)");
LOG.info("Database table 'internet_radio' was created successfully.");
}
if (!tableExists(template, "player")) {
LOG.info("Database table 'player' not found. Creating it.");
template.execute("create table player (" +
"id int not null," +
"name varchar," +
"type varchar," +
"username varchar," +
"ip_address varchar," +
"auto_control_enabled boolean not null," +
"last_seen datetime," +
"cover_art_scheme varchar not null," +
"transcode_scheme varchar not null," +
"primary key (id))");
LOG.info("Database table 'player' was created successfully.");
}
// 'dynamic_ip' was added in 2.6.beta2
if (!columnExists(template, "dynamic_ip", "player")) {
LOG.info("Database column 'player.dynamic_ip' not found. Creating it.");
template.execute("alter table player " +
"add dynamic_ip boolean default true not null");
LOG.info("Database column 'player.dynamic_ip' was added successfully.");
}
if (template.queryForObject("select count(*) from role where id = 6",Integer.class) == 0) {
LOG.info("Role 'comment' not found in database. Creating it.");
template.execute("insert into role values (6, 'comment')");
template.execute("insert into user_role " +
"select distinct u.username, 6 from user u, user_role ur " +
"where u.username = ur.username and ur.role_id in (1, 5)");
LOG.info("Role 'comment' was created successfully.");
}
}
}

@ -1,57 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.*;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 2.7.
*
* @author Sindre Mehus
*/
public class Schema27 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema27.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 3",Integer.class) == 0) {
LOG.info("Updating database schema to version 3.");
template.execute("insert into version values (3)");
LOG.info("Converting database column 'music_file_info.path' to varchar_ignorecase.");
template.execute("drop index idx_music_file_info_path");
template.execute("alter table music_file_info alter column path varchar_ignorecase not null");
template.execute("create index idx_music_file_info_path on music_file_info(path)");
LOG.info("Database column 'music_file_info.path' was converted successfully.");
}
if (!columnExists(template, "bytes_streamed", "user")) {
LOG.info("Database columns 'user.bytes_streamed/downloaded/uploaded' not found. Creating them.");
template.execute("alter table user add bytes_streamed bigint default 0 not null");
template.execute("alter table user add bytes_downloaded bigint default 0 not null");
template.execute("alter table user add bytes_uploaded bigint default 0 not null");
LOG.info("Database columns 'user.bytes_streamed/downloaded/uploaded' were added successfully.");
}
}
}

@ -1,113 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.*;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 2.8.
*
* @author Sindre Mehus
*/
public class Schema28 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema28.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 4",Integer.class) == 0) {
LOG.info("Updating database schema to version 4.");
template.execute("insert into version values (4)");
}
if (!tableExists(template, "user_settings")) {
LOG.info("Database table 'user_settings' not found. Creating it.");
template.execute("create table user_settings (" +
"username varchar not null," +
"locale varchar," +
"theme_id varchar," +
"final_version_notification boolean default true not null," +
"beta_version_notification boolean default false not null," +
"main_caption_cutoff int default 35 not null," +
"main_track_number boolean default true not null," +
"main_artist boolean default true not null," +
"main_album boolean default false not null," +
"main_genre boolean default false not null," +
"main_year boolean default false not null," +
"main_bit_rate boolean default false not null," +
"main_duration boolean default true not null," +
"main_format boolean default false not null," +
"main_file_size boolean default false not null," +
"playlist_caption_cutoff int default 35 not null," +
"playlist_track_number boolean default false not null," +
"playlist_artist boolean default true not null," +
"playlist_album boolean default true not null," +
"playlist_genre boolean default false not null," +
"playlist_year boolean default true not null," +
"playlist_bit_rate boolean default false not null," +
"playlist_duration boolean default true not null," +
"playlist_format boolean default true not null," +
"playlist_file_size boolean default true not null," +
"primary key (username)," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'user_settings' was created successfully.");
}
if (!tableExists(template, "transcoding")) {
LOG.info("Database table 'transcoding' not found. Creating it.");
template.execute("create table transcoding (" +
"id identity," +
"name varchar not null," +
"source_format varchar not null," +
"target_format varchar not null," +
"step1 varchar not null," +
"step2 varchar," +
"step3 varchar," +
"enabled boolean not null)");
template.execute("insert into transcoding values(null,'wav > mp3', 'wav', 'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'flac > mp3','flac','mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'ogg > mp3' ,'ogg' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'wma > mp3' ,'wma' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'m4a > mp3' ,'m4a' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,false)");
template.execute("insert into transcoding values(null,'aac > mp3' ,'aac' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,false)");
template.execute("insert into transcoding values(null,'ape > mp3' ,'ape' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'mpc > mp3' ,'mpc' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'mv > mp3' ,'mv' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
template.execute("insert into transcoding values(null,'shn > mp3' ,'shn' ,'mp3','ffmpeg -i %s -v 0 -f wav -','lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -',null,true)");
LOG.info("Database table 'transcoding' was created successfully.");
}
if (!tableExists(template, "player_transcoding")) {
LOG.info("Database table 'player_transcoding' not found. Creating it.");
template.execute("create table player_transcoding (" +
"player_id int not null," +
"transcoding_id int not null," +
"primary key (player_id, transcoding_id)," +
"foreign key (player_id) references player(id) on delete cascade," +
"foreign key (transcoding_id) references transcoding(id) on delete cascade)");
LOG.info("Database table 'player_transcoding' was created successfully.");
}
}
}

@ -1,58 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.*;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 2.9.
*
* @author Sindre Mehus
*/
public class Schema29 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema29.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 5",Integer.class) == 0) {
LOG.info("Updating database schema to version 5.");
template.execute("insert into version values (5)");
}
if (!tableExists(template, "user_rating")) {
LOG.info("Database table 'user_rating' not found. Creating it.");
template.execute("create table user_rating (" +
"username varchar not null," +
"path varchar not null," +
"rating double not null," +
"primary key (username, path)," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'user_rating' was created successfully.");
template.execute("insert into user_rating select 'admin', path, rating from music_file_info " +
"where rating is not null and rating > 0");
LOG.info("Migrated data from 'music_file_info' to 'user_rating'.");
}
}
}

@ -1,58 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.*;
import org.libresonic.player.dao.schema.Schema;
import org.libresonic.player.domain.TranscodeScheme;
import org.springframework.jdbc.core.*;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.0.
*
* @author Sindre Mehus
*/
public class Schema30 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema30.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 6", Integer.class) == 0) {
LOG.info("Updating database schema to version 6.");
template.execute("insert into version values (6)");
}
if (!columnExists(template, "last_fm_enabled", "user_settings")) {
LOG.info("Database columns 'user_settings.last_fm_*' not found. Creating them.");
template.execute("alter table user_settings add last_fm_enabled boolean default false not null");
template.execute("alter table user_settings add last_fm_username varchar null");
template.execute("alter table user_settings add last_fm_password varchar null");
LOG.info("Database columns 'user_settings.last_fm_*' were added successfully.");
}
if (!columnExists(template, "transcode_scheme", "user_settings")) {
LOG.info("Database column 'user_settings.transcode_scheme' not found. Creating it.");
template.execute("alter table user_settings add transcode_scheme varchar default '" +
TranscodeScheme.OFF.name() + "' not null");
LOG.info("Database column 'user_settings.transcode_scheme' was added successfully.");
}
}
}

@ -1,55 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.1.
*
* @author Sindre Mehus
*/
public class Schema31 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema31.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 7",Integer.class) == 0) {
LOG.info("Updating database schema to version 7.");
template.execute("insert into version values (7)");
}
if (!columnExists(template, "enabled", "music_file_info")) {
LOG.info("Database column 'music_file_info.enabled' not found. Creating it.");
template.execute("alter table music_file_info add enabled boolean default true not null");
LOG.info("Database column 'music_file_info.enabled' was added successfully.");
}
if (!columnExists(template, "default_active", "transcoding")) {
LOG.info("Database column 'transcoding.default_active' not found. Creating it.");
template.execute("alter table transcoding add default_active boolean default true not null");
LOG.info("Database column 'transcoding.default_active' was added successfully.");
}
}
}

@ -1,96 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.2.
*
* @author Sindre Mehus
*/
public class Schema32 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema32.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 8",Integer.class) == 0) {
LOG.info("Updating database schema to version 8.");
template.execute("insert into version values (8)");
}
if (!columnExists(template, "show_now_playing", "user_settings")) {
LOG.info("Database column 'user_settings.show_now_playing' not found. Creating it.");
template.execute("alter table user_settings add show_now_playing boolean default true not null");
LOG.info("Database column 'user_settings.show_now_playing' was added successfully.");
}
if (!columnExists(template, "selected_music_folder_id", "user_settings")) {
LOG.info("Database column 'user_settings.selected_music_folder_id' not found. Creating it.");
template.execute("alter table user_settings add selected_music_folder_id int default -1 not null");
LOG.info("Database column 'user_settings.selected_music_folder_id' was added successfully.");
}
if (!tableExists(template, "podcast_channel")) {
LOG.info("Database table 'podcast_channel' not found. Creating it.");
template.execute("create table podcast_channel (" +
"id identity," +
"url varchar not null," +
"title varchar," +
"description varchar," +
"status varchar not null," +
"error_message varchar)");
LOG.info("Database table 'podcast_channel' was created successfully.");
}
if (!tableExists(template, "podcast_episode")) {
LOG.info("Database table 'podcast_episode' not found. Creating it.");
template.execute("create table podcast_episode (" +
"id identity," +
"channel_id int not null," +
"url varchar not null," +
"path varchar," +
"title varchar," +
"description varchar," +
"publish_date datetime," +
"duration varchar," +
"bytes_total bigint," +
"bytes_downloaded bigint," +
"status varchar not null," +
"error_message varchar," +
"foreign key (channel_id) references podcast_channel(id) on delete cascade)");
LOG.info("Database table 'podcast_episode' was created successfully.");
}
if (template.queryForObject("select count(*) from role where id = 7",Integer.class) == 0) {
LOG.info("Role 'podcast' not found in database. Creating it.");
template.execute("insert into role values (7, 'podcast')");
template.execute("insert into user_role " +
"select distinct u.username, 7 from user u, user_role ur " +
"where u.username = ur.username and ur.role_id = 1");
LOG.info("Role 'podcast' was created successfully.");
}
}
}

@ -1,50 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.3.
*
* @author Sindre Mehus
*/
public class Schema33 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema33.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 9",Integer.class) == 0) {
LOG.info("Updating database schema to version 9.");
template.execute("insert into version values (9)");
}
if (!columnExists(template, "client_side_playlist", "player")) {
LOG.info("Database column 'player.client_side_playlist' not found. Creating it.");
template.execute("alter table player add client_side_playlist boolean default false not null");
LOG.info("Database column 'player.client_side_playlist' was added successfully.");
}
}
}

@ -1,56 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.4.
*
* @author Sindre Mehus
*/
public class Schema34 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema34.class);
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 10",Integer.class) == 0) {
LOG.info("Updating database schema to version 10.");
template.execute("insert into version values (10)");
}
if (!columnExists(template, "ldap_authenticated", "user")) {
LOG.info("Database column 'user.ldap_authenticated' not found. Creating it.");
template.execute("alter table user add ldap_authenticated boolean default false not null");
LOG.info("Database column 'user.ldap_authenticated' was added successfully.");
}
if (!columnExists(template, "party_mode_enabled", "user_settings")) {
LOG.info("Database column 'user_settings.party_mode_enabled' not found. Creating it.");
template.execute("alter table user_settings add party_mode_enabled boolean default false not null");
LOG.info("Database column 'user_settings.party_mode_enabled' was added successfully.");
}
}
}

@ -1,154 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.apache.commons.io.IOUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.5.
*
* @author Sindre Mehus
*/
public class Schema35 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema35.class);
private static final String[] AVATARS = {
"Formal", "Engineer", "Footballer", "Green-Boy",
"Linux-Zealot", "Mac-Zealot", "Windows-Zealot", "Army-Officer", "Beatnik",
"All-Caps", "Clown", "Commie-Pinko", "Forum-Flirt", "Gamer", "Hopelessly-Addicted",
"Jekyll-And-Hyde", "Joker", "Lurker", "Moderator", "Newbie", "No-Dissent",
"Performer", "Push-My-Button", "Ray-Of-Sunshine", "Red-Hot-Chili-Peppers-1",
"Red-Hot-Chili-Peppers-2", "Red-Hot-Chili-Peppers-3", "Red-Hot-Chili-Peppers-4",
"Ringmaster", "Rumor-Junkie", "Sozzled-Surfer", "Statistician", "Tech-Support",
"The-Guru", "The-Referee", "Troll", "Uptight",
"Fire-Guitar", "Drum", "Headphones", "Mic", "Turntable", "Vinyl",
"Cool", "Laugh", "Study"
};
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 11",Integer.class) == 0) {
LOG.info("Updating database schema to version 11.");
template.execute("insert into version values (11)");
}
if (!columnExists(template, "now_playing_allowed", "user_settings")) {
LOG.info("Database column 'user_settings.now_playing_allowed' not found. Creating it.");
template.execute("alter table user_settings add now_playing_allowed boolean default true not null");
LOG.info("Database column 'user_settings.now_playing_allowed' was added successfully.");
}
if (!columnExists(template, "web_player_default", "user_settings")) {
LOG.info("Database column 'user_settings.web_player_default' not found. Creating it.");
template.execute("alter table user_settings add web_player_default boolean default false not null");
LOG.info("Database column 'user_settings.web_player_default' was added successfully.");
}
if (template.queryForObject("select count(*) from role where id = 8",Integer.class) == 0) {
LOG.info("Role 'stream' not found in database. Creating it.");
template.execute("insert into role values (8, 'stream')");
template.execute("insert into user_role select distinct u.username, 8 from user u");
LOG.info("Role 'stream' was created successfully.");
}
if (!tableExists(template, "system_avatar")) {
LOG.info("Database table 'system_avatar' not found. Creating it.");
template.execute("create table system_avatar (" +
"id identity," +
"name varchar," +
"created_date datetime not null," +
"mime_type varchar not null," +
"width int not null," +
"height int not null," +
"data binary not null)");
LOG.info("Database table 'system_avatar' was created successfully.");
}
for (String avatar : AVATARS) {
createAvatar(template, avatar);
}
if (!tableExists(template, "custom_avatar")) {
LOG.info("Database table 'custom_avatar' not found. Creating it.");
template.execute("create table custom_avatar (" +
"id identity," +
"name varchar," +
"created_date datetime not null," +
"mime_type varchar not null," +
"width int not null," +
"height int not null," +
"data binary not null," +
"username varchar not null," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'custom_avatar' was created successfully.");
}
if (!columnExists(template, "avatar_scheme", "user_settings")) {
LOG.info("Database column 'user_settings.avatar_scheme' not found. Creating it.");
template.execute("alter table user_settings add avatar_scheme varchar default 'NONE' not null");
LOG.info("Database column 'user_settings.avatar_scheme' was added successfully.");
}
if (!columnExists(template, "system_avatar_id", "user_settings")) {
LOG.info("Database column 'user_settings.system_avatar_id' not found. Creating it.");
template.execute("alter table user_settings add system_avatar_id int");
template.execute("alter table user_settings add foreign key (system_avatar_id) references system_avatar(id)");
LOG.info("Database column 'user_settings.system_avatar_id' was added successfully.");
}
if (!columnExists(template, "jukebox", "player")) {
LOG.info("Database column 'player.jukebox' not found. Creating it.");
template.execute("alter table player add jukebox boolean default false not null");
LOG.info("Database column 'player.jukebox' was added successfully.");
}
}
private void createAvatar(JdbcTemplate template, String avatar) {
if (template.queryForObject("select count(*) from system_avatar where name = ?", new Object[]{avatar},Integer.class) == 0) {
InputStream in = null;
try {
in = getClass().getResourceAsStream("/org/libresonic/player/dao/schema/" + avatar + ".png");
byte[] imageData = IOUtils.toByteArray(in);
template.update("insert into system_avatar values (null, ?, ?, ?, ?, ?, ?)",
new Object[]{avatar, new Date(), "image/png", 48, 48, imageData});
LOG.info("Created avatar '" + avatar + "'.");
} catch (IOException x) {
LOG.error("Failed to create avatar '" + avatar + "'.", x);
} finally {
IOUtils.closeQuietly(in);
}
}
}
}

@ -1,51 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implementes the database schema for Libresonic version 3.6.
*
* @author Sindre Mehus
*/
public class Schema36 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema36.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 12",Integer.class) == 0) {
LOG.info("Updating database schema to version 12.");
template.execute("insert into version values (12)");
}
if (!columnExists(template, "technology", "player")) {
LOG.info("Database column 'player.technology' not found. Creating it.");
template.execute("alter table player add technology varchar default 'WEB' not null");
LOG.info("Database column 'player.technology' was added successfully.");
}
}
}

@ -1,80 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 3.7.
*
* @author Sindre Mehus
*/
public class Schema37 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema37.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 13",Integer.class) == 0) {
LOG.info("Updating database schema to version 13.");
template.execute("insert into version values (13)");
}
if (template.queryForObject("select count(*) from role where id = 9",Integer.class) == 0) {
LOG.info("Role 'settings' not found in database. Creating it.");
template.execute("insert into role values (9, 'settings')");
template.execute("insert into user_role select distinct u.username, 9 from user u");
LOG.info("Role 'settings' was created successfully.");
}
if (template.queryForObject("select count(*) from role where id = 10",Integer.class) == 0) {
LOG.info("Role 'jukebox' not found in database. Creating it.");
template.execute("insert into role values (10, 'jukebox')");
template.execute("insert into user_role " +
"select distinct u.username, 10 from user u, user_role ur " +
"where u.username = ur.username and ur.role_id = 1");
LOG.info("Role 'jukebox' was created successfully.");
}
if (!columnExists(template, "changed", "music_folder")) {
LOG.info("Database column 'music_folder.changed' not found. Creating it.");
template.execute("alter table music_folder add changed datetime default 0 not null");
LOG.info("Database column 'music_folder.changed' was added successfully.");
}
if (!columnExists(template, "changed", "internet_radio")) {
LOG.info("Database column 'internet_radio.changed' not found. Creating it.");
template.execute("alter table internet_radio add changed datetime default 0 not null");
LOG.info("Database column 'internet_radio.changed' was added successfully.");
}
if (!columnExists(template, "changed", "user_settings")) {
LOG.info("Database column 'user_settings.changed' not found. Creating it.");
template.execute("alter table user_settings add changed datetime default 0 not null");
LOG.info("Database column 'user_settings.changed' was added successfully.");
}
}
}

@ -1,57 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 3.8.
*
* @author Sindre Mehus
*/
public class Schema38 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema38.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 14",Integer.class) == 0) {
LOG.info("Updating database schema to version 14.");
template.execute("insert into version values (14)");
}
if (!columnExists(template, "client_id", "player")) {
LOG.info("Database column 'player.client_id' not found. Creating it.");
template.execute("alter table player add client_id varchar");
LOG.info("Database column 'player.client_id' was added successfully.");
}
if (!columnExists(template, "show_chat", "user_settings")) {
LOG.info("Database column 'user_settings.show_chat' not found. Creating it.");
template.execute("alter table user_settings add show_chat boolean default true not null");
LOG.info("Database column 'user_settings.show_chat' was added successfully.");
}
}
}

@ -1,49 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.0.
*
* @author Sindre Mehus
*/
public class Schema40 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema40.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 15",Integer.class) == 0) {
LOG.info("Updating database schema to version 15.");
template.execute("insert into version values (15)");
// Reset stream byte count since they have been wrong in earlier releases.
template.execute("update user set bytes_streamed = 0");
LOG.info("Reset stream byte count statistics.");
}
}
}

@ -1,67 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import java.util.Arrays;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.3.
*
* @author Sindre Mehus
*/
public class Schema43 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema43.class);
@Override
public void execute(JdbcTemplate template) {
// version 16 was used for 4.3.beta1
if (template.queryForObject("select count(*) from version where version = 16",Integer.class) == 0) {
LOG.info("Updating database schema to version 16.");
template.execute("insert into version values (16)");
}
if (template.queryForObject("select count(*) from version where version = 17",Integer.class) == 0) {
LOG.info("Updating database schema to version 17.");
template.execute("insert into version values (17)");
for (String format : Arrays.asList("avi", "mpg", "mpeg", "mp4", "m4v", "mkv", "mov", "wmv", "ogv")) {
template.update("delete from transcoding where source_format=? and target_format=?", new Object[] {format, "flv"});
template.execute("insert into transcoding values(null,'" + format + " > flv' ,'" + format + "' ,'flv','ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -',null,null,true,true)");
template.execute("insert into player_transcoding select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = '" + format + " > flv'");
}
LOG.info("Created video transcoding configuration.");
}
if (!columnExists(template, "email", "user")) {
LOG.info("Database column 'user.email' not found. Creating it.");
template.execute("alter table user add email varchar");
LOG.info("Database column 'user.email' was added successfully.");
}
}
}

@ -1,79 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.5.
*
* @author Sindre Mehus
*/
public class Schema45 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema45.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 18",Integer.class) == 0) {
LOG.info("Updating database schema to version 18.");
template.execute("insert into version values (18)");
}
if (template.queryForObject("select count(*) from role where id = 11",Integer.class) == 0) {
LOG.info("Role 'share' not found in database. Creating it.");
template.execute("insert into role values (11, 'share')");
template.execute("insert into user_role " +
"select distinct u.username, 11 from user u, user_role ur " +
"where u.username = ur.username and ur.role_id = 1");
LOG.info("Role 'share' was created successfully.");
}
if (!tableExists(template, "share")) {
LOG.info("Table 'share' not found in database. Creating it.");
template.execute("create cached table share (" +
"id identity," +
"name varchar not null," +
"description varchar," +
"username varchar not null," +
"created datetime not null," +
"expires datetime," +
"last_visited datetime," +
"visit_count int default 0 not null," +
"unique (name)," +
"foreign key (username) references user(username) on delete cascade)");
template.execute("create index idx_share_name on share(name)");
LOG.info("Table 'share' was created successfully.");
LOG.info("Table 'share_file' not found in database. Creating it.");
template.execute("create cached table share_file (" +
"id identity," +
"share_id int not null," +
"path varchar not null," +
"foreign key (share_id) references share(id) on delete cascade)");
LOG.info("Table 'share_file' was created successfully.");
}
}
}

@ -1,89 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.6.
*
* @author Sindre Mehus
*/
public class Schema46 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema46.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 19",Integer.class) == 0) {
LOG.info("Updating database schema to version 19.");
template.execute("insert into version values (19)");
}
if (!tableExists(template, "transcoding2")) {
LOG.info("Database table 'transcoding2' not found. Creating it.");
template.execute("create table transcoding2 (" +
"id identity," +
"name varchar not null," +
"source_formats varchar not null," +
"target_format varchar not null," +
"step1 varchar not null," +
"step2 varchar," +
"step3 varchar)");
template.execute("insert into transcoding2(name, source_formats, target_format, step1) values('mp3 audio'," +
"'ogg oga aac m4a flac wav wma aif aiff ape mpc shn', 'mp3', " +
"'ffmpeg -i %s -ab %bk -v 0 -f mp3 -')");
template.execute("insert into transcoding2(name, source_formats, target_format, step1) values('flv/h264 video', " +
"'avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts', 'flv', " +
"'ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -vcodec libx264 -preset superfast -threads 0 -')");
LOG.info("Database table 'transcoding2' was created successfully.");
}
if (!tableExists(template, "player_transcoding2")) {
LOG.info("Database table 'player_transcoding2' not found. Creating it.");
template.execute("create table player_transcoding2 (" +
"player_id int not null," +
"transcoding_id int not null," +
"primary key (player_id, transcoding_id)," +
"foreign key (player_id) references player(id) on delete cascade," +
"foreign key (transcoding_id) references transcoding2(id) on delete cascade)");
template.execute("insert into player_transcoding2(player_id, transcoding_id) " +
"select distinct p.id, t.id from player p, transcoding2 t");
LOG.info("Database table 'player_transcoding2' was created successfully.");
}
if (!columnExists(template, "default_active", "transcoding2")) {
LOG.info("Database column 'transcoding2.default_active' not found. Creating it.");
template.execute("alter table transcoding2 add default_active boolean default true not null");
LOG.info("Database column 'transcoding2.default_active' was added successfully.");
}
}
}

@ -1,263 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.7.
*
* @author Sindre Mehus
*/
public class Schema47 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema47.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 20",Integer.class) == 0) {
LOG.info("Updating database schema to version 20.");
template.execute("insert into version values (20)");
}
if (!tableExists(template, "media_file")) {
LOG.info("Database table 'media_file' not found. Creating it.");
template.execute("create cached table media_file (" +
"id identity," +
"path varchar not null," +
"folder varchar," +
"type varchar not null," +
"format varchar," +
"title varchar," +
"album varchar," +
"artist varchar," +
"album_artist varchar," +
"disc_number int," +
"track_number int," +
"year int," +
"genre varchar," +
"bit_rate int," +
"variable_bit_rate boolean not null," +
"duration_seconds int," +
"file_size bigint," +
"width int," +
"height int," +
"cover_art_path varchar," +
"parent_path varchar," +
"play_count int not null," +
"last_played datetime," +
"comment varchar," +
"created datetime not null," +
"changed datetime not null," +
"last_scanned datetime not null," +
"children_last_updated datetime not null," +
"present boolean not null," +
"version int not null," +
"unique (path))");
template.execute("create index idx_media_file_path on media_file(path)");
template.execute("create index idx_media_file_parent_path on media_file(parent_path)");
template.execute("create index idx_media_file_type on media_file(type)");
template.execute("create index idx_media_file_album on media_file(album)");
template.execute("create index idx_media_file_artist on media_file(artist)");
template.execute("create index idx_media_file_album_artist on media_file(album_artist)");
template.execute("create index idx_media_file_present on media_file(present)");
template.execute("create index idx_media_file_genre on media_file(genre)");
template.execute("create index idx_media_file_play_count on media_file(play_count)");
template.execute("create index idx_media_file_created on media_file(created)");
template.execute("create index idx_media_file_last_played on media_file(last_played)");
LOG.info("Database table 'media_file' was created successfully.");
}
if (!tableExists(template, "artist")) {
LOG.info("Database table 'artist' not found. Creating it.");
template.execute("create cached table artist (" +
"id identity," +
"name varchar not null," +
"cover_art_path varchar," +
"album_count int default 0 not null," +
"last_scanned datetime not null," +
"present boolean not null," +
"unique (name))");
template.execute("create index idx_artist_name on artist(name)");
template.execute("create index idx_artist_present on artist(present)");
LOG.info("Database table 'artist' was created successfully.");
}
if (!tableExists(template, "album")) {
LOG.info("Database table 'album' not found. Creating it.");
template.execute("create cached table album (" +
"id identity," +
"path varchar not null," +
"name varchar not null," +
"artist varchar not null," +
"song_count int default 0 not null," +
"duration_seconds int default 0 not null," +
"cover_art_path varchar," +
"play_count int default 0 not null," +
"last_played datetime," +
"comment varchar," +
"created datetime not null," +
"last_scanned datetime not null," +
"present boolean not null," +
"unique (artist, name))");
template.execute("create index idx_album_artist_name on album(artist, name)");
template.execute("create index idx_album_play_count on album(play_count)");
template.execute("create index idx_album_last_played on album(last_played)");
template.execute("create index idx_album_present on album(present)");
LOG.info("Database table 'album' was created successfully.");
}
// Added in 4.7.beta3
if (!rowExists(template, "table_name='ALBUM' and column_name='NAME' and ordinal_position=1",
"information_schema.system_indexinfo")) {
template.execute("create index idx_album_name on album(name)");
}
if (!tableExists(template, "starred_media_file")) {
LOG.info("Database table 'starred_media_file' not found. Creating it.");
template.execute("create table starred_media_file (" +
"id identity," +
"media_file_id int not null," +
"username varchar not null," +
"created datetime not null," +
"foreign key (media_file_id) references media_file(id) on delete cascade,"+
"foreign key (username) references user(username) on delete cascade," +
"unique (media_file_id, username))");
template.execute("create index idx_starred_media_file_media_file_id on starred_media_file(media_file_id)");
template.execute("create index idx_starred_media_file_username on starred_media_file(username)");
LOG.info("Database table 'starred_media_file' was created successfully.");
}
if (!tableExists(template, "starred_album")) {
LOG.info("Database table 'starred_album' not found. Creating it.");
template.execute("create table starred_album (" +
"id identity," +
"album_id int not null," +
"username varchar not null," +
"created datetime not null," +
"foreign key (album_id) references album(id) on delete cascade," +
"foreign key (username) references user(username) on delete cascade," +
"unique (album_id, username))");
template.execute("create index idx_starred_album_album_id on starred_album(album_id)");
template.execute("create index idx_starred_album_username on starred_album(username)");
LOG.info("Database table 'starred_album' was created successfully.");
}
if (!tableExists(template, "starred_artist")) {
LOG.info("Database table 'starred_artist' not found. Creating it.");
template.execute("create table starred_artist (" +
"id identity," +
"artist_id int not null," +
"username varchar not null," +
"created datetime not null," +
"foreign key (artist_id) references artist(id) on delete cascade,"+
"foreign key (username) references user(username) on delete cascade," +
"unique (artist_id, username))");
template.execute("create index idx_starred_artist_artist_id on starred_artist(artist_id)");
template.execute("create index idx_starred_artist_username on starred_artist(username)");
LOG.info("Database table 'starred_artist' was created successfully.");
}
if (!tableExists(template, "playlist")) {
LOG.info("Database table 'playlist' not found. Creating it.");
template.execute("create table playlist (" +
"id identity," +
"username varchar not null," +
"is_public boolean not null," +
"name varchar not null," +
"comment varchar," +
"file_count int default 0 not null," +
"duration_seconds int default 0 not null," +
"created datetime not null," +
"changed datetime not null," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'playlist' was created successfully.");
}
if (!columnExists(template, "imported_from", "playlist")) {
LOG.info("Database column 'playlist.imported_from' not found. Creating it.");
template.execute("alter table playlist add imported_from varchar");
LOG.info("Database column 'playlist.imported_from' was added successfully.");
}
if (!tableExists(template, "playlist_file")) {
LOG.info("Database table 'playlist_file' not found. Creating it.");
template.execute("create cached table playlist_file (" +
"id identity," +
"playlist_id int not null," +
"media_file_id int not null," +
"foreign key (playlist_id) references playlist(id) on delete cascade," +
"foreign key (media_file_id) references media_file(id) on delete cascade)");
LOG.info("Database table 'playlist_file' was created successfully.");
}
if (!tableExists(template, "playlist_user")) {
LOG.info("Database table 'playlist_user' not found. Creating it.");
template.execute("create table playlist_user (" +
"id identity," +
"playlist_id int not null," +
"username varchar not null," +
"unique(playlist_id, username)," +
"foreign key (playlist_id) references playlist(id) on delete cascade," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'playlist_user' was created successfully.");
}
if (!tableExists(template, "bookmark")) {
LOG.info("Database table 'bookmark' not found. Creating it.");
template.execute("create table bookmark (" +
"id identity," +
"media_file_id int not null," +
"position_millis bigint not null," +
"username varchar not null," +
"comment varchar," +
"created datetime not null," +
"changed datetime not null," +
"foreign key (media_file_id) references media_file(id) on delete cascade,"+
"foreign key (username) references user(username) on delete cascade," +
"unique (media_file_id, username))");
template.execute("create index idx_bookmark_media_file_id on bookmark(media_file_id)");
template.execute("create index idx_bookmark_username on bookmark(username)");
LOG.info("Database table 'bookmark' was created successfully.");
}
}
}

@ -1,72 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 4.9.
*
* @author Sindre Mehus
*/
public class Schema49 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema49.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 21",Integer.class) == 0) {
LOG.info("Updating database schema to version 21.");
template.execute("insert into version values (21)");
}
if (!columnExists(template, "year", "album")) {
LOG.info("Database column 'album.year' not found. Creating it.");
template.execute("alter table album add year int");
LOG.info("Database column 'album.year' was added successfully.");
}
if (!columnExists(template, "genre", "album")) {
LOG.info("Database column 'album.genre' not found. Creating it.");
template.execute("alter table album add genre varchar");
LOG.info("Database column 'album.genre' was added successfully.");
}
if (!tableExists(template, "genre")) {
LOG.info("Database table 'genre' not found. Creating it.");
template.execute("create table genre (" +
"name varchar not null," +
"song_count int not null)");
LOG.info("Database table 'genre' was created successfully.");
}
if (!columnExists(template, "album_count", "genre")) {
LOG.info("Database column 'genre.album_count' not found. Creating it.");
template.execute("alter table genre add album_count int default 0 not null");
LOG.info("Database column 'genre.album_count' was added successfully.");
}
}
}

@ -1,66 +0,0 @@
/*
This file is part of Libresonic.
Libresonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Libresonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Libresonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 5.0.
*
* @author Sindre Mehus
*/
public class Schema50 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema50.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 22",Integer.class) == 0) {
LOG.info("Updating database schema to version 22.");
template.execute("insert into version values (22)");
template.execute("insert into transcoding2(name, source_formats, target_format, step1, default_active) values('mkv video', " +
"'avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts', 'mkv', " +
"'ffmpeg -ss %o -i %s -c:v libx264 -preset superfast -b:v %bk -c:a libvorbis -f matroska -threads 0 -', 'true')");
template.execute("insert into player_transcoding2(player_id, transcoding_id) " +
"select distinct p.id, t.id from player p, transcoding2 t where t.name='mkv video'");
LOG.info("Added mkv transcoding.");
}
if (!columnExists(template, "song_notification", "user_settings")) {
LOG.info("Database column 'user_settings.song_notification' not found. Creating it.");
template.execute("alter table user_settings add song_notification boolean default true not null");
LOG.info("Database column 'user_settings.song_notification' was added successfully.");
}
// Added in 5.0.beta2
if (template.queryForObject("select count(*) from version where version = 23",Integer.class) == 0) {
LOG.info("Updating database schema to version 23.");
template.execute("insert into version values (23)");
template.execute("update transcoding2 set step1='ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -' where name='mp3 audio'");
}
}
}

@ -1,62 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2014 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 5.1.
*
* @author Sindre Mehus
*/
public class Schema51 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema51.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 23",Integer.class) == 0) {
LOG.info("Updating database schema to version 23.");
template.execute("insert into version values (23)");
}
if (!columnExists(template, "show_artist_info", "user_settings")) {
LOG.info("Database column 'user_settings.show_artist_info' not found. Creating it.");
template.execute("alter table user_settings add show_artist_info boolean default true not null");
LOG.info("Database column 'user_settings.show_artist_info' was added successfully.");
}
if (!columnExists(template, "auto_hide_play_queue", "user_settings")) {
LOG.info("Database column 'user_settings.auto_hide_play_queue' not found. Creating it.");
template.execute("alter table user_settings add auto_hide_play_queue boolean default true not null");
LOG.info("Database column 'user_settings.auto_hide_play_queue' was added successfully.");
}
if (!columnExists(template, "view_as_list", "user_settings")) {
LOG.info("Database column 'user_settings.view_as_list' not found. Creating it.");
template.execute("alter table user_settings add view_as_list boolean default false not null");
LOG.info("Database column 'user_settings.view_as_list' was added successfully.");
}
}
}

@ -1,87 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 5.2.
*
* @author Sindre Mehus
*/
public class Schema52 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema52.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 24",Integer.class) == 0) {
LOG.info("Updating database schema to version 24.");
template.execute("insert into version values (24)");
}
if (!tableExists(template, "music_folder_user")) {
LOG.info("Database table 'music_folder_user' not found. Creating it.");
template.execute("create table music_folder_user (" +
"music_folder_id int not null," +
"username varchar not null, " +
"foreign key (username) references user(username) on delete cascade, " +
"foreign key (music_folder_id) references music_folder(id) on delete cascade)");
template.execute("create index idx_music_folder_user_username on music_folder_user(username)");
template.execute("insert into music_folder_user select music_folder.id, user.username from music_folder, user");
LOG.info("Database table 'music_folder_user' was created successfully.");
}
if (!columnExists(template, "folder_id", "album")) {
LOG.info("Database column 'album.folder_id' not found. Creating it.");
template.execute("alter table album add folder_id int");
LOG.info("Database column 'album.folder_id' was added successfully.");
}
if (!tableExists(template, "play_queue")) {
LOG.info("Database table 'play_queue' not found. Creating it.");
template.execute("create table play_queue (" +
"id identity," +
"username varchar not null," +
"current int," +
"position_millis bigint," +
"changed datetime not null," +
"changed_by varchar not null," +
"foreign key (username) references user(username) on delete cascade)");
LOG.info("Database table 'play_queue' was created successfully.");
}
if (!tableExists(template, "play_queue_file")) {
LOG.info("Database table 'play_queue_file' not found. Creating it.");
template.execute("create cached table play_queue_file (" +
"id identity," +
"play_queue_id int not null," +
"media_file_id int not null," +
"foreign key (play_queue_id) references play_queue(id) on delete cascade," +
"foreign key (media_file_id) references media_file(id) on delete cascade)");
LOG.info("Database table 'play_queue_file' was created successfully.");
}
}
}

@ -1,83 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 (C) Sindre Mehus
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
import org.libresonic.player.domain.AlbumListType;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 5.3.
*
* @author Sindre Mehus
*/
public class Schema53 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema53.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 25",Integer.class) == 0) {
LOG.info("Updating database schema to version 25.");
template.execute("insert into version values (25)");
}
if (!rowExists(template, "table_name='PODCAST_EPISODE' and column_name='URL' and ordinal_position=1",
"information_schema.system_indexinfo")) {
template.execute("create index idx_podcast_episode_url on podcast_episode(url)");
LOG.info("Created index for podcast_episode.url");
}
if (!columnExists(template, "default_album_list", "user_settings")) {
LOG.info("Database column 'user_settings.default_album_list' not found. Creating it.");
template.execute("alter table user_settings add default_album_list varchar default '" +
AlbumListType.RANDOM.getId() + "' not null");
LOG.info("Database column 'user_settings.default_album_list' was added successfully.");
}
if (!columnExists(template, "queue_following_songs", "user_settings")) {
LOG.info("Database column 'user_settings.queue_following_songs' not found. Creating it.");
template.execute("alter table user_settings add queue_following_songs boolean default true not null");
LOG.info("Database column 'user_settings.queue_following_songs' was added successfully.");
}
if (!columnExists(template, "image_url", "podcast_channel")) {
LOG.info("Database column 'podcast_channel.image_url' not found. Creating it.");
template.execute("alter table podcast_channel add image_url varchar");
LOG.info("Database column 'podcast_channel.image_url' was added successfully.");
}
if (!columnExists(template, "show_side_bar", "user_settings")) {
LOG.info("Database column 'user_settings.show_side_bar' not found. Creating it.");
template.execute("alter table user_settings add show_side_bar boolean default true not null");
LOG.info("Database column 'user_settings.show_side_bar' was added successfully.");
}
if (!columnExists(template, "folder_id", "artist")) {
LOG.info("Database column 'artist.folder_id' not found. Creating it.");
template.execute("alter table artist add folder_id int");
LOG.info("Database column 'artist.folder_id' was added successfully.");
}
}
}

@ -1,54 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 6.1.
*
* @author François-Xavier Thomas
*/
public class Schema61 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema61.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 26",Integer.class) == 0) {
LOG.info("Updating database schema to version 26.");
template.execute("insert into version values (26)");
}
if (!columnExists(template, "list_reload_delay", "user_settings")) {
LOG.info("Database column 'user_settings.list_reload_delay' not found. Creating it.");
template.execute("alter table user_settings add list_reload_delay int default 60 not null");
LOG.info("Database column 'user_settings.list_reload_delay' was added successfully.");
}
if (!columnExists(template, "keyboard_shortcuts_enabled", "user_settings")) {
LOG.info("Database column 'user_settings.keyboard_shortcuts_enabled' not found. Creating it.");
template.execute("alter table user_settings add keyboard_shortcuts_enabled boolean default false not null");
LOG.info("Database column 'user_settings.keyboard_shortcuts_enabled' was added successfully.");
}
}
}

@ -1,48 +0,0 @@
/*
* This file is part of Libresonic.
*
* Libresonic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Libresonic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Libresonic. If not, see <http://www.gnu.org/licenses/>.
*/
package org.libresonic.player.dao.schema.hsql;
import org.springframework.jdbc.core.JdbcTemplate;
import org.libresonic.player.Logger;
import org.libresonic.player.dao.schema.Schema;
/**
* Used for creating and evolving the database schema.
* This class implements the database schema for Libresonic version 6.1.
*
* @author Shen-Ta Hsieh
*/
public class Schema62 extends Schema {
private static final Logger LOG = Logger.getLogger(Schema62.class);
@Override
public void execute(JdbcTemplate template) {
if (template.queryForObject("select count(*) from version where version = 27",Integer.class) == 0) {
LOG.info("Updating database schema to version 27.");
template.execute("insert into version values (27)");
}
if (!columnExists(template, "m3u_bom_enabled", "player")) {
LOG.info("Database column 'player.m3u_bom_enabled' not found. Creating it.");
template.execute("alter table player add m3u_bom_enabled boolean default false not null");
LOG.info("Database column 'player.m3u_bom_enabled' was added successfully.");
}
}
}

@ -0,0 +1,103 @@
package org.libresonic.player.service;
import com.google.common.collect.Lists;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.configuration2.FileBasedConfiguration;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.apache.commons.configuration2.MapConfiguration;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.PropertiesConfigurationLayout;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Parameters;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.configuration2.sync.ReadWriteSynchronizer;
import org.apache.commons.io.FileUtils;
import org.libresonic.player.Logger;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
public class ApacheCommonsConfigurationService {
private static final Logger LOG = Logger.getLogger(ApacheCommonsConfigurationService.class);
private final FileBasedConfigurationBuilder<FileBasedConfiguration> builder;
private final Configuration config;
public static final String HEADER_COMMENT = "Libresonic preferences. NOTE: This file is automatically generated."
+ " Do not modify while application is running";
public ApacheCommonsConfigurationService() {
File propertyFile = SettingsService.getPropertyFile();
if(!propertyFile.exists()) {
try {
FileUtils.touch(propertyFile);
} catch (IOException e) {
throw new RuntimeException("Could not create new property file", e);
}
}
Parameters params = new Parameters();
PropertiesConfigurationLayout layout = new PropertiesConfigurationLayout();
// https://issues.apache.org/jira/browse/CONFIGURATION-644
// layout.setHeaderComment(HEADER_COMMENT);
layout.setGlobalSeparator("=");
builder = new FileBasedConfigurationBuilder<FileBasedConfiguration>(PropertiesConfiguration.class).configure(
params.properties()
.setFile(propertyFile)
.setSynchronizer(new ReadWriteSynchronizer())
.setLayout(layout));
try {
config = builder.getConfiguration();
} catch (ConfigurationException e) {
throw new RuntimeException("Could not load property file at " + propertyFile, e);
}
}
public void save() {
try {
builder.save();
} catch (ConfigurationException e) {
LOG.error("Unable to write to property file.", e);
}
}
public Object getProperty(String key) {
return config.getProperty(key);
}
public boolean containsKey(String key) {
return config.containsKey(key);
}
public void clearProperty(String key) {
config.clearProperty(key);
}
public String getString(String key, String defaultValue) {
return config.getString(key, defaultValue);
}
public void setProperty(String key, Object value) {
config.setProperty(key, value);
}
public long getLong(String key, long defaultValue) {
return config.getLong(key, defaultValue);
}
public int getInteger(String key, int defaultValue) {
return config.getInteger(key, defaultValue);
}
public boolean getBoolean(String key, boolean defaultValue) {
return config.getBoolean(key, defaultValue);
}
public ImmutableConfiguration getImmutableSnapshot() {
MapConfiguration mapConfiguration = new MapConfiguration(new HashMap<>());
mapConfiguration.copy(config);
return mapConfiguration;
}
}

@ -19,29 +19,6 @@
*/ */
package org.libresonic.player.service; package org.libresonic.player.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.libresonic.player.Logger; import org.libresonic.player.Logger;
@ -61,6 +38,12 @@ import org.libresonic.player.util.FileUtil;
import org.libresonic.player.util.StringUtil; import org.libresonic.player.util.StringUtil;
import org.libresonic.player.util.Util; import org.libresonic.player.util.Util;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.*;
/** /**
* Provides persistent storage of application settings and preferences. * Provides persistent storage of application settings and preferences.
* *
@ -219,13 +202,13 @@ public class SettingsService {
private static final Logger LOG = Logger.getLogger(SettingsService.class); private static final Logger LOG = Logger.getLogger(SettingsService.class);
private Properties properties = new Properties();
private List<Theme> themes; private List<Theme> themes;
private List<Locale> locales; private List<Locale> locales;
private InternetRadioDao internetRadioDao; private InternetRadioDao internetRadioDao;
private MusicFolderDao musicFolderDao; private MusicFolderDao musicFolderDao;
private UserDao userDao; private UserDao userDao;
private AvatarDao avatarDao; private AvatarDao avatarDao;
private ApacheCommonsConfigurationService configurationService;
private VersionService versionService; private VersionService versionService;
private String[] cachedCoverArtFileTypesArray; private String[] cachedCoverArtFileTypesArray;
@ -234,34 +217,33 @@ public class SettingsService {
private List<MusicFolder> cachedMusicFolders; private List<MusicFolder> cachedMusicFolders;
private final ConcurrentMap<String, List<MusicFolder>> cachedMusicFoldersPerUser = new ConcurrentHashMap<String, List<MusicFolder>>(); private final ConcurrentMap<String, List<MusicFolder>> cachedMusicFoldersPerUser = new ConcurrentHashMap<String, List<MusicFolder>>();
private static File libresonicHome;
private static final long LOCAL_IP_LOOKUP_DELAY_SECONDS = 60;
private String localIpAddress; private String localIpAddress;
public SettingsService() { private void removeObseleteProperties() {
File propertyFile = getPropertyFile();
if (propertyFile.exists()) { OBSOLETE_KEYS.forEach( oKey -> {
FileInputStream in = null; if(configurationService.containsKey(oKey)) {
try { LOG.info("Removing obsolete property [" + oKey + ']');
in = new FileInputStream(propertyFile); configurationService.clearProperty(oKey);
properties.load(in);
} catch (Exception x) {
LOG.error("Unable to read from property file.", x);
} finally {
IOUtils.closeQuietly(in);
} }
});
// Remove obsolete properties.
for (Iterator<Object> iterator = properties.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
if (OBSOLETE_KEYS.contains(key)) {
LOG.info("Removing obsolete property [" + key + ']');
iterator.remove();
}
} }
public static synchronized File getLibresonicHome() {
File home;
String overrideHome = System.getProperty("libresonic.home");
if (overrideHome != null) {
home = new File(overrideHome);
} else {
boolean isWindows = System.getProperty("os.name", "Windows").toLowerCase().startsWith("windows");
home = isWindows ? LIBRESONIC_HOME_WINDOWS : LIBRESONIC_HOME_OTHER;
} }
ensureDirectoryPresent(home);
return home;
} }
@ -284,92 +266,58 @@ public class SettingsService {
save(true); save(true);
} }
public void save(boolean updateChangedDate) { public void save(boolean updateSettingsChanged) {
if (updateChangedDate) { if(updateSettingsChanged) {
setProperty(KEY_SETTINGS_CHANGED, String.valueOf(System.currentTimeMillis())); removeObseleteProperties();
} this.setLong(KEY_SETTINGS_CHANGED, System.currentTimeMillis());
OutputStream out = null;
try {
out = new FileOutputStream(getPropertyFile());
properties.store(out, "Libresonic preferences. NOTE: This file is automatically generated.");
} catch (Exception x) {
LOG.error("Unable to write to property file.", x);
} finally {
IOUtils.closeQuietly(out);
}
}
private File getPropertyFile() {
return new File(getLibresonicHome(), "libresonic.properties");
} }
configurationService.save();
/**
* Returns the Libresonic home directory.
*
* @return The Libresonic home directory, if it exists.
* @throws RuntimeException If directory doesn't exist.
*/
public static synchronized File getLibresonicHome() {
if (libresonicHome != null) {
return libresonicHome;
}
File home;
String overrideHome = System.getProperty("libresonic.home");
if (overrideHome != null) {
home = new File(overrideHome);
} else {
boolean isWindows = System.getProperty("os.name", "Windows").toLowerCase().startsWith("windows");
home = isWindows ? LIBRESONIC_HOME_WINDOWS : LIBRESONIC_HOME_OTHER;
} }
private static void ensureDirectoryPresent(File home) {
// Attempt to create home directory if it doesn't exist. // Attempt to create home directory if it doesn't exist.
if (!home.exists() || !home.isDirectory()) { if (!home.exists() || !home.isDirectory()) {
boolean success = home.mkdirs(); boolean success = home.mkdirs();
if (success) { if (!success) {
libresonicHome = home;
} else {
String message = "The directory " + home + " does not exist. Please create it and make it writable. " + String message = "The directory " + home + " does not exist. Please create it and make it writable. " +
"(You can override the directory location by specifying -Dlibresonic.home=... when " + "(You can override the directory location by specifying -Dlibresonic.home=... when " +
"starting the servlet container.)"; "starting the servlet container.)";
System.err.println("ERROR: " + message); throw new RuntimeException(message);
}
} }
} else {
libresonicHome = home;
} }
return home; public static File getPropertyFile() {
File propertyFile = getLibresonicHome();
return new File(propertyFile, "libresonic.properties");
} }
private int getInt(String key, int defaultValue) { private int getInt(String key, int defaultValue) {
return Integer.valueOf(properties.getProperty(key, String.valueOf(defaultValue))); return configurationService.getInteger(key, defaultValue);
} }
private void setInt(String key, int value) { private void setInt(String key, Integer value) {
setProperty(key, String.valueOf(value)); setProperty(key, value);
} }
private long getLong(String key, long defaultValue) { private long getLong(String key, long defaultValue) {
return Long.valueOf(properties.getProperty(key, String.valueOf(defaultValue))); return configurationService.getLong(key, defaultValue);
} }
private void setLong(String key, long value) { private void setLong(String key, Long value) {
setProperty(key, String.valueOf(value)); setProperty(key, value);
} }
private boolean getBoolean(String key, boolean defaultValue) { private boolean getBoolean(String key, boolean defaultValue) {
return Boolean.valueOf(properties.getProperty(key, String.valueOf(defaultValue))); return configurationService.getBoolean(key, defaultValue);
} }
private void setBoolean(String key, boolean value) { private void setBoolean(String key, Boolean value) {
setProperty(key, String.valueOf(value)); setProperty(key, value);
} }
private String getString(String key, String defaultValue) { private String getString(String key, String defaultValue) {
return properties.getProperty(key, defaultValue); return getProperty(key, defaultValue);
} }
private void setString(String key, String value) { private void setString(String key, String value) {
@ -377,7 +325,11 @@ public class SettingsService {
} }
public String getIndexString() { public String getIndexString() {
return properties.getProperty(KEY_INDEX_STRING, DEFAULT_INDEX_STRING); return getProperty(KEY_INDEX_STRING, DEFAULT_INDEX_STRING);
}
private String getProperty(String key, String defaultValue) {
return configurationService.getString(key, defaultValue);
} }
public void setIndexString(String indexString) { public void setIndexString(String indexString) {
@ -385,7 +337,7 @@ public class SettingsService {
} }
public String getIgnoredArticles() { public String getIgnoredArticles() {
return properties.getProperty(KEY_IGNORED_ARTICLES, DEFAULT_IGNORED_ARTICLES); return getProperty(KEY_IGNORED_ARTICLES, DEFAULT_IGNORED_ARTICLES);
} }
public String[] getIgnoredArticlesAsArray() { public String[] getIgnoredArticlesAsArray() {
@ -397,7 +349,7 @@ public class SettingsService {
} }
public String getShortcuts() { public String getShortcuts() {
return properties.getProperty(KEY_SHORTCUTS, DEFAULT_SHORTCUTS); return getProperty(KEY_SHORTCUTS, DEFAULT_SHORTCUTS);
} }
public String[] getShortcutsAsArray() { public String[] getShortcutsAsArray() {
@ -409,7 +361,7 @@ public class SettingsService {
} }
public String getPlaylistFolder() { public String getPlaylistFolder() {
return properties.getProperty(KEY_PLAYLIST_FOLDER, DEFAULT_PLAYLIST_FOLDER); return getProperty(KEY_PLAYLIST_FOLDER, DEFAULT_PLAYLIST_FOLDER);
} }
public void setPlaylistFolder(String playlistFolder) { public void setPlaylistFolder(String playlistFolder) {
@ -417,7 +369,7 @@ public class SettingsService {
} }
public String getMusicFileTypes() { public String getMusicFileTypes() {
return properties.getProperty(KEY_MUSIC_FILE_TYPES, DEFAULT_MUSIC_FILE_TYPES); return getProperty(KEY_MUSIC_FILE_TYPES, DEFAULT_MUSIC_FILE_TYPES);
} }
public synchronized void setMusicFileTypes(String fileTypes) { public synchronized void setMusicFileTypes(String fileTypes) {
@ -433,7 +385,7 @@ public class SettingsService {
} }
public String getVideoFileTypes() { public String getVideoFileTypes() {
return properties.getProperty(KEY_VIDEO_FILE_TYPES, DEFAULT_VIDEO_FILE_TYPES); return getProperty(KEY_VIDEO_FILE_TYPES, DEFAULT_VIDEO_FILE_TYPES);
} }
public synchronized void setVideoFileTypes(String fileTypes) { public synchronized void setVideoFileTypes(String fileTypes) {
@ -449,7 +401,7 @@ public class SettingsService {
} }
public String getCoverArtFileTypes() { public String getCoverArtFileTypes() {
return properties.getProperty(KEY_COVER_ART_FILE_TYPES, DEFAULT_COVER_ART_FILE_TYPES); return getProperty(KEY_COVER_ART_FILE_TYPES, DEFAULT_COVER_ART_FILE_TYPES);
} }
public synchronized void setCoverArtFileTypes(String fileTypes) { public synchronized void setCoverArtFileTypes(String fileTypes) {
@ -469,7 +421,7 @@ public class SettingsService {
} }
public String getWelcomeTitle() { public String getWelcomeTitle() {
return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_TITLE, DEFAULT_WELCOME_TITLE)); return StringUtils.trimToNull(getProperty(KEY_WELCOME_TITLE, DEFAULT_WELCOME_TITLE));
} }
public void setWelcomeTitle(String title) { public void setWelcomeTitle(String title) {
@ -477,7 +429,7 @@ public class SettingsService {
} }
public String getWelcomeSubtitle() { public String getWelcomeSubtitle() {
return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_SUBTITLE, DEFAULT_WELCOME_SUBTITLE)); return StringUtils.trimToNull(getProperty(KEY_WELCOME_SUBTITLE, DEFAULT_WELCOME_SUBTITLE));
} }
public void setWelcomeSubtitle(String subtitle) { public void setWelcomeSubtitle(String subtitle) {
@ -485,7 +437,7 @@ public class SettingsService {
} }
public String getWelcomeMessage() { public String getWelcomeMessage() {
return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_MESSAGE, DEFAULT_WELCOME_MESSAGE)); return StringUtils.trimToNull(getProperty(KEY_WELCOME_MESSAGE, DEFAULT_WELCOME_MESSAGE));
} }
public void setWelcomeMessage(String message) { public void setWelcomeMessage(String message) {
@ -493,7 +445,7 @@ public class SettingsService {
} }
public String getLoginMessage() { public String getLoginMessage() {
return StringUtils.trimToNull(properties.getProperty(KEY_LOGIN_MESSAGE, DEFAULT_LOGIN_MESSAGE)); return StringUtils.trimToNull(getProperty(KEY_LOGIN_MESSAGE, DEFAULT_LOGIN_MESSAGE));
} }
public void setLoginMessage(String message) { public void setLoginMessage(String message) {
@ -586,7 +538,7 @@ public class SettingsService {
* Returns the Podcast download folder. * Returns the Podcast download folder.
*/ */
public String getPodcastFolder() { public String getPodcastFolder() {
return properties.getProperty(KEY_PODCAST_FOLDER, DEFAULT_PODCAST_FOLDER); return getProperty(KEY_PODCAST_FOLDER, DEFAULT_PODCAST_FOLDER);
} }
/** /**
@ -600,7 +552,7 @@ public class SettingsService {
* @return The download bitrate limit in Kbit/s. Zero if unlimited. * @return The download bitrate limit in Kbit/s. Zero if unlimited.
*/ */
public long getDownloadBitrateLimit() { public long getDownloadBitrateLimit() {
return Long.parseLong(properties.getProperty(KEY_DOWNLOAD_BITRATE_LIMIT, "" + DEFAULT_DOWNLOAD_BITRATE_LIMIT)); return Long.parseLong(getProperty(KEY_DOWNLOAD_BITRATE_LIMIT, "" + DEFAULT_DOWNLOAD_BITRATE_LIMIT));
} }
/** /**
@ -625,7 +577,7 @@ public class SettingsService {
} }
public String getDownsamplingCommand() { public String getDownsamplingCommand() {
return properties.getProperty(KEY_DOWNSAMPLING_COMMAND, DEFAULT_DOWNSAMPLING_COMMAND); return getProperty(KEY_DOWNSAMPLING_COMMAND, DEFAULT_DOWNSAMPLING_COMMAND);
} }
public void setDownsamplingCommand(String command) { public void setDownsamplingCommand(String command) {
@ -633,7 +585,7 @@ public class SettingsService {
} }
public String getHlsCommand() { public String getHlsCommand() {
return properties.getProperty(KEY_HLS_COMMAND, DEFAULT_HLS_COMMAND); return getProperty(KEY_HLS_COMMAND, DEFAULT_HLS_COMMAND);
} }
public void setHlsCommand(String command) { public void setHlsCommand(String command) {
@ -641,10 +593,10 @@ public class SettingsService {
} }
public String getJukeboxCommand() { public String getJukeboxCommand() {
return properties.getProperty(KEY_JUKEBOX_COMMAND, DEFAULT_JUKEBOX_COMMAND); return getProperty(KEY_JUKEBOX_COMMAND, DEFAULT_JUKEBOX_COMMAND);
} }
public String getVideoImageCommand() { public String getVideoImageCommand() {
return properties.getProperty(KEY_VIDEO_IMAGE_COMMAND, DEFAULT_VIDEO_IMAGE_COMMAND); return getProperty(KEY_VIDEO_IMAGE_COMMAND, DEFAULT_VIDEO_IMAGE_COMMAND);
} }
public boolean isRewriteUrlEnabled() { public boolean isRewriteUrlEnabled() {
@ -664,31 +616,31 @@ public class SettingsService {
} }
public String getLdapUrl() { public String getLdapUrl() {
return properties.getProperty(KEY_LDAP_URL, DEFAULT_LDAP_URL); return getProperty(KEY_LDAP_URL, DEFAULT_LDAP_URL);
} }
public void setLdapUrl(String ldapUrl) { public void setLdapUrl(String ldapUrl) {
properties.setProperty(KEY_LDAP_URL, ldapUrl); setProperty(KEY_LDAP_URL, ldapUrl);
} }
public String getLdapSearchFilter() { public String getLdapSearchFilter() {
return properties.getProperty(KEY_LDAP_SEARCH_FILTER, DEFAULT_LDAP_SEARCH_FILTER); return getProperty(KEY_LDAP_SEARCH_FILTER, DEFAULT_LDAP_SEARCH_FILTER);
} }
public void setLdapSearchFilter(String ldapSearchFilter) { public void setLdapSearchFilter(String ldapSearchFilter) {
properties.setProperty(KEY_LDAP_SEARCH_FILTER, ldapSearchFilter); setProperty(KEY_LDAP_SEARCH_FILTER, ldapSearchFilter);
} }
public String getLdapManagerDn() { public String getLdapManagerDn() {
return properties.getProperty(KEY_LDAP_MANAGER_DN, DEFAULT_LDAP_MANAGER_DN); return getProperty(KEY_LDAP_MANAGER_DN, DEFAULT_LDAP_MANAGER_DN);
} }
public void setLdapManagerDn(String ldapManagerDn) { public void setLdapManagerDn(String ldapManagerDn) {
properties.setProperty(KEY_LDAP_MANAGER_DN, ldapManagerDn); setProperty(KEY_LDAP_MANAGER_DN, ldapManagerDn);
} }
public String getLdapManagerPassword() { public String getLdapManagerPassword() {
String s = properties.getProperty(KEY_LDAP_MANAGER_PASSWORD, DEFAULT_LDAP_MANAGER_PASSWORD); String s = getProperty(KEY_LDAP_MANAGER_PASSWORD, DEFAULT_LDAP_MANAGER_PASSWORD);
try { try {
return StringUtil.utf8HexDecode(s); return StringUtil.utf8HexDecode(s);
} catch (Exception x) { } catch (Exception x) {
@ -703,7 +655,7 @@ public class SettingsService {
} catch (Exception x) { } catch (Exception x) {
LOG.warn("Failed to encode LDAP manager password.", x); LOG.warn("Failed to encode LDAP manager password.", x);
} }
properties.setProperty(KEY_LDAP_MANAGER_PASSWORD, ldapManagerPassword); setProperty(KEY_LDAP_MANAGER_PASSWORD, ldapManagerPassword);
} }
public boolean isLdapAutoShadowing() { public boolean isLdapAutoShadowing() {
@ -762,43 +714,43 @@ public class SettingsService {
} }
public String getUrlRedirectFrom() { public String getUrlRedirectFrom() {
return properties.getProperty(KEY_URL_REDIRECT_FROM, DEFAULT_URL_REDIRECT_FROM); return getProperty(KEY_URL_REDIRECT_FROM, DEFAULT_URL_REDIRECT_FROM);
} }
public void setUrlRedirectFrom(String urlRedirectFrom) { public void setUrlRedirectFrom(String urlRedirectFrom) {
properties.setProperty(KEY_URL_REDIRECT_FROM, urlRedirectFrom); setProperty(KEY_URL_REDIRECT_FROM, urlRedirectFrom);
} }
public UrlRedirectType getUrlRedirectType() { public UrlRedirectType getUrlRedirectType() {
return UrlRedirectType.valueOf(properties.getProperty(KEY_URL_REDIRECT_TYPE, DEFAULT_URL_REDIRECT_TYPE.name())); return UrlRedirectType.valueOf(getProperty(KEY_URL_REDIRECT_TYPE, DEFAULT_URL_REDIRECT_TYPE.name()));
} }
public void setUrlRedirectType(UrlRedirectType urlRedirectType) { public void setUrlRedirectType(UrlRedirectType urlRedirectType) {
properties.setProperty(KEY_URL_REDIRECT_TYPE, urlRedirectType.name()); setProperty(KEY_URL_REDIRECT_TYPE, urlRedirectType.name());
} }
public String getUrlRedirectContextPath() { public String getUrlRedirectContextPath() {
return properties.getProperty(KEY_URL_REDIRECT_CONTEXT_PATH, DEFAULT_URL_REDIRECT_CONTEXT_PATH); return getProperty(KEY_URL_REDIRECT_CONTEXT_PATH, DEFAULT_URL_REDIRECT_CONTEXT_PATH);
} }
public void setUrlRedirectContextPath(String contextPath) { public void setUrlRedirectContextPath(String contextPath) {
properties.setProperty(KEY_URL_REDIRECT_CONTEXT_PATH, contextPath); setProperty(KEY_URL_REDIRECT_CONTEXT_PATH, contextPath);
} }
public String getUrlRedirectCustomUrl() { public String getUrlRedirectCustomUrl() {
return StringUtils.trimToNull(properties.getProperty(KEY_URL_REDIRECT_CUSTOM_URL, DEFAULT_URL_REDIRECT_CUSTOM_URL)); return StringUtils.trimToNull(getProperty(KEY_URL_REDIRECT_CUSTOM_URL, DEFAULT_URL_REDIRECT_CUSTOM_URL));
} }
public void setUrlRedirectCustomUrl(String customUrl) { public void setUrlRedirectCustomUrl(String customUrl) {
properties.setProperty(KEY_URL_REDIRECT_CUSTOM_URL, customUrl); setProperty(KEY_URL_REDIRECT_CUSTOM_URL, customUrl);
} }
public String getServerId() { public String getServerId() {
return properties.getProperty(KEY_SERVER_ID, DEFAULT_SERVER_ID); return getProperty(KEY_SERVER_ID, DEFAULT_SERVER_ID);
} }
public void setServerId(String serverId) { public void setServerId(String serverId) {
properties.setProperty(KEY_SERVER_ID, serverId); setProperty(KEY_SERVER_ID, serverId);
} }
public long getSettingsChanged() { public long getSettingsChanged() {
@ -806,13 +758,13 @@ public class SettingsService {
} }
public Date getLastScanned() { public Date getLastScanned() {
String lastScanned = properties.getProperty(KEY_LAST_SCANNED); String lastScanned = getProperty(KEY_LAST_SCANNED, null);
return lastScanned == null ? null : new Date(Long.parseLong(lastScanned)); return lastScanned == null ? null : new Date(Long.parseLong(lastScanned));
} }
public void setLastScanned(Date date) { public void setLastScanned(Date date) {
if (date == null) { if (date == null) {
properties.remove(KEY_LAST_SCANNED); setProperty(KEY_LAST_SCANNED, null);
} else { } else {
setLong(KEY_LAST_SCANNED, date.getTime()); setLong(KEY_LAST_SCANNED, date.getTime());
} }
@ -848,9 +800,9 @@ public class SettingsService {
* @return The locale. * @return The locale.
*/ */
public Locale getLocale() { public Locale getLocale() {
String language = properties.getProperty(KEY_LOCALE_LANGUAGE, DEFAULT_LOCALE_LANGUAGE); String language = getProperty(KEY_LOCALE_LANGUAGE, DEFAULT_LOCALE_LANGUAGE);
String country = properties.getProperty(KEY_LOCALE_COUNTRY, DEFAULT_LOCALE_COUNTRY); String country = getProperty(KEY_LOCALE_COUNTRY, DEFAULT_LOCALE_COUNTRY);
String variant = properties.getProperty(KEY_LOCALE_VARIANT, DEFAULT_LOCALE_VARIANT); String variant = getProperty(KEY_LOCALE_VARIANT, DEFAULT_LOCALE_VARIANT);
return new Locale(language, country, variant); return new Locale(language, country, variant);
} }
@ -872,7 +824,7 @@ public class SettingsService {
* @return The theme ID. * @return The theme ID.
*/ */
public String getThemeId() { public String getThemeId() {
return properties.getProperty(KEY_THEME_ID, DEFAULT_THEME_ID); return getProperty(KEY_THEME_ID, DEFAULT_THEME_ID);
} }
/** /**
@ -1309,11 +1261,11 @@ public class SettingsService {
getPort()); getPort());
} }
private void setProperty(String key, String value) { private void setProperty(String key, Object value) {
if (value == null) { if (value == null) {
properties.remove(key); configurationService.clearProperty(key);
} else { } else {
properties.setProperty(key, value); configurationService.setProperty(key, value);
} }
} }
@ -1356,7 +1308,7 @@ public class SettingsService {
} }
public String getSmtpServer() { public String getSmtpServer() {
return properties.getProperty(KEY_SMTP_SERVER, DEFAULT_SMTP_SERVER); return getProperty(KEY_SMTP_SERVER, DEFAULT_SMTP_SERVER);
} }
public void setSmtpServer(String smtpServer) { public void setSmtpServer(String smtpServer) {
@ -1372,7 +1324,7 @@ public class SettingsService {
} }
public String getSmtpEncryption() { public String getSmtpEncryption() {
return properties.getProperty(KEY_SMTP_ENCRYPTION, DEFAULT_SMTP_ENCRYPTION); return getProperty(KEY_SMTP_ENCRYPTION, DEFAULT_SMTP_ENCRYPTION);
} }
public void setSmtpEncryption(String encryptionMethod) { public void setSmtpEncryption(String encryptionMethod) {
@ -1380,7 +1332,7 @@ public class SettingsService {
} }
public String getSmtpUser() { public String getSmtpUser() {
return properties.getProperty(KEY_SMTP_USER, DEFAULT_SMTP_USER); return getProperty(KEY_SMTP_USER, DEFAULT_SMTP_USER);
} }
public void setSmtpUser(String smtpUser) { public void setSmtpUser(String smtpUser) {
@ -1388,7 +1340,7 @@ public class SettingsService {
} }
public String getSmtpPassword() { public String getSmtpPassword() {
String s = properties.getProperty(KEY_SMTP_PASSWORD, DEFAULT_SMTP_PASSWORD); String s = getProperty(KEY_SMTP_PASSWORD, DEFAULT_SMTP_PASSWORD);
try { try {
return StringUtil.utf8HexDecode(s); return StringUtil.utf8HexDecode(s);
} catch (Exception x) { } catch (Exception x) {
@ -1402,14 +1354,18 @@ public class SettingsService {
} catch (Exception x) { } catch (Exception x) {
LOG.warn("Failed to encode Smtp password.", x); LOG.warn("Failed to encode Smtp password.", x);
} }
properties.setProperty(KEY_SMTP_PASSWORD, smtpPassword); setProperty(KEY_SMTP_PASSWORD, smtpPassword);
} }
public String getSmtpFrom() { public String getSmtpFrom() {
return properties.getProperty(KEY_SMTP_FROM, DEFAULT_SMTP_FROM); return getProperty(KEY_SMTP_FROM, DEFAULT_SMTP_FROM);
} }
public void setSmtpFrom(String smtpFrom) { public void setSmtpFrom(String smtpFrom) {
setString(KEY_SMTP_FROM, smtpFrom); setString(KEY_SMTP_FROM, smtpFrom);
} }
public void setConfigurationService(ApacheCommonsConfigurationService configurationService) {
this.configurationService = configurationService;
}
} }

@ -0,0 +1,22 @@
package org.libresonic.player.spring;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.libresonic.player.service.ApacheCommonsConfigurationService;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.core.env.PropertySource;
import org.springframework.web.context.ConfigurableWebApplicationContext;
public class AdditionalPropertySourceConfigurer implements
ApplicationContextInitializer<ConfigurableWebApplicationContext> {
public void initialize(ConfigurableWebApplicationContext ctx) {
ApacheCommonsConfigurationService configurationService = new ApacheCommonsConfigurationService();
ImmutableConfiguration snapshot = configurationService.getImmutableSnapshot();
PropertySource ps = new DatasourceProfileActivatorPropertySource(new CommonsConfigurationPropertySource(
"libresonic-pre-init-configs",
snapshot));
ctx.getEnvironment().getPropertySources().addLast(ps);
}
}

@ -0,0 +1,19 @@
package org.libresonic.player.spring;
import org.apache.commons.configuration2.ImmutableConfiguration;
import org.springframework.core.env.PropertySource;
public class CommonsConfigurationPropertySource extends PropertySource {
private final ImmutableConfiguration configuration;
public CommonsConfigurationPropertySource(String name, ImmutableConfiguration configuration) {
super(name);
this.configuration = configuration;
}
@Override
public Object getProperty(String s) {
return configuration.getProperty(s);
}
}

@ -0,0 +1,7 @@
package org.libresonic.player.spring;
public enum DataSourceConfigType {
JNDI,
EMBED,
LEGACY
}

@ -0,0 +1,40 @@
package org.libresonic.player.spring;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.PropertySource;
public class DatasourceProfileActivatorPropertySource extends PropertySource {
public static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";
public static final String DATASOURCE_CONFIG_TYPE = "database.config.type";
final PropertySource parent;
public DatasourceProfileActivatorPropertySource(PropertySource parent) {
super(parent.getName());
this.parent = parent;
}
@Override
public Object getProperty(String name) {
if(StringUtils.equalsIgnoreCase(name, SPRING_PROFILES_ACTIVE)) {
String appendTo = "";
Object existing = parent.getProperty(SPRING_PROFILES_ACTIVE);
if(existing != null && existing instanceof String) {
appendTo += (String) existing;
}
DataSourceConfigType dataSourceConfigType;
Object rawType = parent.getProperty(DATASOURCE_CONFIG_TYPE);
if(rawType != null && rawType instanceof String) {
dataSourceConfigType = DataSourceConfigType.valueOf(StringUtils.upperCase((String) rawType));
} else {
dataSourceConfigType = DataSourceConfigType.LEGACY;
}
if(StringUtils.isNotBlank(appendTo)) {
appendTo += ",";
}
appendTo += StringUtils.lowerCase(dataSourceConfigType.name());
return appendTo;
} else {
return parent.getProperty(name);
}
}
}

@ -0,0 +1,77 @@
package org.libresonic.player.spring;
import liquibase.changelog.ChangeSet;
import liquibase.changelog.DatabaseChangeLog;
import liquibase.database.Database;
import liquibase.exception.PreconditionErrorException;
import liquibase.exception.PreconditionFailedException;
import liquibase.exception.ValidationErrors;
import liquibase.exception.Warnings;
import liquibase.precondition.Precondition;
import liquibase.serializer.AbstractLiquibaseSerializable;
public class DbmsVersionPrecondition extends AbstractLiquibaseSerializable implements Precondition {
private Integer major;
private Integer minor;
@Override
public String getName() {
return "dbmsVersion";
}
@Override
public Warnings warn(Database database) {
return new Warnings();
}
@Override
public ValidationErrors validate(Database database) {
return new ValidationErrors();
}
@Override
public void check(
Database database, DatabaseChangeLog changeLog, ChangeSet changeSet
) throws PreconditionFailedException, PreconditionErrorException {
try {
int dbMajor = database.getDatabaseMajorVersion();
int dbMinor = database.getDatabaseMinorVersion();
if(major != null && !major.equals(dbMajor)) {
throw new PreconditionFailedException("DBMS Major Version Precondition failed: expected " + major + ", got " + dbMajor, changeLog, this);
}
if(minor != null && !minor.equals(dbMinor)) {
throw new PreconditionFailedException("DBMS Minor Version Precondition failed: expected " + minor + ", got " + dbMinor, changeLog, this);
}
} catch (PreconditionFailedException e) {
throw e;
} catch (Exception e) {
throw new PreconditionErrorException(e, changeLog, this);
}
}
@Override
public String getSerializedObjectName() {
return getName();
}
@Override
public String getSerializedObjectNamespace() {
return GENERIC_CHANGELOG_EXTENSION_NAMESPACE;
}
public Integer getMajor() {
return major;
}
public void setMajor(Integer major) {
this.major = major;
}
public Integer getMinor() {
return minor;
}
public void setMinor(Integer minor) {
this.minor = minor;
}
}

@ -0,0 +1,19 @@
package org.libresonic.player.spring;
import liquibase.exception.DatabaseException;
public class HsqlDatabase extends liquibase.database.core.HsqlDatabase {
@Override
public boolean supportsSchemas() {
try {
if(getDatabaseMajorVersion() < 2) {
return false;
} else {
return super.supportsSchemas();
}
} catch (DatabaseException e) {
return false;
}
}
}

@ -0,0 +1,75 @@
package org.libresonic.player.spring;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.OfflineConnection;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
import liquibase.resource.ResourceAccessor;
import liquibase.util.StringUtils;
import org.libresonic.player.Logger;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;
public class SpringLiquibase extends liquibase.integration.spring.SpringLiquibase {
private static final Logger logger = Logger.getLogger(SpringLiquibase.class);
@Override
public void afterPropertiesSet() throws LiquibaseException {
try {
super.afterPropertiesSet();
} catch(Exception e) {
logger.error("===============================================");
logger.error("An exception occurred during database migration");
logger.error("A rollback file has been generated at " + rollbackFile);
logger.error("Execute it within your database to rollback any changes");
logger.error("The exception is as follows\n", e);
logger.error("===============================================");
throw(e);
}
}
@Override
protected Database createDatabase(
Connection c, ResourceAccessor resourceAccessor
) throws DatabaseException {
DatabaseConnection liquibaseConnection;
if (c == null) {
log.warning("Null connection returned by liquibase datasource. Using offline unknown database");
liquibaseConnection = new OfflineConnection("offline:unknown", resourceAccessor);
} else {
liquibaseConnection = new JdbcConnection(c);
}
DatabaseFactory factory = DatabaseFactory.getInstance();
overrideHsqlDbImplementation(factory);
Database database = factory.findCorrectDatabaseImplementation(liquibaseConnection);
if (StringUtils.trimToNull(this.defaultSchema) != null) {
database.setDefaultSchemaName(this.defaultSchema);
}
liquibase.precondition.PreconditionFactory.getInstance().register(DbmsVersionPrecondition.class);
return database;
}
private void overrideHsqlDbImplementation(DatabaseFactory factory) {
List<Database> implementedDatabases = factory.getImplementedDatabases();
factory.clearRegistry();
removeCurrentHsqlDb(implementedDatabases);
implementedDatabases.forEach(factory::register);
factory.register(new HsqlDatabase());
}
private void removeCurrentHsqlDb(List<Database> implementedDatabases) {
Iterator<Database> iterator = implementedDatabases.iterator();
while(iterator.hasNext()) {
Database db = iterator.next();
if(db instanceof liquibase.database.core.HsqlDatabase) {
iterator.remove();
}
}
}
}

@ -0,0 +1,11 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<property name="binary_type" dbms="hsqldb" value="binary" />
<property name="binary_type" value="blob" />
<property name="curr_date_expr" value="current_timestamp" />
<property name="varchar_type" dbms="mysql" value="varchar(${varcharLimit})" />
<property name="varchar_type" value="varchar" />
<include file="legacy/legacy-changelog.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

@ -0,0 +1,31 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<include file="schema25.xml" relativeToChangelogFile="true"/>
<include file="schema26.xml" relativeToChangelogFile="true"/>
<include file="schema27.xml" relativeToChangelogFile="true"/>
<include file="schema28.xml" relativeToChangelogFile="true"/>
<include file="schema29.xml" relativeToChangelogFile="true"/>
<include file="schema30.xml" relativeToChangelogFile="true"/>
<include file="schema31.xml" relativeToChangelogFile="true"/>
<include file="schema32.xml" relativeToChangelogFile="true"/>
<include file="schema33.xml" relativeToChangelogFile="true"/>
<include file="schema34.xml" relativeToChangelogFile="true"/>
<include file="schema35.xml" relativeToChangelogFile="true"/>
<include file="schema36.xml" relativeToChangelogFile="true"/>
<include file="schema37.xml" relativeToChangelogFile="true"/>
<include file="schema38.xml" relativeToChangelogFile="true"/>
<include file="schema40.xml" relativeToChangelogFile="true"/>
<include file="schema43.xml" relativeToChangelogFile="true"/>
<include file="schema45.xml" relativeToChangelogFile="true"/>
<include file="schema46.xml" relativeToChangelogFile="true"/>
<include file="schema47.xml" relativeToChangelogFile="true"/>
<include file="schema49.xml" relativeToChangelogFile="true"/>
<include file="schema50.xml" relativeToChangelogFile="true"/>
<include file="schema51.xml" relativeToChangelogFile="true"/>
<include file="schema52.xml" relativeToChangelogFile="true"/>
<include file="schema53.xml" relativeToChangelogFile="true"/>
<include file="schema61.xml" relativeToChangelogFile="true"/>
<include file="schema62.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

@ -0,0 +1,131 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema25_001" author="muff1nman" dbms="hsqldb">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="version" />
</not>
</preConditions>
<sql>set property &quot;hsqldb.cache_file_scale&quot; 8</sql>
<rollback/>
</changeSet>
<changeSet id="schema25_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="version" />
</not>
</preConditions>
<createTable tableName="version">
<column name="version" type="int">
<constraints nullable="false" />
</column>
</createTable>
<insert tableName="version" >
<column name="version" valueNumeric="1" />
</insert>
<rollback>
<dropTable tableName="version" />
</rollback>
</changeSet>
<changeSet id="schema25_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="role" />
</not>
</preConditions>
<createTable tableName="role">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
</createTable>
<insert tableName="role">
<column name="id" valueNumeric="1" />
<column name="name" value="admin"/>
</insert>
<insert tableName="role">
<column name="id" valueNumeric="2" />
<column name="name" value="download"/>
</insert>
<insert tableName="role">
<column name="id" valueNumeric="3" />
<column name="name" value="upload"/>
</insert>
<insert tableName="role">
<column name="id" valueNumeric="4" />
<column name="name" value="playlist"/>
</insert>
<insert tableName="role">
<column name="id" valueNumeric="5" />
<column name="name" value="coverart"/>
</insert>
<rollback>
<dropTable tableName="role" />
</rollback>
</changeSet>
<changeSet id="schema25_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="user" />
</not>
</preConditions>
<createTable tableName="user">
<column name="username" type="${varchar_type}">
<constraints primaryKey="true" />
</column>
<column name="password" type="${varchar_type}" >
<constraints nullable="false" />
</column>
</createTable>
<insert tableName="user">
<column name="username" value="admin"/>
<column name="password" value="admin"/>
</insert>
<rollback>
<dropTable tableName="user" />
</rollback>
</changeSet>
<changeSet id="schema25_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="user_role" />
</not>
</preConditions>
<createTable tableName="user_role">
<column name="username" type="${varchar_type}">
<constraints nullable="false" foreignKeyName="ur_u_fk" referencedTableName="user" referencedColumnNames="username"/>
</column>
<column name="role_id" type="int" >
<constraints nullable="false" foreignKeyName="ur_r_fk" referencedTableName="role" referencedColumnNames="id"/>
</column>
</createTable>
<addPrimaryKey tableName="user_role" columnNames="username,role_id" />
<insert tableName="user_role">
<column name="username" value="admin"/>
<column name="role_id" valueNumeric="1" />
</insert>
<insert tableName="user_role">
<column name="username" value="admin"/>
<column name="role_id" valueNumeric="2" />
</insert>
<insert tableName="user_role">
<column name="username" value="admin"/>
<column name="role_id" valueNumeric="3" />
</insert>
<insert tableName="user_role">
<column name="username" value="admin"/>
<column name="role_id" valueNumeric="4" />
</insert>
<insert tableName="user_role">
<column name="username" value="admin"/>
<column name="role_id" valueNumeric="5" />
</insert>
<rollback>
<dropTable tableName="user_role" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,163 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema26_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 2</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="2" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 2</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema26_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="music_folder" />
</not>
</preConditions>
<createTable tableName="music_folder">
<column name="id" type="int" autoIncrement="true" >
<constraints primaryKey="true" nullable="false" />
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="enabled" type="boolean" >
<constraints nullable="false" />
</column>
</createTable>
<insert tableName="music_folder">
<column name="path" value="${defaultMusicFolder}"/>
<column name="name" value="Music"/>
<column name="enabled" valueBoolean="true" />
</insert>
<rollback>
<dropTable tableName="music_folder" />
</rollback>
</changeSet>
<changeSet id="schema26_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="music_file_info" />
</not>
</preConditions>
<createTable tableName="music_file_info">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="rating" type="int" />
<column name="comment" type="${varchar_type}" />
<column name="play_count" type="int" />
<column name="last_played" type="datetime" />
</createTable>
<createIndex tableName="music_file_info" indexName="idx_music_file_info_path">
<column name="path"/>
</createIndex>
<rollback>
<dropTable tableName="music_file_info" />
</rollback>
</changeSet>
<changeSet id="schema26_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="internet_radio" />
</not>
</preConditions>
<createTable tableName="internet_radio">
<column name="id" type="int" autoIncrement="true" >
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="stream_url" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="homepage_url" type="${varchar_type}" />
<column name="enabled" type="boolean" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="internet_radio" />
</rollback>
</changeSet>
<changeSet id="schema26_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="player" />
</not>
</preConditions>
<createTable tableName="player">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" />
<column name="type" type="${varchar_type}" />
<column name="username" type="${varchar_type}" />
<column name="ip_address" type="${varchar_type}" />
<column name="auto_control_enabled" type="boolean" >
<constraints nullable="false" />
</column>
<column name="last_seen" type="datetime" />
<column name="cover_art_scheme" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="transcode_scheme" type="${varchar_type}" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="player" />
</rollback>
</changeSet>
<changeSet id="schema26_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="dynamic_ip" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="dynamic_ip" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
<rollback>
<dropColumn tableName="player" columnName="dynamic_ip"/>
</rollback>
</changeSet>
<changeSet id="schema26_007" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 6</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="6"/>
<column name="name" value="comment" />
</insert>
<sql>
insert into user_role(username, role_id)
select distinct u.username, 6 from ${userTableQuote}user${userTableQuote} u, user_role ur
where u.username = ur.username and ur.role_id in (1, 5)
</sql>
<rollback>
<delete tableName="user_role" >
<where>role_id = 6</where>
</delete>
<delete tableName="role">
<where>id = 6</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,37 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<!-- Note that music_file_info.path case insensitive change was ignored -->
<changeSet id="schema27_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 3</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="3" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 3</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema27_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user" columnName="bytes_streamed" />
</not>
</preConditions>
<addColumn tableName="user">
<column name="bytes_streamed" type="bigint" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="bytes_downloaded" type="bigint" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="bytes_uploaded" type="bigint" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,232 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema28_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 4</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="4" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 4</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema28_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="user_settings" />
</not>
</preConditions>
<createTable tableName="user_settings">
<column name="username" type="${varchar_type}">
<constraints primaryKey="true" foreignKeyName="us_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true"/>
</column>
<column name="locale" type="${varchar_type}" />
<column name="theme_id" type="${varchar_type}" />
<column name="final_version_notification" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="beta_version_notification" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_caption_cutoff" type="int" defaultValueNumeric="35" >
<constraints nullable="false" />
</column>
<column name="main_track_number" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="main_artist" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="main_album" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_genre" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_year" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_bit_rate" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_duration" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="main_format" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="main_file_size" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="playlist_caption_cutoff" type="int" defaultValueNumeric="35" >
<constraints nullable="false" />
</column>
<column name="playlist_track_number" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="playlist_artist" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="playlist_album" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="playlist_genre" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="playlist_year" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="playlist_bit_rate" type="boolean" defaultValueBoolean="false" >
<constraints nullable="false" />
</column>
<column name="playlist_duration" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="playlist_format" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
<column name="playlist_file_size" type="boolean" defaultValueBoolean="true" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="user_settings" />
</rollback>
</changeSet>
<changeSet id="schema28_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="transcoding" />
</not>
</preConditions>
<createTable tableName="transcoding">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="source_format" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="target_format" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="step1" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="step2" type="${varchar_type}" />
<column name="step3" type="${varchar_type}" />
<column name="enabled" type="boolean" >
<constraints nullable="false" />
</column>
</createTable>
<insert tableName="transcoding">
<column name="name" value="wav > mp3"/>
<column name="source_format" value="wav" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="flac > mp3"/>
<column name="source_format" value="flac" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="ogg > mp3"/>
<column name="source_format" value="ogg" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="wma > mp3"/>
<column name="source_format" value="wma" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="m4a > mp3"/>
<column name="source_format" value="m4a" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="false" />
</insert>
<insert tableName="transcoding">
<column name="name" value="aac > mp3"/>
<column name="source_format" value="aac" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="false" />
</insert>
<insert tableName="transcoding">
<column name="name" value="ape > mp3"/>
<column name="source_format" value="ape" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="mpc > mp3"/>
<column name="source_format" value="mpc" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="mv > mp3"/>
<column name="source_format" value="mv" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<insert tableName="transcoding">
<column name="name" value="shn > mp3"/>
<column name="source_format" value="shn" />
<column name="target_format" value="mp3"/>
<column name="step1" value="ffmpeg -i %s -v 0 -f wav -" />
<column name="step2" value="lame -b %b --tt %t --ta %a --tl %l -S --resample 44.1 - -" />
<column name="enabled" valueBoolean="true" />
</insert>
<rollback>
<dropTable tableName="transcoding" />
</rollback>
</changeSet>
<changeSet id="schema28_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="player_transcoding" />
</not>
</preConditions>
<createTable tableName="player_transcoding">
<column name="player_id" type="int">
<constraints nullable="false" foreignKeyName="pt_p_fk" referencedTableName="player" referencedColumnNames="id" deleteCascade="true"/>
</column>
<column name="transcoding_id" type="int" >
<constraints nullable="false" foreignKeyName="pt_t_fk" referencedTableName="transcoding" referencedColumnNames="id" deleteCascade="true"/>
</column>
</createTable>
<addPrimaryKey tableName="player_transcoding" columnNames="player_id,transcoding_id" />
<rollback>
<dropTable tableName="player_transcoding" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,44 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema29_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 5</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="5" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 5</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema29_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="user_rating" />
</not>
</preConditions>
<createTable tableName="user_rating">
<column name="username" type="${varchar_type}">
<constraints nullable="false" foreignKeyName="ura_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true"/>
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="rating" type="double" >
<constraints nullable="false" />
</column>
</createTable>
<addPrimaryKey tableName="user_rating" columnNames="username,path" />
<sql>
insert into user_rating select 'admin', path, rating from music_file_info
where rating is not null and rating > 0
</sql>
<rollback>
<dropTable tableName="user_rating" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,44 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema30_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 6</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="6" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 6</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema30_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="last_fm_enabled" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="last_fm_enabled" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
<column name="last_fm_username" type="${varchar_type}" />
<column name="last_fm_password" type="${varchar_type}" />
</addColumn>
</changeSet>
<changeSet id="schema30_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="transcode_scheme" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="transcode_scheme" type="${varchar_type}" defaultValue="OFF">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,42 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema31_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 7</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="7" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 7</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema31_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="music_file_info" columnName="enabled" />
</not>
</preConditions>
<addColumn tableName="music_file_info">
<column name="enabled" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema31_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="transcoding" columnName="default_active" />
</not>
</preConditions>
<addColumn tableName="transcoding">
<column name="default_active" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,121 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema32_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 8</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="8" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 8</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema32_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="show_now_playing" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="show_now_playing" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema32_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="selected_music_folder_id" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="selected_music_folder_id" type="int" defaultValueNumeric="-1">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema32_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="podcast_channel" />
</not>
</preConditions>
<createTable tableName="podcast_channel">
<column name="id" type="int" autoIncrement="true" >
<constraints primaryKey="true" />
</column>
<column name="url" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="title" type="${varchar_type}" />
<column name="description" type="${varchar_type}" />
<column name="status" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="error_message" type="${varchar_type}" />
</createTable>
<rollback>
<dropTable tableName="podcast_channel" />
</rollback>
</changeSet>
<changeSet id="schema32_005" author="muff1nman">
<validCheckSum>7:0d4fbe2adcca829c14d12a5b1a8229c5</validCheckSum>
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="podcast_episode"/>
</not>
</preConditions>
<createTable tableName="podcast_episode">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="channel_id" type="int" >
<constraints nullable="false" foreignKeyName="pe_pc_fk" referencedTableName="podcast_channel" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="url" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="path" type="${varchar_type}" />
<column name="title" type="${varchar_type}" />
<column name="description" type="${varchar_type}" />
<column name="publish_date" type="datetime" />
<column name="duration" type="${varchar_type}" />
<column name="bytes_total" type="bigint" />
<column name="bytes_downloaded" type="bigint" />
<column name="status" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="error_message" type="${varchar_type}" />
</createTable>
<rollback>
<dropTable tableName="podcast_episode" />
</rollback>
</changeSet>
<changeSet id="schema32_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 7</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="7"/>
<column name="name" value="podcast" />
</insert>
<sql>
insert into user_role(username, role_id)
select distinct u.username, 7 from ${userTableQuote}user${userTableQuote} u, user_role ur
where u.username = ur.username and ur.role_id = 1
</sql>
<rollback>
<delete tableName="user_role">
<where>role_id = 7</where>
</delete>
<delete tableName="role">
<where>id = 7</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,30 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema33_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 9</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="9" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 9</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema33_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="client_side_playlist" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="client_side_playlist" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,42 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema34_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 10</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="10" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 10</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema34_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user" columnName="ldap_authenticated" />
</not>
</preConditions>
<addColumn tableName="user">
<column name="ldap_authenticated" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema34_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="party_mode_enabled" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="party_mode_enabled" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,945 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema35_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 11</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="11" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 11</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="now_playing_allowed" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="now_playing_allowed" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema35_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="web_player_default" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="web_player_default" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema35_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 8</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="8"/>
<column name="name" value="stream" />
</insert>
<sql>
insert into user_role(username, role_id) select distinct u.username, 8 from ${userTableQuote}user${userTableQuote} u
</sql>
<rollback>
<delete tableName="user_role" >
<where>role_id = 8</where>
</delete>
<delete tableName="role">
<where>id = 8</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="system_avatar" />
</not>
</preConditions>
<createTable tableName="system_avatar">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" />
<column name="created_date" type="datetime" defaultValueComputed="${curr_date_expr}">
<constraints nullable="false" />
</column>
<column name="mime_type" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="width" type="int" >
<constraints nullable="false" />
</column>
<column name="height" type="int" >
<constraints nullable="false" />
</column>
<column name="data" type="${binary_type}" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="system_avatar" />
</rollback>
</changeSet>
<changeSet id="schema35_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="custom_avatar" />
</not>
</preConditions>
<createTable tableName="custom_avatar">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}"/>
<column name="created_date" type="datetime" defaultValueComputed="${curr_date_expr}">
<constraints nullable="false" />
</column>
<column name="mime_type" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="width" type="int" >
<constraints nullable="false" />
</column>
<column name="height" type="int" >
<constraints nullable="false" />
</column>
<column name="data" type="${binary_type}" >
<constraints nullable="false" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" deleteCascade="true" foreignKeyName="ca_u_fk" referencedTableName="user" referencedColumnNames="username" />
</column>
</createTable>
<rollback>
<dropTable tableName="custom_avatar" />
</rollback>
</changeSet>
<changeSet id="schema35_007" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="avatar_scheme" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="avatar_scheme" type="${varchar_type}" defaultValue="NONE">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema35_008" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="system_avatar_id" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="system_avatar_id" type="int">
<constraints foreignKeyName="us_sai_fk" referencedTableName="system_avatar" referencedColumnNames="id" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema35_009" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="jukebox" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="jukebox" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema35_010_Formal" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Formal'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Formal" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Formal.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Formal'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Engineer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Engineer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Engineer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Engineer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Engineer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Footballer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Footballer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Footballer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Footballer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Footballer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Green-Boy" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Green-Boy'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Green-Boy" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Green-Boy.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Green-Boy'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Linux-Zealot" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Linux-Zealot'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Linux-Zealot" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Linux-Zealot.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Linux-Zealot'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Mac-Zealot" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Mac-Zealot'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Mac-Zealot" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Mac-Zealot.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Mac-Zealot'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Windows-Zealot" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Windows-Zealot'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Windows-Zealot" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Windows-Zealot.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Windows-Zealot'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Army-Officer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Army-Officer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Army-Officer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Army-Officer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Army-Officer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Beatnik" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Beatnik'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Beatnik" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Beatnik.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Beatnik'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_All-Caps" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'All-Caps'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="All-Caps" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/All-Caps.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'All-Caps'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Clown" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Clown'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Clown" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Clown.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Clown'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Commie-Pinko" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Commie-Pinko'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Commie-Pinko" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Commie-Pinko.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Commie-Pinko'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Forum-Flirt" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Forum-Flirt'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Forum-Flirt" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Forum-Flirt.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Forum-Flirt'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Gamer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Gamer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Gamer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Gamer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Gamer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Hopelessly-Addicted" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Hopelessly-Addicted'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Hopelessly-Addicted" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Hopelessly-Addicted.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Hopelessly-Addicted'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Jekyll-And-Hyde" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Jekyll-And-Hyde'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Jekyll-And-Hyde" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Jekyll-And-Hyde.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Jekyll-And-Hyde'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Joker" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Joker'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Joker" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Joker.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Joker'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Lurker" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Lurker'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Lurker" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Lurker.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Lurker'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Moderator" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Moderator'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Moderator" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Moderator.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Moderator'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Newbie" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Newbie'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Newbie" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Newbie.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Newbie'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_No-Dissent" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'No-Dissent'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="No-Dissent" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/No-Dissent.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'No-Dissent'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Performer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Performer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Performer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Performer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Performer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Push-My-Button" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Push-My-Button'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Push-My-Button" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Push-My-Button.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Push-My-Button'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Ray-Of-Sunshine" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Ray-Of-Sunshine'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Ray-Of-Sunshine" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Ray-Of-Sunshine.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Ray-Of-Sunshine'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Red-Hot-Chili-Peppers-1" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Red-Hot-Chili-Peppers-1'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Red-Hot-Chili-Peppers-1" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Red-Hot-Chili-Peppers-1.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Red-Hot-Chili-Peppers-1'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Red-Hot-Chili-Peppers-2" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Red-Hot-Chili-Peppers-2'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Red-Hot-Chili-Peppers-2" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Red-Hot-Chili-Peppers-2.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Red-Hot-Chili-Peppers-2'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Red-Hot-Chili-Peppers-3" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Red-Hot-Chili-Peppers-3'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Red-Hot-Chili-Peppers-3" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Red-Hot-Chili-Peppers-3.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Red-Hot-Chili-Peppers-3'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Red-Hot-Chili-Peppers-4" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Red-Hot-Chili-Peppers-4'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Red-Hot-Chili-Peppers-4" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Red-Hot-Chili-Peppers-4.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Red-Hot-Chili-Peppers-4'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Ringmaster" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Ringmaster'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Ringmaster" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Ringmaster.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Ringmaster'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Rumor-Junkie" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Rumor-Junkie'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Rumor-Junkie" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Rumor-Junkie.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Rumor-Junkie'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Sozzled-Surfer" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Sozzled-Surfer'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Sozzled-Surfer" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Sozzled-Surfer.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Sozzled-Surfer'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Statistician" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Statistician'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Statistician" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Statistician.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Statistician'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Tech-Support" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Tech-Support'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Tech-Support" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Tech-Support.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Tech-Support'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_The-Guru" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'The-Guru'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="The-Guru" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/The-Guru.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'The-Guru'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_The-Referee" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'The-Referee'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="The-Referee" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/The-Referee.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'The-Referee'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Troll" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Troll'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Troll" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Troll.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Troll'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Uptight" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Uptight'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Uptight" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Uptight.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Uptight'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Fire-Guitar" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Fire-Guitar'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Fire-Guitar" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Fire-Guitar.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Fire-Guitar'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Drum" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Drum'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Drum" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Drum.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Drum'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Headphones" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Headphones'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Headphones" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Headphones.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Headphones'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Mic" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Mic'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Mic" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Mic.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Mic'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Turntable" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Turntable'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Turntable" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Turntable.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Turntable'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Vinyl" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Vinyl'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Vinyl" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Vinyl.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Vinyl'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Cool" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Cool'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Cool" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Cool.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Cool'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Laugh" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Laugh'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Laugh" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Laugh.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Laugh'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema35_010_Study" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from system_avatar where name = 'Study'</sqlCheck>
</preConditions>
<insert tableName="system_avatar">
<column name="name" value="Study" />
<column name="mime_type" value="img/png" />
<column name="width" valueNumeric="48" />
<column name="height" valueNumeric="48" />
<column name="data" valueBlobFile="../../org/libresonic/player/dao/schema/Study.png" />
</insert>
<rollback>
<delete tableName="system_avatar">
<where>name = 'Study'</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,30 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema36_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 12</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="12" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 12</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema36_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="technology" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="technology" type="${varchar_type}" defaultValue="WEB">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,81 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<property name="curr_date_expr" value="current_timestamp" />
<changeSet id="schema37_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 13</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="13" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 13</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema37_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 9</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="9"/>
<column name="name" value="settings" />
</insert>
<sql>
insert into user_role(username, role_id) select distinct u.username, 9 from ${userTableQuote}user${userTableQuote} u
</sql>
<rollback />
</changeSet>
<changeSet id="schema37_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 10</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="10"/>
<column name="name" value="jukebox" />
</insert>
<sql>
insert into user_role(username, role_id) select distinct u.username, 10 from ${userTableQuote}user${userTableQuote} u
</sql>
<rollback />
</changeSet>
<changeSet id="schema37_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="music_folder" columnName="changed" />
</not>
</preConditions>
<addColumn tableName="music_folder">
<column name="changed" type="datetime" defaultValueComputed="${curr_date_expr}">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema37_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="internet_radio" columnName="changed" />
</not>
</preConditions>
<addColumn tableName="internet_radio">
<column name="changed" type="datetime" defaultValueComputed="${curr_date_expr}">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema37_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="changed" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="changed" type="datetime" defaultValueComputed="${curr_date_expr}">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,40 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema38_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 14</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="14" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 14</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema38_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="client_id" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="client_id" type="${varchar_type}"/>
</addColumn>
</changeSet>
<changeSet id="schema38_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="show_chat" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="show_chat" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,22 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema40_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 15</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="15" />
</insert>
<!--Reset stream byte count since they have been wrong in earlier releases.-->
<update tableName="user">
<column name="bytes_streamed" valueNumeric="0" />
</update>
<rollback>
<delete tableName="version" >
<where>version = 15</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,130 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema43_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 16</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="16" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 16</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema43_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 17</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="17" />
</insert>
<delete tableName="transcoding">
<where>target_format = 'flv' and source_format in ('avi', 'mpg', 'mpeg', 'mp4', 'm4v', 'mkv', 'mov', 'wmv', 'ogv')</where>
</delete>
<insert tableName="transcoding">
<column name="name" value="avi > flv"/>
<column name="source_format" value="avi" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'avi > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="mpg > flv"/>
<column name="source_format" value="mpg" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'mpg > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="mpeg > flv"/>
<column name="source_format" value="mpeg" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'mpeg > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="mp4 > flv"/>
<column name="source_format" value="mp4" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'mp4 > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="m4v > flv"/>
<column name="source_format" value="m4v" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'm4v > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="mkv > flv"/>
<column name="source_format" value="mkv" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'mkv > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="mov > flv"/>
<column name="source_format" value="mov" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'mov > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="wmv > flv"/>
<column name="source_format" value="wmv" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'wmv > flv'
</sql>
<insert tableName="transcoding">
<column name="name" value="ogv > flv"/>
<column name="source_format" value="ogv" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -" />
<column name="enabled" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding(player_id, transcoding_id) select p.id as player_id, t.id as transaction_id from player p, transcoding t where t.name = 'ogv > flv'
</sql>
<rollback/>
</changeSet>
<changeSet id="schema43_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user" columnName="email" />
</not>
</preConditions>
<addColumn tableName="user">
<column name="email" type="${varchar_type}"/>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,84 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema45_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 18</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="18" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 18</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema45_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from role where id = 11</sqlCheck>
</preConditions>
<insert tableName="role">
<column name="id" valueNumeric="11"/>
<column name="name" value="share" />
</insert>
<sql>
insert into user_role(username, role_id)
select distinct u.username, 11 from ${userTableQuote}user${userTableQuote} u, user_role ur
where u.username = ur.username and ur.role_id = 1
</sql>
<rollback />
</changeSet>
<changeSet id="schema45_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="share" />
</not>
</preConditions>
<createTable tableName="share">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="description" type="${varchar_type}" />
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="s_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true"/>
</column>
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
<column name="expires" type="datetime" />
<column name="last_visited" type="datetime" />
<column name="visit_count" type="int" defaultValueNumeric="0" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex unique="true" tableName="share" indexName="idx_share_name">
<column name="name"/>
</createIndex>
<sql dbms="hsql">
set table share type cached
</sql>
<createTable tableName="share_file">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="share_id" type="int">
<constraints nullable="false" foreignKeyName="sf_s_fk" referencedTableName="share" referencedColumnNames="id" deleteCascade="true"/>
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
</createTable>
<sql dbms="hsql">
set table share_file type cached
</sql>
<rollback>
<dropTable tableName="share" />
<dropTable tableName="share_file" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,94 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema46_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 19</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="19" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 19</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema46_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="transcoding2" />
</not>
</preConditions>
<createTable tableName="transcoding2">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="source_formats" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="target_format" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="step1" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="step2" type="${varchar_type}" />
<column name="step3" type="${varchar_type}" />
</createTable>
<insert tableName="transcoding2">
<column name="name" value="mp3 audio" />
<column name="source_formats" value="ogg oga aac m4a flac wav wma aif aiff ape mpc shn" />
<column name="target_format" value="mp3" />
<column name="step1" value="ffmpeg -i %s -ab %bk -v 0 -f mp3 -"/>
</insert>
<insert tableName="transcoding2">
<column name="name" value="flv/h264 video" />
<column name="source_formats" value="avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts" />
<column name="target_format" value="flv" />
<column name="step1" value="ffmpeg -ss %o -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f flv -vcodec libx264 -preset superfast -threads 0 -"/>
</insert>
<rollback>
<dropTable tableName="transcoding2" />
</rollback>
</changeSet>
<changeSet id="schema46_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="player_transcoding2" />
</not>
</preConditions>
<createTable tableName="player_transcoding2">
<column name="player_id" type="int">
<constraints nullable="false" foreignKeyName="pt2_p_fk" referencedTableName="player" referencedColumnNames="id" deleteCascade="true"/>
</column>
<column name="transcoding_id" type="int" >
<constraints nullable="false" foreignKeyName="pt2_t_fk" referencedTableName="transcoding2" referencedColumnNames="id" deleteCascade="true"/>
</column>
</createTable>
<addPrimaryKey tableName="player_transcoding2" columnNames="player_id,transcoding_id" />
<sql>
insert into player_transcoding2(player_id, transcoding_id)
select distinct p.id, t.id from player p, transcoding2 t
</sql>
<rollback>
<dropTable tableName="player_transcoding2" />
</rollback>
</changeSet>
<changeSet id="schema46_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="transcoding2" columnName="default_active" />
</not>
</preConditions>
<addColumn tableName="transcoding2">
<column name="default_active" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,472 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema47_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 20</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="20" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 20</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema47_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="media_file" />
</not>
</preConditions>
<createTable tableName="media_file">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="folder" type="${varchar_type}" />
<column name="type" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="format" type="${varchar_type}" />
<column name="title" type="${varchar_type}" />
<column name="album" type="${varchar_type}" />
<column name="artist" type="${varchar_type}" />
<column name="album_artist" type="${varchar_type}" />
<column name="disc_number" type="int" />
<column name="track_number" type="int" />
<column name="year" type="int" />
<column name="genre" type="${varchar_type}" />
<column name="bit_rate" type="int" />
<column name="variable_bit_rate" type="boolean" >
<constraints nullable="false" />
</column>
<column name="duration_seconds" type="int" />
<column name="file_size" type="bigint" />
<column name="width" type="int" />
<column name="height" type="int" />
<column name="cover_art_path" type="${varchar_type}" />
<column name="parent_path" type="${varchar_type}" />
<column name="play_count" type="int" >
<constraints nullable="false" />
</column>
<column name="last_played" type="datetime" />
<column name="comment" type="${varchar_type}" />
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
<column name="changed" type="datetime" >
<constraints nullable="false" />
</column>
<column name="last_scanned" type="datetime" >
<constraints nullable="false" />
</column>
<column name="children_last_updated" type="datetime" >
<constraints nullable="false" />
</column>
<column name="present" type="boolean" >
<constraints nullable="false" />
</column>
<column name="version" type="int" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex unique="true" tableName="media_file" indexName="idx_media_file_path">
<column name="path"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_parent_path">
<column name="parent_path"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_type">
<column name="type"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_album">
<column name="album"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_artist">
<column name="artist"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_album_artist">
<column name="album_artist"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_present">
<column name="present"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_genre">
<column name="genre"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_play_count">
<column name="play_count"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_created">
<column name="created"/>
</createIndex>
<createIndex tableName="media_file" indexName="idx_media_file_last_played">
<column name="last_played"/>
</createIndex>
<sql dbms="hsql">
set table media_file type cached
</sql>
<rollback>
<dropTable tableName="media_file" />
</rollback>
</changeSet>
<changeSet id="schema47_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="artist" />
</not>
</preConditions>
<createTable tableName="artist">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="cover_art_path" type="${varchar_type}" />
<column name="album_count" type="int" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="last_scanned" type="datetime" >
<constraints nullable="false" />
</column>
<column name="present" type="boolean" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="artist" indexName="idx_artist_name" unique="true">
<column name="name"/>
</createIndex>
<createIndex tableName="artist" indexName="idx_artist_present" >
<column name="present"/>
</createIndex>
<sql dbms="hsql">
set table artist type cached
</sql>
<rollback>
<dropTable tableName="artist" />
</rollback>
</changeSet>
<changeSet id="schema47_004" author="muff1nman">
<validCheckSum>7:a3a788d87fd58508ecb4acac39e255c6</validCheckSum>
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="album" />
</not>
</preConditions>
<createTable tableName="album">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="path" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="artist" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="song_count" type="int" defaultValueNumeric="0" >
<constraints nullable="false" />
</column>
<column name="duration_seconds" type="int" defaultValueNumeric="0" >
<constraints nullable="false" />
</column>
<column name="cover_art_path" type="${varchar_type}" />
<column name="play_count" type="int" defaultValueNumeric="0" >
<constraints nullable="false" />
</column>
<column name="last_played" type="datetime" />
<column name="comment" type="${varchar_type}" />
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
<column name="last_scanned" type="datetime" >
<constraints nullable="false" />
</column>
<column name="present" type="boolean" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="album" indexName="idx_album_artist_name" unique="true">
<column name="artist" />
<column name="name"/>
</createIndex>
<createIndex tableName="album" indexName="idx_album_play_count" >
<column name="play_count"/>
</createIndex>
<createIndex tableName="album" indexName="idx_album_last_played" >
<column name="last_played"/>
</createIndex>
<createIndex tableName="album" indexName="idx_album_present" >
<column name="present"/>
</createIndex>
<sql dbms="hsql">
set table album type cached
</sql>
<rollback>
<dropTable tableName="album" />
</rollback>
</changeSet>
<changeSet id="schema47_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<dbms type="hsqldb" />
<customPrecondition className="org.libresonic.player.spring.DbmsVersionPrecondition" >
<param name="major" value="1" />
</customPrecondition>
</not>
<not>
<indexExists indexName="idx_album_name" />
</not>
</preConditions>
<createIndex tableName="album" indexName="idx_album_name">
<column name="name"/>
</createIndex>
</changeSet>
<changeSet id="schema47_005_2" author="muff1nman">
<preConditions onFail="MARK_RAN">
<dbms type="hsqldb" />
<customPrecondition className="org.libresonic.player.spring.DbmsVersionPrecondition" >
<param name="major" value="1" />
</customPrecondition>
<sqlCheck expectedResult="0">
select count(*) from INFORMATION_SCHEMA.SYSTEM_INDEXINFO where
TABLE_NAME = 'album' and INDEX_NAME = 'idx_album_name';
</sqlCheck>
</preConditions>
<createIndex tableName="album" indexName="idx_album_name">
<column name="name"/>
</createIndex>
</changeSet>
<changeSet id="schema47_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="starred_media_file" />
</not>
</preConditions>
<createTable tableName="starred_media_file">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="media_file_id" type="int" >
<constraints nullable="false" foreignKeyName="smf_mf_fk" referencedTableName="media_file" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="smf_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="starred_media_file" indexName="idx_starred_media_file_media_file_id">
<column name="media_file_id"/>
</createIndex>
<createIndex tableName="starred_media_file" indexName="idx_starred_media_file_username">
<column name="username"/>
</createIndex>
<addUniqueConstraint tableName="starred_media_file" columnNames="media_file_id,username" />
<rollback>
<dropTable tableName="starred_media_file" />
</rollback>
</changeSet>
<changeSet id="schema47_007" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="starred_album" />
</not>
</preConditions>
<createTable tableName="starred_album">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="album_id" type="int" >
<constraints nullable="false" foreignKeyName="sa_a_fk" referencedTableName="album" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="sa_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="starred_album" indexName="idx_starred_album_album_id">
<column name="album_id"/>
</createIndex>
<createIndex tableName="starred_album" indexName="idx_starred_album_username">
<column name="username"/>
</createIndex>
<addUniqueConstraint tableName="starred_album" columnNames="album_id,username" />
<rollback>
<dropTable tableName="starred_album" />
</rollback>
</changeSet>
<changeSet id="schema47_008" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="starred_artist" />
</not>
</preConditions>
<createTable tableName="starred_artist">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="artist_id" type="int">
<constraints nullable="false" foreignKeyName="sar_a_fk" referencedTableName="artist" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="sar_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="starred_artist" indexName="idx_starred_artist_artist_id">
<column name="artist_id"/>
</createIndex>
<createIndex tableName="starred_artist" indexName="idx_starred_artist_username">
<column name="username"/>
</createIndex>
<addUniqueConstraint tableName="starred_artist" columnNames="artist_id,username" />
<rollback>
<dropTable tableName="starred_artist" />
</rollback>
</changeSet>
<changeSet id="schema47_009" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="playlist" />
</not>
</preConditions>
<createTable tableName="playlist">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="p_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="is_public" type="boolean" >
<constraints nullable="false" />
</column>
<column name="name" type="${varchar_type}" >
<constraints nullable="false" />
</column>
<column name="comment" type="${varchar_type}" />
<column name="file_count" type="int" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
<column name="duration_seconds" type="int" defaultValueNumeric="0" >
<constraints nullable="false" />
</column>
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
<column name="changed" type="datetime" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="playlist" />
</rollback>
</changeSet>
<changeSet id="schema47_010" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="playlist" columnName="imported_from" />
</not>
</preConditions>
<addColumn tableName="playlist">
<column name="imported_from" type="${varchar_type}"/>
</addColumn>
</changeSet>
<changeSet id="schema47_011" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="playlist_file" />
</not>
</preConditions>
<createTable tableName="playlist_file">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="playlist_id" type="int" >
<constraints nullable="false" foreignKeyName="pf_p_fk" referencedTableName="playlist" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="media_file_id" type="int" >
<constraints nullable="false" foreignKeyName="pf_mf_fk" referencedTableName="media_file" referencedColumnNames="id" deleteCascade="true" />
</column>
</createTable>
<sql dbms="hsql">
set table playlist_file type cached
</sql>
<rollback>
<dropTable tableName="playlist_file" />
</rollback>
</changeSet>
<changeSet id="schema47_012" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="playlist_user" />
</not>
</preConditions>
<createTable tableName="playlist_user">
<column name="id" type="int" autoIncrement="true" >
<constraints primaryKey="true" />
</column>
<column name="playlist_id" type="int" >
<constraints nullable="false" foreignKeyName="pu_p_fk" referencedTableName="playlist" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="pu_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
</createTable>
<addUniqueConstraint tableName="playlist_user" columnNames="playlist_id,username" />
<rollback>
<dropTable tableName="playlist_user" />
</rollback>
</changeSet>
<changeSet id="schema47_013" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="bookmark" />
</not>
</preConditions>
<createTable tableName="bookmark">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="media_file_id" type="int" >
<constraints nullable="false" foreignKeyName="b_mf_fk" referencedTableName="media_file" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="position_millis" type="bigint" >
<constraints nullable="false" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="b_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="comment" type="${varchar_type}" />
<column name="created" type="datetime" >
<constraints nullable="false" />
</column>
<column name="changed" type="datetime" >
<constraints nullable="false" />
</column>
</createTable>
<createIndex tableName="bookmark" indexName="idx_bookmark_media_file_id">
<column name="media_file_id"/>
</createIndex>
<createIndex tableName="bookmark" indexName="idx_bookmark_username">
<column name="username"/>
</createIndex>
<addUniqueConstraint columnNames="media_file_id,username" tableName="bookmark" />
<rollback>
<dropTable tableName="bookmark" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,65 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema49_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 21</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="21" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 21</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema49_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="album" columnName="year" />
</not>
</preConditions>
<addColumn tableName="album">
<column name="year" type="int"/>
</addColumn>
</changeSet>
<changeSet id="schema49_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="album" columnName="genre" />
</not>
</preConditions>
<addColumn tableName="album">
<column name="genre" type="${varchar_type}"/>
</addColumn>
</changeSet>
<changeSet id="schema49_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="genre" />
</not>
</preConditions>
<createTable tableName="genre">
<column name="name" type="${varchar_type}">
<constraints nullable="false"/>
</column>
<column name="song_count" type="int" >
<constraints nullable="false" />
</column>
</createTable>
</changeSet>
<changeSet id="schema49_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="genre" columnName="album_count" />
</not>
</preConditions>
<addColumn tableName="genre">
<column name="album_count" type="int" defaultValueNumeric="0">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,64 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema50_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 22</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="22" />
</insert>
<insert tableName="transcoding2">
<column name="name" value="mkv video"/>
<column name="source_formats" value="avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts" />
<column name="target_format" value="mkv" />
<column name="step1" value="ffmpeg -ss %o -i %s -c:v libx264 -preset superfast -b:v %bk -c:a libvorbis -f matroska -threads 0 -" />
<column name="default_active" valueBoolean="true" />
</insert>
<sql>
insert into player_transcoding2(player_id, transcoding_id)
select distinct p.id, t.id from player p, transcoding2 t where t.name='mkv video'
</sql>
<rollback>
<delete tableName="version" >
<where>version = 22</where>
</delete>
<delete tableName="player_transcoding" >
<where>transcoding_id in (select id from transcoding_2 where name = 'mkv video')</where>
</delete>
<delete tableName="transcoding2" >
<where>name = 'mkv video'</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema50_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="song_notification" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="song_notification" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema50_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 23</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="23" />
</insert>
<update tableName="transcoding2">
<column name="step1" value="ffmpeg -i %s -map 0:0 -b:a %bk -v 0 -f mp3 -" />
<where>name='mp3 audio'</where>
</update>
<rollback>
<delete tableName="version" >
<where>version = 23</where>
</delete>
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,54 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema51_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 23</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="23" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 23</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema51_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="show_artist_info" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="show_artist_info" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema51_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="auto_hide_play_queue" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="auto_hide_play_queue" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema51_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="view_as_list" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="view_as_list" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,96 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema52_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 24</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="24" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 24</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema52_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="music_folder_user" />
</not>
</preConditions>
<createTable tableName="music_folder_user">
<column name="music_folder_id" type="int">
<constraints nullable="false" foreignKeyName="mfu_fm_fk" referencedTableName="music_folder" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="mfu_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
</createTable>
<createIndex tableName="music_folder_user" indexName="idx_music_folder_user_username">
<column name="username"/>
</createIndex>
</changeSet>
<changeSet id="schema52_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="album" columnName="folder_id"/>
</not>
</preConditions>
<addColumn tableName="album">
<column name="folder_id" type="int"/>
</addColumn>
</changeSet>
<changeSet id="schema52_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="play_queue" />
</not>
</preConditions>
<createTable tableName="play_queue">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="username" type="${varchar_type}" >
<constraints nullable="false" foreignKeyName="pq_u_fk" referencedTableName="user" referencedColumnNames="username" deleteCascade="true" />
</column>
<column name="current" type="int" />
<column name="position_millis" type="bigint" />
<column name="changed" type="datetime" >
<constraints nullable="false" />
</column>
<column name="changed_by" type="${varchar_type}" >
<constraints nullable="false" />
</column>
</createTable>
<rollback>
<dropTable tableName="play_queue" />
</rollback>
</changeSet>
<changeSet id="schema52_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<tableExists tableName="play_queue_file" />
</not>
</preConditions>
<createTable tableName="play_queue_file">
<column name="id" type="int" autoIncrement="true">
<constraints primaryKey="true" />
</column>
<column name="play_queue_id" type="int" >
<constraints nullable="false" foreignKeyName="pqf_pq_fk" referencedTableName="play_queue" referencedColumnNames="id" deleteCascade="true" />
</column>
<column name="media_file_id" type="int" >
<constraints nullable="false" foreignKeyName="pqf_mf_fk" referencedTableName="media_file" referencedColumnNames="id" deleteCascade="true" />
</column>
</createTable>
<sql dbms="hsql">
set table play_queue_file type cached
</sql>
<rollback>
<dropTable tableName="play_queue_file" />
</rollback>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,105 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema53_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 25</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="25" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 25</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema53_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<dbms type="hsqldb" />
<customPrecondition className="org.libresonic.player.spring.DbmsVersionPrecondition" >
<param name="major" value="1" />
</customPrecondition>
</not>
<not>
<indexExists indexName="idx_podcast_episode_url" />
</not>
</preConditions>
<createIndex tableName="podcast_episode" indexName="idx_podcast_episode_url">
<column name="url"/>
</createIndex>
</changeSet>
<changeSet id="schema53_002_1" author="muff1nman">
<preConditions onFail="MARK_RAN">
<dbms type="hsqldb" />
<customPrecondition className="org.libresonic.player.spring.DbmsVersionPrecondition" >
<param name="major" value="1" />
</customPrecondition>
<sqlCheck expectedResult="0">
select count(*) from INFORMATION_SCHEMA.SYSTEM_INDEXINFO where
TABLE_NAME = 'podcast_episode' and INDEX_NAME = 'idx_podcast_episode_url';
</sqlCheck>
</preConditions>
<createIndex tableName="podcast_episode" indexName="idx_podcast_episode_url">
<column name="url"/>
</createIndex>
</changeSet>
<changeSet id="schema53_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="default_album_list" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="default_album_list" type="${varchar_type}" defaultValue="random">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema53_004" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="queue_following_songs" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="queue_following_songs" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema53_005" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="podcast_channel" columnName="image_url" />
</not>
</preConditions>
<addColumn tableName="podcast_channel">
<column name="image_url" type="${varchar_type}" />
</addColumn>
</changeSet>
<changeSet id="schema53_006" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="show_side_bar" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="show_side_bar" type="boolean" defaultValueBoolean="true">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema53_007" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="artist" columnName="folder_id" />
</not>
</preConditions>
<addColumn tableName="artist">
<column name="folder_id" type="int"/>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,42 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema61_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 26</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="26" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 26</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema61_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="list_reload_delay" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="list_reload_delay" type="int" defaultValueNumeric="60">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
<changeSet id="schema61_003" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="user_settings" columnName="keyboard_shortcuts_enabled" />
</not>
</preConditions>
<addColumn tableName="user_settings">
<column name="keyboard_shortcuts_enabled" type="boolean" defaultValueBoolean="false">
<constraints nullable="false" />
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -0,0 +1,30 @@
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet id="schema62_001" author="muff1nman">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from version where version = 27</sqlCheck>
</preConditions>
<insert tableName="version">
<column name="version" valueNumeric="27" />
</insert>
<rollback>
<delete tableName="version" >
<where>version = 27</where>
</delete>
</rollback>
</changeSet>
<changeSet id="schema62_002" author="muff1nman">
<preConditions onFail="MARK_RAN">
<not>
<columnExists tableName="player" columnName="m3u_bom_enabled" />
</not>
</preConditions>
<addColumn tableName="player">
<column name="m3u_bom_enabled" type="boolean" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
</changeSet>
</databaseChangeLog>

@ -7,3 +7,7 @@ log4j.appender.A1=org.apache.log4j.ConsoleAppender
# A1 uses PatternLayout. # A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%d{ISO8601}] %-5p %c - %m%n log4j.appender.A1.layout.ConversionPattern=[%d{ISO8601}] %-5p %c - %m%n
# TODO remove this once https://issues.apache.org/jira/browse/CONFIGURATION-627 is fixed
log4j.logger.org.apache.commons.beanutils.FluentPropertyBeanIntrospector=ERROR, A1
log4j.additivity.org.apache.commons.beanutils.FluentPropertyBeanIntrospector=false

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"
profile="embed">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${database.config.embed.driver}" />
<property name="url" value="${database.config.embed.url}" />
<property name="username" value="${database.config.embed.username}" />
<property name="password" value="${database.config.embed.password}" />
</bean>
</beans>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"
profile="jndi">
<jee:jndi-lookup id="dataSource"
jndi-name="${database.config.jndi.name}"
expected-type="javax.sql.DataSource" />
</beans>

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd"
profile="legacy">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:file:#{systemProperties['libresonic.home']}/db/libresonic" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
</beans>

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="applicationContext-db-jndi.xml" />
<import resource="applicationContext-db-embed.xml" />
<import resource="applicationContext-db-legacy.xml" />
<bean id="daoHelper" class="org.libresonic.player.dao.GenericDaoHelper">
<constructor-arg name="dataSource" ref="dataSource" />
</bean>
<bean id="rollbackFile" class="java.io.File">
<constructor-arg type="java.io.File" index="0" value="#{T(org.libresonic.player.service.SettingsService).libresonicHome}" />
<constructor-arg type="java.lang.String" index="1" value="rollback.sql" />
</bean>
<bean id="userTableQuote" class="java.lang.String">
<constructor-arg value="${database.usertable.quote:}" />
</bean>
<bean id="liquibase" class="org.libresonic.player.spring.SpringLiquibase">
<property name="dataSource" ref="dataSource" />
<property name="changeLog" value="classpath:liquibase/db-changelog.xml" />
<property name="rollbackFile" ref="rollbackFile" />
<property name="changeLogParameters">
<map>
<entry key="defaultMusicFolder" value="#{T(org.libresonic.player.util.Util).getDefaultMusicFolder()}" />
<entry key="varcharLimit" value="${database.varchar.maxlength:512}" />
<entry key="userTableQuote" value-ref="userTableQuote" />
</map>
</property>
</bean>
</beans>

@ -2,7 +2,12 @@
<beans xmlns="http://www.springframework.org/schema/beans" <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder />
<import resource="applicationContext-db.xml" />
<!-- DAO's --> <!-- DAO's -->
@ -44,6 +49,7 @@
<bean id="userDao" class="org.libresonic.player.dao.UserDao"> <bean id="userDao" class="org.libresonic.player.dao.UserDao">
<property name="daoHelper" ref="daoHelper"/> <property name="daoHelper" ref="daoHelper"/>
<property name="userTableQuote" ref="userTableQuote"/>
</bean> </bean>
<bean id="transcodingDao" class="org.libresonic.player.dao.TranscodingDao"> <bean id="transcodingDao" class="org.libresonic.player.dao.TranscodingDao">
@ -66,9 +72,6 @@
<property name="daoHelper" ref="daoHelper"/> <property name="daoHelper" ref="daoHelper"/>
</bean> </bean>
<bean id="daoHelper" class="org.libresonic.player.dao.DaoHelperFactory" factory-method="create"/>
<!-- Services --> <!-- Services -->
<bean id="mediaFileService" class="org.libresonic.player.service.MediaFileService"> <bean id="mediaFileService" class="org.libresonic.player.service.MediaFileService">
@ -86,12 +89,15 @@
<property name="userCache" ref="userCache"/> <property name="userCache" ref="userCache"/>
</bean> </bean>
<bean id="configurationService" class="org.libresonic.player.service.ApacheCommonsConfigurationService" />
<bean id="settingsService" class="org.libresonic.player.service.SettingsService" init-method="init"> <bean id="settingsService" class="org.libresonic.player.service.SettingsService" init-method="init">
<property name="internetRadioDao" ref="internetRadioDao"/> <property name="internetRadioDao" ref="internetRadioDao"/>
<property name="musicFolderDao" ref="musicFolderDao"/> <property name="musicFolderDao" ref="musicFolderDao"/>
<property name="userDao" ref="userDao"/> <property name="userDao" ref="userDao"/>
<property name="avatarDao" ref="avatarDao"/> <property name="avatarDao" ref="avatarDao"/>
<property name="versionService" ref="versionService"/> <property name="versionService" ref="versionService"/>
<property name="configurationService" ref="configurationService" />
</bean> </bean>
<bean id="mediaScannerService" class="org.libresonic.player.service.MediaScannerService" init-method="init" depends-on="metaDataParserFactory"> <bean id="mediaScannerService" class="org.libresonic.player.service.MediaScannerService" init-method="init" depends-on="metaDataParserFactory">

@ -16,6 +16,11 @@
</param-value> </param-value>
</context-param> </context-param>
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>org.libresonic.player.spring.AdditionalPropertySourceConfigurer</param-value>
</context-param>
<listener> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </listener>

@ -1,11 +1,21 @@
package org.libresonic.player.dao; package org.libresonic.player.dao;
import javax.sql.DataSource;
import junit.framework.TestCase; import junit.framework.TestCase;
import liquibase.exception.LiquibaseException;
import org.libresonic.player.TestCaseUtils; import org.libresonic.player.TestCaseUtils;
import org.libresonic.player.service.SettingsService;
import org.libresonic.player.spring.SpringLiquibase;
import org.libresonic.player.util.FileUtil; import org.libresonic.player.util.FileUtil;
import org.libresonic.player.util.Util;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import java.io.File; import java.io.File;
import java.util.HashMap;
import java.util.Map;
/** /**
* Superclass for all DAO test cases. * Superclass for all DAO test cases.
@ -32,7 +42,10 @@ public abstract class DaoTestCaseBase extends TestCase {
protected PodcastDao podcastDao; protected PodcastDao podcastDao;
protected DaoTestCaseBase() { protected DaoTestCaseBase() {
daoHelper = new HsqlDaoHelper(); DataSource dataSource = createDataSource();
daoHelper = new GenericDaoHelper(dataSource);
runDatabaseMigration(dataSource);
playerDao = new PlayerDao(); playerDao = new PlayerDao();
internetRadioDao = new InternetRadioDao(); internetRadioDao = new InternetRadioDao();
@ -57,6 +70,34 @@ public abstract class DaoTestCaseBase extends TestCase {
getJdbcTemplate().execute("shutdown"); getJdbcTemplate().execute("shutdown");
} }
private void runDatabaseMigration(DataSource dataSource) {
SpringLiquibase springLiquibase = new SpringLiquibase();
springLiquibase.setDataSource(dataSource);
springLiquibase.setChangeLog("classpath:liquibase/db-changelog.xml");
springLiquibase.setResourceLoader(new DefaultResourceLoader());
Map<String,String> parameters = new HashMap<>();
parameters.put("defaultMusicFolder", Util.getDefaultMusicFolder());
parameters.put("varcharLimit", "512");
parameters.put("userTableQuote", "");
springLiquibase.setChangeLogParameters(parameters);
try {
springLiquibase.afterPropertiesSet();
} catch (LiquibaseException e) {
throw new RuntimeException(e);
}
}
private DataSource createDataSource() {
File libresonicHome = SettingsService.getLibresonicHome();
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUrl("jdbc:hsqldb:file:" + libresonicHome.getPath() + "/db/libresonic");
ds.setUsername("sa");
ds.setPassword("");
return ds;
}
protected JdbcTemplate getJdbcTemplate() { protected JdbcTemplate getJdbcTemplate() {
return daoHelper.getJdbcTemplate(); return daoHelper.getJdbcTemplate();
} }

@ -41,7 +41,13 @@ public class SettingsServiceTestCase extends TestCase {
String libresonicHome = TestCaseUtils.libresonicHomePathForTest(); String libresonicHome = TestCaseUtils.libresonicHomePathForTest();
System.setProperty("libresonic.home", libresonicHome); System.setProperty("libresonic.home", libresonicHome);
new File(libresonicHome, "libresonic.properties").delete(); new File(libresonicHome, "libresonic.properties").delete();
settingsService = new SettingsService(); settingsService = newSettingsService();
}
private SettingsService newSettingsService() {
SettingsService settingsService = new SettingsService();
settingsService.setConfigurationService(new ApacheCommonsConfigurationService());
return settingsService;
} }
public void testLibresonicHome() { public void testLibresonicHome() {
@ -97,7 +103,7 @@ public class SettingsServiceTestCase extends TestCase {
settingsService.save(); settingsService.save();
verifySettings(settingsService); verifySettings(settingsService);
verifySettings(new SettingsService()); verifySettings(newSettingsService());
} }
private void verifySettings(SettingsService ss) { private void verifySettings(SettingsService ss) {

@ -0,0 +1,32 @@
package org.libresonic.player.service;
import junit.framework.TestCase;
import org.apache.commons.io.FileUtils;
import org.libresonic.player.TestCaseUtils;
import org.springframework.context.ApplicationContext;
import java.io.File;
public class StartupTestCase extends TestCase {
private static String baseResources = "/org/libresonic/player/service/mediaScannerServiceTestCase/";
@Override
protected void setUp() throws Exception {
super.setUp();
}
public void testStartup() throws Exception {
String homeParent = TestCaseUtils.libresonicHomePathForTest();
System.setProperty("libresonic.home", TestCaseUtils.libresonicHomePathForTest());
TestCaseUtils.cleanLibresonicHomeForTest();
File dbDirectory = new File(homeParent, "/db");
FileUtils.forceMkdir(dbDirectory);
org.libresonic.player.util.FileUtils.copyResourcesRecursively(getClass().getResource("/db/pre-liquibase/db"), new File(homeParent));
// load spring context
ApplicationContext context = TestCaseUtils.loadSpringApplicationContext(baseResources);
}
}

@ -0,0 +1,123 @@
package org.libresonic.player.util;
import org.apache.commons.lang.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class FileUtils {
public static boolean copyFile(final File toCopy, final File destFile) {
try {
return FileUtils.copyStream(new FileInputStream(toCopy), new FileOutputStream(destFile));
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyFilesRecusively(
final File toCopy, final File destDir
) {
assert destDir.isDirectory();
if (!toCopy.isDirectory()) {
return FileUtils.copyFile(toCopy, new File(destDir, toCopy.getName()));
} else {
final File newDestDir = new File(destDir, toCopy.getName());
if (!newDestDir.exists() && !newDestDir.mkdir()) {
return false;
}
for (final File child : toCopy.listFiles()) {
if (!FileUtils.copyFilesRecusively(child, newDestDir)) {
return false;
}
}
}
return true;
}
public static boolean copyJarResourcesRecursively(
final File destDir, final JarURLConnection jarConnection
) throws IOException {
final JarFile jarFile = jarConnection.getJarFile();
for (final Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
final JarEntry entry = e.nextElement();
if (entry.getName().startsWith(jarConnection.getEntryName())) {
final String filename = StringUtils.removeStart(
entry.getName(), //
jarConnection.getEntryName());
final File f = new File(destDir, filename);
if (!entry.isDirectory()) {
final InputStream entryInputStream = jarFile.getInputStream(entry);
if (!FileUtils.copyStream(entryInputStream, f)) {
return false;
}
entryInputStream.close();
} else {
if (!FileUtils.ensureDirectoryExists(f)) {
throw new IOException("Could not create directory: " + f.getAbsolutePath());
}
}
}
}
return true;
}
public static boolean copyResourcesRecursively(final URL originUrl, final File destination) {
try {
final URLConnection urlConnection = originUrl.openConnection();
if (urlConnection instanceof JarURLConnection) {
return FileUtils.copyJarResourcesRecursively(destination, (JarURLConnection) urlConnection);
} else {
return FileUtils.copyFilesRecusively(new File(originUrl.getPath()), destination);
}
} catch (final IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyStream(final InputStream is, final File f) {
try {
return FileUtils.copyStream(is, new FileOutputStream(f));
} catch (final FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
private static boolean copyStream(final InputStream is, final OutputStream os) {
try {
final byte[] buf = new byte[1024];
int len = 0;
while ((len = is.read(buf)) > 0) {
os.write(buf, 0, len);
}
is.close();
os.close();
return true;
} catch (final IOException e) {
e.printStackTrace();
}
return false;
}
private static boolean ensureDirectoryExists(final File f) {
return f.exists() || f.mkdir();
}
}

File diff suppressed because one or more lines are too long

@ -0,0 +1,17 @@
#HSQL Database Engine 1.8.0.5
#Sun Dec 18 21:11:59 MST 2016
hsqldb.script_format=0
runtime.gc_interval=0
sql.enforce_strict_size=false
hsqldb.cache_size_scale=8
readonly=false
hsqldb.nio_data_file=true
hsqldb.cache_scale=14
version=1.8.0
hsqldb.default_table_type=memory
hsqldb.cache_file_scale=1
hsqldb.log_size=200
modified=yes
hsqldb.cache_version=1.7.0
hsqldb.original_version=1.8.0
hsqldb.compatible_version=1.8.0

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save