package org.dyndns.jkiddo.service.dpap.server; import java.io.IOException; import java.sql.SQLException; import java.util.Collection; import java.util.HashMap; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.dyndns.jkiddo.dmap.chunks.audio.DatabaseItems; import org.dyndns.jkiddo.dmp.chunks.VersionChunk; import org.dyndns.jkiddo.dmp.chunks.media.DatabaseCount; import org.dyndns.jkiddo.dmp.chunks.media.ItemName; import org.dyndns.jkiddo.dmp.chunks.media.Listing; import org.dyndns.jkiddo.dmp.chunks.media.LoginRequired; import org.dyndns.jkiddo.dmp.chunks.media.MediaProtocolVersion; import org.dyndns.jkiddo.dmp.chunks.media.ReturnedCount; import org.dyndns.jkiddo.dmp.chunks.media.ServerInfoResponse; import org.dyndns.jkiddo.dmp.chunks.media.SpecifiedTotalCount; import org.dyndns.jkiddo.dmp.chunks.media.Status; import org.dyndns.jkiddo.dmp.chunks.media.SupportsAutoLogout; import org.dyndns.jkiddo.dmp.chunks.media.SupportsIndex; import org.dyndns.jkiddo.dmp.chunks.media.TimeoutInterval; import org.dyndns.jkiddo.dmp.chunks.media.UpdateType; import org.dyndns.jkiddo.dmp.model.MediaItem; import org.dyndns.jkiddo.dmp.util.DmapUtil; import org.dyndns.jkiddo.dpap.chunks.picture.PictureProtocolVersion; import org.dyndns.jkiddo.service.dmap.DMAPResource; import org.dyndns.jkiddo.service.dmap.Util; import org.dyndns.jkiddo.zeroconf.IZeroconfManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Consumes(MediaType.WILDCARD) // @Produces(MediaType.WILDCARD) public class DPAPResource extends DMAPResource<ImageItemManager> implements IImageLibrary { private static final Logger LOGGER = LoggerFactory.getLogger(DPAPResource.class); public static final String DPAP_SERVER_PORT_NAME = "DPAP_SERVER_PORT_NAME"; public static final String DPAP_RESOURCE = "DPAP_IMPLEMENTATION"; private static final VersionChunk dpapProtocolVersion = new PictureProtocolVersion(DmapUtil.PPRO_VERSION_101); private static final VersionChunk dmapProtocolVersion = new MediaProtocolVersion(DmapUtil.MPRO_VERSION_200); private static final String TXT_VERSION = "1"; private static final String TXT_VERSION_KEY = "txtvers"; private static final String DPAP_VERSION_KEY = "Version"; private static final String MACHINE_ID_KEY = "Machine ID"; private static final String IPSH_VERSION_KEY = "iPSh Version"; private static final String PASSWORD_KEY = "Password"; @Inject public DPAPResource(final IZeroconfManager mDNS, @Named(DPAP_SERVER_PORT_NAME) final Integer port, @Named(Util.APPLICATION_NAME) final String applicationName, @Named(DPAP_RESOURCE) final ImageItemManager itemManager) throws IOException { super(mDNS, port, itemManager); this.name = applicationName; this.register(); } @Override @Path("server-info") @GET public Response serverInfo(@QueryParam("hsgid") final String hsgid) throws IOException, SQLException { final ServerInfoResponse serverInfoResponse = new ServerInfoResponse(); serverInfoResponse.add(new Status(200)); serverInfoResponse.add(dmapProtocolVersion); serverInfoResponse.add(dpapProtocolVersion); serverInfoResponse.add(new ItemName(name)); serverInfoResponse.add(new LoginRequired(false)); serverInfoResponse.add(new TimeoutInterval(1800)); serverInfoResponse.add(new SupportsAutoLogout(false)); serverInfoResponse.add(new SupportsIndex(false)); serverInfoResponse.add(new DatabaseCount(itemManager.getDatabases().size())); return Util.buildResponse(serverInfoResponse, getDMAPKey(), name); } @Override protected IZeroconfManager.ServiceInfo getServiceInfoToRegister() { String hash = Integer.toHexString(hostname.hashCode()).toUpperCase(); hash = (hash + hash).substring(0, 13); final HashMap<String, String> records = new HashMap<>(); records.put(TXT_VERSION_KEY, TXT_VERSION); records.put(DPAP_VERSION_KEY, DmapUtil.PPRO_VERSION_201 + ""); records.put(IPSH_VERSION_KEY, DmapUtil.PPRO_VERSION_201 + ""); records.put(MACHINE_ID_KEY, hash); records.put(PASSWORD_KEY, "0"); return new IZeroconfManager.ServiceInfo(DPAP_SERVICE_TYPE, name, port, records); } @Override @Path("databases/{databaseId}/items") @GET public Response items(@PathParam("databaseId") final long databaseId, @QueryParam("session-id") final long sessionId, @QueryParam("revision-number") final long revisionNumber, @QueryParam("delta") final long delta, @QueryParam("type") final String type, @QueryParam("meta") final String meta, @QueryParam("query") final String query, @QueryParam("hsgid") final String hsgid) throws IOException { // dpap: limited by query // GET dpap://192.168.1.2:8770/databases/1/items?session-id=1101478641&meta=dpap.thumb,dmap.itemid,dpap.filedata&query=('dmap.itemid:2770','dmap.itemid:2771','dmap.itemid:2772','dmap.itemid:2773','dmap.itemid:2774','dmap.itemid:2775','dmap.itemid:2776','dmap.itemid:2777','dmap.itemid:2778','dmap.itemid:2779','dmap.itemid:2780','dmap.itemid:2781','dmap.itemid:2782','dmap.itemid:2783','dmap.itemid:2784','dmap.itemid:2785','dmap.itemid:2786','dmap.itemid:2787','dmap.itemid:2788','dmap.itemid:2789') HTTP/1.1 // GET dpap://192.168.1.2:8770/databases/1/items?session-id=1101478641&meta=dpap.hires,dmap.itemid,dpap.filedata&query=('dmap.itemid:2742') HTTP/1.1 // look at meta to determine if it should be full size or not final Collection<MediaItem> items = getMediaItems(databaseId, query); DmapUtil.parseMeta(meta); final DatabaseItems databaseItems = new DatabaseItems(); databaseItems.add(new Status(200)); databaseItems.add(new UpdateType(0)); databaseItems.add(new SpecifiedTotalCount(items.size())); databaseItems.add(new ReturnedCount(items.size())); final Listing listing = new Listing(); /*for(MediaItem item : items) { ListingItem listingItem = new ListingItem(); listingItem.add(item.getChunk("dmap.itemkind")); for(String key : metaParameters) { Chunk chunk = item.getChunk(key); if(chunk != null) { listingItem.add(chunk); } else if("dpap.thumb".equals(key)) { listingItem.add(new FileData(itemManager.getThumb(databaseId, ((ItemId) item.getChunk("dmap.itemid")).getUnsignedValue()))); } else if("dpap.hires".equals(key)) { listingItem.add(new FileData(itemManager.getItemAsByteArray(databaseId, ((ItemId) item.getChunk("dmap.itemid")).getUnsignedValue()))); } else if("dpap.filedata".equals(key)) { continue; } else logger.info("Unknown chunk type: " + key); // if("dpap.filedata".equals(chunk.getName()) && !isThumbRequest) // { // listingItem.add(new FileData(itemManager.getItemAsByteArray(databaseId, ((ItemId) item.getChunk("dmap.itemid")).getUnsignedValue()))); // } // else if("dpap.filedata".equals(chunk.getName()) && isThumbRequest) // { // listingItem.add(new FileData(itemManager.getThumb(databaseId, ((ItemId) item.getChunk("dmap.itemid")).getUnsignedValue()))); // } // else // } // else // { // if(isThumbRequest && "dpap.thumb".equals(key)) // continue; // if(!isThumbRequest && "dpap.hires".equals(key)) // continue; // logger.info("Unknown chunk type: " + key); // } } listing.add(listingItem); }*/ databaseItems.add(listing); // if(request.isUpdateType() && deletedSongs != null) // { // DeletedIdListing deletedListing = new DeletedIdListing(); // // for(Song song : deletedSongs) // { // deletedListing.add(song.getChunk("dmap.itemid")); // } // // databaseSongs.add(deletedListing); // } return Util.buildResponse(databaseItems, getDMAPKey(), name); } private Collection<MediaItem> getMediaItems(final long databaseId, final String query) { /*final Collection<Integer> itemIds = Collections2.transform(Sets.newHashSet(Splitter.on(",").split(query.replace("(", "").replace(")", ""))), new Function<String, Integer>() { public Integer apply(String s) { if(s.startsWith("'dmap.itemid:")) return Integer.parseInt(s.replace("'", "").split(":")[1]); return null; } }); Collection<MediaItem> items = Collections2.filter(itemManager.getDatabase(databaseId).getItems(), new Predicate<MediaItem>() { @Override public boolean apply(MediaItem obj) { ItemId chunk = (ItemId) obj.getChunk("dmap.itemid"); return itemIds.contains(new Integer(chunk.getValue())); } }); return items;*/ return null; } @Override @Path("this_request_is_simply_to_send_a_close_connection_header") @GET public Response closeConnection() throws IOException { return Util.buildEmptyResponse(getDMAPKey(), name); } @Override public String getDMAPKey() { return "DPAP-Server"; } }