package org.dyndns.jkiddo.service.dmap; import java.io.IOException; import java.sql.SQLException; import java.util.Collection; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.dyndns.jkiddo.dmap.chunks.audio.DatabaseContainerns; import org.dyndns.jkiddo.dmap.chunks.audio.ItemsContainer; import org.dyndns.jkiddo.dmap.chunks.audio.ServerDatabases; import org.dyndns.jkiddo.dmp.chunks.ContentCodesResponseImpl; import org.dyndns.jkiddo.dmp.chunks.media.Listing; import org.dyndns.jkiddo.dmp.chunks.media.LoginResponse; import org.dyndns.jkiddo.dmp.chunks.media.ReturnedCount; import org.dyndns.jkiddo.dmp.chunks.media.ServerRevision; import org.dyndns.jkiddo.dmp.chunks.media.SessionId; import org.dyndns.jkiddo.dmp.chunks.media.SpecifiedTotalCount; import org.dyndns.jkiddo.dmp.chunks.media.Status; import org.dyndns.jkiddo.dmp.chunks.media.UpdateResponse; import org.dyndns.jkiddo.dmp.chunks.media.UpdateType; import org.dyndns.jkiddo.dmp.util.DmapUtil; import org.dyndns.jkiddo.zeroconf.IZeroconfManager; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; @Consumes(MediaType.WILDCARD) @Produces(MediaType.WILDCARD) public abstract class DMAPResource<T extends IItemManager> extends MDNSResource implements ILibraryResource { final protected T itemManager; protected String name; public DMAPResource(final IZeroconfManager mDNS, final Integer port, final T itemManager) throws IOException { super(mDNS, port); this.itemManager = itemManager; } @Override @Path("login") @GET public Response login(@QueryParam("pairing-guid") final String guid, @QueryParam("hasFP") final int value, @QueryParam("hsgid") final String hsgid) throws IOException { final String s = String.valueOf(Thread.currentThread().getId()); final LoginResponse loginResponse = new LoginResponse(); loginResponse.add(new Status(200)); loginResponse.add(new SessionId(itemManager.getSessionId(s))); return Util.buildResponse(loginResponse, getDMAPKey(), name); } @Override @Path("update") @GET public Response update(@QueryParam("session-id") final long sessionId, @QueryParam("revision-number") final long revisionNumber, @QueryParam("delta") final long delta, @QueryParam("daap-no-disconnect") final int daapNoDisconnect, @QueryParam("hsgid") final String hsgid) throws IOException { final String s = String.valueOf(Thread.currentThread().getId()); if(revisionNumber == delta || revisionNumber == itemManager.getRevision(s, sessionId)) { itemManager.waitForUpdate(); } final UpdateResponse updateResponse = new UpdateResponse(); updateResponse.add(new Status(200)); updateResponse.add(new ServerRevision(itemManager.getRevision(s, sessionId))); return Util.buildResponse(updateResponse, getDMAPKey(), name); } @Override @Path("databases") @GET public Response databases(@QueryParam("session-id") final long sessionId, @QueryParam("revision-number") final long revisionNumber, @QueryParam("delta") final long delta, @QueryParam("hsgid") final String hsgid) throws IOException, SQLException { final ServerDatabases serverDatabases = new ServerDatabases(); serverDatabases.add(new Status(200)); serverDatabases.add(new UpdateType(0)); final Listing listing = itemManager.getDatabases(); serverDatabases.add(new SpecifiedTotalCount(Iterables.size(listing.getListingItems()))); serverDatabases.add(new ReturnedCount(Iterables.size(listing.getListingItems()))); serverDatabases.add(listing); // if(request.isUpdateType() && deletedDatabases != null) // { // DeletedIdListing deletedListing = new DeletedIdListing(); // // for(Database database : deletedDatabases) // deletedListing.add(new ItemId(database.getItemId())); // // serverDatabases.add(deletedListing); // } return Util.buildResponse(serverDatabases, getDMAPKey(), name); } @Override @Path("databases/{databaseId}/containers") @GET public Response containers(@PathParam("databaseId") final long databaseId, @QueryParam("session-id") final long sessionId, @QueryParam("revision-number") final long revisionNumber, @QueryParam("delta") final long delta, @QueryParam("meta") final String meta, @QueryParam("hsgid") final String hsgid) throws IOException, SQLException { //Collection<Container> containers = itemManager.getDatabase(databaseId).getContainers(); Collection<String> parameters = DmapUtil.parseMeta(meta); // iPhoto does not deliver any meta data which means the parameters is empty. Because of this, default parameters are set in if(parameters.isEmpty()) { parameters = Lists.newArrayList("dmap.itemkind", "dmap.itemid", "dmap.itemname", "daap.baseplaylist", "dmap.itemcount"); } final Listing listing = itemManager.getContainers(databaseId, parameters); final DatabaseContainerns databaseContainers = new DatabaseContainerns(); databaseContainers.add(new Status(200)); databaseContainers.add(new UpdateType(0)); /* Listing listing = new Listing(); // iTunes got a bug - if playlists is a empty set, it keeps quering for(Container container : containers) { ListingItem listingItem = new ListingItem(); for(String key : parameters) { Chunk chunk = container.getChunk(key); if(chunk != null) { listingItem.add(chunk); } else { logger.info("Unknown chunk type: " + key); } } listingItem.add(new ItemCount(container.getMediaItems().size())); listing.add(listingItem); }*/ databaseContainers.add(new SpecifiedTotalCount(Iterables.size(listing.getListingItems()))); databaseContainers.add(new ReturnedCount(Iterables.size(listing.getListingItems()))); databaseContainers.add(listing); // if(request.isUpdateType() && deletedPlaylists != null) // { // DeletedIdListing deletedListing = new DeletedIdListing(); // // for(Playlist playlist : deletedPlaylists) // { // deletedListing.add(new ItemId(playlist.getItemId())); // } // // databasePlaylists.add(deletedListing); // } return Util.buildResponse(databaseContainers, getDMAPKey(), name); } @Override @Path("databases/{databaseId}/containers/{containerId}/items") @GET public Response containerItems(@PathParam("containerId") final long containerId, @PathParam("databaseId") final long databaseId, @QueryParam("session-id") final long sessionId, @QueryParam("revision-number") final long revisionNumber, @QueryParam("delta") final long delta, @QueryParam("meta") final String meta, @QueryParam("type") final String type, @QueryParam("group-type") final String group_type, @QueryParam("sort") final String sort, @QueryParam("include-sort-headers") final String include_sort_headers, @QueryParam("query") final String query, @QueryParam("index") final String index, @QueryParam("hsgid") final String hsgid) throws IOException, SQLException { // switch on type - for DPAP type is 'photo' // dpap: // http://192.168.1.2dpap://192.168.1.2:8770/databases/1/containers/5292/items?session-id=1101478641&meta=dpap.aspectratio,dmap.itemid,dmap.itemname,dpap.imagefilename,dpap.imagefilesize,dpap.creationdate,dpap.imagepixelwidth,dpap.imagepixelheight,dpap.imageformat,dpap.imagerating,dpap.imagecomments,dpap.imagelargefilesize&type=photo //Container container = itemManager.getDatabase(databaseId).getContainer(containerId); // throw new NotImplementedException(); // /databases/0/containers/1/items?session-id=1570434761&revision-number=2&delta=0&type=music&meta=dmap.itemkind,dmap.itemid,dmap.containeritemid final Iterable<String> parameters = DmapUtil.parseMeta(meta); final ItemsContainer itemsContainer = new ItemsContainer(); itemsContainer.add(new Status(200)); itemsContainer.add(new UpdateType(0)); final Listing listing = itemManager.getMediaItems(databaseId, containerId, parameters); /* Listing listing = new Listing(); for(MediaItem item : container.getMediaItems()) { ListingItem listingItem = new ListingItem(); // Added as itemkind is only request in query param which is not yet understood listingItem.add(item.getChunk("dmap.itemkind")); for(String key : parameters) { Chunk chunk = item.getChunk(key); if(chunk != null) { listingItem.add(chunk); } else { logger.info("Unknown chunk type: " + key); } } listing.add(listingItem); }*/ itemsContainer.add(new SpecifiedTotalCount(Iterables.size(listing.getListingItems()))); itemsContainer.add(new ReturnedCount(Iterables.size(listing.getListingItems()))); itemsContainer.add(listing); // if(request.isUpdateType() && deletedSongs != null) // { // DeletedIdListing deletedListing = new DeletedIdListing(); // // for(Song song : deletedSongs) // { // deletedListing.add(song.getChunk("dmap.itemid")); // } // playlistSongs.add(deletedListing); // } /* SortingHeaderListing sortingHeaderListing = new SortingHeaderListing(); ListingItem item = new ListingItem(); item.add(new SortingHeaderChar(0x54));// item.add(new SortingHeaderIndex(0)); item.add(new SortingHeaderNumber(listing.size())); sortingHeaderListing.add(item); itemsContainer.add(sortingHeaderListing);*/ return Util.buildResponse(itemsContainer, getDMAPKey(), name); } @Override @Path("content-codes") @GET public Response contentCodes() throws IOException { return Util.buildResponse(new ContentCodesResponseImpl(), getDMAPKey(), name); } @Override @Path("logout") @GET public Response logout(@QueryParam("session-id") final long sessionId) { return Util.buildEmptyResponse(getDMAPKey(), name); } abstract public String getDMAPKey(); }