mirror of
https://github.com/TeamNewPipe/PipeCast
synced 2025-10-05 16:02:48 +02:00
Implement basic queueing
This commit is contained in:
@@ -2,7 +2,9 @@ apply plugin: 'java-library'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.github.wb9688:nanohttpd:f23888e8b63baa6a3010d1e75ebdf3a48d14147d'
|
||||
}
|
||||
|
@@ -20,5 +20,9 @@ public abstract class Device {
|
||||
|
||||
public abstract void play(String url, String title, String creator, MediaFormat mediaFormat) throws IOException, XMLStreamException;
|
||||
|
||||
public abstract void addToQueue(String url, String title, String creator, MediaFormat mediaFormat) throws IOException, XMLStreamException;
|
||||
|
||||
public abstract List<MediaFormat> getSupportedFormats() throws IOException, XMLStreamException, ParserConfigurationException, SAXException;
|
||||
|
||||
public abstract void startBackgroundWork() throws IOException;
|
||||
}
|
||||
|
@@ -0,0 +1,80 @@
|
||||
package org.schabi.newpipe.cast.protocols.upnp;
|
||||
|
||||
import org.nanohttpd.protocols.http.IHTTPSession;
|
||||
import org.nanohttpd.protocols.http.NanoHTTPD;
|
||||
import org.nanohttpd.protocols.http.response.Response;
|
||||
import org.nanohttpd.protocols.http.response.Status;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.StringReader;
|
||||
import java.net.Socket;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
public class GenaServer extends NanoHTTPD {
|
||||
private UpnpDevice upnpDevice;
|
||||
|
||||
public GenaServer(UpnpDevice upnpDevice) throws IOException {
|
||||
super(30303);
|
||||
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
|
||||
|
||||
this.upnpDevice = upnpDevice;
|
||||
|
||||
String request = "SUBSCRIBE " + upnpDevice.avTransportEventUrl.getPath() + " HTTP/1.1\r\n"
|
||||
+ "HOST: " + upnpDevice.avTransportEventUrl.getHost() + ":" + upnpDevice.avTransportEventUrl.getPort() + "\r\n"
|
||||
+ "CALLBACK: <http://192.168.1.16:30303>\r\n"
|
||||
+ "NT: upnp:event\r\n"
|
||||
+ "TIMEOUT: Second-infinite\r\n\r\n"; // TODO: implement timeout properly as infinite is deprecated, also implement UNSUBSCRIBE
|
||||
|
||||
Socket socket = new Socket(upnpDevice.avTransportEventUrl.getHost(), upnpDevice.avTransportEventUrl.getPort());
|
||||
OutputStream outputStream = socket.getOutputStream();
|
||||
outputStream.write(request.getBytes());
|
||||
outputStream.close();
|
||||
socket.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response serve(IHTTPSession session) {
|
||||
try {
|
||||
String response = session.parseBody();
|
||||
|
||||
InputSource inputSource = new InputSource(new StringReader(response));
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
Document document = documentBuilder.parse(inputSource);
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
Node lastChange = ((Element) document.getDocumentElement().getElementsByTagName("e:property").item(0)).getElementsByTagName("LastChange").item(0);
|
||||
|
||||
InputSource eventInputSource = new InputSource(new StringReader(lastChange.getTextContent()));
|
||||
DocumentBuilderFactory eventDocumentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder eventDocumentBuilder = eventDocumentBuilderFactory.newDocumentBuilder();
|
||||
Document eventDocument = eventDocumentBuilder.parse(eventInputSource);
|
||||
eventDocument.getDocumentElement().normalize();
|
||||
|
||||
NodeList nextAvTransportUri = ((Element) eventDocument.getDocumentElement().getElementsByTagName("InstanceID").item(0)).getElementsByTagName("NextAVTransportURI");
|
||||
if (nextAvTransportUri.getLength() == 1) {
|
||||
if (((Element) nextAvTransportUri.item(0)).getAttribute("val").equals("")) {
|
||||
upnpDevice.queue.remove(0);
|
||||
if (!upnpDevice.queue.isEmpty()) {
|
||||
upnpDevice.setNextAvTransportUri();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException | SAXException | ParserConfigurationException e) {
|
||||
return Response.newFixedLengthResponse(Status.INTERNAL_ERROR, "text/plain", e.getMessage() + "\n");
|
||||
}
|
||||
|
||||
return Response.newFixedLengthResponse("\n");
|
||||
}
|
||||
}
|
@@ -1,5 +1,13 @@
|
||||
package org.schabi.newpipe.cast.protocols.upnp;
|
||||
|
||||
import org.schabi.newpipe.cast.Device;
|
||||
import org.schabi.newpipe.cast.MediaFormat;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
@@ -7,7 +15,6 @@ import java.io.OutputStream;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -19,20 +26,16 @@ import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
|
||||
import org.schabi.newpipe.cast.Device;
|
||||
import org.schabi.newpipe.cast.MediaFormat;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class UpnpDevice extends Device {
|
||||
private Document description;
|
||||
private Element device;
|
||||
|
||||
private URL avTransportUrl;
|
||||
URL avTransportEventUrl;
|
||||
private URL connectionManagerUrl;
|
||||
|
||||
List<String> queue = new ArrayList<>();
|
||||
|
||||
public UpnpDevice(String location) throws IOException, ParserConfigurationException, SAXException {
|
||||
super(location);
|
||||
getDescription();
|
||||
@@ -73,6 +76,8 @@ public class UpnpDevice extends Device {
|
||||
if (service.getElementsByTagName("serviceType").item(0).getTextContent().equals("urn:schemas-upnp-org:service:AVTransport:1")) {
|
||||
String serviceUrl = service.getElementsByTagName("controlURL").item(0).getTextContent();
|
||||
avTransportUrl = new URL(baseUrl, serviceUrl);
|
||||
String serviceEventUrl = service.getElementsByTagName("eventSubURL").item(0).getTextContent();
|
||||
avTransportEventUrl = new URL(baseUrl, serviceEventUrl);
|
||||
} else if (service.getElementsByTagName("serviceType").item(0).getTextContent().equals("urn:schemas-upnp-org:service:ConnectionManager:1")) {
|
||||
String serviceUrl = service.getElementsByTagName("controlURL").item(0).getTextContent();
|
||||
connectionManagerUrl = new URL(baseUrl, serviceUrl);
|
||||
@@ -122,16 +127,7 @@ public class UpnpDevice extends Device {
|
||||
connection.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play(String url, String title, String creator, MediaFormat mediaFormat) throws IOException, XMLStreamException {
|
||||
HttpURLConnection connection = (HttpURLConnection) avTransportUrl.openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
|
||||
connection.setRequestProperty("Soapaction", "\"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI\"");
|
||||
connection.setDoOutput(true);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
|
||||
private String createDidl(String url, String title, String creator, MediaFormat mediaFormat) throws XMLStreamException {
|
||||
StringWriter didlSw = new StringWriter();
|
||||
XMLOutputFactory didlXmlof = XMLOutputFactory.newInstance();
|
||||
XMLStreamWriter didlWriter = didlXmlof.createXMLStreamWriter(didlSw);
|
||||
@@ -172,6 +168,19 @@ public class UpnpDevice extends Device {
|
||||
didlWriter.writeEndDocument();
|
||||
didlWriter.close();
|
||||
|
||||
return didlSw.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void play(String url, String title, String creator, MediaFormat mediaFormat) throws IOException, XMLStreamException {
|
||||
HttpURLConnection connection = (HttpURLConnection) avTransportUrl.openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
|
||||
connection.setRequestProperty("Soapaction", "\"urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI\"");
|
||||
connection.setDoOutput(true);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
|
||||
XMLStreamWriter writer = xmlof.createXMLStreamWriter(sw);
|
||||
@@ -189,7 +198,7 @@ public class UpnpDevice extends Device {
|
||||
writer.writeCharacters(url);
|
||||
writer.writeEndElement();
|
||||
writer.writeStartElement("CurrentURIMetaData");
|
||||
writer.writeCharacters(didlSw.toString());
|
||||
writer.writeCharacters(createDidl(url, title, creator, mediaFormat));
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
@@ -205,6 +214,55 @@ public class UpnpDevice extends Device {
|
||||
play();
|
||||
}
|
||||
|
||||
void setNextAvTransportUri() throws IOException {
|
||||
HttpURLConnection connection = (HttpURLConnection) avTransportUrl.openConnection();
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "text/xml;charset=utf-8");
|
||||
connection.setRequestProperty("Soapaction", "\"urn:schemas-upnp-org:service:AVTransport:1#SetNextAVTransportURI\"");
|
||||
connection.setDoOutput(true);
|
||||
OutputStream outputStream = connection.getOutputStream();
|
||||
|
||||
byte[] xml = queue.get(0).getBytes();
|
||||
outputStream.write(xml);
|
||||
outputStream.close();
|
||||
connection.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToQueue(String url, String title, String creator, MediaFormat mediaFormat) throws IOException, XMLStreamException {
|
||||
StringWriter sw = new StringWriter();
|
||||
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
|
||||
XMLStreamWriter writer = xmlof.createXMLStreamWriter(sw);
|
||||
writer.writeStartDocument("utf-8", "1.0");
|
||||
writer.writeStartElement("s:Envelope");
|
||||
writer.writeAttribute("s:encodingStyle", "http://schemas.xmlsoap.org/soap/encoding/");
|
||||
writer.writeNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/");
|
||||
writer.writeStartElement("s:Body");
|
||||
writer.writeStartElement("u:SetNextAVTransportURI");
|
||||
writer.writeNamespace("u", "urn:schemas-upnp-org:service:AVTransport:1");
|
||||
writer.writeStartElement("InstanceID");
|
||||
writer.writeCharacters("0");
|
||||
writer.writeEndElement();
|
||||
writer.writeStartElement("NextURI");
|
||||
writer.writeCharacters(url);
|
||||
writer.writeEndElement();
|
||||
writer.writeStartElement("NextURIMetaData");
|
||||
writer.writeCharacters(createDidl(url, title, creator, mediaFormat));
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
writer.writeEndDocument();
|
||||
writer.close();
|
||||
|
||||
queue.add(sw.toString());
|
||||
|
||||
if (queue.size() == 1) {
|
||||
setNextAvTransportUri();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaFormat> getSupportedFormats() throws IOException, XMLStreamException, ParserConfigurationException, SAXException {
|
||||
HttpURLConnection connection = (HttpURLConnection) connectionManagerUrl.openConnection();
|
||||
@@ -243,8 +301,6 @@ public class UpnpDevice extends Device {
|
||||
}
|
||||
input.close();
|
||||
|
||||
System.out.println(response.toString());
|
||||
|
||||
InputSource inputSource = new InputSource(new StringReader(response.toString()));
|
||||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
|
||||
@@ -270,4 +326,9 @@ public class UpnpDevice extends Device {
|
||||
|
||||
return supportedFormats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startBackgroundWork() throws IOException {
|
||||
new GenaServer(this);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user