My fork of airsonic with experimental fixes and improvements. See branch "custom"
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
airsonic-custom/airsonic-main/src/main/java/org/airsonic/player/service/upnp/DispatchingContentDirectory...

287 lines
11 KiB

/*
This file is part of Airsonic.
Airsonic 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.
Airsonic 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 Airsonic. If not, see <http://www.gnu.org/licenses/>.
Copyright 2016 (C) Airsonic Authors
Based upon Subsonic, Copyright 2009 (C) Sindre Mehus
*/
package org.airsonic.player.service.upnp;
import org.airsonic.player.domain.CoverArtScheme;
import org.airsonic.player.domain.MediaFile;
import org.airsonic.player.service.*;
import org.fourthline.cling.support.contentdirectory.ContentDirectoryErrorCode;
import org.fourthline.cling.support.contentdirectory.ContentDirectoryException;
import org.fourthline.cling.support.model.*;
import org.fourthline.cling.support.model.item.Item;
import org.fourthline.cling.support.model.item.MusicTrack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
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$
*/
@Service
public class DispatchingContentDirectory extends CustomContentDirectory {
public static final Logger LOG = LoggerFactory.getLogger(DispatchingContentDirectory.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
@Autowired//first checks type then field name to autowire
private AlbumUpnpProcessor albumUpnpProcessor;
//@Autowired can't autowire because of the subclassing :P
@Autowired//first checks type then field name to autowire
private RecentAlbumUpnpProcessor recentAlbumUpnpProcessor;
@Autowired
private ArtistUpnpProcessor artistProcessor;
@Autowired
private GenreUpnpProcessor genreProcessor;
@Autowired
private RootUpnpProcessor rootProcessor;
@Autowired
private MediaFileService mediaFileService;
@Autowired
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;
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 albumUpnpProcessor;
}
public void setAlbumProcessor(AlbumUpnpProcessor albumProcessor) {
this.albumUpnpProcessor = albumProcessor;
}
public RecentAlbumUpnpProcessor getRecentAlbumProcessor() {
return recentAlbumUpnpProcessor;
}
public void setRecentAlbumProcessor(RecentAlbumUpnpProcessor recentAlbumProcessor) {
this.recentAlbumUpnpProcessor = 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;
}
}