private int server_BrowseDirectChildren(Action action, String object_id, String filter, Int32 starting_index, Int32 requested_count, String sort_criteria, HttpRequestContext context) { Console.WriteLine("BrowseDirectChildren: " + object_id); if (object_id != "0") { return(-1); } var item = new MediaItem(); item.Title = "Item"; item.ObjectID = "1"; item.ParentID = "0"; item.Class = new ObjectClass("object.item.audioItem.musicTrack", ""); var resource = new MediaResource(); resource.ProtoInfo = ProtocolInfo.GetProtocolInfoFromMimeType("audio/mp3", true, context); // get list of ips and make sure the ip the request came from is used for the first resource returned // this ensures that clients which look only at the first resource will be able to reach the item List <String> ips = UPnP.GetIpAddresses(true); String localIP = context.LocalAddress.ip; if (localIP != "0.0.0.0") { ips.Remove(localIP); ips.Insert(0, localIP); } // iterate through all ips and create a resource for each foreach (String ip in ips) { resource.URI = new Uri("http://" + ip + ":" + context.LocalAddress.port + "/test/test.mp3").ToString(); item.AddResource(resource); } var didl = Didl.header + item.ToDidl(filter) + Didl.footer; action.SetArgumentValue("Result", didl); action.SetArgumentValue("NumberReturned", "1"); action.SetArgumentValue("TotalMatches", "1"); // update ID may be wrong here, it should be the one of the container? // TODO: We need to keep track of the overall updateID of the CDS action.SetArgumentValue("UpdateId", "1"); return(0); }
private int server_BrowseMetadata(Platinum.Action action, string object_id, string filter, int starting_index, int requested_count, string sort_criteria, Platinum.HttpRequestContext context) { Logger.Debug("BrowseMetadata Entered - Parameters: action:{0} object_id:\"{1}\" filter:\"{2}\" starting_index:{3} requested_count:{4} sort_criteria:\"{5}\" context:{6}", action.ToLogString(), object_id, filter, starting_index, requested_count, sort_criteria, context.ToLogString()); this.LogUserActivity(context.Signature); //nothing much seems to call BrowseMetadata so far //but perhaps that is because we aren't handing out the correct info for the client to call .. I don't know //PS3 calls it //Parameters: action:Action Name:Browse Description:Platinum.ActionDescription Arguments: object_id:0 //filter:@id,upnp:class,res,res@protocolInfo,res@av:authenticationUri,res@size,dc:title,upnp:albumArtURI,res@dlna:ifoFileURI,res@protection,res@bitrate,res@duration,res@sampleFrequency,res@bitsPerSample,res@nrAudioChannels,res@resolution,res@colorDepth,dc:date,av:dateTime,upnp:artist,upnp:album,upnp:genre,dc:contributer,upnp:storageFree,upnp:storageUsed,upnp:originalTrackNumber,dc:publisher,dc:language,dc:region,dc:description,upnp:toc,@childCount,upnp:albumArtURI@dlna:profileID,res@dlna:cleartextSize //starting_index:0 requested_count:1 sort_criteria: context:HttpRequestContext LocalAddress:HttpRequestContext.SocketAddress IP:192.168.1.56 Port:1845 RemoteAddress:HttpRequestContext.SocketAddress IP:192.168.1.40 Port:49277 Request:http://192.168.1.56:1845/ContentDirectory/7c6b1b90-872b-2cda-3c5c-21a0e430ce5e/control.xml Signature:PS3 //Some temp code to enable the client to make a call to a url with a file extension //so we can test whether or not this is the reason why artwork doesn't show up for many/most clients Model.PlatinumAlbumArtInfoHelper.DlnaHttpServerPrefixes = GetDlnaHttpServerPrefixes(context); //var objectIDMatch = Model.NavigationHelper.GetObjectByPath(this.CurrentUser, object_id); var objectIDMatch = GetItemFromID(object_id); int itemCount = 0; var didl = Platinum.Didl.header; if (objectIDMatch != null) { Logger.Debug("BrowseMetadata Found ObjectID:{0} MbItemName:{1}", object_id, objectIDMatch.MbItem == null ? "MbItem Null" : objectIDMatch.MbItem.Name); var urlPrefixes = GetHttpServerPrefixes(context); using (var item = objectIDMatch.GetMediaObject(context, urlPrefixes)) { didl += item.ToDidl(filter); itemCount++; } } didl += Platinum.Didl.footer; action.SetArgumentValue("Result", didl); action.SetArgumentValue("NumberReturned", itemCount.ToString()); action.SetArgumentValue("TotalMatches", itemCount.ToString()); // update ID may be wrong here, it should be the one of the container? action.SetArgumentValue("UpdateId", "1"); Logger.Debug("BrowseMetadata Returning - NumberReturned:{0} TotalMatches:{1} UpdateId:{2}", itemCount, itemCount, "1"); return(NEP_Success); }
private int server_SearchContainer(Platinum.Action action, string object_id, string searchCriteria, string filter, int starting_index, int requested_count, string sort_criteria, Platinum.HttpRequestContext context) { Logger.Debug("SearchContainer Entered - Parameters: action:{0} object_id:\"{1}\" searchCriteria:\"{7}\" filter:\"{2}\" starting_index:{3} requested_count:{4} sort_criteria:\"{5}\" context:{6}", action.ToLogString(), object_id, filter, starting_index, requested_count, sort_criteria, context.ToLogString(), searchCriteria); this.LogUserActivity(context.Signature); //Doesn't call search at all: // XBox360 Video App //Calls search but does not require it to be implemented: // WMP, probably uses it just for its "images" section //Calls search Seems to require it: // XBox360 Music App //WMP // action: "Search" // object_id: "0" // searchCriteria: "upnp:class derivedfrom \"object.item.imageItem\" and @refID exists false" // filter: "*" // starting_index: 0 // requested_count: 200 // sort_criteria: "-dc:date" //XBox360 Music App // action: "Search" // object_id: "7" // searchCriteria: "(upnp:class = \"object.container.album.musicAlbum\")" // filter: "dc:title,upnp:artist" // starting_index: 0 // requested_count: 1000 // sort_criteria: "+dc:title" // //XBox360 Music App seems to work souly using SearchContainer and ProcessFileRequest //I think the current resource Uri's aren't going to work because it seems to require an extension like .mp3 to work, but this requires further testing //When hitting the Album tab of the app it's searching criteria is object.container.album.musicAlbum //this means it wants albums put into containers, I thought Platinum might do this for us, but it doesn't //Some temp code to enable the client to make a call to a url with a file extension //so we can test whether or not this is the reason why artwork doesn't show up for many/most clients Model.PlatinumAlbumArtInfoHelper.DlnaHttpServerPrefixes = GetDlnaHttpServerPrefixes(context); int itemCount = 0; var totalMatches = 0; var didl = Platinum.Didl.header; //var objectIDMatch = Model.NavigationHelper.GetObjectByPath(this.CurrentUser, object_id); var objectIDMatch = GetItemFromID(object_id); if (objectIDMatch != null) { Logger.Debug("SearchContainer Found ObjectID:{0} MbItemName:{1}", object_id, objectIDMatch.MbItem == null ? "MbItem Null" : objectIDMatch.MbItem.Name); var searchUpnpClasses = GetClassFromCritera(searchCriteria); IEnumerable <Model.ModelBase> children; if (searchUpnpClasses.Any()) { Logger.Debug("SearchContainer Searching for: \"{0}\" items", searchUpnpClasses.First()); children = Model.NavigationHelper.GetRecursiveChildren(objectIDMatch, searchUpnpClasses, starting_index, requested_count).ToList(); //this call gets the top hundred thousand items or less - that should be enough head room //just gotta see if its quick enough on a large library totalMatches = Model.NavigationHelper.GetRecursiveChildren(objectIDMatch, searchUpnpClasses).Count(); } else { Logger.Debug("SearchContainer Ignoring search critera and returning all recursive children"); children = Model.NavigationHelper.GetRecursiveChildren(objectIDMatch, starting_index, requested_count).ToList(); //on even a resonable sized library this RecursiveChildren.Count call can take too long //apparently its acceptable to return zero for total matches if the actaul count can't be returned in a timely manner //page 49 of the UPnP Content directory Spec totalMatches = 0; } if (children != null) { var urlPrefixes = GetHttpServerPrefixes(context); foreach (var child in children) { AddItemToMap(child); using (var item = child.GetMediaObject(context, urlPrefixes)) { didl += item.ToDidl(filter); itemCount++; } } } } didl += Platinum.Didl.footer; //Logger.Debug(didl); action.SetArgumentValue("Result", didl); action.SetArgumentValue("NumberReturned", itemCount.ToString()); action.SetArgumentValue("TotalMatches", totalMatches.ToString()); // update ID may be wrong here, it should be the one of the container? action.SetArgumentValue("UpdateId", "1"); Logger.Debug("SearchContainer Returning - NumberReturned:{0} TotalMatches:{1} UpdateId:{2}", itemCount, totalMatches, "1"); return(NEP_Success); }
private int server_BrowseDirectChildren(Platinum.Action action, String object_id, String filter, Int32 starting_index, Int32 requested_count, String sort_criteria, Platinum.HttpRequestContext context) { Logger.Debug("BrowseDirectChildren Entered - Parameters: action:{0} object_id:\"{1}\" filter:\"{2}\" starting_index:{3} requested_count:{4} sort_criteria:\"{5}\" context:{6}", action.ToLogString(), object_id, filter, starting_index, requested_count, sort_criteria, context.ToLogString()); this.LogUserActivity(context.Signature); //WMP doesn't care how many results we return and what type they are //Xbox360 Music App is unknown, it calls SearchContainer and stops, not sure what will happen once we return it results //XBox360 Video App has fairly specific filter string and it need it - if you serve it music (mp3), it'll put music in the video list, so we have to do our own filtering //XBox360 Video App // action: "Browse" // object_id: "15" // filter: "dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec" // starting_index: 0 // requested_count: 100 // sort_criteria: "+upnp:class,+dc:title" // //the wierd thing about the filter is that there isn't much in it that says "only give me video"... except... //if we look at the doc available here: http://msdn.microsoft.com/en-us/library/windows/hardware/gg487545.aspx which describes the way WMP does its DLNA serving (not clienting) //doc is also a search cached google docs here: https://docs.google.com/viewer?a=v&q=cache:tnrdpTFCc84J:download.microsoft.com/download/0/0/b/00bba048-35e6-4e5b-a3dc-36da83cbb0d1/NetCompat_WMP11.docx+&hl=en&gl=au&pid=bl&srcid=ADGEESiBSKE1ZJeWmgYVOkKmRJuYaSL3_50KL1o6Ugp28JL1Ytq-2QbEeu6xFD8rbWcX35ZG4d7qPQnzqURGR5vig79S2Arj5umQNPnLeJo1k5_iWYbqPejeMHwwAv0ATmq2ynoZCBNL&sig=AHIEtbQ2qZJ8xMXLZYBWHHerezzXShKoVg //it describes object 15 as beeing Root/Video/Folders which can contain object.storageFolder //so perhaps thats what is saying 'only give me video' //I'm just not sure if those folders listed with object IDs are all well known across clients or if these ones are WMP specific //if they are device specific but also significant, then that might explain why Plex goes to the trouble of having configurable client device profiles for its DLNA server //XBOX360 Video //BrowseDirectChildren Entered - Parameters: //action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", //Arguments:[ ] } //object_id:15 //filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec //starting_index:0 //requested_count:1000 //sort_criteria:+upnp:class,+dc:title //context: { LocalAddress:{ IP:192.168.1.56, Port:1143 }, RemoteAddress:{ IP:192.168.1.27, Port:43702 }, Request:"http://192.168.1.56:1143/ContentDirectory/41d4bbfc-aff3-f300-7e69-14762558a3ba/control.xml", Signature:XBox } //a working log from Xbox360 Video App //no metadata (other than Title) that works, but the xbox doesn't seem to support anything more than that //image/thumbnail serving works if you feed it the direct path, current 'api url' doesn't work, perhaps because it has no extension /* * 2013-02-24 22:46:52.3962, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:15 filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:48040 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:46:54.5234, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:af99c816-0c3b-4770-099e-d5c039548e4f filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:11538 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:46:56.6050, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:5da43a95-3c34-3c3a-e123-50f20623d650 filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:33536 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:46:58.8234, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:26d7de4d-2e5d-7f8c-6d61-71674afc6503 filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:59852 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:46:59.9894, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:e51e6a3e-fe62-4bae-04c9-e8b6efb41b1e filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:8285 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:47:01.0216, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:d42fe500-8ac1-7476-6445-eec48085cc4a filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:46319 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:47:01.1244, Info, App, ProcessFileRequest Entered - Parameters: context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:9842 }, Request:"http://192.168.1.56:1733/7cb7f497-234f-05e3-64c0-926ff07d3fa6?albumArt=true", Signature:XBox } response:Platinum.HttpResponse * 2013-02-24 22:47:01.1244, Info, App, ProcessFileRequest Entered - Parameters: context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:65477 }, Request:"http://192.168.1.56:1733/76ab6bd1-a914-b126-45fc-e84a6402f8b0?albumArt=true", Signature:XBox } response:Platinum.HttpResponse * 2013-02-24 22:47:08.9738, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:da407897-748f-065a-f020-d9dbaf598ff2 filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:37906 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } * 2013-02-24 22:47:09.0699, Info, App, ProcessFileRequest Entered - Parameters: context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:9842 }, Request:"http://192.168.1.56:1733/1ce95963-d31a-3052-8cf1-f31e934bd4fe?albumArt=true", Signature:XBox } response:Platinum.HttpResponse * 2013-02-24 22:47:24.0908, Info, App, BrowseDirectChildren Entered - Parameters: action: { Name:"Browse", Description:" { Name:"Browse", Arguments:[ ] } ", Arguments:[ ] } object_id:90a8b701-b1ca-325d-e00f-d3f60267584d filter:dc:title,res,res@protection,res@duration,res@bitrate,upnp:genre,upnp:actor,res@microsoft:codec starting_index:0 requested_count:1000 sort_criteria:+upnp:class,+dc:title context: { LocalAddress:{ IP:192.168.1.56, Port:1733 }, RemoteAddress:{ IP:192.168.1.27, Port:44378 }, Request:"http://192.168.1.56:1733/ContentDirectory/944ef00a-1bd9-d8f2-02ab-9a5de207da75/control.xml", Signature:XBox } */ //Some temp code to enable the client to make a call to a url with a file extension //so we can test whether or not this is the reason why artwork doesn't show up for many/most clients Model.PlatinumAlbumArtInfoHelper.DlnaHttpServerPrefixes = GetDlnaHttpServerPrefixes(context); int itemCount = 0; int totalMatches = 0; var didl = Platinum.Didl.header; //var objectIDMatch = Model.NavigationHelper.GetObjectByPath(this.CurrentUser, object_id); var objectIDMatch = GetItemFromID(object_id); if (objectIDMatch != null) { Logger.Debug("BrowseDirectChildren Found ObjectID:{0} MbItemName:{1}", object_id, objectIDMatch.MbItem == null ? "MbItem Null" : objectIDMatch.MbItem.Name); var children = Model.NavigationHelper.GetChildren(objectIDMatch, starting_index, requested_count); totalMatches = objectIDMatch.Children.Count(); if (children != null) { var urlPrefixes = GetHttpServerPrefixes(context); foreach (var child in children) { AddItemToMap(child); using (var item = child.GetMediaObject(context, urlPrefixes)) { didl += item.ToDidl(filter); itemCount++; } } } } didl += Platinum.Didl.footer; action.SetArgumentValue("Result", didl); action.SetArgumentValue("NumberReturned", itemCount.ToString()); action.SetArgumentValue("TotalMatches", totalMatches.ToString()); // update ID may be wrong here, it should be the one of the container? action.SetArgumentValue("UpdateId", "1"); Logger.Debug("BrowseDirectChildren Returning - NumberReturned:{0} TotalMatches:{1} UpdateId:{2}", itemCount, totalMatches, "1"); return(NEP_Success); }