Improve Playlist Handling
- Use external library chamelon (lizzy) - Adds the ability to specify playlist export format - Fixes some deficiences with playlist handling Signed-off-by: Andrew DeMaria <lostonamountain@gmail.com>
This commit is contained in:
+108
@@ -0,0 +1,108 @@
|
||||
package org.libresonic.player.service;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.output.ByteArrayOutputStream;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.dao.PlaylistDao;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.service.playlist.DefaultPlaylistExportHandler;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PlaylistServiceTestExport {
|
||||
|
||||
PlaylistService playlistService;
|
||||
|
||||
@InjectMocks
|
||||
DefaultPlaylistExportHandler defaultPlaylistExportHandler;
|
||||
|
||||
@Mock
|
||||
MediaFileDao mediaFileDao;
|
||||
|
||||
@Mock
|
||||
PlaylistDao playlistDao;
|
||||
|
||||
@Mock
|
||||
MediaFileService mediaFileService;
|
||||
|
||||
@Mock
|
||||
SettingsService settingsService;
|
||||
|
||||
@Mock
|
||||
SecurityService securityService;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<Playlist> actual;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<List<MediaFile>> medias;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
playlistService = new PlaylistService(mediaFileDao,
|
||||
playlistDao,
|
||||
securityService,
|
||||
settingsService,
|
||||
Lists.newArrayList(
|
||||
defaultPlaylistExportHandler),
|
||||
Collections.emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExportToM3U() throws Exception {
|
||||
|
||||
when(mediaFileDao.getFilesInPlaylist(eq(23))).thenReturn(getPlaylistFiles());
|
||||
when(settingsService.getPlaylistExportFormat()).thenReturn("m3u");
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
playlistService.exportPlaylist(23, outputStream);
|
||||
String actual = outputStream.toString();
|
||||
Assert.assertEquals(IOUtils.toString(getClass().getResourceAsStream("/PLAYLISTS/23.m3u")), actual);
|
||||
}
|
||||
|
||||
private List<MediaFile> getPlaylistFiles() {
|
||||
List<MediaFile> mediaFiles = new ArrayList<>();
|
||||
|
||||
MediaFile mf1 = new MediaFile();
|
||||
mf1.setId(142);
|
||||
mf1.setPath("/some/path/to_album/to_artist/name - of - song.mp3");
|
||||
mf1.setPresent(true);
|
||||
mediaFiles.add(mf1);
|
||||
|
||||
MediaFile mf2 = new MediaFile();
|
||||
mf2.setId(1235);
|
||||
mf2.setPath("/some/path/to_album2/to_artist/another song.mp3");
|
||||
mf2.setPresent(true);
|
||||
mediaFiles.add(mf2);
|
||||
|
||||
MediaFile mf3 = new MediaFile();
|
||||
mf3.setId(198403);
|
||||
mf3.setPath("/some/path/to_album2/to_artist/another song2.mp3");
|
||||
mf3.setPresent(false);
|
||||
mediaFiles.add(mf3);
|
||||
|
||||
return mediaFiles;
|
||||
}
|
||||
}
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
package org.libresonic.player.service;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.libresonic.player.dao.MediaFileDao;
|
||||
import org.libresonic.player.dao.PlaylistDao;
|
||||
import org.libresonic.player.domain.MediaFile;
|
||||
import org.libresonic.player.domain.Playlist;
|
||||
import org.libresonic.player.service.playlist.DefaultPlaylistExportHandler;
|
||||
import org.libresonic.player.service.playlist.DefaultPlaylistImportHandler;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PlaylistServiceTestImport {
|
||||
|
||||
PlaylistService playlistService;
|
||||
|
||||
@InjectMocks
|
||||
DefaultPlaylistImportHandler defaultPlaylistImportHandler;
|
||||
|
||||
@Mock
|
||||
MediaFileDao mediaFileDao;
|
||||
|
||||
@Mock
|
||||
PlaylistDao playlistDao;
|
||||
|
||||
@Mock
|
||||
MediaFileService mediaFileService;
|
||||
|
||||
@Mock
|
||||
SettingsService settingsService;
|
||||
|
||||
@Mock
|
||||
SecurityService securityService;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<Playlist> actual;
|
||||
|
||||
@Captor
|
||||
ArgumentCaptor<List<MediaFile>> medias;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
playlistService = new PlaylistService(
|
||||
mediaFileDao,
|
||||
playlistDao,
|
||||
securityService,
|
||||
settingsService,
|
||||
Collections.emptyList(),
|
||||
Lists.newArrayList(defaultPlaylistImportHandler));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFromM3U() throws Exception {
|
||||
String username = "testUser";
|
||||
String playlistName = "test-playlist";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("#EXTM3U\n");
|
||||
File mf1 = folder.newFile();
|
||||
FileUtils.touch(mf1);
|
||||
File mf2 = folder.newFile();
|
||||
FileUtils.touch(mf2);
|
||||
File mf3 = folder.newFile();
|
||||
FileUtils.touch(mf3);
|
||||
builder.append(mf1.getAbsolutePath() + "\n");
|
||||
builder.append(mf2.getAbsolutePath() + "\n");
|
||||
builder.append(mf3.getAbsolutePath() + "\n");
|
||||
doAnswer(new PersistPlayList(23)).when(playlistDao).createPlaylist(any());
|
||||
doAnswer(new MediaFileHasEverything()).when(mediaFileService).getMediaFile(any(File.class));
|
||||
InputStream inputStream = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
|
||||
String path = "/path/to/"+playlistName+".m3u";
|
||||
playlistService.importPlaylist(username, playlistName, path, inputStream, null);
|
||||
verify(playlistDao).createPlaylist(actual.capture());
|
||||
verify(playlistDao).setFilesInPlaylist(eq(23), medias.capture());
|
||||
Playlist expected = new Playlist();
|
||||
expected.setUsername(username);
|
||||
expected.setName(playlistName);
|
||||
expected.setComment("Auto-imported from " + path);
|
||||
expected.setImportedFrom(path);
|
||||
expected.setShared(true);
|
||||
expected.setId(23);
|
||||
assertTrue("\n" + ToStringBuilder.reflectionToString(actual.getValue()) + "\n\n did not equal \n\n" + ToStringBuilder.reflectionToString(expected), EqualsBuilder.reflectionEquals(actual.getValue(), expected, "created", "changed"));
|
||||
List<MediaFile> mediaFiles = medias.getValue();
|
||||
assertEquals(3, mediaFiles.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFromPLS() throws Exception {
|
||||
String username = "testUser";
|
||||
String playlistName = "test-playlist";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[playlist]\n");
|
||||
File mf1 = folder.newFile();
|
||||
FileUtils.touch(mf1);
|
||||
File mf2 = folder.newFile();
|
||||
FileUtils.touch(mf2);
|
||||
File mf3 = folder.newFile();
|
||||
FileUtils.touch(mf3);
|
||||
builder.append("File1=" + mf1.getAbsolutePath() + "\n");
|
||||
builder.append("File2=" + mf2.getAbsolutePath() + "\n");
|
||||
builder.append("File3=" + mf3.getAbsolutePath() + "\n");
|
||||
doAnswer(new PersistPlayList(23)).when(playlistDao).createPlaylist(any());
|
||||
doAnswer(new MediaFileHasEverything()).when(mediaFileService).getMediaFile(any(File.class));
|
||||
InputStream inputStream = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
|
||||
String path = "/path/to/"+playlistName+".pls";
|
||||
playlistService.importPlaylist(username, playlistName, path, inputStream, null);
|
||||
verify(playlistDao).createPlaylist(actual.capture());
|
||||
verify(playlistDao).setFilesInPlaylist(eq(23), medias.capture());
|
||||
Playlist expected = new Playlist();
|
||||
expected.setUsername(username);
|
||||
expected.setName(playlistName);
|
||||
expected.setComment("Auto-imported from " + path);
|
||||
expected.setImportedFrom(path);
|
||||
expected.setShared(true);
|
||||
expected.setId(23);
|
||||
assertTrue("\n" + ToStringBuilder.reflectionToString(actual.getValue()) + "\n\n did not equal \n\n" + ToStringBuilder.reflectionToString(expected), EqualsBuilder.reflectionEquals(actual.getValue(), expected, "created", "changed"));
|
||||
List<MediaFile> mediaFiles = medias.getValue();
|
||||
assertEquals(3, mediaFiles.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFromXSPF() throws Exception {
|
||||
String username = "testUser";
|
||||
String playlistName = "test-playlist";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<playlist version=\"1\" xmlns=\"http://xspf.org/ns/0/\">\n"
|
||||
+ " <trackList>\n");
|
||||
File mf1 = folder.newFile();
|
||||
FileUtils.touch(mf1);
|
||||
File mf2 = folder.newFile();
|
||||
FileUtils.touch(mf2);
|
||||
File mf3 = folder.newFile();
|
||||
FileUtils.touch(mf3);
|
||||
builder.append("<track><location>file://" + mf1.getAbsolutePath() + "</location></track>\n");
|
||||
builder.append("<track><location>file://" + mf2.getAbsolutePath() + "</location></track>\n");
|
||||
builder.append("<track><location>file://" + mf3.getAbsolutePath() + "</location></track>\n");
|
||||
builder.append(" </trackList>\n" + "</playlist>\n");
|
||||
doAnswer(new PersistPlayList(23)).when(playlistDao).createPlaylist(any());
|
||||
doAnswer(new MediaFileHasEverything()).when(mediaFileService).getMediaFile(any(File.class));
|
||||
InputStream inputStream = new ByteArrayInputStream(builder.toString().getBytes("UTF-8"));
|
||||
String path = "/path/to/"+playlistName+".xspf";
|
||||
playlistService.importPlaylist(username, playlistName, path, inputStream, null);
|
||||
verify(playlistDao).createPlaylist(actual.capture());
|
||||
verify(playlistDao).setFilesInPlaylist(eq(23), medias.capture());
|
||||
Playlist expected = new Playlist();
|
||||
expected.setUsername(username);
|
||||
expected.setName(playlistName);
|
||||
expected.setComment("Auto-imported from " + path);
|
||||
expected.setImportedFrom(path);
|
||||
expected.setShared(true);
|
||||
expected.setId(23);
|
||||
assertTrue("\n" + ToStringBuilder.reflectionToString(actual.getValue()) + "\n\n did not equal \n\n" + ToStringBuilder.reflectionToString(expected), EqualsBuilder.reflectionEquals(actual.getValue(), expected, "created", "changed"));
|
||||
List<MediaFile> mediaFiles = medias.getValue();
|
||||
assertEquals(3, mediaFiles.size());
|
||||
}
|
||||
|
||||
private class PersistPlayList implements Answer {
|
||||
private final int id;
|
||||
public PersistPlayList(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
Playlist playlist = invocationOnMock.getArgument(0);
|
||||
playlist.setId(id);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaFileHasEverything implements Answer {
|
||||
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||
File file = invocationOnMock.getArgument(0);
|
||||
MediaFile mediaFile = new MediaFile();
|
||||
mediaFile.setPath(file.getPath());
|
||||
return mediaFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
/some/path/to_album/to_artist/name - of - song.mp3
|
||||
/some/path/to_album2/to_artist/another song.mp3
|
||||
/some/path/to_album2/to_artist/another song2.mp3
|
||||
@@ -0,0 +1,6 @@
|
||||
[playlist]
|
||||
File1=/some/path/to_album/to_artist/name - of - song.mp3
|
||||
File2=/some/path/to_album2/to_artist/another song.mp3
|
||||
File3=/some/path/to_album2/to_artist/another song2.mp3
|
||||
NumberOfEntries=3
|
||||
Version=2
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<playlist version="1" xmlns="http://xspf.org/ns/0/">
|
||||
<trackList>
|
||||
<track><location>file:///some/path/to_album/to_artist/name - of - song.mp3</location></track>
|
||||
<track><location>file:///some/path/to_album2/to_artist/another song.mp3</location></track>
|
||||
<track><location>file:///some/path/to_album2/to_artist/another song2.mp3</location></track>
|
||||
</trackList>
|
||||
</playlist>
|
||||
Reference in New Issue
Block a user