This replaces the existing FolderBasedContentDirectory with a new DispatchingContentDirectory. The new system then dispatches requests to subclasses of UpnpContentProcessor, which then handle the requests as appropriate. This version also implements a new searchByName method in SearchService, which is used to provide artist, album, and track search functionality for the upnp service.master
parent
bcd330e74d
commit
b3ae8faac6
@ -0,0 +1,59 @@ |
||||
/* |
||||
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.domain; |
||||
|
||||
import org.libresonic.player.service.SearchService; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* The outcome of a search. |
||||
* |
||||
* @author Sindre Mehus |
||||
* @see SearchService#search |
||||
*/ |
||||
public class ParamSearchResult<T> { |
||||
|
||||
private final List<T> items = new ArrayList<T>(); |
||||
|
||||
private int offset; |
||||
private int totalHits; |
||||
|
||||
public List<T> getItems() { |
||||
return items; |
||||
} |
||||
|
||||
public int getOffset() { |
||||
return offset; |
||||
} |
||||
|
||||
public void setOffset(int offset) { |
||||
this.offset = offset; |
||||
} |
||||
|
||||
public int getTotalHits() { |
||||
return totalHits; |
||||
} |
||||
|
||||
public void setTotalHits(int totalHits) { |
||||
this.totalHits = totalHits; |
||||
} |
||||
} |
@ -0,0 +1,136 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.PersonWithRole; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.MusicAlbum; |
||||
import org.libresonic.player.dao.AlbumDao; |
||||
import org.libresonic.player.dao.MediaFileDao; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.service.SearchService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.web.util.UriComponentsBuilder; |
||||
|
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public class AlbumUpnpProcessor extends UpnpContentProcessor <Album, MediaFile> { |
||||
|
||||
public static final String ALL_BY_ARTIST = "allByArtist"; |
||||
public static final String ALL_RECENT = "allRecent"; |
||||
|
||||
@Autowired |
||||
AlbumDao albumDao; |
||||
|
||||
@Autowired |
||||
SearchService searchService; |
||||
|
||||
public AlbumUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_ALBUM_PREFIX); |
||||
setRootTitle("Albums"); |
||||
} |
||||
|
||||
public Container createContainer(Album album) throws Exception { |
||||
MusicAlbum container = new MusicAlbum(); |
||||
|
||||
if (album.getId() == -1) { |
||||
container.setId(getRootId() + DispatchingContentDirectory.SEPARATOR + album.getComment()); |
||||
} else { |
||||
container.setId(getRootId() + DispatchingContentDirectory.SEPARATOR + album.getId()); |
||||
container.setAlbumArtURIs(new URI[] { getAlbumArtURI(album.getId()) }); |
||||
container.setDescription(album.getComment()); |
||||
} |
||||
container.setParentID(getRootId()); |
||||
container.setTitle(album.getName()); |
||||
// TODO: correct artist?
|
||||
if (album.getArtist() != null) { |
||||
container.setArtists(getAlbumArtists(album.getArtist())); |
||||
} |
||||
return container; |
||||
} |
||||
|
||||
public List<Album> getAllItems() { |
||||
List<MusicFolder> allFolders = getDispatchingContentDirectory().getSettingsService().getAllMusicFolders(); |
||||
return getAlbumDao().getAlphabetialAlbums(0, 0, false, allFolders); |
||||
} |
||||
|
||||
public Album getItemById(String id) throws Exception { |
||||
Album returnValue = null; |
||||
if (id.startsWith(ALL_BY_ARTIST) || id.equalsIgnoreCase(ALL_RECENT)) { |
||||
returnValue = new Album(); |
||||
returnValue.setId(-1); |
||||
returnValue.setComment(id); |
||||
} else { |
||||
returnValue = getAlbumDao().getAlbum(Integer.parseInt(id)); |
||||
} |
||||
return returnValue; |
||||
} |
||||
|
||||
public List<MediaFile> getChildren(Album album) throws Exception { |
||||
List<MediaFile> allFiles = getMediaFileDao().getSongsForAlbum(album.getArtist(), album.getName()); |
||||
if (album.getId() == -1) { |
||||
List<Album> albumList = null; |
||||
if (album.getComment().startsWith(ALL_BY_ARTIST)) { |
||||
ArtistUpnpProcessor ap = getDispatcher().getArtistProcessor(); |
||||
albumList = ap.getChildren(ap.getItemById(album.getComment().replaceAll(ALL_BY_ARTIST + "_", ""))); |
||||
} else if (album.getComment().equalsIgnoreCase(ALL_RECENT)) { |
||||
albumList = getDispatcher().getRecentAlbumProcessor().getAllItems(); |
||||
} |
||||
for (Album a: albumList) { |
||||
if (a.getId() != -1) { |
||||
allFiles.addAll(getMediaFileDao().getSongsForAlbum(a.getArtist(), a.getName())); |
||||
} |
||||
} |
||||
} else { |
||||
allFiles = getMediaFileDao().getSongsForAlbum(album.getArtist(), album.getName()); |
||||
} |
||||
return allFiles; |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, MediaFile child) throws Exception { |
||||
didl.addItem(getDispatcher().getMediaFileProcessor().createItem(child)); |
||||
} |
||||
|
||||
public URI getAlbumArtURI(int albumId) throws URISyntaxException { |
||||
return getDispatcher().getJwtSecurityService().addJWTToken(UriComponentsBuilder.fromUriString(getDispatcher().getBaseUrl() + "/ext/coverArt.view").queryParam("id", albumId).queryParam("size", CoverArtScheme.LARGE.getSize())).build().encode().toUri(); |
||||
} |
||||
|
||||
public PersonWithRole[] getAlbumArtists(String artist) { |
||||
return new PersonWithRole[] { new PersonWithRole(artist) }; |
||||
} |
||||
|
||||
public AlbumDao getAlbumDao() { |
||||
return albumDao; |
||||
} |
||||
public void setAlbumDao(AlbumDao albumDao) { |
||||
this.albumDao = albumDao; |
||||
} |
||||
|
||||
public MediaFileDao getMediaFileDao() { |
||||
return getDispatcher().getMediaFileProcessor().getMediaFileDao(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,99 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
|
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.MusicArtist; |
||||
import org.libresonic.player.dao.ArtistDao; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.service.SearchService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public class ArtistUpnpProcessor extends UpnpContentProcessor <Artist, Album> { |
||||
|
||||
@Autowired |
||||
private ArtistDao artistDao; |
||||
|
||||
@Autowired |
||||
SearchService searchService; |
||||
|
||||
public ArtistUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_ARTIST_PREFIX); |
||||
setRootTitle("Artists"); |
||||
} |
||||
|
||||
public Container createContainer(Artist artist) { |
||||
MusicArtist container = new MusicArtist(); |
||||
container.setId(getRootId() + DispatchingContentDirectory.SEPARATOR + artist.getId()); |
||||
container.setParentID(getRootId()); |
||||
container.setTitle(artist.getName()); |
||||
container.setChildCount(artist.getAlbumCount()); |
||||
|
||||
return container; |
||||
} |
||||
|
||||
public List<Artist> getAllItems() { |
||||
List<MusicFolder> allFolders = getDispatcher().getSettingsService().getAllMusicFolders(); |
||||
List<Artist> allArtists = getArtistDao().getAlphabetialArtists(0, Integer.MAX_VALUE, allFolders); |
||||
// alpha artists doesn't quite work :P
|
||||
allArtists.sort((Artist o1, Artist o2)->o1.getName().replaceAll("\\W", "").compareToIgnoreCase(o2.getName().replaceAll("\\W", ""))); |
||||
|
||||
return allArtists; |
||||
} |
||||
|
||||
public Artist getItemById(String id) throws Exception { |
||||
return getArtistDao().getArtist(Integer.parseInt(id)); |
||||
} |
||||
|
||||
public List<Album> getChildren(Artist artist) { |
||||
List<MusicFolder> allFolders = getDispatcher().getSettingsService().getAllMusicFolders(); |
||||
List<Album> allAlbums = getAlbumProcessor().getAlbumDao().getAlbumsForArtist(artist.getName(), allFolders); |
||||
if (allAlbums.size() > 1) { |
||||
Album viewAll = new Album(); |
||||
viewAll.setName("- All Albums -"); |
||||
viewAll.setId(-1); |
||||
viewAll.setComment(AlbumUpnpProcessor.ALL_BY_ARTIST + "_" + artist.getId()); |
||||
allAlbums.add(0, viewAll); |
||||
} |
||||
return allAlbums; |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, Album album) throws Exception { |
||||
didl.addContainer(getAlbumProcessor().createContainer(album)); |
||||
} |
||||
|
||||
public ArtistDao getArtistDao() { |
||||
return artistDao; |
||||
} |
||||
public void setArtistDao(ArtistDao artistDao) { |
||||
this.artistDao = artistDao; |
||||
} |
||||
|
||||
public AlbumUpnpProcessor getAlbumProcessor() { |
||||
return getDispatcher().getAlbumProcessor(); |
||||
} |
||||
} |
@ -0,0 +1,292 @@ |
||||
/* |
||||
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.service.upnp; |
||||
|
||||
import org.fourthline.cling.support.contentdirectory.ContentDirectoryErrorCode; |
||||
import org.fourthline.cling.support.contentdirectory.ContentDirectoryException; |
||||
import org.fourthline.cling.support.model.BrowseFlag; |
||||
import org.fourthline.cling.support.model.BrowseResult; |
||||
import org.fourthline.cling.support.model.DIDLObject; |
||||
import org.fourthline.cling.support.model.PersonWithRole; |
||||
import org.fourthline.cling.support.model.SortCriterion; |
||||
import org.fourthline.cling.support.model.item.Item; |
||||
import org.fourthline.cling.support.model.item.MusicTrack; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.service.JWTSecurityService; |
||||
import org.libresonic.player.service.MediaFileService; |
||||
import org.libresonic.player.service.MusicIndexService; |
||||
import org.libresonic.player.service.PlaylistService; |
||||
import org.libresonic.player.service.SearchService; |
||||
import org.libresonic.player.service.SettingsService; |
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.web.util.UriComponentsBuilder; |
||||
|
||||
import java.net.URI; |
||||
import java.net.URISyntaxException; |
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @author Sindre Mehus |
||||
* @version $Id$ |
||||
*/ |
||||
public class DispatchingContentDirectory extends LibresonicContentDirectory { |
||||
|
||||
public static final Logger LOG = LoggerFactory.getLogger(FolderBasedContentDirectory.class); |
||||
|
||||
public static final String CONTAINER_ID_ROOT = "0"; |
||||
public static final String CONTAINER_ID_PLAYLIST_PREFIX = "playlist"; |
||||
public static final String CONTAINER_ID_FOLDER_PREFIX = "folder"; |
||||
public static final String CONTAINER_ID_ALBUM_PREFIX = "album"; |
||||
public static final String CONTAINER_ID_ARTIST_PREFIX = "artist"; |
||||
public static final String CONTAINER_ID_ARTISTALBUM_PREFIX = "artistalbum"; |
||||
public static final String CONTAINER_ID_GENRE_PREFIX = "genre"; |
||||
public static final String CONTAINER_ID_RECENT_PREFIX = "recent"; |
||||
|
||||
protected static final String SEPARATOR = "-"; |
||||
|
||||
@Autowired |
||||
private PlaylistUpnpProcessor playlistProcessor; |
||||
@Autowired |
||||
private MediaFileUpnpProcessor mediaFileProcessor; |
||||
//@Autowired can't autowire because of the subclassing :P
|
||||
private AlbumUpnpProcessor albumProcessor; |
||||
//@Autowired can't autowire because of the subclassing :P
|
||||
private RecentAlbumUpnpProcessor recentAlbumProcessor; |
||||
@Autowired |
||||
private ArtistUpnpProcessor artistProcessor; |
||||
@Autowired |
||||
private GenreUpnpProcessor genreProcessor; |
||||
@Autowired |
||||
private RootUpnpProcessor rootProcessor; |
||||
|
||||
@Autowired |
||||
private MediaFileService mediaFileService; |
||||
|
||||
private PlaylistService playlistService; |
||||
|
||||
@Autowired |
||||
private MusicIndexService musicIndexService; |
||||
|
||||
@Autowired |
||||
private SearchService searchService; |
||||
|
||||
|
||||
@Override |
||||
public BrowseResult browse(String objectId, BrowseFlag browseFlag, |
||||
String filter, long firstResult, |
||||
long maxResults, SortCriterion[] orderBy) |
||||
throws ContentDirectoryException { |
||||
|
||||
LOG.info("UPnP request - objectId: " + objectId + ", browseFlag: " + browseFlag + ", filter: " + filter + ", firstResult: " + firstResult + ", maxResults: " + maxResults); |
||||
|
||||
if (objectId == null) |
||||
throw new ContentDirectoryException(ContentDirectoryErrorCode.CANNOT_PROCESS, "objectId is null"); |
||||
|
||||
// maxResult == 0 means all.
|
||||
if (maxResults == 0) { |
||||
maxResults = Long.MAX_VALUE; |
||||
} |
||||
|
||||
BrowseResult returnValue = null; |
||||
try { |
||||
String[] splitId = objectId.split(SEPARATOR); |
||||
String browseRoot = splitId[0]; |
||||
String itemId = splitId.length == 1 ? null : splitId[1]; |
||||
|
||||
UpnpContentProcessor processor = findProcessor(browseRoot); |
||||
if (processor == null) { |
||||
// if it's null then assume it's a file, and that the id
|
||||
// is all that's there.
|
||||
itemId = browseRoot; |
||||
browseRoot = "folder"; |
||||
processor = getMediaFileProcessor(); |
||||
} |
||||
|
||||
if (itemId == null) { |
||||
returnValue = browseFlag == BrowseFlag.METADATA ? processor.browseRootMetadata() : processor.browseRoot(filter, firstResult, maxResults, orderBy); |
||||
} else { |
||||
returnValue = browseFlag == BrowseFlag.METADATA ? processor.browseObjectMetadata(itemId) : processor.browseObject(itemId, filter, firstResult, maxResults, orderBy); |
||||
} |
||||
return returnValue; |
||||
} catch (Throwable x) { |
||||
LOG.error("UPnP error: " + x, x); |
||||
throw new ContentDirectoryException(ContentDirectoryErrorCode.CANNOT_PROCESS, x.toString()); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public BrowseResult search(String containerId, |
||||
String searchCriteria, String filter, |
||||
long firstResult, long maxResults, |
||||
SortCriterion[] orderBy) throws ContentDirectoryException { |
||||
// i don't see a parser for upnp search criteria anywhere, so this will
|
||||
// have to do
|
||||
String upnpClass = searchCriteria.replaceAll("^.*upnp:class\\s+[\\S]+\\s+\"([\\S]*)\".*$", "$1"); |
||||
String titleSearch = searchCriteria.replaceAll("^.*dc:title\\s+[\\S]+\\s+\"([\\S]*)\".*$", "$1"); |
||||
BrowseResult returnValue = null; |
||||
if ("object.container.person.musicArtist".equalsIgnoreCase(upnpClass)) { |
||||
returnValue = getArtistProcessor().searchByName(titleSearch, firstResult, maxResults, orderBy); |
||||
} else if ("object.item.audioItem".equalsIgnoreCase(upnpClass)) { |
||||
returnValue = getMediaFileProcessor().searchByName(titleSearch, firstResult, maxResults, orderBy); |
||||
} else if ("object.container.album.musicAlbum".equalsIgnoreCase(upnpClass)) { |
||||
returnValue = getAlbumProcessor().searchByName(titleSearch, firstResult, maxResults, orderBy); |
||||
} |
||||
|
||||
return returnValue != null ? returnValue : super.search(containerId, searchCriteria, filter, firstResult, maxResults, orderBy); |
||||
} |
||||
|
||||
|
||||
private UpnpContentProcessor findProcessor(String type) { |
||||
switch(type) { |
||||
case CONTAINER_ID_ROOT: |
||||
return getRootProcessor(); |
||||
case CONTAINER_ID_PLAYLIST_PREFIX: |
||||
return getPlaylistProcessor(); |
||||
case CONTAINER_ID_FOLDER_PREFIX: |
||||
return getMediaFileProcessor(); |
||||
case CONTAINER_ID_ALBUM_PREFIX: |
||||
return getAlbumProcessor(); |
||||
case CONTAINER_ID_RECENT_PREFIX: |
||||
return getRecentAlbumProcessor(); |
||||
case CONTAINER_ID_ARTIST_PREFIX: |
||||
return getArtistProcessor(); |
||||
case CONTAINER_ID_GENRE_PREFIX: |
||||
return getGenreProcessor(); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public Item createItem(MediaFile song) throws Exception { |
||||
MediaFile parent = mediaFileService.getParentOf(song); |
||||
MusicTrack item = new MusicTrack(); |
||||
item.setId(String.valueOf(song.getId())); |
||||
item.setParentID(String.valueOf(parent.getId())); |
||||
item.setTitle(song.getTitle()); |
||||
item.setAlbum(song.getAlbumName()); |
||||
if (song.getArtist() != null) { |
||||
item.setArtists(new PersonWithRole[]{new PersonWithRole(song.getArtist())}); |
||||
} |
||||
Integer year = song.getYear(); |
||||
if (year != null) { |
||||
item.setDate(year + "-01-01"); |
||||
} |
||||
item.setOriginalTrackNumber(song.getTrackNumber()); |
||||
if (song.getGenre() != null) { |
||||
item.setGenres(new String[]{song.getGenre()}); |
||||
} |
||||
item.setResources(Arrays.asList(createResourceForSong(song))); |
||||
item.setDescription(song.getComment()); |
||||
item.addProperty(new DIDLObject.Property.UPNP.ALBUM_ART_URI(getAlbumArtUrl(parent.getId()))); |
||||
|
||||
return item; |
||||
} |
||||
|
||||
public URI getAlbumArtUrl(int id) throws URISyntaxException { |
||||
return jwtSecurityService.addJWTToken(UriComponentsBuilder.fromUriString(getBaseUrl() + "/ext/coverArt.view").queryParam("id", id).queryParam("size", CoverArtScheme.LARGE.getSize())).build().encode().toUri(); |
||||
} |
||||
|
||||
public PlaylistUpnpProcessor getPlaylistProcessor() { |
||||
return playlistProcessor; |
||||
} |
||||
public void setPlaylistProcessor(PlaylistUpnpProcessor playlistProcessor) { |
||||
this.playlistProcessor = playlistProcessor; |
||||
} |
||||
|
||||
public MediaFileUpnpProcessor getMediaFileProcessor() { |
||||
return mediaFileProcessor; |
||||
} |
||||
public void setMediaFileProcessor(MediaFileUpnpProcessor mediaFileProcessor) { |
||||
this.mediaFileProcessor = mediaFileProcessor; |
||||
} |
||||
|
||||
public AlbumUpnpProcessor getAlbumProcessor() { |
||||
return albumProcessor; |
||||
} |
||||
public void setAlbumProcessor(AlbumUpnpProcessor albumProcessor) { |
||||
this.albumProcessor = albumProcessor; |
||||
} |
||||
|
||||
public RecentAlbumUpnpProcessor getRecentAlbumProcessor() { |
||||
return recentAlbumProcessor; |
||||
} |
||||
public void setRecentAlbumProcessor(RecentAlbumUpnpProcessor recentAlbumProcessor) { |
||||
this.recentAlbumProcessor = recentAlbumProcessor; |
||||
} |
||||
|
||||
public ArtistUpnpProcessor getArtistProcessor() { |
||||
return artistProcessor; |
||||
} |
||||
public void setArtistProcessor(ArtistUpnpProcessor artistProcessor) { |
||||
this.artistProcessor = artistProcessor; |
||||
} |
||||
|
||||
public GenreUpnpProcessor getGenreProcessor() { |
||||
return genreProcessor; |
||||
} |
||||
public void setGenreProcessor(GenreUpnpProcessor genreProcessor) { |
||||
this.genreProcessor = genreProcessor; |
||||
} |
||||
|
||||
public RootUpnpProcessor getRootProcessor() { |
||||
return rootProcessor; |
||||
} |
||||
public void setRootProcessor(RootUpnpProcessor rootProcessor) { |
||||
this.rootProcessor = rootProcessor; |
||||
} |
||||
|
||||
public MediaFileService getMediaFileService() { |
||||
return mediaFileService; |
||||
} |
||||
public void setMediaFileService(MediaFileService mediaFileService) { |
||||
this.mediaFileService = mediaFileService; |
||||
} |
||||
|
||||
public SettingsService getSettingsService() { |
||||
return settingsService; |
||||
} |
||||
|
||||
public PlaylistService getPlaylistService() { |
||||
return playlistService; |
||||
} |
||||
public void setPlaylistService(PlaylistService playlistService) { |
||||
this.playlistService = playlistService; |
||||
} |
||||
|
||||
public JWTSecurityService getJwtSecurityService() { |
||||
return jwtSecurityService; |
||||
} |
||||
|
||||
public MusicIndexService getMusicIndexService() { |
||||
return this.musicIndexService; |
||||
} |
||||
public void setMusicIndexService(MusicIndexService musicIndexService) { |
||||
this.musicIndexService = musicIndexService; |
||||
} |
||||
|
||||
public SearchService getSearchService() { |
||||
return this.searchService; |
||||
} |
||||
public void setSearchService(SearchService searchService) { |
||||
this.searchService = searchService; |
||||
} |
||||
} |
@ -0,0 +1,99 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
import org.fourthline.cling.support.model.BrowseResult; |
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.SortCriterion; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.GenreContainer; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.util.Util; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public class GenreUpnpProcessor extends UpnpContentProcessor <Genre, MediaFile> { |
||||
|
||||
public GenreUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_GENRE_PREFIX); |
||||
setRootTitle("Genres"); |
||||
} |
||||
|
||||
/** |
||||
* Browses the top-level content of a type. |
||||
*/ |
||||
public BrowseResult browseRoot(String filter, long firstResult, long maxResults, SortCriterion[] orderBy) throws Exception { |
||||
// we have to override this to do an index-based id.
|
||||
DIDLContent didl = new DIDLContent(); |
||||
List<Genre> allItems = getAllItems(); |
||||
if (filter != null) { |
||||
// filter items
|
||||
} |
||||
if (orderBy != null) { |
||||
// sort items
|
||||
} |
||||
List<Genre> selectedItems = Util.subList(allItems, firstResult, maxResults); |
||||
for (int i=0; i < selectedItems.size(); i++) { |
||||
Genre item = selectedItems.get(i); |
||||
didl.addContainer(createContainer(item, (int) (i+firstResult))); |
||||
} |
||||
return createBrowseResult(didl, (int) didl.getCount(), allItems.size()); |
||||
} |
||||
|
||||
public Container createContainer(Genre item) { |
||||
// genre uses index because we don't have a proper id
|
||||
return null; |
||||
} |
||||
|
||||
public Container createContainer(Genre item, int index) { |
||||
GenreContainer container = new GenreContainer(); |
||||
container.setId(getRootId() + DispatchingContentDirectory.SEPARATOR + index); |
||||
container.setParentID(getRootId()); |
||||
container.setTitle(item.getName()); |
||||
container.setChildCount(item.getAlbumCount()); |
||||
|
||||
return container; |
||||
} |
||||
|
||||
public List<Genre> getAllItems() { |
||||
return getDispatcher().getMediaFileService().getGenres(false); |
||||
} |
||||
|
||||
public Genre getItemById(String id) { |
||||
int index = Integer.parseInt(id); |
||||
List<Genre> allGenres = getAllItems(); |
||||
if (allGenres.size() > index) { |
||||
return allGenres.get(index); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public List<MediaFile> getChildren(Genre item) { |
||||
List<MusicFolder> allFolders = getDispatcher().getSettingsService().getAllMusicFolders(); |
||||
return getDispatcher().getMediaFileProcessor().getMediaFileDao().getSongsByGenre(item.getName(), 0, Integer.MAX_VALUE, allFolders); |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, MediaFile child) throws Exception { |
||||
didl.addItem(getDispatcher().getMediaFileProcessor().createItem(child)); |
||||
} |
||||
} |
@ -0,0 +1,165 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
|
||||
import org.fourthline.cling.support.model.BrowseResult; |
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.DIDLObject; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.MusicAlbum; |
||||
import org.fourthline.cling.support.model.item.Item; |
||||
import org.fourthline.cling.support.model.item.MusicTrack; |
||||
import org.libresonic.player.dao.MediaFileDao; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.service.MediaFileService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
import java.net.URI; |
||||
import java.util.ArrayList; |
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public class MediaFileUpnpProcessor extends UpnpContentProcessor <MediaFile, MediaFile> { |
||||
|
||||
@Autowired |
||||
MediaFileDao mediaFileDao; |
||||
|
||||
public MediaFileUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_FOLDER_PREFIX); |
||||
setRootTitle("Folders"); |
||||
} |
||||
|
||||
@Override |
||||
// overriding for the case of browsing a file
|
||||
public BrowseResult browseObjectMetadata(String id) throws Exception { |
||||
MediaFile item = getItemById(id); |
||||
DIDLContent didl = new DIDLContent(); |
||||
addChild(didl, item); |
||||
return createBrowseResult(didl, 1, 1); |
||||
} |
||||
|
||||
public Container createContainer(MediaFile item) throws Exception { |
||||
MusicAlbum container = new MusicAlbum(); |
||||
if (item.isAlbum()) { |
||||
container.setAlbumArtURIs(new URI[] { getDispatcher().getAlbumProcessor().getAlbumArtURI(item.getId())}); |
||||
|
||||
if (item.getArtist() != null) { |
||||
container.setArtists(getDispatcher().getAlbumProcessor().getAlbumArtists(item.getArtist())); |
||||
} |
||||
container.setDescription(item.getComment()); |
||||
} |
||||
container.setId(DispatchingContentDirectory.CONTAINER_ID_FOLDER_PREFIX + DispatchingContentDirectory.SEPARATOR + item.getId()); |
||||
container.setTitle(item.getName()); |
||||
List<MediaFile> children = getChildren(item); |
||||
container.setChildCount(children.size()); |
||||
|
||||
if (! getMediaFileService().isRoot(item)) { |
||||
MediaFile parent = getMediaFileService().getParentOf(item); |
||||
if (parent != null) { |
||||
container.setParentID(String.valueOf(parent.getId())); |
||||
} |
||||
} else { |
||||
container.setParentID(DispatchingContentDirectory.CONTAINER_ID_FOLDER_PREFIX); |
||||
} |
||||
return container; |
||||
} |
||||
|
||||
public List<MediaFile> getAllItems() throws Exception { |
||||
List<MusicFolder> allFolders = getDispatcher().getSettingsService().getAllMusicFolders(); |
||||
List<MediaFile> returnValue = new ArrayList<MediaFile>(); |
||||
if (allFolders.size() == 1) { |
||||
// if there's only one root folder just return it
|
||||
return getChildren(getMediaFileService().getMediaFile(allFolders.get(0).getPath())); |
||||
} else { |
||||
for (MusicFolder folder : allFolders) { |
||||
returnValue.add(getMediaFileService().getMediaFile(folder.getPath())); |
||||
} |
||||
} |
||||
return returnValue; |
||||
} |
||||
|
||||
public MediaFile getItemById(String id) throws Exception { |
||||
return getMediaFileService().getMediaFile(Integer.parseInt(id)); |
||||
} |
||||
|
||||
public List<MediaFile> getChildren(MediaFile item) { |
||||
List<MediaFile> children = getMediaFileService().getChildrenOf(item, true, true, true); |
||||
children.sort((MediaFile o1, MediaFile o2)->o1.getPath().replaceAll("\\W", "").compareToIgnoreCase(o2.getPath().replaceAll("\\W", ""))); |
||||
return children; |
||||
} |
||||
|
||||
public void addItem(DIDLContent didl, MediaFile item) throws Exception { |
||||
if (item.isFile()) { |
||||
didl.addItem(createItem(item)); |
||||
} else { |
||||
didl.addContainer(createContainer(item)); |
||||
} |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, MediaFile child) throws Exception { |
||||
if (child.isFile()) { |
||||
didl.addItem(createItem(child)); |
||||
} else { |
||||
didl.addContainer(createContainer(child)); |
||||
} |
||||
} |
||||
|
||||
public Item createItem(MediaFile song) throws Exception { |
||||
MediaFile parent = getMediaFileService().getParentOf(song); |
||||
MusicTrack item = new MusicTrack(); |
||||
item.setId(String.valueOf(song.getId())); |
||||
item.setParentID(String.valueOf(parent.getId())); |
||||
item.setTitle(song.getTitle()); |
||||
item.setAlbum(song.getAlbumName()); |
||||
if (song.getArtist() != null) { |
||||
item.setArtists(getDispatcher().getAlbumProcessor().getAlbumArtists(song.getArtist())); |
||||
} |
||||
Integer year = song.getYear(); |
||||
if (year != null) { |
||||
item.setDate(year + "-01-01"); |
||||
} |
||||
item.setOriginalTrackNumber(song.getTrackNumber()); |
||||
if (song.getGenre() != null) { |
||||
item.setGenres(new String[]{song.getGenre()}); |
||||
} |
||||
item.setResources(Arrays.asList(getDispatcher().createResourceForSong(song))); |
||||
item.setDescription(song.getComment()); |
||||
item.addProperty(new DIDLObject.Property.UPNP.ALBUM_ART_URI(getDispatcher().getAlbumProcessor().getAlbumArtURI(parent.getId()))); |
||||
|
||||
return item; |
||||
} |
||||
|
||||
public MediaFileService getMediaFileService() { |
||||
return getDispatchingContentDirectory().getMediaFileService(); |
||||
} |
||||
|
||||
public MediaFileDao getMediaFileDao() { |
||||
return mediaFileDao; |
||||
} |
||||
|
||||
public void setMediaFileDao(MediaFileDao mediaFileDao) { |
||||
this.mediaFileDao = mediaFileDao; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
|
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.PlaylistContainer; |
||||
import org.libresonic.player.domain.*; |
||||
import org.libresonic.player.service.PlaylistService; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
@Component |
||||
public class PlaylistUpnpProcessor extends UpnpContentProcessor <Playlist, MediaFile> { |
||||
@Autowired |
||||
private PlaylistService playlistService; |
||||
|
||||
public PlaylistUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_PLAYLIST_PREFIX); |
||||
setRootTitle("Playlists"); |
||||
} |
||||
|
||||
public Container createContainer(Playlist item) { |
||||
PlaylistContainer container = new PlaylistContainer(); |
||||
container.setId(getRootId() + DispatchingContentDirectory.SEPARATOR + item.getId()); |
||||
container.setParentID(getRootId()); |
||||
container.setTitle(item.getName()); |
||||
container.setDescription(item.getComment()); |
||||
container.setChildCount(getPlaylistService().getFilesInPlaylist(item.getId()).size()); |
||||
|
||||
return container; |
||||
} |
||||
|
||||
public List<Playlist> getAllItems() { |
||||
List<Playlist> playlists = getPlaylistService().getAllPlaylists(); |
||||
return playlists; |
||||
} |
||||
|
||||
public Playlist getItemById(String id) throws Exception { |
||||
return getDispatcher().getPlaylistService().getPlaylist(Integer.parseInt(id)); |
||||
} |
||||
|
||||
public List<MediaFile> getChildren(Playlist item) { |
||||
return getPlaylistService().getFilesInPlaylist(item.getId()); |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, MediaFile child) throws Exception { |
||||
didl.addItem(getDispatchingContentDirectory().createItem(child)); |
||||
} |
||||
|
||||
public PlaylistService getPlaylistService() { |
||||
return this.playlistService; |
||||
} |
||||
public void setPlaylistService(PlaylistService playlistService) { |
||||
this.playlistService = playlistService; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,50 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
import org.libresonic.player.domain.Album; |
||||
import org.libresonic.player.domain.MusicFolder; |
||||
|
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public class RecentAlbumUpnpProcessor extends AlbumUpnpProcessor { |
||||
private final static int RECENT_COUNT = 50; |
||||
|
||||
public RecentAlbumUpnpProcessor() { |
||||
setRootId(DispatchingContentDirectory.CONTAINER_ID_RECENT_PREFIX); |
||||
setRootTitle("RecentAlbums"); |
||||
} |
||||
|
||||
public List<Album> getAllItems() { |
||||
List<MusicFolder> allFolders = getDispatchingContentDirectory().getSettingsService().getAllMusicFolders(); |
||||
List<Album> recentAlbums = getAlbumDao().getNewestAlbums(0, RECENT_COUNT, allFolders); |
||||
if (recentAlbums.size() > 1) { |
||||
Album viewAll = new Album(); |
||||
viewAll.setName("- All Albums -"); |
||||
viewAll.setId(-1); |
||||
viewAll.setComment(AlbumUpnpProcessor.ALL_RECENT); |
||||
recentAlbums.add(0, viewAll); |
||||
} |
||||
return recentAlbums; |
||||
} |
||||
} |
@ -0,0 +1,83 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.WriteStatus; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.StorageFolder; |
||||
import org.libresonic.player.domain.MediaLibraryStatistics; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
@Component |
||||
public class RootUpnpProcessor extends UpnpContentProcessor <Container, Container> { |
||||
public Container createRootContainer() { |
||||
StorageFolder root = new StorageFolder(); |
||||
root.setId(DispatchingContentDirectory.CONTAINER_ID_ROOT); |
||||
root.setParentID("-1"); |
||||
|
||||
MediaLibraryStatistics statistics = getDispatchingContentDirectory().getSettingsService().getMediaLibraryStatistics(); |
||||
// returning large storageUsed values doesn't play nicely with
|
||||
// some upnp clients
|
||||
//root.setStorageUsed(statistics == null ? 0 : statistics.getTotalLengthInBytes());
|
||||
root.setStorageUsed(-1L); |
||||
root.setTitle("Libresonic Media"); |
||||
root.setRestricted(true); |
||||
root.setSearchable(true); |
||||
root.setWriteStatus(WriteStatus.NOT_WRITABLE); |
||||
|
||||
root.setChildCount(6); |
||||
return root; |
||||
} |
||||
|
||||
public Container createContainer(Container item) { |
||||
// the items are the containers in this case.
|
||||
return item; |
||||
} |
||||
|
||||
public List<Container> getAllItems() throws Exception { |
||||
ArrayList<Container> allItems = new ArrayList<Container>(); |
||||
allItems.add(getDispatchingContentDirectory().getAlbumProcessor().createRootContainer()); |
||||
allItems.add(getDispatchingContentDirectory().getArtistProcessor().createRootContainer()); |
||||
allItems.add(getDispatchingContentDirectory().getMediaFileProcessor().createRootContainer()); |
||||
allItems.add(getDispatchingContentDirectory().getGenreProcessor().createRootContainer()); |
||||
allItems.add(getDispatchingContentDirectory().getPlaylistProcessor().createRootContainer()); |
||||
allItems.add(getDispatchingContentDirectory().getRecentAlbumProcessor().createRootContainer()); |
||||
return allItems; |
||||
} |
||||
|
||||
public Container getItemById(String id) { |
||||
return createRootContainer(); |
||||
} |
||||
|
||||
public List<Container> getChildren(Container item) throws Exception { |
||||
return getAllItems(); |
||||
} |
||||
|
||||
public void addChild(DIDLContent didl, Container child) { |
||||
// special case; root doesn't have object instances
|
||||
} |
||||
} |
@ -0,0 +1,188 @@ |
||||
/* |
||||
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 2017 (C) Libresonic Authors |
||||
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus |
||||
*/ |
||||
package org.libresonic.player.service.upnp; |
||||
|
||||
import org.fourthline.cling.support.contentdirectory.ContentDirectoryException; |
||||
import org.fourthline.cling.support.contentdirectory.DIDLParser; |
||||
import org.fourthline.cling.support.model.BrowseResult; |
||||
import org.fourthline.cling.support.model.DIDLContent; |
||||
import org.fourthline.cling.support.model.SortCriterion; |
||||
import org.fourthline.cling.support.model.container.Container; |
||||
import org.fourthline.cling.support.model.container.StorageFolder; |
||||
import org.libresonic.player.domain.MusicFolder; |
||||
import org.libresonic.player.domain.ParamSearchResult; |
||||
import org.libresonic.player.util.Util; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
||||
import java.lang.reflect.ParameterizedType; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* @author Allen Petersen |
||||
* @version $Id$ |
||||
*/ |
||||
public abstract class UpnpContentProcessor<T extends Object, U extends Object> { |
||||
|
||||
@Autowired |
||||
private DispatchingContentDirectory dispatchingContentDirectory; |
||||
|
||||
protected String rootTitle; |
||||
protected String rootId; |
||||
|
||||
/** |
||||
* Browses the root metadata for a type. |
||||
*/ |
||||
public BrowseResult browseRootMetadata() throws Exception { |
||||
DIDLContent didl = new DIDLContent(); |
||||
didl.addContainer(createRootContainer()); |
||||
return createBrowseResult(didl, 1, 1); |
||||
} |
||||
|
||||
public Container createRootContainer() throws Exception { |
||||
Container container = new StorageFolder(); |
||||
container.setId(getRootId()); |
||||
container.setTitle(getRootTitle()); |
||||
|
||||
int childCount = getAllItemsSize(); |
||||
container.setChildCount(childCount); |
||||
container.setParentID(DispatchingContentDirectory.CONTAINER_ID_ROOT); |
||||
return container; |
||||
} |
||||
|
||||
/** |
||||
* Browses the top-level content of a type. |
||||
*/ |
||||
public BrowseResult browseRoot(String filter, long firstResult, long maxResults, SortCriterion[] orderBy) throws Exception { |
||||
DIDLContent didl = new DIDLContent(); |
||||
List<T> allItems = getAllItems(); |
||||
if (filter != null) { |
||||
// filter items (not implemented yet)
|
||||
} |
||||
if (orderBy != null) { |
||||
// sort items (not implemented yet)
|
||||
} |
||||
List<T> selectedItems = Util.subList(allItems, firstResult, maxResults); |
||||
for (T item : selectedItems) { |
||||
addItem(didl, item); |
||||
} |
||||
|
||||
return createBrowseResult(didl, (int) didl.getCount(), allItems.size()); |
||||
} |
||||
|
||||
/** |
||||
* Browses metadata for a child. |
||||
*/ |
||||
public BrowseResult browseObjectMetadata(String id) throws Exception { |
||||
T item = getItemById(id); |
||||
DIDLContent didl = new DIDLContent(); |
||||
addItem(didl, item); |
||||
return createBrowseResult(didl, 1, 1); |
||||
} |
||||
|
||||
/** |
||||
* Browses a child of the container. |
||||
*/ |
||||
public BrowseResult browseObject(String id, String filter, long firstResult, long maxResults, SortCriterion[] orderBy) throws Exception { |
||||
T item = getItemById(id); |
||||
List<U> allChildren = getChildren(item); |
||||
if (filter != null) { |
||||
// filter items (not implemented yet)
|
||||
} |
||||
if (orderBy != null) { |
||||
// sort items (not implemented yet)
|
||||
} |
||||
List<U> selectedChildren = Util.subList(allChildren, firstResult, maxResults); |
||||
|
||||
DIDLContent didl = new DIDLContent(); |
||||
for (U child : selectedChildren) { |
||||
addChild(didl, child); |
||||
} |
||||
return createBrowseResult(didl, selectedChildren.size(), allChildren.size()); |
||||
} |
||||
|
||||
protected BrowseResult createBrowseResult(DIDLContent didl, int count, int totalMatches) throws Exception { |
||||
return new BrowseResult(new DIDLParser().generate(didl), count, totalMatches); |
||||
} |
||||
|
||||
public BrowseResult searchByName(String name, |
||||
long firstResult, long maxResults, |
||||
SortCriterion[] orderBy) |
||||
throws ContentDirectoryException { |
||||
DIDLContent didl = new DIDLContent(); |
||||
|
||||
Class clazz = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; |
||||
|
||||
try { |
||||
List<MusicFolder> allFolders = getDispatchingContentDirectory().getSettingsService().getAllMusicFolders(); |
||||
ParamSearchResult<T> result = getDispatcher().getSearchService().searchByName(name, (int) firstResult, (int) maxResults, allFolders, clazz); |
||||
List<T> selectedItems = result.getItems(); |
||||
for (T item : selectedItems) { |
||||
addItem(didl, item); |
||||
} |
||||
|
||||
return createBrowseResult(didl, (int) didl.getCount(), result.getTotalHits()); |
||||
} catch (Exception e) { |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
public DispatchingContentDirectory getDispatchingContentDirectory() { |
||||
return dispatchingContentDirectory; |
||||
} |
||||
public void setDispatchingContentDirectory(DispatchingContentDirectory dispatchingContentDirectory) { |
||||
this.dispatchingContentDirectory = dispatchingContentDirectory; |
||||
} |
||||
public DispatchingContentDirectory getDispatcher() { |
||||
return getDispatchingContentDirectory(); |
||||
} |
||||
|
||||
public void addItem(DIDLContent didl, T item) throws Exception { |
||||
didl.addContainer(createContainer(item)); |
||||
} |
||||
|
||||
// this can probably be optimized in some cases
|
||||
public int getAllItemsSize() throws Exception { |
||||
return getAllItems().size(); |
||||
} |
||||
|
||||
public abstract Container createContainer(T item) throws Exception; |
||||
|
||||
public abstract List<T> getAllItems() throws Exception; |
||||
|
||||
public abstract T getItemById(String id) throws Exception; |
||||
|
||||
public abstract List<U> getChildren(T item) throws Exception; |
||||
|
||||
public abstract void addChild(DIDLContent didl, U child) throws Exception; |
||||
|
||||
public String getRootTitle() { |
||||
return rootTitle; |
||||
} |
||||
public void setRootTitle(String rootTitle) { |
||||
this.rootTitle = rootTitle; |
||||
} |
||||
public String getRootId() { |
||||
return rootId; |
||||
} |
||||
public void setRootId(String rootId) { |
||||
this.rootId = rootId; |
||||
} |
||||
} |
||||
|
Loading…
Reference in new issue