[PR-174] - Replaced user submitted icon cache with more generic image cache.
This allows file:// paths to be specified for channel icons even if image cache support is disabled. The image cache functionality is compile time optional (for those without curl support) and also run-time configurable for those that don't want it. All images, including EPG ones should be cached.
This commit is contained in:
parent
acdc094fe7
commit
93fe784960
18 changed files with 713 additions and 537 deletions
5
Makefile
5
Makefile
|
@ -32,7 +32,7 @@ CFLAGS += -Wmissing-prototypes -fms-extensions
|
||||||
CFLAGS += -g -funsigned-char -O2
|
CFLAGS += -g -funsigned-char -O2
|
||||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||||
CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
|
CFLAGS += -I${BUILDDIR} -I${CURDIR}/src -I${CURDIR}
|
||||||
LDFLAGS += -lrt -ldl -lpthread -lm -lcurl
|
LDFLAGS += -lrt -ldl -lpthread -lm
|
||||||
|
|
||||||
#
|
#
|
||||||
# Other config
|
# Other config
|
||||||
|
@ -109,6 +109,7 @@ SRCS = src/main.c \
|
||||||
src/config2.c \
|
src/config2.c \
|
||||||
src/lang_codes.c \
|
src/lang_codes.c \
|
||||||
src/lang_str.c \
|
src/lang_str.c \
|
||||||
|
src/imagecache.c
|
||||||
|
|
||||||
SRCS += src/epggrab/module.c\
|
SRCS += src/epggrab/module.c\
|
||||||
src/epggrab/channel.c\
|
src/epggrab/channel.c\
|
||||||
|
@ -140,8 +141,6 @@ SRCS += src/muxer.c \
|
||||||
src/muxer_pass.c \
|
src/muxer_pass.c \
|
||||||
src/muxer_tvh.c \
|
src/muxer_tvh.c \
|
||||||
|
|
||||||
SRCS += src/iconserve.c \
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Optional code
|
# Optional code
|
||||||
#
|
#
|
||||||
|
|
13
configure
vendored
13
configure
vendored
|
@ -20,6 +20,7 @@ OPTIONS=(
|
||||||
"v4l:yes"
|
"v4l:yes"
|
||||||
"linuxdvb:yes"
|
"linuxdvb:yes"
|
||||||
"dvbscan:yes"
|
"dvbscan:yes"
|
||||||
|
"imagecache:auto"
|
||||||
"avahi:auto"
|
"avahi:auto"
|
||||||
"zlib:auto"
|
"zlib:auto"
|
||||||
"bundle:no"
|
"bundle:no"
|
||||||
|
@ -126,7 +127,17 @@ if enabled cwc && enabled dvbcsa; then
|
||||||
die "Failed to find dvbcsa support (use --disable-dvbcsa)"
|
die "Failed to find dvbcsa support (use --disable-dvbcsa)"
|
||||||
LDFLAGS="$LDFLAGS -ldvbcsa"
|
LDFLAGS="$LDFLAGS -ldvbcsa"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
# Icon caching
|
||||||
|
#
|
||||||
|
if enabled_or_auto imagecache; then
|
||||||
|
if check_pkg libcurl; then
|
||||||
|
enable imagecache
|
||||||
|
elif enabled imagecache; then
|
||||||
|
die "Libcurl support not found (use --disable-imagecache)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
# Write config
|
# Write config
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "dvr/dvr.h"
|
#include "dvr/dvr.h"
|
||||||
#include "htsp_server.h"
|
#include "htsp_server.h"
|
||||||
|
#include "imagecache.h"
|
||||||
|
|
||||||
struct channel_tree channel_name_tree;
|
struct channel_tree channel_name_tree;
|
||||||
static struct channel_tree channel_identifier_tree;
|
static struct channel_tree channel_identifier_tree;
|
||||||
|
@ -268,6 +269,7 @@ channel_load_one(htsmsg_t *c, int id)
|
||||||
epggrab_channel_add(ch);
|
epggrab_channel_add(ch);
|
||||||
|
|
||||||
tvh_str_update(&ch->ch_icon, htsmsg_get_str(c, "icon"));
|
tvh_str_update(&ch->ch_icon, htsmsg_get_str(c, "icon"));
|
||||||
|
imagecache_get_id(ch->ch_icon);
|
||||||
|
|
||||||
htsmsg_get_s32(c, "dvr_extra_time_pre", &ch->ch_dvr_extra_time_pre);
|
htsmsg_get_s32(c, "dvr_extra_time_pre", &ch->ch_dvr_extra_time_pre);
|
||||||
htsmsg_get_s32(c, "dvr_extra_time_post", &ch->ch_dvr_extra_time_post);
|
htsmsg_get_s32(c, "dvr_extra_time_post", &ch->ch_dvr_extra_time_post);
|
||||||
|
@ -452,6 +454,7 @@ channel_set_icon(channel_t *ch, const char *icon)
|
||||||
|
|
||||||
free(ch->ch_icon);
|
free(ch->ch_icon);
|
||||||
ch->ch_icon = strdup(icon);
|
ch->ch_icon = strdup(icon);
|
||||||
|
imagecache_get_id(icon);
|
||||||
channel_save(ch);
|
channel_save(ch);
|
||||||
htsp_channel_update(ch);
|
htsp_channel_update(ch);
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,50 +74,3 @@ int config_set_muxconfpath ( const char *path )
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *config_get_iconserve ( void )
|
|
||||||
{
|
|
||||||
return htsmsg_get_str(config, "iconserve");
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_set_iconserve ( const char *setting )
|
|
||||||
{
|
|
||||||
const char *c = config_get_iconserve();
|
|
||||||
if (!c || strcmp(c, setting)) {
|
|
||||||
if (c) htsmsg_delete_field(config, "iconserve");
|
|
||||||
htsmsg_add_str(config, "iconserve", setting);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const char *config_get_iconserve_periodicdownload ( void )
|
|
||||||
{
|
|
||||||
return htsmsg_get_str(config, "iconserve_periodicdownload");
|
|
||||||
}
|
|
||||||
|
|
||||||
int config_set_iconserve_periodicdownload ( const char *setting )
|
|
||||||
{
|
|
||||||
const char *c = config_get_iconserve_periodicdownload();
|
|
||||||
if (!c || strcmp(c, setting)) {
|
|
||||||
if (c) htsmsg_delete_field(config, "iconserve_periodicdownload");
|
|
||||||
htsmsg_add_str(config, "iconserve_periodicdownload", setting);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *config_get_serverip ( void )
|
|
||||||
{
|
|
||||||
return htsmsg_get_str(config, "serverip");
|
|
||||||
};
|
|
||||||
|
|
||||||
int config_set_serverip ( const char *setting )
|
|
||||||
{
|
|
||||||
const char *c = config_get_serverip();
|
|
||||||
if (!c || strcmp(c, setting)) {
|
|
||||||
if (c) htsmsg_delete_field(config, "serverip");
|
|
||||||
htsmsg_add_str(config, "serverip", setting);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
|
@ -32,18 +32,6 @@ const char *config_get_muxconfpath ( void );
|
||||||
int config_set_muxconfpath ( const char *str )
|
int config_set_muxconfpath ( const char *str )
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
|
|
||||||
const char *config_get_iconserve ( void );
|
|
||||||
int config_set_iconserve ( const char *str )
|
|
||||||
__attribute__((warn_unused_result));
|
|
||||||
|
|
||||||
const char *config_get_iconserve_periodicdownload ( void );
|
|
||||||
int config_set_iconserve_periodicdownload ( const char *str )
|
|
||||||
__attribute__((warn_unused_result));
|
|
||||||
|
|
||||||
const char *config_get_serverip ( void );
|
|
||||||
int config_set_serverip ( const char *str )
|
|
||||||
__attribute__((warn_unused_result));
|
|
||||||
|
|
||||||
const char *config_get_language ( void );
|
const char *config_get_language ( void );
|
||||||
int config_set_language ( const char *str )
|
int config_set_language ( const char *str )
|
||||||
__attribute__((warn_unused_result));
|
__attribute__((warn_unused_result));
|
||||||
|
|
19
src/epg.c
19
src/epg.c
|
@ -32,6 +32,7 @@
|
||||||
#include "dvr/dvr.h"
|
#include "dvr/dvr.h"
|
||||||
#include "htsp_server.h"
|
#include "htsp_server.h"
|
||||||
#include "epggrab.h"
|
#include "epggrab.h"
|
||||||
|
#include "imagecache.h"
|
||||||
|
|
||||||
/* Broadcast hashing */
|
/* Broadcast hashing */
|
||||||
#define EPG_HASH_WIDTH 1024
|
#define EPG_HASH_WIDTH 1024
|
||||||
|
@ -459,8 +460,12 @@ int epg_brand_set_summary
|
||||||
int epg_brand_set_image
|
int epg_brand_set_image
|
||||||
( epg_brand_t *brand, const char *image, epggrab_module_t *src )
|
( epg_brand_t *brand, const char *image, epggrab_module_t *src )
|
||||||
{
|
{
|
||||||
|
int save;
|
||||||
if (!brand || !image) return 0;
|
if (!brand || !image) return 0;
|
||||||
return _epg_object_set_str(brand, &brand->image, image, src);
|
save = _epg_object_set_str(brand, &brand->image, image, src);
|
||||||
|
if (save)
|
||||||
|
imagecache_get_id(image);
|
||||||
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
int epg_brand_set_season_count
|
int epg_brand_set_season_count
|
||||||
|
@ -628,8 +633,12 @@ int epg_season_set_summary
|
||||||
int epg_season_set_image
|
int epg_season_set_image
|
||||||
( epg_season_t *season, const char *image, epggrab_module_t *src )
|
( epg_season_t *season, const char *image, epggrab_module_t *src )
|
||||||
{
|
{
|
||||||
|
int save;
|
||||||
if (!season || !image) return 0;
|
if (!season || !image) return 0;
|
||||||
return _epg_object_set_str(season, &season->image, image, src);
|
save = _epg_object_set_str(season, &season->image, image, src);
|
||||||
|
if (save)
|
||||||
|
imagecache_get_id(image);
|
||||||
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
int epg_season_set_episode_count
|
int epg_season_set_episode_count
|
||||||
|
@ -891,8 +900,12 @@ int epg_episode_set_description
|
||||||
int epg_episode_set_image
|
int epg_episode_set_image
|
||||||
( epg_episode_t *episode, const char *image, epggrab_module_t *src )
|
( epg_episode_t *episode, const char *image, epggrab_module_t *src )
|
||||||
{
|
{
|
||||||
|
int save;
|
||||||
if (!episode || !image) return 0;
|
if (!episode || !image) return 0;
|
||||||
return _epg_object_set_str(episode, &episode->image, image, src);
|
save = _epg_object_set_str(episode, &episode->image, image, src);
|
||||||
|
if (save)
|
||||||
|
imagecache_get_id(image);
|
||||||
|
return save;
|
||||||
}
|
}
|
||||||
|
|
||||||
int epg_episode_set_number
|
int epg_episode_set_number
|
||||||
|
|
|
@ -42,8 +42,7 @@
|
||||||
#include "htsmsg_binary.h"
|
#include "htsmsg_binary.h"
|
||||||
#include "epg.h"
|
#include "epg.h"
|
||||||
#include "plumbing/tsfix.h"
|
#include "plumbing/tsfix.h"
|
||||||
#include "iconserve.h"
|
#include "imagecache.h"
|
||||||
#include "config2.h"
|
|
||||||
|
|
||||||
#include <sys/statvfs.h>
|
#include <sys/statvfs.h>
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -434,7 +433,7 @@ htsp_file_destroy(htsp_file_t *hf)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static htsmsg_t *
|
static htsmsg_t *
|
||||||
htsp_build_channel(channel_t *ch, const char *method)
|
htsp_build_channel(channel_t *ch, const char *method, htsp_connection_t *htsp)
|
||||||
{
|
{
|
||||||
channel_tag_mapping_t *ctm;
|
channel_tag_mapping_t *ctm;
|
||||||
channel_tag_t *ct;
|
channel_tag_t *ct;
|
||||||
|
@ -449,10 +448,26 @@ htsp_build_channel(channel_t *ch, const char *method)
|
||||||
htsmsg_add_u32(out, "channelNumber", ch->ch_number);
|
htsmsg_add_u32(out, "channelNumber", ch->ch_number);
|
||||||
|
|
||||||
htsmsg_add_str(out, "channelName", ch->ch_name);
|
htsmsg_add_str(out, "channelName", ch->ch_name);
|
||||||
|
|
||||||
if(ch->ch_icon != NULL) {
|
if(ch->ch_icon != NULL) {
|
||||||
htsmsg_add_str(out, "channelIcon", logo_query(ch->ch_id, ch->ch_icon));
|
uint32_t id = imagecache_get_id(ch->ch_icon);
|
||||||
};
|
if (id) {
|
||||||
|
size_t p = 0;
|
||||||
|
char url[256];
|
||||||
|
if (htsp->htsp_version <= 7) {
|
||||||
|
strcpy(url, "http://");
|
||||||
|
p = 7;
|
||||||
|
inet_ntop(AF_INET, &(htsp->htsp_peer->sin_addr), url+p, sizeof(url)-p);
|
||||||
|
p = strlen(url);
|
||||||
|
p += snprintf(url+p, sizeof(url)-p, ":%hd", webui_port);
|
||||||
|
}
|
||||||
|
if (tvheadend_webroot)
|
||||||
|
p += snprintf(url+p, sizeof(url)-p, "%s", tvheadend_webroot);
|
||||||
|
snprintf(url+p, sizeof(url)-p, "/imagecache/%d", id);
|
||||||
|
htsmsg_add_str(out, "channelIcon", url);
|
||||||
|
} else {
|
||||||
|
htsmsg_add_str(out, "channelIcon", ch->ch_icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
now = ch->ch_epg_now;
|
now = ch->ch_epg_now;
|
||||||
next = ch->ch_epg_next;
|
next = ch->ch_epg_next;
|
||||||
|
@ -802,7 +817,7 @@ htsp_method_async(htsp_connection_t *htsp, htsmsg_t *in)
|
||||||
|
|
||||||
/* Send all channels */
|
/* Send all channels */
|
||||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
|
RB_FOREACH(ch, &channel_name_tree, ch_name_link)
|
||||||
htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd"), NULL);
|
htsp_send_message(htsp, htsp_build_channel(ch, "channelAdd", htsp), NULL);
|
||||||
|
|
||||||
/* Send all enabled and external tags (now with channel mappings) */
|
/* Send all enabled and external tags (now with channel mappings) */
|
||||||
TAILQ_FOREACH(ct, &channel_tags, ct_link)
|
TAILQ_FOREACH(ct, &channel_tags, ct_link)
|
||||||
|
@ -1880,23 +1895,30 @@ htsp_channel_update_current(channel_t *ch)
|
||||||
/**
|
/**
|
||||||
* Called from channel.c when a new channel is created
|
* Called from channel.c when a new channel is created
|
||||||
*/
|
*/
|
||||||
|
static void
|
||||||
|
_htsp_channel_update(channel_t *ch, const char *msg)
|
||||||
|
{
|
||||||
|
htsp_connection_t *htsp;
|
||||||
|
LIST_FOREACH(htsp, &htsp_async_connections, htsp_async_link)
|
||||||
|
if (htsp->htsp_async_mode & HTSP_ASYNC_ON)
|
||||||
|
htsp_send_message(htsp, htsp_build_channel(ch, msg, htsp), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
htsp_channel_add(channel_t *ch)
|
htsp_channel_add(channel_t *ch)
|
||||||
{
|
{
|
||||||
htsp_async_send(htsp_build_channel(ch, "channelAdd"), HTSP_ASYNC_ON);
|
_htsp_channel_update(ch, "channelAdd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from channel.c when a channel is updated
|
* Called from channel.c when a channel is updated
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
htsp_channel_update(channel_t *ch)
|
htsp_channel_update(channel_t *ch)
|
||||||
{
|
{
|
||||||
htsp_async_send(htsp_build_channel(ch, "channelUpdate"), HTSP_ASYNC_ON);
|
_htsp_channel_update(ch, "channelUpdate");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from channel.c when a channel is deleted
|
* Called from channel.c when a channel is deleted
|
||||||
*/
|
*/
|
||||||
|
|
358
src/iconserve.c
358
src/iconserve.c
|
@ -1,358 +0,0 @@
|
||||||
/*
|
|
||||||
* Icon file server operations
|
|
||||||
* Copyright (C) 2012 Andy Brown
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define CURL_STATICLIB
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <curl/curl.h>
|
|
||||||
#include <curl/easy.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "settings.h"
|
|
||||||
#include "tvheadend.h"
|
|
||||||
#include "channels.h"
|
|
||||||
#include "http.h"
|
|
||||||
#include "webui/webui.h"
|
|
||||||
#include "filebundle.h"
|
|
||||||
#include "iconserve.h"
|
|
||||||
#include "config2.h"
|
|
||||||
#include "queue.h"
|
|
||||||
#include "spawn.h"
|
|
||||||
|
|
||||||
/* Queue, Cond to signal data and Mutex to protect it */
|
|
||||||
static TAILQ_HEAD(,iconserve_grab_queue) iconserve_queue;
|
|
||||||
static pthread_mutex_t iconserve_mutex;
|
|
||||||
static pthread_cond_t iconserve_cond;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://github.com/andyb2000 Function to provide local icon files
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
page_logo(http_connection_t *hc, const char *remain, void *opaque)
|
|
||||||
{
|
|
||||||
const char *homedir = hts_settings_get_root();
|
|
||||||
channel_t *ch = NULL;
|
|
||||||
char *inpath, *inpath2;
|
|
||||||
const char *outpath = "none";
|
|
||||||
char homepath[254];
|
|
||||||
char iconpath[100];
|
|
||||||
pthread_mutex_lock(&global_lock);
|
|
||||||
fb_file *fp;
|
|
||||||
ssize_t size;
|
|
||||||
char buf[4096];
|
|
||||||
char *mimetest_outbuf;
|
|
||||||
|
|
||||||
if(remain == NULL) {
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
|
||||||
return 404;
|
|
||||||
};
|
|
||||||
|
|
||||||
if(strstr(remain, "..")) {
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
|
||||||
return HTTP_STATUS_BAD_REQUEST;
|
|
||||||
};
|
|
||||||
|
|
||||||
ch = channel_find_by_identifier(atoi(remain));
|
|
||||||
if (ch == NULL || ch->ch_icon == NULL) {
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
|
||||||
return 404;
|
|
||||||
};
|
|
||||||
|
|
||||||
snprintf(homepath, sizeof(homepath), "%s/icons", homedir);
|
|
||||||
inpath = NULL;
|
|
||||||
inpath2 = NULL;
|
|
||||||
outpath = NULL;
|
|
||||||
/* split icon to last component */
|
|
||||||
inpath = strdup(ch->ch_icon);
|
|
||||||
inpath2 = strtok(inpath, "/");
|
|
||||||
while (inpath2 != NULL) {
|
|
||||||
inpath2 = strtok(NULL, "/");
|
|
||||||
if (inpath2 != NULL) {
|
|
||||||
outpath = strdup(inpath2);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath);
|
|
||||||
fp = fb_open(iconpath, 1, 0);
|
|
||||||
if (!fp) {
|
|
||||||
tvhlog(LOG_DEBUG, "page_logo",
|
|
||||||
"failed to open %s redirecting to http link for icon (%s)",
|
|
||||||
iconpath, ch->ch_icon);
|
|
||||||
http_redirect(hc, ch->ch_icon);
|
|
||||||
iconserve_queue_add ( ch->ch_id, ch->ch_icon );
|
|
||||||
} else {
|
|
||||||
tvhlog(LOG_DEBUG, "page_logo", "File %s opened", iconpath);
|
|
||||||
size = fb_size(fp);
|
|
||||||
mimetest_outbuf = strdup("image/jpeg");
|
|
||||||
http_send_header(hc, 200, mimetest_outbuf, size, NULL, NULL, 300, 0, NULL);
|
|
||||||
while (!fb_eof(fp)) {
|
|
||||||
ssize_t c = fb_read(fp, buf, sizeof(buf));
|
|
||||||
if (c < 0) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
if (write(hc->hc_fd, buf, c) != c) {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
fb_close(fp);
|
|
||||||
};
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Logo loader functions, called from http htsp
|
|
||||||
* Will return local cache url instead of icon stored
|
|
||||||
*/
|
|
||||||
const char
|
|
||||||
*logo_query(int ch_id, const char *ch_icon)
|
|
||||||
{
|
|
||||||
const char *setting = config_get_iconserve();
|
|
||||||
const char *serverip = config_get_serverip();
|
|
||||||
char outiconpath[255];
|
|
||||||
char *return_icon = strdup(ch_icon);
|
|
||||||
|
|
||||||
if (!setting || !*setting || (strcmp(setting, "off") == 0)) {
|
|
||||||
return return_icon;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!serverip || !*serverip) {
|
|
||||||
return return_icon;
|
|
||||||
};
|
|
||||||
|
|
||||||
snprintf(outiconpath, sizeof(outiconpath),
|
|
||||||
"http://%s:%d/channellogo/%d", serverip, webui_port, ch_id);
|
|
||||||
return_icon = strdup(outiconpath);
|
|
||||||
return return_icon;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Icon grabber queue thread
|
|
||||||
*/
|
|
||||||
void *iconserve_thread ( void *aux )
|
|
||||||
{
|
|
||||||
iconserve_grab_queue_t *qe;
|
|
||||||
pthread_mutex_lock(&iconserve_mutex);
|
|
||||||
char *inpath, *inpath2;
|
|
||||||
const char *header_parse = NULL, *header_maxage = NULL;
|
|
||||||
const char *outpath = "none";
|
|
||||||
CURL *curl;
|
|
||||||
FILE *curl_fp, *curl_fp_header;
|
|
||||||
CURLcode res;
|
|
||||||
fb_file *fp;
|
|
||||||
char iconpath[100], iconpath_header[100];
|
|
||||||
char homepath[254];
|
|
||||||
const char *homedir = hts_settings_get_root();
|
|
||||||
struct stat fileStat;
|
|
||||||
int trigger_download = 0;
|
|
||||||
char buf[256];
|
|
||||||
int file = 0;
|
|
||||||
time_t seconds;
|
|
||||||
int dif, compare_seconds, rc;
|
|
||||||
const char *periodicdownload = config_get_iconserve_periodicdownload();
|
|
||||||
struct timespec timertrigger;
|
|
||||||
channel_t *ch;
|
|
||||||
|
|
||||||
tvhlog(LOG_INFO, "iconserve_thread", "Thread startup");
|
|
||||||
curl = curl_easy_init();
|
|
||||||
snprintf(homepath, sizeof(homepath), "%s/icons", homedir);
|
|
||||||
if(stat(homepath, &fileStat) == 0 || mkdir(homepath, 0700) == 0) {
|
|
||||||
if (curl) {
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
/* Get entry from queue */
|
|
||||||
qe = TAILQ_FIRST(&iconserve_queue);
|
|
||||||
/* Check for queue data */
|
|
||||||
if (!qe) { /* Queue Empty */
|
|
||||||
periodicdownload = config_get_iconserve_periodicdownload();
|
|
||||||
if (!periodicdownload || !*periodicdownload ||
|
|
||||||
(strcmp(periodicdownload, "off") == 0)) {
|
|
||||||
tvhlog(LOG_DEBUG, "iconserve_thread", "Non-timer wakeup");
|
|
||||||
rc = pthread_cond_wait(&iconserve_cond, &iconserve_mutex);
|
|
||||||
} else {
|
|
||||||
tvhlog(LOG_DEBUG, "iconserve_thread", "Timer wakeup set");
|
|
||||||
timertrigger.tv_sec = time(NULL) + 86400;
|
|
||||||
timertrigger.tv_nsec = 0;
|
|
||||||
rc = pthread_cond_timedwait(&iconserve_cond,
|
|
||||||
&iconserve_mutex, &timertrigger);
|
|
||||||
};
|
|
||||||
if (rc == ETIMEDOUT) {
|
|
||||||
tvhlog(LOG_INFO, "iconserve_thread", "Thread wakeup by timer");
|
|
||||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
|
|
||||||
if (ch->ch_icon != NULL) {
|
|
||||||
iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t));
|
|
||||||
qe->chan_number = ch->ch_id;
|
|
||||||
qe->icon_url = ch->ch_icon;
|
|
||||||
TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
TAILQ_REMOVE(&iconserve_queue, qe, iconserve_link);
|
|
||||||
pthread_mutex_unlock(&iconserve_mutex);
|
|
||||||
|
|
||||||
inpath = NULL;
|
|
||||||
inpath2 = NULL;
|
|
||||||
outpath = NULL;
|
|
||||||
curl_fp = NULL;
|
|
||||||
/* split icon to last component */
|
|
||||||
inpath = strdup(qe->icon_url);
|
|
||||||
inpath2 = strtok(inpath, "/");
|
|
||||||
while (inpath2 != NULL) {
|
|
||||||
inpath2 = strtok(NULL, "/");
|
|
||||||
if (inpath2 != NULL)
|
|
||||||
outpath = strdup(inpath2);
|
|
||||||
};
|
|
||||||
if (outpath != NULL) {
|
|
||||||
snprintf(iconpath, sizeof(iconpath), "%s/%s", homepath, outpath);
|
|
||||||
snprintf(iconpath_header, sizeof(iconpath_header), "%s/%s.head",
|
|
||||||
homepath, outpath);
|
|
||||||
fp = fb_open(iconpath, 0, 1);
|
|
||||||
if (!fp) {
|
|
||||||
/* No file exists so grab immediately */
|
|
||||||
tvhlog(LOG_INFO, "logo_loader", "No logo, downloading file %s", outpath);
|
|
||||||
trigger_download = 1;
|
|
||||||
} else {
|
|
||||||
/* File exists so compare expiry times to re-grab */
|
|
||||||
fb_close(fp);
|
|
||||||
fp = fb_open(iconpath_header, 0, 0);
|
|
||||||
while (!fb_eof(fp)) {
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
if (!fb_gets(fp, buf, sizeof(buf) - 1)) break;
|
|
||||||
if (buf[strlen(buf) - 1] == '\n') {
|
|
||||||
buf[strlen(buf) - 1] = '\0';
|
|
||||||
};
|
|
||||||
if(strstr(buf, "Cache-Control: ")) {
|
|
||||||
header_parse = strtok(buf, "=");
|
|
||||||
header_parse = strtok ( NULL, "=");
|
|
||||||
header_maxage = strdup(header_parse);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
fb_close(fp);
|
|
||||||
file=open(iconpath, O_RDONLY);
|
|
||||||
fstat(file,&fileStat);
|
|
||||||
seconds = time (NULL);
|
|
||||||
dif = difftime (seconds,fileStat.st_mtime);
|
|
||||||
compare_seconds=atoi(header_maxage);
|
|
||||||
if (dif > compare_seconds) {
|
|
||||||
tvhlog(LOG_DEBUG, "logo_loader", "Logo expired, downloading %s", outpath);
|
|
||||||
trigger_download = 1;
|
|
||||||
} else {
|
|
||||||
tvhlog(LOG_INFO, "logo_loader", "Logo not expired %s", outpath);
|
|
||||||
};
|
|
||||||
close(file);
|
|
||||||
};
|
|
||||||
if (trigger_download == 1) {
|
|
||||||
curl_fp=fopen(iconpath,"wb");
|
|
||||||
curl_fp_header=fopen(iconpath_header,"w");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, qe->icon_url);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_fp);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, curl_fp_header);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
|
|
||||||
res = curl_easy_perform(curl);
|
|
||||||
if (res == 0) {
|
|
||||||
tvhlog(LOG_INFO, "logo_loader", "Downloaded icon via curl (%s)",
|
|
||||||
qe->icon_url);
|
|
||||||
} else {
|
|
||||||
tvhlog(LOG_WARNING, "logo_loader", "Error with curl download (%s)",
|
|
||||||
qe->icon_url);
|
|
||||||
};
|
|
||||||
fclose(curl_fp);
|
|
||||||
fclose(curl_fp_header);
|
|
||||||
trigger_download = 0;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}; /* while loop */
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
} else {
|
|
||||||
tvhlog(LOG_WARNING, "iconserve", "CURL cannot initialise");
|
|
||||||
};
|
|
||||||
};
|
|
||||||
return NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add data to the queue
|
|
||||||
*/
|
|
||||||
void iconserve_queue_add ( int chan_number, char *icon_url )
|
|
||||||
{
|
|
||||||
/* Create entry */
|
|
||||||
tvhlog(LOG_DEBUG, "iconserve_queue_add", "Adding chan_number to queue: %i",
|
|
||||||
chan_number);
|
|
||||||
iconserve_grab_queue_t *qe = calloc(1, sizeof(iconserve_grab_queue_t));
|
|
||||||
qe->chan_number = chan_number;
|
|
||||||
qe->icon_url = strdup(icon_url);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&iconserve_mutex);
|
|
||||||
TAILQ_INSERT_TAIL(&iconserve_queue, qe, iconserve_link);
|
|
||||||
pthread_cond_signal(&iconserve_cond);
|
|
||||||
pthread_mutex_unlock(&iconserve_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loader for icons, check config params and pull them in one go
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
logo_loader(void)
|
|
||||||
{
|
|
||||||
channel_t *ch;
|
|
||||||
const char *setting = config_get_iconserve();
|
|
||||||
const char *serverip = config_get_serverip();
|
|
||||||
|
|
||||||
if (!setting || !*setting || (strcmp(setting, "off") == 0)) {
|
|
||||||
tvhlog(LOG_DEBUG, "logo_loader", "Disabled by config, skipping");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!serverip || !*serverip) {
|
|
||||||
tvhlog(LOG_ALERT, "logo_loader", "No server IP, skipping icon cache");
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
pthread_t tid;
|
|
||||||
pthread_mutex_init(&iconserve_mutex, NULL);
|
|
||||||
pthread_cond_init(&iconserve_cond, NULL);
|
|
||||||
TAILQ_INIT(&iconserve_queue);
|
|
||||||
/* Start thread - presumably permanently active */
|
|
||||||
pthread_create(&tid, NULL, iconserve_thread, NULL); // last param is passed as aux
|
|
||||||
// as this is single global
|
|
||||||
// you can probably use global
|
|
||||||
// vars
|
|
||||||
|
|
||||||
tvhlog(LOG_INFO, "logo_loader", "Caching logos locally");
|
|
||||||
/* loop through channels and load logo files */
|
|
||||||
RB_FOREACH(ch, &channel_name_tree, ch_name_link) {
|
|
||||||
if (ch->ch_icon != NULL) {
|
|
||||||
iconserve_queue_add ( ch->ch_id, ch->ch_icon );
|
|
||||||
};
|
|
||||||
};
|
|
||||||
pthread_mutex_lock(&iconserve_mutex);
|
|
||||||
pthread_cond_signal(&iconserve_cond); // tell thread data is available
|
|
||||||
pthread_mutex_unlock(&iconserve_mutex);
|
|
||||||
};
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*
|
|
||||||
* Icon file serve operations
|
|
||||||
* Copyright (C) 2012 Andy Brown
|
|
||||||
*
|
|
||||||
* 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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ICONSERVE_H
|
|
||||||
#define ICONSERVE_H
|
|
||||||
|
|
||||||
#include "http.h"
|
|
||||||
|
|
||||||
/* Struct of entries for icon grabbing
|
|
||||||
* FIELD: chan_number
|
|
||||||
* FIELD: icon url
|
|
||||||
*/
|
|
||||||
typedef struct iconserve_grab_queue
|
|
||||||
{
|
|
||||||
TAILQ_ENTRY(iconserve_grab_queue) iconserve_link;
|
|
||||||
int chan_number;
|
|
||||||
char *icon_url;
|
|
||||||
} iconserve_grab_queue_t;
|
|
||||||
|
|
||||||
|
|
||||||
int page_logo(http_connection_t *hc, const char *remain, void *opaque);
|
|
||||||
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
|
||||||
|
|
||||||
void *iconserve_thread ( void *aux );
|
|
||||||
|
|
||||||
const char *logo_query(int ch_id, const char *ch_icon);
|
|
||||||
|
|
||||||
void iconserve_queue_add ( int chan_number, char *icon_url );
|
|
||||||
|
|
||||||
void logo_loader(void);
|
|
||||||
|
|
||||||
#endif /* ICONSERVE_H */
|
|
440
src/imagecache.c
Normal file
440
src/imagecache.c
Normal file
|
@ -0,0 +1,440 @@
|
||||||
|
/*
|
||||||
|
* Icon file server operations
|
||||||
|
* Copyright (C) 2012 Andy Brown
|
||||||
|
*
|
||||||
|
* 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 <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "tvheadend.h"
|
||||||
|
#include "filebundle.h"
|
||||||
|
#include "imagecache.h"
|
||||||
|
#include "queue.h"
|
||||||
|
#include "redblack.h"
|
||||||
|
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
#define CURL_STATICLIB
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <curl/easy.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// TODO: icon cache flushing?
|
||||||
|
// TODO: md5 validation?
|
||||||
|
// TODO: allow cache to be disabled by users
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Image metadata
|
||||||
|
*/
|
||||||
|
typedef struct imagecache_image
|
||||||
|
{
|
||||||
|
int id; ///< Internal ID
|
||||||
|
const char *url; ///< Upstream URL
|
||||||
|
int failed; ///< Last update failed
|
||||||
|
time_t updated; ///< Last time the file was checked
|
||||||
|
enum {
|
||||||
|
IDLE,
|
||||||
|
QUEUED,
|
||||||
|
FETCHING
|
||||||
|
} state; ///< fetch status
|
||||||
|
|
||||||
|
TAILQ_ENTRY(imagecache_image) q_link; ///< Fetch Q link
|
||||||
|
RB_ENTRY(imagecache_image) id_link; ///< Index by ID
|
||||||
|
RB_ENTRY(imagecache_image) url_link; ///< Index by URL
|
||||||
|
} imagecache_image_t;
|
||||||
|
|
||||||
|
static int _imagecache_id;
|
||||||
|
static RB_HEAD(,imagecache_image) _imagecache_by_id;
|
||||||
|
static RB_HEAD(,imagecache_image) _imagecache_by_url;
|
||||||
|
|
||||||
|
pthread_mutex_t imagecache_mutex;
|
||||||
|
|
||||||
|
static void _imagecache_save ( imagecache_image_t *img );
|
||||||
|
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
uint32_t imagecache_enabled;
|
||||||
|
uint32_t imagecache_ok_period;
|
||||||
|
uint32_t imagecache_fail_period;
|
||||||
|
|
||||||
|
static pthread_cond_t _imagecache_cond;
|
||||||
|
static TAILQ_HEAD(, imagecache_image) _imagecache_queue;
|
||||||
|
static void _imagecache_add ( imagecache_image_t *img );
|
||||||
|
static void* _imagecache_thread ( void *p );
|
||||||
|
static int _imagecache_fetch ( imagecache_image_t *img );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int _url_cmp ( void *a, void *b )
|
||||||
|
{
|
||||||
|
return strcmp(((imagecache_image_t*)a)->url, ((imagecache_image_t*)b)->url);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _id_cmp ( void *a, void *b )
|
||||||
|
{
|
||||||
|
return ((imagecache_image_t*)a)->id - ((imagecache_image_t*)b)->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialise
|
||||||
|
*/
|
||||||
|
void imagecache_init ( void )
|
||||||
|
{
|
||||||
|
htsmsg_t *m, *e;
|
||||||
|
htsmsg_field_t *f;
|
||||||
|
imagecache_image_t *img, *i;
|
||||||
|
const char *url;
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
/* Init vars */
|
||||||
|
_imagecache_id = 0;
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
imagecache_enabled = 0;
|
||||||
|
imagecache_ok_period = 24 * 7; // weekly
|
||||||
|
imagecache_fail_period = 24; // daily
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create threads */
|
||||||
|
pthread_mutex_init(&imagecache_mutex, NULL);
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
pthread_cond_init(&_imagecache_cond, NULL);
|
||||||
|
TAILQ_INIT(&_imagecache_queue);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Load settings */
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
if ((m = hts_settings_load("imagecache/config"))) {
|
||||||
|
htsmsg_get_u32(m, "enabled", &imagecache_enabled);
|
||||||
|
htsmsg_get_u32(m, "ok_period", &imagecache_ok_period);
|
||||||
|
htsmsg_get_u32(m, "fail_period", &imagecache_fail_period);
|
||||||
|
htsmsg_destroy(m);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if ((m = hts_settings_load("imagecache/meta"))) {
|
||||||
|
HTSMSG_FOREACH(f, m) {
|
||||||
|
if (!(e = htsmsg_get_map_by_field(f))) continue;
|
||||||
|
if (!(id = atoi(f->hmf_name))) continue;
|
||||||
|
if (!(url = htsmsg_get_str(e, "url"))) continue;
|
||||||
|
img = calloc(1, sizeof(imagecache_image_t));
|
||||||
|
img->id = id;
|
||||||
|
img->url = strdup(url);
|
||||||
|
img->updated = htsmsg_get_s64_or_default(e, "updated", 0);
|
||||||
|
i = RB_INSERT_SORTED(&_imagecache_by_url, img, url_link, _url_cmp);
|
||||||
|
if (i) {
|
||||||
|
hts_settings_remove("imagecache/meta/%d", id);
|
||||||
|
hts_settings_remove("imagecache/data/%d", id);
|
||||||
|
free(img);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i = RB_INSERT_SORTED(&_imagecache_by_id, img, id_link, _id_cmp);
|
||||||
|
assert(!i);
|
||||||
|
if (id > _imagecache_id)
|
||||||
|
_imagecache_id = id;
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
if (!img->updated)
|
||||||
|
_imagecache_add(img);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
htsmsg_destroy(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start threads */
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
{
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_create(&tid, NULL, _imagecache_thread, NULL);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save settings
|
||||||
|
*/
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
void imagecache_save ( void )
|
||||||
|
{
|
||||||
|
htsmsg_t *m = htsmsg_create_map();
|
||||||
|
htsmsg_add_u32(m, "enabled", imagecache_enabled);
|
||||||
|
htsmsg_add_u32(m, "ok_period", imagecache_ok_period);
|
||||||
|
htsmsg_add_u32(m, "fail_period", imagecache_fail_period);
|
||||||
|
hts_settings_save(m, "imagecache/config");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable/disable
|
||||||
|
*/
|
||||||
|
int imagecache_set_enabled ( uint32_t e )
|
||||||
|
{
|
||||||
|
if (e == imagecache_enabled)
|
||||||
|
return 0;
|
||||||
|
imagecache_enabled = e;
|
||||||
|
if (e)
|
||||||
|
pthread_cond_broadcast(&_imagecache_cond);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set ok period
|
||||||
|
*/
|
||||||
|
int imagecache_set_ok_period ( uint32_t p )
|
||||||
|
{
|
||||||
|
if (p == imagecache_ok_period)
|
||||||
|
return 0;
|
||||||
|
imagecache_ok_period = p;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set fail period
|
||||||
|
*/
|
||||||
|
int imagecache_set_fail_period ( uint32_t p )
|
||||||
|
{
|
||||||
|
if (p == imagecache_fail_period)
|
||||||
|
return 0;
|
||||||
|
imagecache_fail_period = p;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fetch a URLs ID
|
||||||
|
*/
|
||||||
|
uint32_t imagecache_get_id ( const char *url )
|
||||||
|
{
|
||||||
|
uint32_t id = 0;
|
||||||
|
imagecache_image_t *i;
|
||||||
|
static imagecache_image_t *skel = NULL;
|
||||||
|
|
||||||
|
/* Invalid */
|
||||||
|
if (!url)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Disabled */
|
||||||
|
#if !ENABLE_IMAGECACHE
|
||||||
|
if (strncasecmp(url, "file://", 7))
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Skeleton */
|
||||||
|
if (!skel)
|
||||||
|
skel = calloc(1, sizeof(imagecache_image_t));
|
||||||
|
skel->url = url;
|
||||||
|
|
||||||
|
/* Create/Find */
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
i = RB_INSERT_SORTED(&_imagecache_by_url, skel, url_link, _url_cmp);
|
||||||
|
if (!i) {
|
||||||
|
i = skel;
|
||||||
|
i->url = strdup(url);
|
||||||
|
i->id = ++_imagecache_id;
|
||||||
|
skel = RB_INSERT_SORTED(&_imagecache_by_id, i, id_link, _id_cmp);
|
||||||
|
assert(!skel);
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
_imagecache_add(i);
|
||||||
|
#endif
|
||||||
|
_imagecache_save(i);
|
||||||
|
}
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
if (!strncasecmp(url, "file://", 7) || imagecache_enabled)
|
||||||
|
id = i->id;
|
||||||
|
#else
|
||||||
|
if (!strncasecmp(url, "file://", 7))
|
||||||
|
id = i->id;
|
||||||
|
#endif
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get data
|
||||||
|
*/
|
||||||
|
int imagecache_open ( uint32_t id )
|
||||||
|
{
|
||||||
|
imagecache_image_t skel, *i;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
|
||||||
|
/* Find */
|
||||||
|
skel.id = id;
|
||||||
|
i = RB_FIND(&_imagecache_by_id, &skel, id_link, _id_cmp);
|
||||||
|
|
||||||
|
/* Invalid */
|
||||||
|
if (!i) {
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Local file */
|
||||||
|
if (!strncasecmp(i->url, "file://", 7))
|
||||||
|
fd = open(i->url + 7, O_RDONLY);
|
||||||
|
|
||||||
|
/* Remote file */
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
else if (imagecache_enabled) {
|
||||||
|
struct timespec ts;
|
||||||
|
int err;
|
||||||
|
if (i->updated) {
|
||||||
|
// use existing
|
||||||
|
} else if (i->state == FETCHING) {
|
||||||
|
ts.tv_nsec = 0;
|
||||||
|
time(&ts.tv_sec);
|
||||||
|
ts.tv_sec += 10; // TODO: sensible timeout?
|
||||||
|
err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts);
|
||||||
|
if (err == ETIMEDOUT) {
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else if (i->state == QUEUED) {
|
||||||
|
i->state = FETCHING;
|
||||||
|
TAILQ_REMOVE(&_imagecache_queue, i, q_link);
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
if (_imagecache_fetch(i))
|
||||||
|
return -1;
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
}
|
||||||
|
fd = hts_settings_open_file(0, "imagecache/data/%d", i->id);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _imagecache_save ( imagecache_image_t *img )
|
||||||
|
{
|
||||||
|
htsmsg_t *m = htsmsg_create_map();
|
||||||
|
|
||||||
|
htsmsg_add_str(m, "url", img->url);
|
||||||
|
if (img->updated)
|
||||||
|
htsmsg_add_s64(m, "updated", img->updated);
|
||||||
|
|
||||||
|
hts_settings_save(m, "imagecache/meta/%d", img->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
static void _imagecache_add ( imagecache_image_t *img )
|
||||||
|
{
|
||||||
|
if (strncasecmp("file://", img->url, 7)) {
|
||||||
|
img->state = QUEUED;
|
||||||
|
TAILQ_INSERT_TAIL(&_imagecache_queue, img, q_link);
|
||||||
|
pthread_cond_broadcast(&_imagecache_cond);
|
||||||
|
} else {
|
||||||
|
time(&img->updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *_imagecache_thread ( void *p )
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
imagecache_image_t *img;
|
||||||
|
struct timespec ts;
|
||||||
|
ts.tv_nsec = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
|
||||||
|
/* Get entry */
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
if (!imagecache_enabled) {
|
||||||
|
pthread_cond_wait(&_imagecache_cond, &imagecache_mutex);
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
img = TAILQ_FIRST(&_imagecache_queue);
|
||||||
|
if (!img) {
|
||||||
|
time(&ts.tv_sec);
|
||||||
|
ts.tv_sec += 60;
|
||||||
|
err = pthread_cond_timedwait(&_imagecache_cond, &imagecache_mutex, &ts);
|
||||||
|
if (err == ETIMEDOUT) {
|
||||||
|
RB_FOREACH(img, &_imagecache_by_url, url_link) {
|
||||||
|
if (img->state != IDLE) continue;
|
||||||
|
if ((ts.tv_sec - img->updated) >
|
||||||
|
(img->failed ? imagecache_fail_period : imagecache_ok_period))
|
||||||
|
_imagecache_add(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
img->state = FETCHING;
|
||||||
|
TAILQ_REMOVE(&_imagecache_queue, img, q_link);
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
|
||||||
|
/* Fetch */
|
||||||
|
_imagecache_fetch(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _imagecache_fetch ( imagecache_image_t *img )
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
CURL *curl;
|
||||||
|
FILE *fp;
|
||||||
|
char tmp[256], path[256];
|
||||||
|
|
||||||
|
/* Open file */
|
||||||
|
if (hts_settings_buildpath(path, sizeof(path), "imagecache/data/%d",
|
||||||
|
img->id))
|
||||||
|
return 1;
|
||||||
|
if (hts_settings_makedirs(path))
|
||||||
|
return 1;
|
||||||
|
snprintf(tmp, sizeof(tmp), "%s.tmp", path);
|
||||||
|
if (!(fp = fopen(tmp, "wb")))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* Fetch file */
|
||||||
|
tvhlog(LOG_DEBUG, "imagecache", "fetch %s", img->url);
|
||||||
|
curl = curl_easy_init();
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, img->url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "TVHeadend");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
|
||||||
|
res = curl_easy_perform(curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
/* Process */
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
img->state = IDLE;
|
||||||
|
time(&img->updated); // even if failed (possibly request sooner?)
|
||||||
|
if (res) {
|
||||||
|
img->failed = 1;
|
||||||
|
unlink(tmp);
|
||||||
|
tvhlog(LOG_WARNING, "imagecache", "failed to download %s", img->url);
|
||||||
|
} else {
|
||||||
|
img->failed = 0;
|
||||||
|
unlink(path);
|
||||||
|
rename(tmp, path);
|
||||||
|
tvhlog(LOG_DEBUG, "imagecache", "downloaded %s", img->url);
|
||||||
|
}
|
||||||
|
_imagecache_save(img);
|
||||||
|
pthread_cond_broadcast(&_imagecache_cond);
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
#endif
|
57
src/imagecache.h
Normal file
57
src/imagecache.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Icon file serve operations
|
||||||
|
* Copyright (C) 2012 Andy Brown
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __IMAGE_CACHE_H__
|
||||||
|
#define __IMAGE_CACHE_H__
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
extern uint32_t imagecache_enabled;
|
||||||
|
extern uint32_t imagecache_ok_period;
|
||||||
|
extern uint32_t imagecache_fail_period;
|
||||||
|
|
||||||
|
extern pthread_mutex_t imagecache_mutex;
|
||||||
|
|
||||||
|
void imagecache_init ( void );
|
||||||
|
|
||||||
|
void imagecache_save ( void );
|
||||||
|
|
||||||
|
int imagecache_set_enabled ( uint32_t e )
|
||||||
|
__attribute__((warn_unused_result));
|
||||||
|
int imagecache_set_ok_period ( uint32_t e )
|
||||||
|
__attribute__((warn_unused_result));
|
||||||
|
int imagecache_set_fail_period ( uint32_t e )
|
||||||
|
__attribute__((warn_unused_result));
|
||||||
|
|
||||||
|
// Note: will return 0 if invalid (must serve original URL)
|
||||||
|
uint32_t imagecache_get_id ( const char *url );
|
||||||
|
|
||||||
|
int imagecache_open ( uint32_t id );
|
||||||
|
|
||||||
|
#define htsmsg_add_imageurl(_msg, _fld, _fmt, _url)\
|
||||||
|
{\
|
||||||
|
char _tmp[64];\
|
||||||
|
uint32_t _id = imagecache_get_id(_url);\
|
||||||
|
if (_id) {\
|
||||||
|
snprintf(_tmp, sizeof(_tmp), _fmt, _id);\
|
||||||
|
} else {\
|
||||||
|
htsmsg_add_str(_msg, _fld, _url);\
|
||||||
|
}\
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __IMAGE_CACHE_H__ */
|
|
@ -60,7 +60,7 @@
|
||||||
#include "ffdecsa/FFdecsa.h"
|
#include "ffdecsa/FFdecsa.h"
|
||||||
#include "muxes.h"
|
#include "muxes.h"
|
||||||
#include "config2.h"
|
#include "config2.h"
|
||||||
#include "iconserve.h"
|
#include "imagecache.h"
|
||||||
|
|
||||||
int running;
|
int running;
|
||||||
time_t dispatch_clock;
|
time_t dispatch_clock;
|
||||||
|
@ -89,6 +89,9 @@ const char *tvheadend_capabilities[] = {
|
||||||
#endif
|
#endif
|
||||||
#if ENABLE_LINUXDVB
|
#if ENABLE_LINUXDVB
|
||||||
"linuxdvb",
|
"linuxdvb",
|
||||||
|
#endif
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
"imagecache",
|
||||||
#endif
|
#endif
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -466,6 +469,8 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
config_init();
|
config_init();
|
||||||
|
|
||||||
|
imagecache_init();
|
||||||
|
|
||||||
service_init();
|
service_init();
|
||||||
|
|
||||||
channels_init();
|
channels_init();
|
||||||
|
@ -489,8 +494,6 @@ main(int argc, char **argv)
|
||||||
http_server_init();
|
http_server_init();
|
||||||
webui_init();
|
webui_init();
|
||||||
|
|
||||||
logo_loader();
|
|
||||||
|
|
||||||
serviceprobe_init();
|
serviceprobe_init();
|
||||||
|
|
||||||
#if ENABLE_CWC
|
#if ENABLE_CWC
|
||||||
|
|
|
@ -101,7 +101,7 @@ hts_settings_makedirs ( const char *inpath )
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
hts_settings_buildpath
|
_hts_settings_buildpath
|
||||||
(char *dst, size_t dstsize, const char *fmt, va_list ap, const char *prefix)
|
(char *dst, size_t dstsize, const char *fmt, va_list ap, const char *prefix)
|
||||||
{
|
{
|
||||||
char tmp[256];
|
char tmp[256];
|
||||||
|
@ -120,6 +120,18 @@ hts_settings_buildpath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
hts_settings_buildpath
|
||||||
|
(char *dst, size_t dstsize, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list va;
|
||||||
|
va_start(va, fmt);
|
||||||
|
if (!settingspath)
|
||||||
|
return 1;
|
||||||
|
_hts_settings_buildpath(dst, dstsize, fmt, va, settingspath);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -139,7 +151,7 @@ hts_settings_save(htsmsg_t *record, const char *pathfmt, ...)
|
||||||
|
|
||||||
/* Clean the path */
|
/* Clean the path */
|
||||||
va_start(ap, pathfmt);
|
va_start(ap, pathfmt);
|
||||||
hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
_hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
/* Create directories */
|
/* Create directories */
|
||||||
|
@ -261,16 +273,16 @@ hts_settings_load(const char *pathfmt, ...)
|
||||||
|
|
||||||
/* Try normal path */
|
/* Try normal path */
|
||||||
va_start(ap, pathfmt);
|
va_start(ap, pathfmt);
|
||||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
_hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||||
pathfmt, ap, settingspath);
|
pathfmt, ap, settingspath);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
ret = _hts_settings_load(fullpath);
|
ret = _hts_settings_load(fullpath);
|
||||||
|
|
||||||
/* Try bundle path */
|
/* Try bundle path */
|
||||||
if (!ret && *pathfmt != '/') {
|
if (!ret && *pathfmt != '/') {
|
||||||
va_start(ap, pathfmt);
|
va_start(ap, pathfmt);
|
||||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
_hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||||
pathfmt, ap, "data/conf");
|
pathfmt, ap, "data/conf");
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
ret = _hts_settings_load(fullpath);
|
ret = _hts_settings_load(fullpath);
|
||||||
}
|
}
|
||||||
|
@ -289,7 +301,7 @@ hts_settings_remove(const char *pathfmt, ...)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
va_start(ap, pathfmt);
|
va_start(ap, pathfmt);
|
||||||
hts_settings_buildpath(fullpath, sizeof(fullpath),
|
_hts_settings_buildpath(fullpath, sizeof(fullpath),
|
||||||
pathfmt, ap, settingspath);
|
pathfmt, ap, settingspath);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
if (stat(fullpath, &st) == 0) {
|
if (stat(fullpath, &st) == 0) {
|
||||||
|
@ -311,7 +323,7 @@ hts_settings_open_file(int for_write, const char *pathfmt, ...)
|
||||||
|
|
||||||
/* Build path */
|
/* Build path */
|
||||||
va_start(ap, pathfmt);
|
va_start(ap, pathfmt);
|
||||||
hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
_hts_settings_buildpath(path, sizeof(path), pathfmt, ap, settingspath);
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
/* Create directories */
|
/* Create directories */
|
||||||
|
|
|
@ -34,6 +34,8 @@ const char *hts_settings_get_root(void);
|
||||||
|
|
||||||
int hts_settings_open_file(int for_write, const char *pathfmt, ...);
|
int hts_settings_open_file(int for_write, const char *pathfmt, ...);
|
||||||
|
|
||||||
|
int hts_settings_buildpath(char *dst, size_t dstsize, const char *pathfmt, ...);
|
||||||
|
|
||||||
int hts_settings_makedirs ( const char *path );
|
int hts_settings_makedirs ( const char *path );
|
||||||
|
|
||||||
#endif /* HTSSETTINGS_H__ */
|
#endif /* HTSSETTINGS_H__ */
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
#include "config2.h"
|
#include "config2.h"
|
||||||
#include "lang_codes.h"
|
#include "lang_codes.h"
|
||||||
#include "subscriptions.h"
|
#include "subscriptions.h"
|
||||||
#include "iconserve.h"
|
#include "imagecache.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -859,7 +859,7 @@ extjs_epg(http_connection_t *hc, const char *remain, void *opaque)
|
||||||
htsmsg_add_str(m, "channel", ch->ch_name);
|
htsmsg_add_str(m, "channel", ch->ch_name);
|
||||||
htsmsg_add_u32(m, "channelid", ch->ch_id);
|
htsmsg_add_u32(m, "channelid", ch->ch_id);
|
||||||
if(ch->ch_icon != NULL)
|
if(ch->ch_icon != NULL)
|
||||||
htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon));
|
htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
|
||||||
|
|
||||||
if((s = epg_episode_get_title(ee, lang)))
|
if((s = epg_episode_get_title(ee, lang)))
|
||||||
htsmsg_add_str(m, "title", s);
|
htsmsg_add_str(m, "title", s);
|
||||||
|
@ -941,7 +941,8 @@ extjs_epgrelated(http_connection_t *hc, const char *remain, void *opaque)
|
||||||
m = htsmsg_create_map();
|
m = htsmsg_create_map();
|
||||||
htsmsg_add_u32(m, "id", ebc->id);
|
htsmsg_add_u32(m, "id", ebc->id);
|
||||||
if ( ch->ch_name ) htsmsg_add_str(m, "channel", ch->ch_name);
|
if ( ch->ch_name ) htsmsg_add_str(m, "channel", ch->ch_name);
|
||||||
if ( ch->ch_icon ) htsmsg_add_str(m, "chicon", logo_query(ch->ch_id, ch->ch_icon));
|
if (ch->ch_icon)
|
||||||
|
htsmsg_add_imageurl(m, "chicon", "imagecache/%d", ch->ch_icon);
|
||||||
htsmsg_add_u32(m, "start", ebc->start);
|
htsmsg_add_u32(m, "start", ebc->start);
|
||||||
htsmsg_add_msg(array, NULL, m);
|
htsmsg_add_msg(array, NULL, m);
|
||||||
}
|
}
|
||||||
|
@ -1340,8 +1341,9 @@ extjs_dvrlist(http_connection_t *hc, const char *remain, void *opaque,
|
||||||
|
|
||||||
if(de->de_channel != NULL) {
|
if(de->de_channel != NULL) {
|
||||||
htsmsg_add_str(m, "channel", de->de_channel->ch_name);
|
htsmsg_add_str(m, "channel", de->de_channel->ch_name);
|
||||||
if(de->de_channel->ch_icon != NULL)
|
if (de->de_channel->ch_icon)
|
||||||
htsmsg_add_str(m, "chicon", logo_query(de->de_channel->ch_id, de->de_channel->ch_icon));
|
htsmsg_add_imageurl(m, "chicon", "imagecache/%d",
|
||||||
|
de->de_channel->ch_icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
htsmsg_add_str(m, "config_name", de->de_config_name);
|
htsmsg_add_str(m, "config_name", de->de_config_name);
|
||||||
|
@ -1915,40 +1917,54 @@ extjs_config(http_connection_t *hc, const char *remain, void *opaque)
|
||||||
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
pthread_mutex_unlock(&global_lock);
|
||||||
|
|
||||||
/* Basic settings (not the advanced schedule) */
|
/* Basic settings */
|
||||||
if(!strcmp(op, "loadSettings")) {
|
if(!strcmp(op, "loadSettings")) {
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
pthread_mutex_lock(&global_lock);
|
pthread_mutex_lock(&global_lock);
|
||||||
m = config_get_all();
|
m = config_get_all();
|
||||||
pthread_mutex_unlock(&global_lock);
|
pthread_mutex_unlock(&global_lock);
|
||||||
|
|
||||||
|
/* Image cache */
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
htsmsg_add_u32(m, "imagecache_enabled", imagecache_enabled);
|
||||||
|
htsmsg_add_u32(m, "imagecache_ok_period", imagecache_ok_period);
|
||||||
|
htsmsg_add_u32(m, "imagecache_fail_period", imagecache_fail_period);
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!m) return HTTP_STATUS_BAD_REQUEST;
|
if (!m) return HTTP_STATUS_BAD_REQUEST;
|
||||||
out = json_single_record(m, "config");
|
out = json_single_record(m, "config");
|
||||||
|
|
||||||
/* Save settings */
|
/* Save settings */
|
||||||
} else if (!strcmp(op, "saveSettings") ) {
|
} else if (!strcmp(op, "saveSettings") ) {
|
||||||
int save = 0;
|
int save = 0;
|
||||||
|
|
||||||
|
/* Misc settings */
|
||||||
pthread_mutex_lock(&global_lock);
|
pthread_mutex_lock(&global_lock);
|
||||||
if ((str = http_arg_get(&hc->hc_req_args, "muxconfpath")))
|
if ((str = http_arg_get(&hc->hc_req_args, "muxconfpath")))
|
||||||
save |= config_set_muxconfpath(str);
|
save |= config_set_muxconfpath(str);
|
||||||
if ((str = http_arg_get(&hc->hc_req_args, "language")))
|
if ((str = http_arg_get(&hc->hc_req_args, "language")))
|
||||||
save |= config_set_language(str);
|
save |= config_set_language(str);
|
||||||
str = http_arg_get(&hc->hc_req_args, "iconserve");
|
if (save)
|
||||||
if (str != NULL) {
|
config_save();
|
||||||
save |= config_set_iconserve(str);
|
|
||||||
} else {
|
|
||||||
save |= config_set_iconserve("off");
|
|
||||||
};
|
|
||||||
str = http_arg_get(&hc->hc_req_args, "iconserve_periodicdownload");
|
|
||||||
if (str != NULL) {
|
|
||||||
save |= config_set_iconserve_periodicdownload(str);
|
|
||||||
} else {
|
|
||||||
save |= config_set_iconserve_periodicdownload("off");
|
|
||||||
};
|
|
||||||
if ((str = http_arg_get(&hc->hc_req_args, "serverip")))
|
|
||||||
save |= config_set_serverip(str);
|
|
||||||
if (save) config_save();
|
|
||||||
/* trigger the iconserve init routine */
|
|
||||||
logo_loader();
|
|
||||||
pthread_mutex_unlock(&global_lock);
|
pthread_mutex_unlock(&global_lock);
|
||||||
|
|
||||||
|
/* Image Cache */
|
||||||
|
#if ENABLE_IMAGECACHE
|
||||||
|
pthread_mutex_lock(&imagecache_mutex);
|
||||||
|
str = http_arg_get(&hc->hc_req_args, "imagecache_enabled");
|
||||||
|
save = imagecache_set_enabled(!!str);
|
||||||
|
if ((str = http_arg_get(&hc->hc_req_args, "imagecache_ok_period")))
|
||||||
|
save |= imagecache_set_ok_period(atoi(str));
|
||||||
|
if ((str = http_arg_get(&hc->hc_req_args, "imagecache_fail_period")))
|
||||||
|
save |= imagecache_set_fail_period(atoi(str));
|
||||||
|
if (save)
|
||||||
|
imagecache_save();
|
||||||
|
pthread_mutex_unlock(&imagecache_mutex);
|
||||||
|
#endif
|
||||||
|
|
||||||
out = htsmsg_create_map();
|
out = htsmsg_create_map();
|
||||||
htsmsg_add_u32(out, "success", 1);
|
htsmsg_add_u32(out, "success", 1);
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
#include "epg.h"
|
#include "epg.h"
|
||||||
#include "psi.h"
|
#include "psi.h"
|
||||||
#include "channels.h"
|
#include "channels.h"
|
||||||
#include "iconserve.h"
|
|
||||||
#if ENABLE_LINUXDVB
|
#if ENABLE_LINUXDVB
|
||||||
#include "dvr/dvr.h"
|
#include "dvr/dvr.h"
|
||||||
#include "dvb/dvb.h"
|
#include "dvb/dvb.h"
|
||||||
|
@ -73,7 +72,7 @@ dumpchannels(htsbuf_queue_t *hq)
|
||||||
ch->ch_refcount,
|
ch->ch_refcount,
|
||||||
ch->ch_zombie,
|
ch->ch_zombie,
|
||||||
ch->ch_number,
|
ch->ch_number,
|
||||||
logo_query(ch->ch_id, ch->ch_icon) ?: "<none set>");
|
ch->ch_icon ?: "<none set>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,18 @@ tvheadend.miscconf = function() {
|
||||||
*/
|
*/
|
||||||
var confreader = new Ext.data.JsonReader({
|
var confreader = new Ext.data.JsonReader({
|
||||||
root : 'config'
|
root : 'config'
|
||||||
}, [ 'muxconfpath', 'language', 'iconserve', 'serverip' ]);
|
}, [ 'muxconfpath', 'language',
|
||||||
|
'imagecache_enabled', 'imagecache_ok_period',
|
||||||
|
'imagecache_fail_period']);
|
||||||
|
|
||||||
/* ****************************************************************
|
/* ****************************************************************
|
||||||
* Form Fields
|
* Form Fields
|
||||||
* ***************************************************************/
|
* ***************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DVB path
|
||||||
|
*/
|
||||||
|
|
||||||
var dvbscanPath = new Ext.form.TextField({
|
var dvbscanPath = new Ext.form.TextField({
|
||||||
fieldLabel : 'DVB scan files path',
|
fieldLabel : 'DVB scan files path',
|
||||||
name : 'muxconfpath',
|
name : 'muxconfpath',
|
||||||
|
@ -50,21 +56,9 @@ tvheadend.miscconf = function() {
|
||||||
width: 400
|
width: 400
|
||||||
});
|
});
|
||||||
|
|
||||||
var iconServeConfig = new Ext.form.Checkbox({
|
/*
|
||||||
name : 'iconserve',
|
* Language
|
||||||
fieldLabel : 'Cache channel icons'
|
*/
|
||||||
});
|
|
||||||
var iconPeriodicDownload = new Ext.form.Checkbox({
|
|
||||||
name : 'iconserve_periodicdownload',
|
|
||||||
fieldLabel : 'Periodically check for updated icons'
|
|
||||||
});
|
|
||||||
var serveripConfig = new Ext.form.TextField({
|
|
||||||
fieldLabel : 'TVH Server IP address',
|
|
||||||
name : 'serverip',
|
|
||||||
allowBlank : true,
|
|
||||||
width: 150
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var language = new Ext.ux.ItemSelector({
|
var language = new Ext.ux.ItemSelector({
|
||||||
name: 'language',
|
name: 'language',
|
||||||
|
@ -81,6 +75,34 @@ tvheadend.miscconf = function() {
|
||||||
fromLegend: 'Available'
|
fromLegend: 'Available'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Image cache
|
||||||
|
*/
|
||||||
|
var imagecacheEnabled = new Ext.form.Checkbox({
|
||||||
|
name: 'imagecache_enabled',
|
||||||
|
fieldLabel: 'Enabled',
|
||||||
|
});
|
||||||
|
|
||||||
|
var imagecacheOkPeriod = new Ext.form.NumberField({
|
||||||
|
name: 'imagecache_ok_period',
|
||||||
|
fieldLabel: 'Re-fetch period (hours)'
|
||||||
|
});
|
||||||
|
|
||||||
|
var imagecacheFailPeriod = new Ext.form.NumberField({
|
||||||
|
name: 'imagecache_fail_period',
|
||||||
|
fieldLabel: 'Re-try period (hours)',
|
||||||
|
});
|
||||||
|
|
||||||
|
var imagecachePanel = new Ext.form.FieldSet({
|
||||||
|
title: 'Image Caching',
|
||||||
|
width: 700,
|
||||||
|
autoHeight: true,
|
||||||
|
collapsible: true,
|
||||||
|
items : [ imagecacheEnabled, imagecacheOkPeriod, imagecacheFailPeriod ]
|
||||||
|
});
|
||||||
|
if (tvheadend.capabilities.indexOf('imagecache') == -1)
|
||||||
|
imagecachePanel.hide();
|
||||||
|
|
||||||
/* ****************************************************************
|
/* ****************************************************************
|
||||||
* Form
|
* Form
|
||||||
* ***************************************************************/
|
* ***************************************************************/
|
||||||
|
@ -111,7 +133,8 @@ tvheadend.miscconf = function() {
|
||||||
layout : 'form',
|
layout : 'form',
|
||||||
defaultType : 'textfield',
|
defaultType : 'textfield',
|
||||||
autoHeight : true,
|
autoHeight : true,
|
||||||
items : [ language, dvbscanPath, iconServeConfig, iconPeriodicDownload, serveripConfig ],
|
items : [ language, dvbscanPath,
|
||||||
|
imagecachePanel ],
|
||||||
tbar : [ saveButton, '->', helpButton ]
|
tbar : [ saveButton, '->', helpButton ]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
#include "muxer.h"
|
#include "muxer.h"
|
||||||
#include "dvb/dvb.h"
|
#include "dvb/dvb.h"
|
||||||
#include "dvb/dvb_support.h"
|
#include "dvb/dvb_support.h"
|
||||||
#include "iconserve.h"
|
#include "imagecache.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -868,7 +868,47 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch image cache image
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Static download of a file from the filesystem
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
if(remain == NULL)
|
||||||
|
return 404;
|
||||||
|
|
||||||
|
if(sscanf(remain, "%d", &id) != 1)
|
||||||
|
return HTTP_STATUS_BAD_REQUEST;
|
||||||
|
|
||||||
|
if ((fd = imagecache_open(id)) < 0)
|
||||||
|
return 404;
|
||||||
|
if (fstat(fd, &st)) {
|
||||||
|
close(fd);
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
http_send_header(hc, 200, NULL, st.st_size, 0, NULL, 10, 0, NULL);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
c = read(fd, buf, sizeof(buf));
|
||||||
|
if (c <= 0)
|
||||||
|
break;
|
||||||
|
if (tvh_write(hc->hc_fd, buf, c))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -910,7 +950,7 @@ webui_init(void)
|
||||||
|
|
||||||
http_path_add("/stream", NULL, http_stream, ACCESS_STREAMING);
|
http_path_add("/stream", NULL, http_stream, ACCESS_STREAMING);
|
||||||
|
|
||||||
http_path_add("/channellogo", NULL, page_logo, ACCESS_ANONYMOUS);
|
http_path_add("/imagecache", NULL, page_imagecache, ACCESS_ANONYMOUS);
|
||||||
|
|
||||||
webui_static_content("/static", "src/webui/static");
|
webui_static_content("/static", "src/webui/static");
|
||||||
webui_static_content("/docs", "docs/html");
|
webui_static_content("/docs", "docs/html");
|
||||||
|
|
Loading…
Add table
Reference in a new issue