-package org.vanbest.xmltv;\r
-\r
-import java.io.BufferedOutputStream;\r
-import java.io.FileNotFoundException;\r
-import java.io.FileOutputStream;\r
-import java.io.FileWriter;\r
-import java.io.PrintWriter;\r
-import java.io.StringWriter;\r
-import java.net.MalformedURLException;\r
-import java.net.URL;\r
-import java.sql.SQLException;\r
-import java.text.SimpleDateFormat;\r
-import java.util.ArrayList;\r
-import java.util.Calendar;\r
-import java.util.Collections;\r
-import java.util.Comparator;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Map;\r
-import javax.xml.parsers.DocumentBuilderFactory;\r
-import javax.xml.stream.XMLOutputFactory;\r
-import javax.xml.stream.XMLStreamWriter;\r
-import javax.xml.transform.Transformer;\r
-import javax.xml.transform.TransformerFactory;\r
-import javax.xml.transform.dom.DOMSource;\r
-import javax.xml.transform.stream.StreamResult;\r
-import javax.xml.transform.TransformerException;\r
-import javax.xml.transform.TransformerConfigurationException;\r
-\r
-import net.sf.json.JSONArray;\r
-import net.sf.json.JSONObject;\r
-\r
-import org.apache.commons.lang.StringUtils;\r
-import org.apache.log4j.Level;\r
-import org.apache.log4j.Logger;\r
-import org.w3c.dom.DOMException;\r
-import org.w3c.dom.Document;\r
-import org.w3c.dom.Element;\r
-import org.w3c.dom.Node;\r
-import org.w3c.dom.NodeList;\r
-\r
-public class RTL extends AbstractEPGSource implements EPGSource {\r
-\r
- private static final String programme_url = "http://www.rtl.nl/active/epg_data/dag_data/";\r
- private static final String detail_url = "http://www.rtl.nl/active/epg_data/uitzending_data/";\r
- private static final String icon_url = "http://www.rtl.nl/service/gids/components/vaste_componenten/";\r
- private static final int MAX_PROGRAMMES_PER_DAY = 9999;\r
- public static final String NAME = "rtl.nl";\r
- static Logger logger = Logger.getLogger(RTL.class);\r
-\r
- String[] xmlKeys = { "zendernr", "pgmsoort", "genre", "bijvnwlanden",\r
- "ondertiteling", "begintijd", "titel", "site_path", "wwwadres",\r
- "presentatie", "omroep", "eindtijd", "inhoud", "tt_inhoud",\r
- "alginhoud", "afl_titel", "kijkwijzer" };\r
- Map<String, Integer> xmlKeyMap = new HashMap<String, Integer>();\r
-\r
- static boolean debug = false;\r
- PrintWriter debugWriter;\r
-\r
- class RTLException extends Exception {\r
- public RTLException(String s) {\r
- super(s);\r
- }\r
- }\r
-\r
- class DateStatus {\r
- Date programDate;\r
- Calendar prevStartTime = null;\r
- final static int START_TIME = 1;\r
- final static int END_TIME = 2;\r
-\r
- public DateStatus(Date date) {\r
- reset(date);\r
- }\r
-\r
- public void reset(Date date) {\r
- this.programDate = date;\r
- this.prevStartTime = null;\r
- }\r
- }\r
-\r
- class DescStatus {\r
- String inhoud;\r
- String alginhoud;\r
- String tt_inhoud;\r
- }\r
-\r
- public RTL(int sourceId, Config config) {\r
- super(sourceId, config);\r
- if (debug) {\r
- for (int i = 0; i < xmlKeys.length; i++) {\r
- xmlKeyMap.put(xmlKeys[i], i);\r
- }\r
- }\r
- }\r
-\r
- public String getName() {\r
- return NAME;\r
- }\r
-\r
- private String xmlToString(Document doc) {\r
- try {\r
- Transformer transformer = TransformerFactory.newInstance().newTransformer();\r
- StringWriter stw = new StringWriter(); \r
- transformer.transform(new DOMSource(doc), new StreamResult(stw)); \r
- return stw.toString();\r
- } catch (TransformerConfigurationException e) {\r
- logger.debug("Cannot convert XML Document to String, e");\r
- return "";\r
- } catch (TransformerException e) {\r
- logger.debug("Cannot convert XML Document to String, e");\r
- return "";\r
- }\r
- }\r
-\r
- private Document fetchXML(URL url) throws Exception {\r
- Document xml = null;\r
- boolean done = false;\r
- logger.debug("Fetching XML from URL "+url);\r
- for (int count = 0; !done; count++) {\r
- Thread.sleep(config.niceMilliseconds*(1<<count));\r
- try {\r
- xml = DocumentBuilderFactory.newInstance()\r
- .newDocumentBuilder().parse(url.openStream());\r
- done = true;\r
- } catch (Exception e) {\r
- if (!config.quiet) {\r
- logger.warn("Error fetching from url " + url + ", count="\r
- + count);\r
- }\r
- if (count >= MAX_FETCH_TRIES) {\r
- stats.fetchErrors++;\r
- logger.debug("Error getting data from url", e);\r
- throw new Exception("Error getting data from url "\r
- + url, e);\r
- }\r
- }\r
- }\r
- logger.debug(xmlToString(xml));\r
- return xml;\r
- }\r
-\r
- public List<Channel> getChannels() {\r
- List<Channel> result = new ArrayList<Channel>(10);\r
-\r
- URL url = null;\r
- try {\r
- url = new URL(programme_url + "1");\r
- } catch (MalformedURLException e) {\r
- logger.error("Exception creating RTL channel list url", e);\r
- }\r
- Document xml = null;\r
- try {\r
- xml = fetchXML(url);\r
- } catch (Exception e) {\r
- logger.error("Exception reading RTL channel listing from " + url\r
- + " and transforming to XML", e);\r
- return result;\r
- }\r
- Element root = xml.getDocumentElement();\r
- String json = root.getTextContent();\r
- JSONObject o = JSONObject.fromObject(json);\r
- for (Object k : o.keySet()) {\r
- JSONArray j = (JSONArray) o.get(k);\r
- String id = genericChannelId(k.toString());\r
- String name = (String) j.get(0);\r
- String icon = icon_url + id + ".gif";\r
- Channel c = Channel.getChannel(getId(), id, name);\r
- c.addIcon(icon);\r
- result.add(c);\r
- }\r
-\r
- Collections.sort(result, new Comparator<Channel>() {\r
- public int compare(Channel o1, Channel o2) {\r
- if (o1.source == o2.source) {\r
- int c1 = Integer.parseInt(o1.id);\r
- int c2 = Integer.parseInt(o2.id);\r
- return (c1 == c2 ? 0 : ((c1 < c2) ? -1 : 1));\r
- } else {\r
- return o1.source < o2.source ? -1 : 1;\r
- }\r
- }\r
- });\r
- return result;\r
- }\r
-\r
- private String genericChannelId(String jsonid) {\r
- return jsonid.replaceAll("^Z", ""); // remove initial Z\r
- }\r
-\r
- /*\r
- * <?xml version="1.0" encoding="iso-8859-1" ?> <uitzending_data>\r
- * <uitzending_data_item> <zendernr>5</zendernr>\r
- * <pgmsoort>Realityserie</pgmsoort> <genre>Amusement</genre>\r
- * <bijvnwlanden></bijvnwlanden> <ondertiteling></ondertiteling>\r
- * <begintijd>05:00</begintijd> <titel>Marriage Under Construction</titel>\r
- * <site_path>0</site_path> <wwwadres></wwwadres>\r
- * <presentatie></presentatie> <omroep></omroep> <eindtijd>06:00</eindtijd>\r
- * <inhoud></inhoud> <tt_inhoud>Een jong stel wordt gevolgd bij het zoeken\r
- * naar, en vervolgens verbouwen en inrichten van, hun eerste huis. Dit\r
- * verloopt uiteraard niet zonder slag of stoot.</tt_inhoud> <alginhoud>Een\r
- * jong stel wordt gevolgd bij het zoeken naar, en vervolgens verbouwen en\r
- * inrichten van, hun eerste huis. Dit verloopt uiteraard niet zonder slag\r
- * of stoot.</alginhoud> <afl_titel></afl_titel> <kijkwijzer></kijkwijzer>\r
- * </uitzending_data_item> </uitzending_data>\r
- */\r
- private void fetchDetail(Programme prog, DateStatus dateStatus, String id)\r
- throws Exception {\r
- URL url = detailUrl(id);\r
- Document xml = fetchXML(url);\r
- Element root = xml.getDocumentElement();\r
- if (root.hasAttributes()) {\r
- logger.warn("Unknown attributes for RTL detail root node");\r
- }\r
- NodeList nodes = root.getChildNodes();\r
- DescStatus descStatus = new DescStatus();\r
- for (int i = 0; i < nodes.getLength(); i++) {\r
- Node n = nodes.item(i);\r
- if (!n.getNodeName().equals("uitzending_data_item")) {\r
- logger.warn("Ignoring RTL detail, tag " + n.getNodeName()\r
- + ", full xml:");\r
- logger.debug(xmlToString(xml));\r
- continue;\r
- }\r
- // we have a uitzending_data_item node\r
- NodeList subnodes = n.getChildNodes();\r
- String[] result = new String[xmlKeys.length];\r
- for (int j = 0; j < subnodes.getLength(); j++) {\r
- try {\r
- if (debug) {\r
- Node sub = subnodes.item(j);\r
- String key = ((Element) sub).getTagName();\r
- int index = xmlKeyMap.get(key);\r
- String value = "\""\r
- + sub.getTextContent().replaceAll("\\s", " ")\r
- + "\"";\r
- result[index] = value;\r
- }\r
- handleNode(prog, dateStatus, descStatus, subnodes.item(j));\r
- } catch (RTLException e) {\r
- logger.debug(xmlToString(xml), e);\r
- continue;\r
- }\r
- }\r
- if (debug) {\r
- for (int k = 0; k < result.length; k++) {\r
- debugWriter.print(result[k]);\r
- debugWriter.print(",");\r
- }\r
- debugWriter.println();\r
- }\r
- }\r
- StringBuilder description = new StringBuilder();\r
- if (descStatus.alginhoud != null)\r
- description.append(descStatus.alginhoud);\r
- if (descStatus.inhoud != null) {\r
- if (description.length() != 0) {\r
- description.append("<p>");\r
- }\r
- description.append(descStatus.inhoud);\r
- }\r
- if (description.length() == 0 && descStatus.tt_inhoud != null) {\r
- // only use tt_inhoud if the other two are both empty, since it is\r
- // almost\r
- // always a summary of those fields and others such as <presenter>\r
- description.append(descStatus.tt_inhoud);\r
- }\r
- prog.addDescription(description.toString());\r
- }\r
-\r
- private void handleNode(Programme prog, DateStatus dateStatus,\r
- DescStatus descStatus, Node n) throws RTLException, DOMException,\r
- SQLException {\r
- if (n.getNodeType() != Node.ELEMENT_NODE) {\r
- throw new RTLException("Ignoring non-element node "\r
- + n.getNodeName());\r
- }\r
- if (n.hasAttributes()) {\r
- throw new RTLException("Unknown attributes for RTL detail node "\r
- + n.getNodeName());\r
- }\r
- if (n.hasChildNodes()) {\r
- NodeList list = n.getChildNodes();\r
- for (int i = 0; i < list.getLength(); i++) {\r
- if (list.item(i).getNodeType() == Node.ELEMENT_NODE) {\r
- throw new RTLException("RTL detail node " + n.getNodeName()\r
- + " has unexpected child element "\r
- + list.item(i).getNodeName());\r
- }\r
- }\r
- }\r
- Element e = (Element) n;\r
- String tag = e.getTagName();\r
-\r
- if (e.getTextContent().isEmpty()) {\r
- return;\r
- }\r
- if (tag.equals("genre")) {\r
- prog.addCategory(config.translateCategory(e.getTextContent()));\r
- } else if (tag.equals("eindtijd")) {\r
- prog.endTime = parseTime(e.getTextContent(), dateStatus,\r
- DateStatus.END_TIME);\r
- } else if (tag.equals("omroep")) {\r
- } else if (tag.equals("kijkwijzer")) {\r
- logger.trace(prog.toString());\r
- logger.trace("Kijkwijzer: \"" + e.getTextContent() + "\"");\r
- String kijkwijzer = e.getTextContent();\r
- List<String> list = parseKijkwijzer(kijkwijzer);\r
- if (config.joinKijkwijzerRatings) {\r
- // mythtv doesn't understand multiple <rating> tags\r
- prog.addRating("kijkwijzer", StringUtils.join(list, ","));\r
- } else {\r
- for (String rating : list) {\r
- prog.addRating("kijkwijzer", rating);\r
- }\r
- }\r
- logger.trace("Kijkwijzer: \"" + StringUtils.join(list, ",") + "\"");\r
- // TODO add kijkwijzer icons?\r
- } else if (tag.equals("presentatie")) {\r
- // A; A en B; A, B, C en D\r
- String[] presentatoren = e.getTextContent().split(", | en ");\r
- for (String pres : presentatoren) {\r
- prog.addPresenter(pres);\r
- }\r
- } else if (tag.equals("wwwadres")) {\r
- prog.addUrl(e.getTextContent());\r
- } else if (tag.equals("alginhoud")) {\r
- descStatus.alginhoud = e.getTextContent();\r
- } else if (tag.equals("inhoud")) {\r
- descStatus.inhoud = e.getTextContent();\r
- } else if (tag.equals("tt_inhoud")) {\r
- descStatus.tt_inhoud = e.getTextContent();\r
- // ignore, is summary of other fields\r
- } else if (tag.equals("zendernr")) {\r
- } else if (tag.equals("titel")) {\r
- } else if (tag.equals("bijvnwlanden")) {\r
- } else if (tag.equals("afl_titel")) {\r
- prog.addSecondaryTitle(e.getTextContent());\r
- } else if (tag.equals("site_path")) {\r
- } else if (tag.equals("ondertiteling")) {\r
- if (e.getTextContent().equals("J")) {\r
- prog.addSubtitle("teletext");\r
- } else {\r
- throw new RTLException("Ignoring unknown value \""\r
- + n.getTextContent() + "\" for tag ondertiteling");\r
- }\r
- } else if (tag.equals("begintijd")) {\r
- } else if (tag.equals("pgmsoort")) {\r
- } else {\r
- throw new RTLException("Ignoring unknown tag " + n.getNodeName()\r
- + ", content: \"" + e.getTextContent() + "\"");\r
- }\r
- // prog.endTime = parseTime(date, root.)\r
- }\r
-\r
- @Override\r
- public List<Programme> getProgrammes(List<Channel> channels, int day)\r
- throws Exception {\r
- List<Programme> result = new LinkedList<Programme>();\r
- Map<String, Channel> channelMap = new HashMap<String, Channel>();\r
- for (Channel c : channels) {\r
- if (c.enabled && c.source == getId())\r
- channelMap.put(c.id, c);\r
- }\r
- URL url = programmeUrl(day);\r
- Document xml = fetchXML(url);\r
- Element root = xml.getDocumentElement();\r
- Date date = new SimpleDateFormat("yyyy-MM-dd").parse(root\r
- .getAttribute("date"));\r
- DateStatus dateStatus = new DateStatus(date);\r
- // System.out.println("date: " + date);\r
- String json = root.getTextContent();\r
- // System.out.println("json: " + json);\r
- JSONObject o = JSONObject.fromObject(json);\r
- String prevChannel = null;\r
- for (Object k : o.keySet()) {\r
- String id = genericChannelId(k.toString());\r
- if (!channelMap.containsKey(id)) {\r
- // if (!config.quiet)\r
- // System.out.println("Skipping programmes for channel " + id);\r
- continue;\r
- }\r
- if (!id.equals(prevChannel)) {\r
- dateStatus.reset(date);\r
- prevChannel = id;\r
- }\r
- JSONArray j = (JSONArray) o.get(k);\r
- // System.out.println(k.toString()+": "+j.toString());\r
- // System.out.println("Channel name:" + j.get(0));\r
- for (int i = 1; i < j.size() && i < MAX_PROGRAMMES_PER_DAY; i++) {\r
- JSONArray p = (JSONArray) j.get(i);\r
- String starttime = p.getString(0);\r
- String title = p.getString(1);\r
- String programme_id = p.getString(2);\r
- String genre_id = p.getString(3); // 1 = amusement, etc\r
- String quark2 = p.getString(4); // 0 of 1, movie flag?\r
- if (debug)\r
- debugWriter.print("\"" + id + "\",\"" + starttime + "\",\""\r
- + title + "\",\"" + genre_id + "\",\"" + quark2\r
- + "\",");\r
- Programme prog = cache.get(getId(), programme_id);\r
- if (prog == null) {\r
- stats.cacheMisses++;\r
- prog = new Programme();\r
- prog.addTitle(title);\r
- prog.startTime = parseTime(starttime, dateStatus,\r
- DateStatus.START_TIME);\r
- prog.channel = channelMap.get(id).getXmltvChannelId();\r
- if (config.fetchDetails) {\r
- fetchDetail(prog, dateStatus, programme_id);\r
- }\r
- cache.put(getId(), programme_id, prog);\r
- } else {\r
- stats.cacheHits++;\r
- }\r
- result.add(prog);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- // Assumption: programmes are more-or-less in ascending order by start time\r
- private Date parseTime(String time, DateStatus status, int mode) {\r
- Calendar result = Calendar.getInstance();\r
- result.setTime(status.programDate);\r
- String[] parts = time.split(":");\r
- if (parts.length != 2) {\r
- if (!config.quiet)\r
- logger.debug("Wrong time format " + time);\r
- // ignore\r
- }\r
- result.set(Calendar.HOUR_OF_DAY, Integer.parseInt(parts[0]));\r
- result.set(Calendar.MINUTE, Integer.parseInt(parts[1]));\r
- Calendar prev = status.prevStartTime;\r
- // Check if the start time of a new program is at most one hour before\r
- // the start time of\r
- // the previous one. End time of a program should be at or after the\r
- // start time of the\r
- // program. Else, assume it's on the next day.\r
- if (prev != null) {\r
- if (mode == DateStatus.START_TIME) {\r
- prev.add(Calendar.HOUR_OF_DAY, -1);\r
- }\r
- if (result.before(prev)) {\r
- result.add(Calendar.DAY_OF_MONTH, 1);\r
- }\r
- }\r
- if (mode == DateStatus.START_TIME) {\r
- status.prevStartTime = result;\r
- }\r
- return result.getTime();\r
- }\r
-\r
- private static URL programmeUrl(int day) throws MalformedURLException {\r
- return new URL(programme_url + day);\r
- }\r
-\r
- private static URL detailUrl(String id) throws Exception {\r
- return new URL(detail_url + id);\r
- }\r
-\r
- /**\r
- * @param args\r
- * @throws FileNotFoundException\r
- */\r
- public static void main(String[] args) throws FileNotFoundException {\r
- debug = true;\r
- Logger.getRootLogger().setLevel(Level.TRACE);\r
-\r
- Config config = Config.getDefaultConfig();\r
- config.niceMilliseconds = 50;\r
- RTL rtl = new RTL(2, config);\r
- if (debug) {\r
- rtl.cache.clear();\r
- logger.info("Writing CSV to rtl.csv");\r
- rtl.debugWriter = new PrintWriter(new BufferedOutputStream(\r
- new FileOutputStream("rtl.csv")));\r
- rtl.debugWriter\r
- .print("\"zender\",\"starttime\",\"title\",\"quark1\",\"quark2\",");\r
- for (int k = 0; k < rtl.xmlKeys.length; k++) {\r
- rtl.debugWriter.print(rtl.xmlKeys[k]);\r
- rtl.debugWriter.print(",");\r
- }\r
- rtl.debugWriter.println();\r
- }\r
-\r
- try {\r
- List<Channel> channels = rtl.getChannels();\r
- logger.info("Channels: " + channels);\r
- XMLStreamWriter writer = XMLOutputFactory.newInstance()\r
- .createXMLStreamWriter(new FileWriter("rtl.xml"));\r
- writer.writeStartDocument();\r
- writer.writeCharacters("\n");\r
- writer.writeDTD("<!DOCTYPE tv SYSTEM \"xmltv.dtd\">");\r
- writer.writeCharacters("\n");\r
- writer.writeStartElement("tv");\r
- for (Channel c : channels) {\r
- c.serialize(writer, true);\r
- }\r
- writer.flush();\r
- // List<Programme> programmes =\r
- // rtl.getProgrammes(channels.subList(6, 9), 0);\r
- for (int day = 0; day < 10; day++) {\r
- List<Programme> programmes = rtl.getProgrammes(channels, day);\r
- for (Programme p : programmes) {\r
- p.serialize(writer);\r
- }\r
- }\r
- writer.writeEndElement();\r
- writer.writeEndDocument();\r
- writer.flush();\r
- if (!config.quiet) {\r
- EPGSource.Stats stats = rtl.getStats();\r
- logger.info("Number of programmes from cache: "\r
- + stats.cacheHits);\r
- logger.info("Number of programmes fetched: "\r
- + stats.cacheMisses);\r
- logger.info("Number of fetch errors: " + stats.fetchErrors);\r
- }\r
- if (debug) {\r
- rtl.debugWriter.flush();\r
- rtl.debugWriter.close();\r
- }\r
- rtl.close();\r
- } catch (Exception e) {\r
- // TODO Auto-generated catch block\r
- logger.debug("Exception in RTL.main()", e);\r
- }\r
- }\r
-\r
-}\r
+package org.vanbest.xmltv;
+
+import java.io.BufferedOutputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.sql.SQLException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerConfigurationException;
+
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class RTL extends AbstractEPGSource implements EPGSource {
+
+ private static final String programme_base_url = "http://www.rtl.nl/system/s4m/tvguide/guide_for_one_day.xml";
+
+ private static final String icon_url = "http://www.rtl.nl/service/gids/components/vaste_componenten/";
+ private static final int MAX_PROGRAMMES_PER_DAY = 9999;
+ public static final String NAME = "rtl.nl";
+ static Logger logger = Logger.getLogger(RTL.class);
+
+ static boolean debug = false;
+ PrintWriter debugWriter;
+
+ private Map<String,JSONObject> abstracts = new HashMap<String,JSONObject>();
+ private Map<String,JSONObject> episodes = new HashMap<String,JSONObject>();
+ private Map<String,JSONObject> seasons = new HashMap<String,JSONObject>();
+
+ class RTLException extends Exception {
+ public RTLException(String s) {
+ super(s);
+ }
+ }
+
+ public RTL(int sourceId, Config config) {
+ super(sourceId, config);
+ }
+
+ public String getName() {
+ return NAME;
+ }
+
+ // http://www.rtl.nl/system/s4m/tvguide/guide_for_one_day.xml?output=json&days_ahead=7&days_back=1&station=ALL
+ // Note: station parameter is either ALL or one of RTL4,RTL5,RTL7,RTL8
+ private static URL programmeUrl(int days_back, int days_ahead) throws MalformedURLException {
+ return new URL(programme_base_url + "?output=json&days_ahead="+days_ahead+"&days_back="+days_back+"&station=ALL");
+ }
+
+ public List<Channel> getChannels() {
+ Map<String,Channel> channels = new HashMap<String,Channel>(5);
+ JSONObject o;
+ try {
+ URL url = programmeUrl(0, 0);
+ o = fetchJSON(url);
+ } catch (Exception e) {
+ logger.error("Error fetching channels for source "+getName(), e);
+ return new ArrayList<Channel>();
+ }
+ JSONArray schedules = o.getJSONArray("schedule");
+ for (int i=0; i<schedules.size(); i++) {
+ JSONObject schedule = schedules.getJSONObject(i);
+ String channel = schedule.getString("station");
+ if(!channels.containsKey(channel)) {
+ Channel c = Channel.getChannel(getId(), channel, channel);
+ // TODO: channel icon
+ channels.put(channel, c);
+ }
+ }
+ List<Channel> result = new ArrayList<Channel>(10);
+ return new ArrayList<Channel>(channels.values());
+ }
+
+ @Override
+ List<String> parseKijkwijzer(String s) {
+ List<String> result = new ArrayList<String>();
+ for (int i = 0; i < s.length(); i++) {
+ }
+ return result;
+ }
+
+ Programme createProgramme(JSONObject schedule, Map<String,Channel> channelMap) {
+ Programme prog = new Programme();
+ prog.startTime = new Date(1000L*schedule.getLong("unixtime"));
+ prog.channel = channelMap.get(schedule.getString("station")).getXmltvChannelId();
+ if(schedule.getBoolean("rerun")) prog.setPreviouslyShown();
+
+ String abstractKey = schedule.optString("abstract_key");
+ if(abstractKey!=null) {
+ JSONObject abstrac = abstracts.get(abstractKey);
+ prog.addTitle(abstrac.getString("name"));
+ }
+ String episodeKey = schedule.optString("episode_key");
+ if(episodeKey!=null) {
+ JSONObject episode = episodes.get(episodeKey);
+ String s = episode.optString("name");
+ if(s!=null && !s.isEmpty()) prog.addSecondaryTitle(s);
+ s = episode.optString("episode_number");
+ if(s!=null && !s.isEmpty()) prog.addEpisode(s, "onscreen");
+ s = episode.optString("synopsis");
+ if(s!=null && !s.isEmpty()) prog.addDescription(s);
+ String kijkwijzer = episode.optString("nicam");
+ if (kijkwijzer != null && !kijkwijzer.isEmpty()) {
+ logger.debug("kijkwijzer: "+kijkwijzer);
+ prog.addRating("kijkwijzer", kijkwijzer);
+ /*
+ List<String> list = parseKijkwijzer(kijkwijzer);
+ if (config.joinKijkwijzerRatings) {
+ // mythtv doesn't understand multiple <rating> tags
+ prog.addRating("kijkwijzer", StringUtils.join(list, ","));
+ } else {
+ for (String rating : list) {
+ prog.addRating("kijkwijzer", rating);
+ }
+ }
+ */
+ }
+ }
+ String seasonKey = schedule.optString("season_key");
+ if(seasonKey!=null) {
+ JSONObject season = seasons.get(seasonKey);
+ // ignored
+ // season_number
+ // name
+ }
+ return prog;
+ }
+
+ void parseLibrary(JSONArray library) {
+ if(library.size()!=1) {
+ logger.warn("RTL library array size is not equals to one!");
+ }
+ JSONObject lib = library.getJSONObject(0);
+ // library: abstracts
+ JSONArray abstractArray = lib.getJSONArray("abstracts");
+ for(int i=0; i<abstractArray.size(); i++) {
+ JSONObject abstrac = abstractArray.getJSONObject(i);
+ abstracts.put(abstrac.getString("abstract_key"), abstrac);
+ }
+ // library: seasons
+ JSONArray seasonArray = lib.getJSONArray("seasons");
+ for(int i=0; i<seasonArray.size(); i++) {
+ JSONObject season = seasonArray.getJSONObject(i);
+ seasons.put(season.getString("season_key"), season);
+ }
+ // library: episodes
+ JSONArray episodeArray = lib.getJSONArray("episodes");
+ for(int i=0; i<episodeArray.size(); i++) {
+ JSONObject episode = episodeArray.getJSONObject(i);
+ episodes.put(episode.getString("episode_key"), episode);
+ }
+ }
+
+ @Override
+ public List<Programme> getProgrammes(List<Channel> channels, int day)
+ throws Exception {
+ List<Programme> result = new LinkedList<Programme>();
+ Map<String, Channel> channelMap = new HashMap<String, Channel>();
+ for (Channel c : channels) {
+ if (c.enabled && c.source == getId())
+ channelMap.put(c.id, c);
+ }
+ URL url = programmeUrl(0, day);
+ JSONObject json = fetchJSON(url);
+
+ // First parse the library
+ JSONArray library = json.getJSONArray("library");
+ parseLibrary(library);
+
+ // Then the schedules
+ JSONArray schedules = json.getJSONArray("schedule");
+ for (int i=0; i<schedules.size(); i++) {
+ JSONObject schedule = schedules.getJSONObject(i);
+
+ // Skip programme if station not in channel list
+ String station=schedule.getString("station");
+ if (!channelMap.containsKey(station)) {
+ if (!config.quiet)
+ logger.info("Skipping programmes for channel " + station);
+ continue;
+ }
+ result.add(createProgramme(schedule, channelMap));
+ }
+
+ return result;
+ }
+
+ /**
+ * @param args
+ * @throws FileNotFoundException
+ */
+ public static void main(String[] args) throws FileNotFoundException {
+ debug = true;
+ Logger.getRootLogger().setLevel(Level.TRACE);
+
+ Config config = Config.getDefaultConfig();
+ config.niceMilliseconds = 50;
+ RTL rtl = new RTL(2, config);
+ if (debug) {
+ rtl.cache.clear();
+ logger.info("Writing CSV to rtl.csv");
+ rtl.debugWriter = new PrintWriter(new BufferedOutputStream(
+ new FileOutputStream("rtl.csv")));
+ rtl.debugWriter
+ .print("\"zender\",\"starttime\",\"title\",\"quark1\",\"quark2\",");
+ /*
+ for (int k = 0; k < rtl.xmlKeys.length; k++) {
+ rtl.debugWriter.print(rtl.xmlKeys[k]);
+ rtl.debugWriter.print(",");
+ }
+ */
+ rtl.debugWriter.println();
+ }
+
+ try {
+ List<Channel> channels = rtl.getChannels();
+ logger.info("Channels: " + channels);
+ XMLStreamWriter writer = XMLOutputFactory.newInstance()
+ .createXMLStreamWriter(new FileWriter("rtl.xml"));
+ writer.writeStartDocument();
+ writer.writeCharacters("\n");
+ writer.writeDTD("<!DOCTYPE tv SYSTEM \"xmltv.dtd\">");
+ writer.writeCharacters("\n");
+ writer.writeStartElement("tv");
+ for (Channel c : channels) {
+ c.serialize(writer, true);
+ }
+ writer.flush();
+ // List<Programme> programmes =
+ // rtl.getProgrammes(channels.subList(6, 9), 0);
+ for (int day = 0; day < 10; day++) {
+ List<Programme> programmes = rtl.getProgrammes(channels, day);
+ for (Programme p : programmes) {
+ p.serialize(writer);
+ }
+ }
+ writer.writeEndElement();
+ writer.writeEndDocument();
+ writer.flush();
+ if (!config.quiet) {
+ EPGSource.Stats stats = rtl.getStats();
+ logger.info("Number of programmes from cache: "
+ + stats.cacheHits);
+ logger.info("Number of programmes fetched: "
+ + stats.cacheMisses);
+ logger.info("Number of fetch errors: " + stats.fetchErrors);
+ }
+ if (debug) {
+ rtl.debugWriter.flush();
+ rtl.debugWriter.close();
+ }
+ rtl.close();
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ logger.debug("Exception in RTL.main()", e);
+ }
+ }
+
+}