package Travelmarx;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLDecoder;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.lang3.StringEscapeUtils;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class SonosQueueExtractor {
public static Integer maxPageResults = 50;
public static String queueName =
"Q:0"
;
public static OutputStreamWriter txtFile;
public static OutputStreamWriter m3uFile;
public static String ipAddress;
public SonosQueueExtractor()
throws MalformedURLException, ProtocolException, IOException, SAXException, ParserConfigurationException {
queryQueue(0);
}
private void queryQueue(Integer pageIndex) throws IOException, ParserConfigurationException, SAXException {
URL url =
new
URL(
"http://"
+ ipAddress +
":1400/MediaServer/ContentDirectory/Control"
);
HttpURLConnection request = (HttpURLConnection)url.openConnection();
request.setRequestMethod(
"POST"
);
request.addRequestProperty(
"SOAPACTION"
,
"\"urn:schemas-upnp-org:service:ContentDirectory:1#Browse\""
);
request.setDoOutput(
true
);
request.setReadTimeout(2000);
Integer startPageResults = maxPageResults*pageIndex;
Integer numTotalMatches = 0;
request.connect();
OutputStreamWriter input =
new
OutputStreamWriter(request.getOutputStream());
input.write(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
);
input.write(
" <s:Body>\r\n"
);
input.write(
" <u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">\r\n"
);
input.write(
" <ObjectID>"
+ queueName +
"</ObjectID>\r\n"
);
input.write(
" <BrowseFlag>BrowseDirectChildren</BrowseFlag>\r\n"
);
input.write(
" <Filter>upnp:artist,dc:title</Filter>\r\n"
);
input.write(
" <StartingIndex>"
+ startPageResults.toString() +
"</StartingIndex>\r\n"
);
input.write(
" <RequestedCount>"
+ maxPageResults.toString() +
"</RequestedCount>\r\n"
);
input.write(
" <SortCriteria></SortCriteria>\r\n"
);
input.write(
" </u:Browse>\r\n"
);
input.write(
" </s:Body>\r\n"
);
input.write(
"</s:Envelope>\r\n"
);
input.flush();
BufferedReader output =
new
BufferedReader(
new
InputStreamReader(request.getInputStream(),
"UTF-8"
));
String oneResponse =
new
String();
String line;
while
((line = output.readLine()) !=
null
) {
oneResponse += line +
"\r\n"
;
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(
true
);
DocumentBuilder db = dbf.newDocumentBuilder();
String root =
"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>"
+ xmlDecode(oneResponse);
Document doc = db.parse(
new
ByteArrayInputStream(root.getBytes()));
NodeList nodeList = doc.getElementsByTagName(
"TotalMatches"
);
numTotalMatches = Integer.parseInt(nodeList.item(0).getTextContent());
Integer nextPageResult = ((startPageResults + maxPageResults) < numTotalMatches) ? startPageResults + maxPageResults : numTotalMatches;
System.out.println(
"Writing "
+ startPageResults.toString() +
" to "
+ nextPageResult +
" out of "
+ numTotalMatches +
" matches."
);
if
(numTotalMatches > maxPageResults*(pageIndex + 1)) {
pageIndex += 1;
writeToFiles(oneResponse);
queryQueue(pageIndex);
}
else
{
writeToFiles(oneResponse);
}
request.disconnect();
}
private void writeToFiles(String response) throws IOException {
int i = 0, j = 0;
i = response.indexOf(
"<item"
, j);
while
(i >= 0) {
j = response.indexOf(
"</item>"
, i);
String track = response.substring(i + 8, j);
String trackNo, artist, title, unc;
trackNo = extract(track,
" id="Q:"
,
"""
);
trackNo = trackNo.substring(trackNo.indexOf('/
') + 1);
unc = URLDecoder.decode(extract(track, ">x-file-cifs:", "<").replace('
/
', '
\\
').replaceAll("%20", " "), "UTF-8");
artist = extract(track, "<dc:creator>", "</dc:creator>");
title = extract(track, "<dc:title>", "</dc:title>");
txtFile.append(decode(trackNo + ". " + artist + ": " + title) + "\r\n");
m3uFile.append(decode(unc) + "\r\n");
i = response.indexOf("<item", j);
}
}
/**
* Extracts text surrounded by markers from given string.
* @param s String to extract text from.
* @param start Start marker.
* @param stop Stop marker.
* @return Extracted text found between start and stop markers, markers
* excluded.
*/
private String extract(String s, String start, String stop) {
int i = s.indexOf(start) + start.length();
return s.substring(i, s.indexOf(stop, i));
}
/**
* Decodes HTML character entities. First changes & to &, then uses Apache
* Commons Lang to decode the standard entities and then manually decodes a non-
* standard entity (').
* @param s Text to be decoded.
* @return Text with HTML character entities decoded.
*/
private String decode(String s) {
// Convert & to &, ' to '
and let Apache Commons Lang about the rest.
return
StringEscapeUtils.unescapeHtml3(s.replaceAll(
"&"
,
"&"
)).replaceAll(
"'"
,
"'"
);
}
private String xmlDecode(String s) {
String out = s.replaceAll(
"&"
,
"&"
);
out = out.replaceAll(
"'"
,
"'"
);
out = out.replaceAll(
"""
,
"\""
);
out = out.replaceAll(
"<"
,
"<"
);
out = out.replaceAll(
">"
,
">"
);
out = out.replaceAll(
" "
,
" "
);
return
out;
}
public static void main(String[] args)
throws MalformedURLException, ProtocolException, IOException, SAXException, ParserConfigurationException {
if
(args.length < 3) {
System.err.println(
"Usage: SonosQueueExtractor sonos_master_ip_address export_file_path playlist_name"
);
System.exit(0);
}
ipAddress = args[0];
txtFile =
new
OutputStreamWriter(
new
FileOutputStream(
new
File(args[1] +
"/"
+ args[2] +
".txt"
)),
"8859_1"
);
m3uFile =
new
OutputStreamWriter(
new
FileOutputStream(
new
File(args[1] +
"/"
+ args[2] +
".m3u"
)),
"8859_1"
);
new
SonosQueueExtractor ();
txtFile.close();
m3uFile.close();
System.out.println(
"Check "
+ args[1] +
" for output files."
);
}
}