Started refactoring UPnP server
This commit is contained in:
parent
ef10430d54
commit
8ba1441572
7 changed files with 99 additions and 164 deletions
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.upnp;
|
||||
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.ssdp.SSDPServiceInfo;
|
||||
/**
|
||||
* This class is a UPnP Server class that will be extended
|
||||
* by all root devices handles all the other UPnP services
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class UPnPRootDevice implements HttpPage, SSDPServiceInfo{
|
||||
|
||||
}
|
||||
|
|
@ -25,28 +25,38 @@
|
|||
package zutil.net.upnp;
|
||||
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.ssdp.SSDPServiceInfo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* This class is a UPnP AV Media Server that handles all the
|
||||
* other UPnP services
|
||||
*
|
||||
* @author Ziver
|
||||
* This class is a UPnP Root Server that can host multiple UPnP services.
|
||||
*/
|
||||
public class UPnPMediaServer extends UPnPRootDevice{
|
||||
public class UPnPServer implements HttpPage, SSDPServiceInfo {
|
||||
public static final String RELATIVE_URL = "upnp/rootdev";
|
||||
private static final String DEVICE_TYPE = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||
|
||||
private String url;
|
||||
private String uuid;
|
||||
private String nameFriendly = "ZupNp Server";
|
||||
private String manufacturer = "Ziver Koc";
|
||||
private String manufacturerUrl = "http://example.com";
|
||||
private String modelName = nameFriendly;
|
||||
private String modelDescription = "UPnP AV Media Server";
|
||||
private float modelNumber = 1.0f;
|
||||
private final String uuid = "uuid:" + UUID.nameUUIDFromBytes(this.getClass().toString().getBytes());
|
||||
private ArrayList<UPnPService> services = new ArrayList<>();
|
||||
|
||||
public UPnPMediaServer(String location) {
|
||||
|
||||
public UPnPServer(String location) {
|
||||
url = location;
|
||||
}
|
||||
|
||||
|
||||
public void respond(HttpPrintStream out,
|
||||
HttpHeader headers,
|
||||
Map<String, Object> session,
|
||||
|
|
@ -54,7 +64,7 @@ public class UPnPMediaServer extends UPnPRootDevice{
|
|||
Map<String, String> request) throws IOException {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
out.setHeader(HttpHeader.HEADER_CONTENT_TYPE, "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<root xmlns=\"urn:schemas-upnp-org:service:ContentDirectory:1\">");
|
||||
|
|
@ -62,32 +72,32 @@ public class UPnPMediaServer extends UPnPRootDevice{
|
|||
out.println(" <major>1</major>");
|
||||
out.println(" <minor>0</minor>");
|
||||
out.println(" </specVersion>");
|
||||
out.println(" <URLBase>" + url + "</URLBase>");//" + ssdp.getLocation() + "
|
||||
out.println(" <URLBase>" + url + "</URLBase>"); //" + ssdp.getLocation() + "
|
||||
out.println(" <device>");
|
||||
out.println(" <deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>");
|
||||
out.println(" <friendlyName>ZupNP AV Media Server</friendlyName>");
|
||||
out.println(" <manufacturer>Ziver Koc</manufacturer>");
|
||||
out.println(" <manufacturerURL>http://ziver.koc.se</manufacturerURL>");
|
||||
out.println("");
|
||||
out.println(" <deviceType>" + DEVICE_TYPE + "</deviceType>");
|
||||
out.println(" <friendlyName>" + nameFriendly + "</friendlyName>");
|
||||
out.println(" <manufacturer>" + manufacturer + "</manufacturer>");
|
||||
out.println(" <manufacturerURL>" + manufacturerUrl + "</manufacturerURL>");
|
||||
out.println(" <modelName>ZupNP Server</modelName>");
|
||||
out.println(" <modelDescription>UPnP AV Media Server</modelDescription>");
|
||||
out.println(" <modelNumber>0.1</modelNumber>");
|
||||
out.println(" <modelDescription>" + modelDescription + "</modelDescription>");
|
||||
out.println(" <modelNumber>" + modelNumber + "</modelNumber>");
|
||||
out.println("");
|
||||
out.println(" <UDN>" + getUUID() + "</UDN>");
|
||||
out.println(" <serviceList>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
|
||||
out.println(" <controlURL>CMGR_Control</controlURL>");
|
||||
out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
|
||||
out.println(" <controlURL>Action/ContentDir</controlURL>");
|
||||
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ConnectionManager:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CMGR_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>CMGR_Control/GetServDesc</SCPDURL>");
|
||||
out.println(" <controlURL>CMGR_Control</controlURL>");
|
||||
out.println(" <eventSubURL>CMGR_Event</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" <service>");
|
||||
out.println(" <serviceType>urn:schemas-upnp-org:service:ContentDirectory:1</serviceType>");
|
||||
out.println(" <serviceId>urn:upnp-org:serviceId:CDS_1-0</serviceId>");
|
||||
out.println(" <SCPDURL>SCP/ContentDir</SCPDURL>");
|
||||
out.println(" <controlURL>Action/ContentDir</controlURL>");
|
||||
out.println(" <eventSubURL>Event/ContentDir</eventSubURL>");
|
||||
out.println(" </service>");
|
||||
out.println(" </serviceList>");
|
||||
out.println(" </device>");
|
||||
out.println("</root>");
|
||||
|
|
@ -108,9 +118,6 @@ public class UPnPMediaServer extends UPnPRootDevice{
|
|||
return getUUID() + "::upnp:rootdevice";
|
||||
}
|
||||
public String getUUID() {
|
||||
if (uuid == null) {
|
||||
uuid = "uuid:" + UUID.nameUUIDFromBytes(this.getClass().toString().getBytes()); //(url+Math.random()).getBytes()
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
|
@ -25,9 +25,7 @@
|
|||
package zutil.net.upnp;
|
||||
|
||||
/**
|
||||
* Information about a UPNP Service
|
||||
*
|
||||
* @author Ziver
|
||||
* A interface that represents a UPnP service.
|
||||
*/
|
||||
public interface UPnPService {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2020 Ziver Koc
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package zutil.net.upnp.service;
|
||||
|
||||
import zutil.net.ws.WSInterface.WSParamName;
|
||||
import zutil.net.ws.WSReturnObject;
|
||||
|
||||
|
||||
public class BrowseRetObj extends WSReturnObject{
|
||||
@WSParamName("Result")
|
||||
public String Result;
|
||||
@WSParamName("NumberReturned")
|
||||
public int NumberReturned;
|
||||
@WSParamName("TotalMatches")
|
||||
public int TotalMatches;
|
||||
@WSParamName("UpdateID")
|
||||
public int UpdateID;
|
||||
}
|
||||
|
|
@ -24,7 +24,10 @@
|
|||
|
||||
package zutil.net.upnp.service;
|
||||
|
||||
import org.dom4j.DocumentException;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.net.http.HttpHeader;
|
||||
import zutil.net.http.HttpPage;
|
||||
|
|
@ -40,16 +43,11 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Information about a UPNP Service
|
||||
*
|
||||
* @author Ziver
|
||||
* A directory browsing UPnP service.
|
||||
*/
|
||||
public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface {
|
||||
public static final int VERSION = 1;
|
||||
|
||||
private static List<File> file_list;
|
||||
|
||||
public UPnPContentDirectory() {}
|
||||
|
||||
public UPnPContentDirectory(File dir) {
|
||||
file_list = FileUtil.search(dir, new LinkedList<>(), true, Integer.MAX_VALUE);
|
||||
|
|
@ -85,7 +83,8 @@ public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface
|
|||
*/
|
||||
@WSReturnName("Id")
|
||||
public int GetSystemUpdateID() {
|
||||
return 0;
|
||||
// Todo: add caching support
|
||||
return (int) (Math.random() * Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -96,56 +95,54 @@ public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface
|
|||
* in any particular object container.
|
||||
*
|
||||
*/
|
||||
//@WSNameSpace("urn:schemas-upnp-org:service:ContentDirectory:1")
|
||||
@WSPath("urn:schemas-upnp-org:service:ContentDirectory:1")
|
||||
public BrowseRetObj Browse(
|
||||
@WSParamName("ObjectID") String ObjectID,
|
||||
@WSParamName("BrowseFlag") String BrowseFlag,
|
||||
@WSParamName("Filter") String Filter,
|
||||
@WSParamName("StartingIndex") int StartingIndex,
|
||||
@WSParamName("RequestedCount") int RequestedCount,
|
||||
@WSParamName("SortCriteria") String SortCriteria) {
|
||||
@WSParamName("ObjectID") String objectID,
|
||||
@WSParamName("BrowseFlag") String browseFlag,
|
||||
@WSParamName("filter") String filter,
|
||||
@WSParamName("StartingIndex") int startingIndex,
|
||||
@WSParamName("RequestedCount") int requestedCount,
|
||||
@WSParamName("SortCriteria") String sortCriteria) {
|
||||
|
||||
BrowseRetObj ret = new BrowseRetObj();
|
||||
if (BrowseFlag.equals("BrowseMetadata")) {
|
||||
|
||||
if (browseFlag.equals("BrowseMetadata")) {
|
||||
|
||||
}
|
||||
else if (BrowseFlag.equals("BrowseDirectChildren")) {
|
||||
StringBuilder xml = new StringBuilder();
|
||||
xml.append("<DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
|
||||
"xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" " +
|
||||
"xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">");
|
||||
List<File> tmp = FileUtil.search(file_list.get(Integer.parseInt(ObjectID)), new LinkedList<>(), false);
|
||||
for (File file : tmp) {
|
||||
xml.append(" <container id=\"").append(file_list.indexOf(file)).append("\" ");
|
||||
if (tmp.get(0) != file) xml.append("parentID=\"").append(file_list.indexOf(file.getParent())).append("\" ");
|
||||
if (file.isDirectory()) xml.append("childCount=\"").append(file.list().length).append("\" ");
|
||||
xml.append("restricted=\"1\" searchable=\"0\" >");
|
||||
else if (browseFlag.equals("BrowseDirectChildren")) {
|
||||
Document document = DocumentHelper.createDocument();
|
||||
Element rootElement = document.addElement("DIDL-Lite", "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/");
|
||||
rootElement.addNamespace("dc", "http://purl.org/dc/elements/1.1/");
|
||||
rootElement.addNamespace("upnp", "urn:schemas-upnp-org:metadata-1-0/upnp/");
|
||||
|
||||
xml.append(" <dc:title>").append(file.getName()).append("</dc:title> ");
|
||||
if (file.isDirectory())
|
||||
xml.append(" <upnp:class>object.container.storageFolder</upnp:class> ");
|
||||
else
|
||||
xml.append(" <upnp:class>object.container</upnp:class> ");
|
||||
xml.append(" <upnp:storageUsed>").append((int) (file.length() / 1000)).append("</upnp:storageUsed> ");
|
||||
xml.append(" </container> ");
|
||||
List<File> fileList = FileUtil.search(file_list.get(Integer.parseInt(objectID)), new LinkedList<>(), false);
|
||||
|
||||
ret.NumberReturned++;
|
||||
ret.TotalMatches++;
|
||||
for (File file : fileList) {
|
||||
Element containerElement = rootElement.addElement("container")
|
||||
.addAttribute("id", "" + file_list.indexOf(file))
|
||||
.addAttribute("restricted", "1")
|
||||
.addAttribute("searchable", "0");
|
||||
|
||||
if (fileList.get(0) != file)
|
||||
containerElement.addAttribute("parentID", "" + file_list.indexOf(file.getParent()));
|
||||
|
||||
containerElement.addElement("dc:title").setText(file.getName());
|
||||
containerElement.addElement("upnp:storageUsed").setText("" + (int) (file.length() / 1000));
|
||||
|
||||
if (file.isDirectory()) {
|
||||
containerElement.addAttribute("childCount", "" + file.list().length);
|
||||
containerElement.addElement("upnp:class").setText("object.container.storageFolder");
|
||||
} else {
|
||||
containerElement.addElement("upnp:class").setText("object.container");
|
||||
}
|
||||
}
|
||||
xml.append("</DIDL-Lite>");
|
||||
|
||||
ret.Result = xml.toString();
|
||||
//Document document = DocumentHelper.parseText(xml.toString());
|
||||
//ret.Result = document.getRootElement();
|
||||
ret.NumberReturned = fileList.size();
|
||||
ret.TotalMatches = rootElement.elements().size();
|
||||
ret.Result = document.asXML();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
public class BrowseRetObj extends WSReturnObject{
|
||||
public String Result;
|
||||
public int NumberReturned;
|
||||
public int TotalMatches;
|
||||
public int UpdateID;
|
||||
}
|
||||
|
||||
|
||||
@WSIgnore
|
||||
|
|
@ -155,7 +152,7 @@ public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface
|
|||
Map<String, String> request) throws IOException {
|
||||
|
||||
out.enableBuffering(true);
|
||||
out.setHeader("Content-Type", "text/xml");
|
||||
out.setHeader(HttpHeader.HEADER_CONTENT_TYPE, "text/xml");
|
||||
|
||||
out.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
|
||||
out.println("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">");
|
||||
|
|
@ -608,4 +605,15 @@ public class UPnPContentDirectory implements UPnPService, HttpPage, WSInterface
|
|||
out.println(" </actionList>");
|
||||
out.println("</scpd>");
|
||||
}
|
||||
}
|
||||
|
||||
public static class BrowseRetObj extends WSReturnObject {
|
||||
@WSParamName("Result")
|
||||
public String Result;
|
||||
@WSParamName("NumberReturned")
|
||||
public int NumberReturned;
|
||||
@WSParamName("TotalMatches")
|
||||
public int TotalMatches;
|
||||
@WSParamName("UpdateID")
|
||||
public int updateID;
|
||||
}
|
||||
}
|
||||
|
|
@ -76,8 +76,7 @@ public interface WSInterface {
|
|||
|
||||
|
||||
/**
|
||||
* Annotation that assigns a name to an parameters
|
||||
* in an method.
|
||||
* Annotation will change the exposed name of the annotated field or method input parameter.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import java.io.IOException;
|
|||
public class UPnPServerTest {
|
||||
|
||||
public static void main(String[] args) throws IOException{
|
||||
UPnPMediaServer upnp = new UPnPMediaServer("http://192.168.0.60:8080/");
|
||||
UPnPServer upnp = new UPnPServer("http://192.168.0.60:8080/");
|
||||
MultiPrintStream.out.println("UPNP Server running");
|
||||
|
||||
UPnPContentDirectory cds = new UPnPContentDirectory(new File("C:\\Users\\Ziver\\Desktop\\lan"));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue