From: Jaroslav Kysela Date: Wed, 10 May 2017 14:54:24 +0000 (+0200) Subject: satip server: allow to pass own satip.m3u file to clients (must be in the config... X-Git-Tag: v4.2.2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0dcdcecea1754fd9d49e08921e9636a47146dbde;p=thirdparty%2Ftvheadend.git satip server: allow to pass own satip.m3u file to clients (must be in the config directory) --- diff --git a/docs/class/satip_server.md b/docs/class/satip_server.md index 3b85b3895..6774968eb 100644 --- a/docs/class/satip_server.md +++ b/docs/class/satip_server.md @@ -59,4 +59,9 @@ Hopefully (and if everything went to plan) your client should have now detected Tvheadend as a SAT\>IP server. If not, restart or force it to perform a service discovery. +###Custom M3U Playlist + +You may put your custom m3u playlist which is advertised to the clients +to your tvheadend's configuration directory - filename *satip.m3u*. + --- diff --git a/src/htsp_server.c b/src/htsp_server.c index 352b05d1a..b1fe329d4 100644 --- a/src/htsp_server.c +++ b/src/htsp_server.c @@ -2705,7 +2705,7 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) { const char *str, *s2; const char *filename = NULL; - + char buf[PATH_MAX]; if((str = htsmsg_get_str(in, "file")) == NULL) return htsp_error(htsp, N_("Invalid arguments")); @@ -2731,7 +2731,9 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in) return htsp_file_open(htsp, filename, 0, de); } else if ((s2 = tvh_strbegins(str, "imagecache/")) != NULL) { - int fd = imagecache_open(atoi(s2)); + int fd = -1; + if (!imagecache_filename(atoi(s2), buf, sizeof(buf))) + fd = tvh_open(buf, O_RDONLY, 0); if (fd < 0) return htsp_error(htsp, N_("Failed to open image")); return htsp_file_open(htsp, str, fd, NULL); diff --git a/src/imagecache.c b/src/imagecache.c index 25f0b51aa..1af597043 100644 --- a/src/imagecache.c +++ b/src/imagecache.c @@ -642,11 +642,10 @@ imagecache_get_id ( const char *url ) * Get data */ int -imagecache_open ( uint32_t id ) +imagecache_filename ( uint32_t id, char *name, size_t len ) { imagecache_image_t skel, *i; char *fn; - int fd = -1; lock_assert(&global_lock); @@ -659,7 +658,8 @@ imagecache_open ( uint32_t id ) if (!strncasecmp(i->url, "file://", 7)) { fn = strdupa(i->url + 7); http_deescape(fn); - fd = open(fn, O_RDONLY); + strncpy(name, fn, len); + name[len-1] = '\0'; } /* Remote file */ @@ -688,9 +688,10 @@ imagecache_open ( uint32_t id ) if (e) return -1; } - fd = hts_settings_open_file(0, "imagecache/data/%d", i->id); + if (hts_settings_buildpath(name, len, "imagecache/data/%d", i->id)) + return -1; } #endif - return fd; + return 0; } diff --git a/src/imagecache.h b/src/imagecache.h index 4783f4de3..f4201bd97 100644 --- a/src/imagecache.h +++ b/src/imagecache.h @@ -44,6 +44,6 @@ void imagecache_trigger ( void ); // Note: will return 0 if invalid (must serve original URL) uint32_t imagecache_get_id ( const char *url ); -int imagecache_open ( uint32_t id ); +int imagecache_filename ( uint32_t id, char *name, size_t len ); #endif /* __IMAGE_CACHE_H__ */ diff --git a/src/satip/server.c b/src/satip/server.c index 9d503fe0d..c00435b72 100644 --- a/src/satip/server.c +++ b/src/satip/server.c @@ -22,6 +22,7 @@ #include "settings.h" #include "config.h" #include "input/mpegts/iptv/iptv_private.h" +#include "webui/webui.h" #include "satip/server.h" #define UPNP_MAX_AGE 1800 @@ -115,7 +116,7 @@ satip_server_http_xml(http_connection_t *hc) htsbuf_queue_t q; mpegts_network_t *mn; int dvbt = 0, dvbs = 0, dvbc = 0, atsc = 0; - int srcs = 0, delim = 0, tuners = 0, i; + int srcs = 0, delim = 0, tuners = 0, i, satipm3u = 0; struct xml_type_xtab *p; http_arg_list_t args; @@ -186,6 +187,9 @@ satip_server_http_xml(http_connection_t *hc) else snprintf(buf2, sizeof(buf2), " %s", satip_server_conf.satip_uuid + 26); + if (!hts_settings_buildpath(buf, sizeof(buf), "satip.m3u")) + satipm3u = access(buf, R_OK) == 0; + snprintf(buf, sizeof(buf), MSG, config_get_server_name(), buf2, tvheadend_version, @@ -197,7 +201,9 @@ satip_server_http_xml(http_connection_t *hc) http_server_ip, http_server_port, devicelist ?: "", satip_server_conf.satip_nom3u ? "" : - "/playlist/satip/channels\n"); + (satipm3u ? + "/satip_server/satip.m3u\n" : + "/playlist/satip/channels\n")); free(devicelist); @@ -220,12 +226,25 @@ satip_server_http_xml(http_connection_t *hc) #undef MSG } +static int +satip_server_satip_m3u(http_connection_t *hc) +{ + char path[PATH_MAX]; + + if (hts_settings_buildpath(path, sizeof(path), "satip.m3u")) + return HTTP_STATUS_SERVICE; + + return http_serve_file(hc, path, 0, MIME_M3U, NULL, NULL, NULL); +} + int satip_server_http_page(http_connection_t *hc, const char *remain, void *opaque) { if (strcmp(remain, "desc.xml") == 0) return satip_server_http_xml(hc); + if (strcmp(remain, "satip.m3u") == 0) + return satip_server_satip_m3u(hc); return 0; } diff --git a/src/webui/webui.c b/src/webui/webui.c index 15fed7df8..4f0421b21 100644 --- a/src/webui/webui.c +++ b/src/webui/webui.c @@ -61,9 +61,6 @@ #include #endif -#define MIME_M3U "audio/x-mpegurl" -#define MIME_E2 "application/x-e2-bouquet" - typedef int (sortfcn_t)(const void *, const void *); enum { @@ -1550,24 +1547,25 @@ page_play(http_connection_t *hc, const char *remain, void *opaque) } /** - * Download a recorded file + * Send a file */ -static int -page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) +int +http_serve_file(http_connection_t *hc, const char *fname, + int fconv, const char *content, + int (*preop)(http_connection_t *hc, off_t file_start, + size_t content_len, void *opaque), + void (*stats)(http_connection_t *hc, size_t len, void *opaque), + void *opaque) { int fd, ret; struct stat st; - const char *content = NULL, *range, *filename; - dvr_entry_t *de; - char *fname, *charset; + const char *range; char *basename; char *str, *str0; char range_buf[255]; char *disposition = NULL; off_t content_len, chunk; intmax_t file_start, file_end; - void *tcp_id; - th_subscription_t *sub; htsbuf_queue_t q; #if defined(PLATFORM_LINUX) ssize_t r; @@ -1575,53 +1573,26 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) off_t r; #endif - if(remain == NULL) - return HTTP_STATUS_BAD_REQUEST; - - if(hc->hc_access == NULL || - (access_verify2(hc->hc_access, ACCESS_OR | - ACCESS_STREAMING | - ACCESS_ADVANCED_STREAMING | - ACCESS_RECORDER))) - return HTTP_STATUS_UNAUTHORIZED; - - pthread_mutex_lock(&global_lock); - de = dvr_entry_find_by_uuid(remain); - if (de == NULL) - de = dvr_entry_find_by_id(atoi(remain)); - if(de == NULL || (filename = dvr_get_filename(de)) == NULL) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_NOT_FOUND; - } - if(dvr_entry_verify(de, hc->hc_access, 1)) { - pthread_mutex_unlock(&global_lock); - return HTTP_STATUS_UNAUTHORIZED; - } - - fname = tvh_strdupa(filename); - content = muxer_container_filename2mime(fname, 1); - charset = de->de_config ? de->de_config->dvr_charset_id : NULL; - - pthread_mutex_unlock(&global_lock); - - basename = strrchr(fname, '/'); - if (basename) { - basename++; /* Skip '/' */ - str0 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1), - basename, strlen(basename) * 3); - if (str0 == NULL) - return HTTP_STATUS_INTERNAL; - htsbuf_queue_init(&q, 0); - htsbuf_append_and_escape_url(&q, basename); - str = htsbuf_to_string(&q); - r = 50 + strlen(str0) + strlen(str); - disposition = alloca(r); - snprintf(disposition, r, - "attachment; filename=\"%s\"; filename*=UTF-8''%s", - str0, str); - htsbuf_queue_flush(&q); - free(str); - free(str0); + if (fconv) { + basename = strrchr(fname, '/'); + if (basename) { + basename++; /* Skip '/' */ + str0 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1), + basename, strlen(basename) * 3); + if (str0 == NULL) + return HTTP_STATUS_INTERNAL; + htsbuf_queue_init(&q, 0); + htsbuf_append_and_escape_url(&q, basename); + str = htsbuf_to_string(&q); + r = 50 + strlen(str0) + strlen(str); + disposition = alloca(r); + snprintf(disposition, r, + "attachment; filename=\"%s\"; filename*=UTF-8''%s", + str0, str); + htsbuf_queue_flush(&q); + free(str); + free(str0); + } } fd = tvh_open(fname, O_RDONLY, 0); @@ -1666,47 +1637,13 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) return HTTP_STATUS_INTERNAL; } - pthread_mutex_lock(&global_lock); - tcp_id = http_stream_preop(hc); - sub = NULL; - if (tcp_id && !hc->hc_no_output && content_len > 64*1024) { - sub = subscription_create(NULL, 1, "HTTP", - SUBSCRIPTION_NONE, NULL, - hc->hc_peer_ipstr, hc->hc_username, - http_arg_get(&hc->hc_args, "User-Agent")); - if (sub == NULL) { - http_stream_postop(tcp_id); - tcp_id = NULL; - } else { - str = intlconv_to_utf8safestr(charset, fname, strlen(fname) * 3); - if (str == NULL) - str = intlconv_to_utf8safestr(intlconv_charset_id("ASCII", 1, 1), - fname, strlen(fname) * 3); - if (str == NULL) - str = strdup("error"); - basename = malloc(strlen(str) + 7 + 1); - strcpy(basename, "file://"); - strcat(basename, str); - sub->ths_dvrfile = basename; - free(str); - } - } - /* Play count + 1 when write access */ - if (!hc->hc_no_output && file_start <= 0 && - !dvr_entry_verify(de, hc->hc_access, 0)) { - de = dvr_entry_find_by_uuid(remain); - if (de == NULL) - de = dvr_entry_find_by_id(atoi(remain)); - if (de) { - de->de_playcount = de->de_playcount + 1; - dvr_entry_changed_notify(de); + if (preop) { + ret = preop(hc, file_start, content_len, opaque); + if (ret) { + close(fd); + return ret; } } - pthread_mutex_unlock(&global_lock); - if (tcp_id == NULL) { - close(fd); - return HTTP_STATUS_NOT_ALLOWED; - } pthread_mutex_lock(&hc->hc_fd_lock); http_send_header(hc, range ? HTTP_STATUS_PARTIAL_CONTENT : HTTP_STATUS_OK, @@ -1716,7 +1653,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) ret = 0; if(!hc->hc_no_output) { while(content_len > 0) { - chunk = MIN(1024 * (sub ? 128 : 1024 * 1024), content_len); + chunk = MIN(1024 * ((stats ? 128 : 1024) * 1024), content_len); #if defined(PLATFORM_LINUX) r = sendfile(hc->hc_fd, fd, NULL, chunk); #elif defined(PLATFORM_FREEBSD) @@ -1730,19 +1667,133 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) break; } content_len -= r; - if (sub) { - subscription_add_bytes_in(sub, r); - subscription_add_bytes_out(sub, r); - } + if (stats) + stats(hc, r, opaque); } } pthread_mutex_unlock(&hc->hc_fd_lock); close(fd); + return ret; +} + +/** + * Download a recorded file + */ +typedef struct page_dvrfile_priv { + const char *uuid; + char *fname; + const char *charset; + const char *content; + void *tcp_id; + th_subscription_t *sub; +} page_dvrfile_priv_t; + +static int +page_dvrfile_preop(http_connection_t *hc, off_t file_start, + size_t content_len, void *opaque) +{ + page_dvrfile_priv_t *priv = opaque; + char *str, *basename; + dvr_entry_t *de; + pthread_mutex_lock(&global_lock); - if (sub) - subscription_unsubscribe(sub, UNSUBSCRIBE_FINAL); - http_stream_postop(tcp_id); + priv->tcp_id = http_stream_preop(hc); + priv->sub = NULL; + if (priv->tcp_id && !hc->hc_no_output && content_len > 64*1024) { + priv->sub = subscription_create(NULL, 1, "HTTP", + SUBSCRIPTION_NONE, NULL, + hc->hc_peer_ipstr, hc->hc_username, + http_arg_get(&hc->hc_args, "User-Agent")); + if (priv->sub == NULL) { + http_stream_postop(priv->tcp_id); + priv->tcp_id = NULL; + } else { + str = intlconv_to_utf8safestr(priv->charset, priv->fname, strlen(priv->fname) * 3); + if (str == NULL) + str = intlconv_to_utf8safestr(intlconv_charset_id("ASCII", 1, 1), + priv->fname, strlen(priv->fname) * 3); + if (str == NULL) + str = strdup("error"); + basename = malloc(strlen(str) + 7 + 1); + strcpy(basename, "file://"); + strcat(basename, str); + priv->sub->ths_dvrfile = basename; + free(str); + } + } + /* Play count + 1 when write access */ + if (!hc->hc_no_output && file_start <= 0) { + de = dvr_entry_find_by_uuid(priv->uuid); + if (de == NULL) + de = dvr_entry_find_by_id(atoi(priv->uuid)); + if (de && !dvr_entry_verify(de, hc->hc_access, 0)) { + de->de_playcount = de->de_playcount + 1; + dvr_entry_changed_notify(de); + } + } + pthread_mutex_unlock(&global_lock); + if (priv->tcp_id == NULL) + return HTTP_STATUS_NOT_ALLOWED; + return 0; +} + +static void +page_dvrfile_stats(http_connection_t *hc, size_t len, void *opaque) +{ + page_dvrfile_priv_t *priv = opaque; + if (priv->sub) { + subscription_add_bytes_in(priv->sub, len); + subscription_add_bytes_out(priv->sub, len); + } +} + +static int +page_dvrfile(http_connection_t *hc, const char *remain, void *opaque) +{ + int ret; + const char *filename; + char *basename; + dvr_entry_t *de; + page_dvrfile_priv_t priv; + + if(remain == NULL) + return HTTP_STATUS_BAD_REQUEST; + + if(hc->hc_access == NULL || + (access_verify2(hc->hc_access, ACCESS_OR | + ACCESS_STREAMING | + ACCESS_ADVANCED_STREAMING | + ACCESS_RECORDER))) + return HTTP_STATUS_UNAUTHORIZED; + + pthread_mutex_lock(&global_lock); + de = dvr_entry_find_by_uuid(remain); + if (de == NULL) + de = dvr_entry_find_by_id(atoi(remain)); + if(de == NULL || (filename = dvr_get_filename(de)) == NULL) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_NOT_FOUND; + } + if(dvr_entry_verify(de, hc->hc_access, 1)) { + pthread_mutex_unlock(&global_lock); + return HTTP_STATUS_UNAUTHORIZED; + } + + priv.uuid = remain; + priv.fname = tvh_strdupa(filename); + priv.content = muxer_container_filename2mime(priv.fname, 1); + priv.charset = de->de_config ? de->de_config->dvr_charset_id : NULL; + + pthread_mutex_unlock(&global_lock); + + ret = http_serve_file(hc, priv.fname, 1, priv.content, + page_dvrfile_preop, page_dvrfile_stats, &priv); + + pthread_mutex_lock(&global_lock); + if (priv.sub) + subscription_unsubscribe(priv.sub, UNSUBSCRIBE_FINAL); + http_stream_postop(priv.tcp_id); pthread_mutex_unlock(&global_lock); return ret; } @@ -1757,10 +1808,8 @@ static int page_imagecache(http_connection_t *hc, const char *remain, void *opaque) { uint32_t id; - int fd; - char buf[8192]; - struct stat st; - ssize_t c; + int r; + char fname[PATH_MAX]; if(remain == NULL) return HTTP_STATUS_NOT_FOUND; @@ -1780,31 +1829,13 @@ page_imagecache(http_connection_t *hc, const char *remain, void *opaque) /* Fetch details */ pthread_mutex_lock(&global_lock); - fd = imagecache_open(id); + r = imagecache_filename(id, fname, sizeof(fname)); pthread_mutex_unlock(&global_lock); - /* Check result */ - if (fd < 0) - return HTTP_STATUS_NOT_FOUND; - if (fstat(fd, &st)) { - close(fd); + if (r) return HTTP_STATUS_NOT_FOUND; - } - pthread_mutex_lock(&hc->hc_fd_lock); - http_send_header(hc, 200, NULL, st.st_size, 0, NULL, 10, 0, NULL, NULL); - - while (!hc->hc_no_output) { - c = read(fd, buf, sizeof(buf)); - if (c <= 0) - break; - if (tvh_write(hc->hc_fd, buf, c)) - break; - } - pthread_mutex_unlock(&hc->hc_fd_lock); - close(fd); - - return 0; + return http_serve_file(hc, fname, 0, NULL, NULL, NULL, NULL); } /** @@ -1814,7 +1845,7 @@ static void webui_static_content(const char *http_path, const char *source) { http_path_add(http_path, (void *)source, page_static_file, - ACCESS_WEB_INTERFACE); + ACCESS_WEB_INTERFACE); } diff --git a/src/webui/webui.h b/src/webui/webui.h index fac1b9733..2ce29a71b 100644 --- a/src/webui/webui.h +++ b/src/webui/webui.h @@ -23,6 +23,9 @@ #include "idnode.h" #include "http.h" +#define MIME_M3U "audio/x-mpegurl" +#define MIME_E2 "application/x-e2-bouquet" + void webui_init(int xspf); void webui_done(void); @@ -33,6 +36,14 @@ void extjs_start(void); size_t html_escaped_len(const char *src); const char* html_escape(char *dst, const char *src, size_t len); +int +http_serve_file(http_connection_t *hc, const char *fname, + int fconv, const char *content, + int (*preop)(http_connection_t *hc, off_t file_start, + size_t content_len, void *opaque), + void (*stats)(http_connection_t *hc, size_t len, void *opaque), + void *opaque); + 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); int page_markdown(http_connection_t *hc, const char *remain, void *opaque);