]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
http server: add initial version of XMLTV exporter
authorJaroslav Kysela <perex@perex.cz>
Wed, 28 Oct 2015 18:55:49 +0000 (19:55 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 28 Oct 2015 18:59:05 +0000 (19:59 +0100)
Makefile
src/http.c
src/http.h
src/webui/webui.c
src/webui/webui.h
src/webui/xmltv.c [new file with mode: 0644]

index e73c93f0db10bc2acded7fafd823d4abeac91b17..88cb9eb3009c959e2a90785a2767972ee4935799 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -273,6 +273,7 @@ SRCS-2 += \
        src/webui/statedump.c \
        src/webui/html.c\
        src/webui/webui_api.c\
+       src/webui/xmltv.c
 
 SRCS-2 += \
        src/muxer.c \
index 9743ccf02e387e2cdda0aa11c8cd8608efba7908..f9214739f4359e1918f4e7831e9e8d6172bcc967 100644 (file)
@@ -503,6 +503,25 @@ http_redirect(http_connection_t *hc, const char *location,
     free((void *)loc);
 }
 
+/**
+ *
+ */
+char *
+http_get_hostpath(http_connection_t *hc)
+{
+  char buf[256];
+  const char *host, *proto;
+
+  host  = http_arg_get(&hc->hc_args, "Host") ?:
+          http_arg_get(&hc->hc_args, "X-Forwarded-Host");
+  proto = http_arg_get(&hc->hc_args, "X-Forwarded-Proto");
+
+  snprintf(buf, sizeof(buf), "%s://%s%s",
+           proto ?: "http", host, tvheadend_webroot ?: "");
+
+  return strdup(buf);
+}
+
 /**
  *
  */
index 74eb1b30ebf1f39a6c8350c4be2afaeddc9ccb8c..2c4134bfb9f4fdb3cc81e732d4d5952f3d8c20a7 100644 (file)
@@ -243,6 +243,8 @@ void http_deescape(char *s);
 
 void http_parse_args(http_arg_list_t *list, char *args);
 
+char *http_get_hostpath(http_connection_t *hc);
+
 /*
  * HTTP/RTSP Client
  */
index f04250b776c1cd3a593fce9e1b379a8cbb2a3664..4880dcd0ea408d0d7958d2e0c91f405aafe19a24 100644 (file)
@@ -1,6 +1,7 @@
 /*
  *  tvheadend, WEBUI / HTML user interface
  *  Copyright (C) 2008 Andreas Ă–man
+ *  Copyright (C) 2014,2015 Jaroslav Kysela
  *
  *  This program is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -460,21 +461,6 @@ http_stream_run(http_connection_t *hc, profile_chain_t *prch,
     muxer_close(mux);
 }
 
-/*
- *
- */
-static char *
-http_get_hostpath(http_connection_t *hc)
-{  
-  char buf[255];
-  const char *host = http_arg_get(&hc->hc_args, "Host") ?: http_arg_get(&hc->hc_args, "X-Forwarded-Host");
-  const char *proto = http_arg_get(&hc->hc_args, "X-Forwarded-Proto");
-
-  snprintf(buf, sizeof(buf), "%s://%s%s", proto ?: "http", host, tvheadend_webroot ?: "");
-
-  return strdup(buf);
-}
-
 /*
  *
  */
@@ -1818,6 +1804,7 @@ webui_init(int xspf)
   http_path_add("/dvrfile", NULL, page_dvrfile, ACCESS_ANONYMOUS);
   http_path_add("/favicon.ico", NULL, favicon, ACCESS_WEB_INTERFACE);
   http_path_add("/playlist", NULL, page_http_playlist, ACCESS_ANONYMOUS);
+  http_path_add("/xmltv", NULL, page_xmltv, ACCESS_ANONYMOUS);
 
   http_path_add("/state", NULL, page_statedump, ACCESS_ADMIN);
 
index 5ffe62125932b5e12e43fdcd210532562bc86fcf..2aae83501da838175dd1892166691afdcf34d5cc 100644 (file)
@@ -34,6 +34,7 @@ size_t html_escaped_len(const char *src);
 const char* html_escape(char *dst, const char *src, size_t len);
 
 int page_static_file(http_connection_t *hc, const char *remain, void *opaque);
+int page_xmltv(http_connection_t *hc, const char *remain, void *opaque);
 
 #if ENABLE_LINUXDVB
 void extjs_start_dvb(void);
diff --git a/src/webui/xmltv.c b/src/webui/xmltv.c
new file mode 100644 (file)
index 0000000..128ede0
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ *  tvheadend, XMLTV exporter
+ *  Copyright (C) 2015 Jaroslav Kysela
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "config.h"
+#include "webui.h"
+#include "channels.h"
+#include "http.h"
+
+/*
+ *
+ */
+static void
+http_xmltv_time(char *dst, time_t t)
+{
+  struct tm tm;
+  /* 20140817060000 +0200 */
+  strftime(dst, 32, "%Y%m%d%H%M%S %z", localtime_r(&t, &tm));
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_begin(htsbuf_queue_t *hq)
+{
+  htsbuf_qprintf(hq, "\
+<?xml version=\"1.0\" ?>\n\
+<!DOCTYPE tv SYSTEM \"xmltv.dtd\">\n\
+<tv generator-info-name=\"TVHeadend-%s\" source-info-name=\"tvh-%s\">\n\
+", tvheadend_version, config.server_name);
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_end(htsbuf_queue_t *hq)
+{
+  htsbuf_qprintf(hq, "</tv>\n");
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_channel_add(htsbuf_queue_t *hq, const char *hostpath, channel_t *ch)
+{
+  htsbuf_qprintf(hq, "<channel id=\"%s\">\n  <display-name>%s</display-name>\n</channel>\n",
+                 idnode_uuid_as_sstr(&ch->ch_id), channel_get_name(ch));
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_programme_one(htsbuf_queue_t *hq, const char *hostpath,
+                         channel_t *ch, epg_broadcast_t *ebc)
+{
+  char start[32], stop[32];
+  epg_episode_t *e = ebc->episode;
+  lang_str_ele_t *lse;
+
+  if (e == NULL || e->title == NULL) return;
+  http_xmltv_time(start, ebc->start);
+  http_xmltv_time(stop, ebc->stop);
+  htsbuf_qprintf(hq, "<programe start=\"%s\" stop=\"%s\" channel=\"%s\">\n",
+                 start, stop, idnode_uuid_as_sstr(&ch->ch_id));
+  RB_FOREACH(lse, e->title, link)
+    htsbuf_qprintf(hq, "  <title lang=\"%s\">%s</title>\n", lse->lang, lse->str);
+  if (e->subtitle)
+    RB_FOREACH(lse, e->subtitle, link)
+      htsbuf_qprintf(hq, "  <sub-title lang=\"%s\">%s</sub-title>\n", lse->lang, lse->str);
+  if (ebc->description)
+    RB_FOREACH(lse, ebc->description, link)
+      htsbuf_qprintf(hq, "  <desc lang=\"%s\">%s</desc>\n", lse->lang, lse->str);
+  htsbuf_qprintf(hq, "</programe>\n");
+}
+
+/*
+ *
+ */
+static void
+http_xmltv_programme_add(htsbuf_queue_t *hq, const char *hostpath, channel_t *ch)
+{
+  epg_broadcast_t *ebc;
+
+  RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link)
+    http_xmltv_programme_one(hq, hostpath, ch, ebc);
+}
+
+/**
+ * Output a XMLTV containing a single channel
+ */
+static int
+http_xmltv_channel(http_connection_t *hc, channel_t *channel)
+{
+  char *hostpath;
+
+  if (http_access_verify_channel(hc, ACCESS_STREAMING, channel, 1))
+    return HTTP_STATUS_UNAUTHORIZED;
+
+  hostpath = http_get_hostpath(hc);
+  http_xmltv_begin(&hc->hc_reply);
+  http_xmltv_channel_add(&hc->hc_reply, hostpath, channel);
+  http_xmltv_programme_add(&hc->hc_reply, hostpath, channel);
+  http_xmltv_end(&hc->hc_reply);
+  free(hostpath);
+  return 0;
+}
+
+
+/**
+ * Output a playlist containing all channels with a specific tag
+ */
+static int
+http_xmltv_tag(http_connection_t *hc, channel_tag_t *tag)
+{
+  idnode_list_mapping_t *ilm;
+  char *hostpath;
+  channel_t *ch;
+
+  if (hc->hc_access == NULL ||
+     access_verify2(hc->hc_access, ACCESS_STREAMING))
+    return HTTP_STATUS_UNAUTHORIZED;
+
+  hostpath = http_get_hostpath(hc);
+
+  http_xmltv_begin(&hc->hc_reply);
+  LIST_FOREACH(ilm, &tag->ct_ctms, ilm_in1_link) {
+    ch = (channel_t *)ilm->ilm_in2;
+    if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 0))
+      continue;
+    http_xmltv_channel_add(&hc->hc_reply, hostpath, ch);
+  }
+  LIST_FOREACH(ilm, &tag->ct_ctms, ilm_in1_link) {
+    ch = (channel_t *)ilm->ilm_in2;
+    if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 0))
+      continue;
+    http_xmltv_programme_add(&hc->hc_reply, hostpath, ch);
+  }
+  http_xmltv_end(&hc->hc_reply);
+
+  free(hostpath);
+  return 0;
+}
+
+/**
+ * Output a flat playlist with all channels
+ */
+static int
+http_xmltv_channel_list(http_connection_t *hc)
+{
+  channel_t *ch;
+  char *hostpath;
+
+  if (hc->hc_access == NULL ||
+     access_verify2(hc->hc_access, ACCESS_STREAMING))
+    return HTTP_STATUS_UNAUTHORIZED;
+
+  hostpath = http_get_hostpath(hc);
+
+  http_xmltv_begin(&hc->hc_reply);
+  CHANNEL_FOREACH(ch) {
+    if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 0))
+      continue;
+    http_xmltv_channel_add(&hc->hc_reply, hostpath, ch);
+  }
+  CHANNEL_FOREACH(ch) {
+    if (http_access_verify_channel(hc, ACCESS_STREAMING, ch, 0))
+      continue;
+    http_xmltv_programme_add(&hc->hc_reply, hostpath, ch);
+  }
+  http_xmltv_end(&hc->hc_reply);
+
+  free(hostpath);
+  return 0;
+}
+
+/**
+ * Handle requests for XMLTV export.
+ */
+int
+page_xmltv(http_connection_t *hc, const char *remain, void *opaque)
+{
+  char *components[2], *cmd;
+  int nc, r;
+  channel_t *ch = NULL;
+  channel_tag_t *tag = NULL;
+
+  if (!remain || *remain == '\0') {
+    http_redirect(hc, "/xmltv/channels", &hc->hc_req_args, 0);
+    return HTTP_STATUS_FOUND;
+  }
+
+  nc = http_tokenize((char *)remain, components, 2, '/');
+  if (!nc)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  cmd = tvh_strdupa(components[0]);
+
+  if (nc == 2)
+    http_deescape(components[1]);
+
+  pthread_mutex_lock(&global_lock);
+
+  if (nc == 2 && !strcmp(components[0], "channeluuid"))
+    ch = channel_find_by_uuid(components[1]);
+  else if (nc == 2 && !strcmp(components[0], "channelnumber"))
+    ch = channel_find_by_number(components[1]);
+  else if (nc == 2 && !strcmp(components[0], "channelname"))
+    ch = channel_find_by_name(components[1]);
+  else if (nc == 2 && !strcmp(components[0], "channel"))
+    ch = channel_find(components[1]);
+  else if (nc == 2 && !strcmp(components[0], "tagid"))
+    tag = channel_tag_find_by_identifier(atoi(components[1]));
+  else if (nc == 2 && !strcmp(components[0], "tag"))
+    tag = channel_tag_find_by_name(components[1], 0);
+
+  if (ch) {
+    r = http_xmltv_channel(hc, ch);
+  } else if (tag) {
+    r = http_xmltv_tag(hc, tag);
+  } else {
+    if (!strcmp(cmd, "channels")) {
+      r = http_xmltv_channel_list(hc);
+    } else {
+      r = HTTP_STATUS_BAD_REQUEST;
+    }
+  }
+
+  pthread_mutex_unlock(&global_lock);
+
+  if (r == 0)
+    http_output_content(hc, "text/xml");
+
+  return r;
+}