- 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>master
parent
e36d64dc04
commit
9584bfaea5
@ -0,0 +1,49 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.content.Content; |
||||||
|
import chameleon.playlist.Media; |
||||||
|
import chameleon.playlist.Playlist; |
||||||
|
import chameleon.playlist.SpecificPlaylist; |
||||||
|
import chameleon.playlist.SpecificPlaylistProvider; |
||||||
|
import org.libresonic.player.dao.MediaFileDao; |
||||||
|
import org.libresonic.player.domain.MediaFile; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Component |
||||||
|
public class DefaultPlaylistExportHandler implements PlaylistExportHandler { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MediaFileDao mediaFileDao; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean canHandle(Class<? extends SpecificPlaylistProvider> providerClass) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SpecificPlaylist handle(int id, SpecificPlaylistProvider provider) throws Exception { |
||||||
|
chameleon.playlist.Playlist playlist = createChameleonGenericPlaylistFromDBId(id); |
||||||
|
return provider.toSpecificPlaylist(playlist); |
||||||
|
} |
||||||
|
|
||||||
|
Playlist createChameleonGenericPlaylistFromDBId(int id) { |
||||||
|
Playlist newPlaylist = new Playlist(); |
||||||
|
List<MediaFile> files = mediaFileDao.getFilesInPlaylist(id); |
||||||
|
files.forEach(file -> { |
||||||
|
Media component = new Media(); |
||||||
|
Content content = new Content(file.getPath()); |
||||||
|
component.setSource(content); |
||||||
|
newPlaylist.getRootSequence().addComponent(component); |
||||||
|
}); |
||||||
|
return newPlaylist; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return Ordered.LOWEST_PRECEDENCE; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,97 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.playlist.*; |
||||||
|
import org.libresonic.player.domain.MediaFile; |
||||||
|
import org.libresonic.player.service.MediaFileService; |
||||||
|
import org.libresonic.player.util.Pair; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.net.URI; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Component |
||||||
|
public class DefaultPlaylistImportHandler implements PlaylistImportHandler { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MediaFileService mediaFileService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean canHandle(Class<? extends SpecificPlaylist> playlistClass) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Pair<List<MediaFile>, List<String>> handle( |
||||||
|
SpecificPlaylist inputSpecificPlaylist |
||||||
|
) { |
||||||
|
List<MediaFile> mediaFiles = new ArrayList<>(); |
||||||
|
List<String> errors = new ArrayList<>(); |
||||||
|
try { |
||||||
|
inputSpecificPlaylist.toPlaylist().acceptDown(new PlaylistVisitor() { |
||||||
|
@Override |
||||||
|
public void beginVisitPlaylist(Playlist playlist) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void endVisitPlaylist(Playlist playlist) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void beginVisitParallel(Parallel parallel) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void endVisitParallel(Parallel parallel) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void beginVisitSequence(Sequence sequence) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void endVisitSequence(Sequence sequence) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void beginVisitMedia(Media media) throws Exception { |
||||||
|
try { |
||||||
|
URI uri = media.getSource().getURI(); |
||||||
|
File file = new File(uri); |
||||||
|
MediaFile mediaFile = mediaFileService.getMediaFile(file); |
||||||
|
if(mediaFile != null) { |
||||||
|
mediaFiles.add(mediaFile); |
||||||
|
} else { |
||||||
|
errors.add("Cannot find media file " + file); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
errors.add(e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void endVisitMedia(Media media) throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
}); |
||||||
|
} catch (Exception e) { |
||||||
|
errors.add(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
return Pair.create(mediaFiles, errors); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return Ordered.LOWEST_PRECEDENCE; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.playlist.SpecificPlaylist; |
||||||
|
import chameleon.playlist.SpecificPlaylistProvider; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
|
||||||
|
public interface PlaylistExportHandler extends Ordered { |
||||||
|
boolean canHandle(Class<? extends SpecificPlaylistProvider> providerClass); |
||||||
|
|
||||||
|
SpecificPlaylist handle(int id, SpecificPlaylistProvider provider) throws Exception; |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.playlist.SpecificPlaylist; |
||||||
|
import org.libresonic.player.domain.MediaFile; |
||||||
|
import org.libresonic.player.util.Pair; |
||||||
|
import org.springframework.core.Ordered; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public interface PlaylistImportHandler extends Ordered { |
||||||
|
boolean canHandle(Class<? extends SpecificPlaylist> playlistClass); |
||||||
|
|
||||||
|
Pair<List<MediaFile>,List<String>> handle(SpecificPlaylist inputSpecificPlaylist); |
||||||
|
} |
@ -0,0 +1,66 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.playlist.SpecificPlaylist; |
||||||
|
import chameleon.playlist.SpecificPlaylistProvider; |
||||||
|
import chameleon.playlist.xspf.Location; |
||||||
|
import chameleon.playlist.xspf.Track; |
||||||
|
import chameleon.playlist.xspf.XspfProvider; |
||||||
|
import org.libresonic.player.dao.MediaFileDao; |
||||||
|
import org.libresonic.player.dao.PlaylistDao; |
||||||
|
import org.libresonic.player.domain.MediaFile; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Component |
||||||
|
public class XspfPlaylistExportHandler implements PlaylistExportHandler { |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MediaFileDao mediaFileDao; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
PlaylistDao playlistDao; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean canHandle(Class<? extends SpecificPlaylistProvider> providerClass) { |
||||||
|
return XspfProvider.class.equals(providerClass); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public SpecificPlaylist handle(int id, SpecificPlaylistProvider provider) throws Exception { |
||||||
|
chameleon.playlist.xspf.Playlist playlist = createXsfpPlaylistFromDBId(id); |
||||||
|
return playlist; |
||||||
|
} |
||||||
|
|
||||||
|
chameleon.playlist.xspf.Playlist createXsfpPlaylistFromDBId(int id) { |
||||||
|
chameleon.playlist.xspf.Playlist newPlaylist = new chameleon.playlist.xspf.Playlist(); |
||||||
|
org.libresonic.player.domain.Playlist playlist = playlistDao.getPlaylist(id); |
||||||
|
newPlaylist.setTitle(playlist.getName()); |
||||||
|
newPlaylist.setCreator("Libresonic user " + playlist.getUsername()); |
||||||
|
newPlaylist.setDate(new Date()); |
||||||
|
List<MediaFile> files = mediaFileDao.getFilesInPlaylist(id); |
||||||
|
|
||||||
|
files.stream().map(mediaFile -> { |
||||||
|
Track track = new Track(); |
||||||
|
track.setTrackNumber(mediaFile.getTrackNumber()); |
||||||
|
track.setCreator(mediaFile.getArtist()); |
||||||
|
track.setTitle(mediaFile.getTitle()); |
||||||
|
track.setAlbum(mediaFile.getAlbumName()); |
||||||
|
track.setDuration(mediaFile.getDurationSeconds()); |
||||||
|
track.setImage(mediaFile.getCoverArtPath()); |
||||||
|
Location location = new Location(); |
||||||
|
location.setText(mediaFile.getPath()); |
||||||
|
track.getStringContainers().add(location); |
||||||
|
return track; |
||||||
|
}).forEach(newPlaylist::addTrack); |
||||||
|
|
||||||
|
return newPlaylist; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package org.libresonic.player.service.playlist; |
||||||
|
|
||||||
|
import chameleon.playlist.SpecificPlaylist; |
||||||
|
import chameleon.playlist.xspf.Location; |
||||||
|
import chameleon.playlist.xspf.Playlist; |
||||||
|
import chameleon.playlist.xspf.StringContainer; |
||||||
|
import org.libresonic.player.Logger; |
||||||
|
import org.libresonic.player.domain.MediaFile; |
||||||
|
import org.libresonic.player.service.MediaFileService; |
||||||
|
import org.libresonic.player.util.Pair; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.net.URI; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
@Component |
||||||
|
public class XspfPlaylistImportHandler implements PlaylistImportHandler { |
||||||
|
|
||||||
|
private static final Logger LOG = Logger.getLogger(XspfPlaylistImportHandler.class); |
||||||
|
|
||||||
|
@Autowired |
||||||
|
MediaFileService mediaFileService; |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean canHandle(Class<? extends SpecificPlaylist> playlistClass) { |
||||||
|
return Playlist.class.equals(playlistClass); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Pair<List<MediaFile>, List<String>> handle(SpecificPlaylist inputSpecificPlaylist) { |
||||||
|
List<MediaFile> mediaFiles = new ArrayList<>(); |
||||||
|
List<String> errors = new ArrayList<>(); |
||||||
|
Playlist xspfPlaylist = (Playlist) inputSpecificPlaylist; |
||||||
|
xspfPlaylist.getTracks().forEach(track -> { |
||||||
|
MediaFile mediaFile = null; |
||||||
|
for(StringContainer sc : track.getStringContainers()) { |
||||||
|
if(sc instanceof Location) { |
||||||
|
Location location = (Location) sc; |
||||||
|
try { |
||||||
|
File file = new File(new URI(location.getText())); |
||||||
|
mediaFile = mediaFileService.getMediaFile(file); |
||||||
|
} catch (Exception ignored) {} |
||||||
|
|
||||||
|
if(mediaFile == null) { |
||||||
|
try { |
||||||
|
File file = new File(sc.getText()); |
||||||
|
mediaFile = mediaFileService.getMediaFile(file); |
||||||
|
} catch (Exception ignored) {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if(mediaFile != null) { |
||||||
|
mediaFiles.add(mediaFile); |
||||||
|
} else { |
||||||
|
String errorMsg = "Could not find media file matching "; |
||||||
|
try { |
||||||
|
errorMsg += track.getStringContainers().stream().map(StringContainer::getText).collect(Collectors.joining(",")); |
||||||
|
} catch (Exception ignored) {} |
||||||
|
errors.add(errorMsg); |
||||||
|
} |
||||||
|
}); |
||||||
|
return Pair.create(mediaFiles, errors); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getOrder() { |
||||||
|
return 40; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
# First one, as it is a binary format that can easily be recognized. |
||||||
|
chameleon.playlist.pla.PLAProvider |
||||||
|
chameleon.playlist.asx.AsxProvider |
||||||
|
chameleon.playlist.b4s.B4sProvider |
||||||
|
# BEFORE SMIL (same root element). |
||||||
|
chameleon.playlist.wpl.WplProvider |
||||||
|
chameleon.playlist.smil.SmilProvider |
||||||
|
chameleon.playlist.rss.RSSProvider |
||||||
|
chameleon.playlist.atom.AtomProvider |
||||||
|
# Before XSPF, because this format is very close to XSPF, |
||||||
|
# but its XML format is strictly checked (and XSPF's format is not) |
||||||
|
chameleon.playlist.hypetape.HypetapeProvider |
||||||
|
chameleon.playlist.xspf.XspfProvider |
||||||
|
chameleon.playlist.rmp.RmpProvider |
||||||
|
chameleon.playlist.plist.PlistProvider |
||||||
|
chameleon.playlist.kpl.KplProvider |
||||||
|
chameleon.playlist.pls.PLSProvider |
||||||
|
chameleon.playlist.mpcpl.MPCPLProvider |
||||||
|
chameleon.playlist.plp.PLPProvider |
||||||
|
# Shall be last, as the M3U format can match almost everything. |
||||||
|
chameleon.playlist.m3u.M3UProvider |
@ -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; |
||||||
|
} |
||||||
|
} |
@ -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> |
Loading…
Reference in new issue