Files
iptv-downloader/config/config.c
T

176 lines
6.7 KiB
C
Raw Normal View History

#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
Config g_cfg;
static char g_cfg_path[512] = "";
static void set_defaults(void) {
memset(&g_cfg, 0, sizeof(g_cfg));
g_cfg.port = 8787;
strncpy(g_cfg.bind_iface, "wg0-mullvad", sizeof(g_cfg.bind_iface)-1);
strncpy(g_cfg.iptv_api, "http://newlogin.freeopened.com/player_api.php", sizeof(g_cfg.iptv_api)-1);
strncpy(g_cfg.dl_dir_tv, "/mnt/media/TV", sizeof(g_cfg.dl_dir_tv)-1);
strncpy(g_cfg.dl_dir_mov, "/mnt/media/Movies", sizeof(g_cfg.dl_dir_mov)-1);
strncpy(g_cfg.template_dir, "/usr/local/share/iptv-dl", sizeof(g_cfg.template_dir)-1);
strncpy(g_cfg.data_dir, "/var/lib/iptv-dl", sizeof(g_cfg.data_dir)-1);
g_cfg.max_recv_speed = 20L * 1024 * 1024; /* 20 MiB/s */
}
/* minimal JSON string field parser — no full JSON lib dependency */
static void cfg_read_str(const char *json, const char *key, char *dst, size_t dsz) {
char needle[128]; snprintf(needle, sizeof(needle), "\"%s\"", key);
const char *p = strstr(json, needle);
if (!p) return;
p += strlen(needle);
while (*p == ' ' || *p == ':') p++;
if (*p != '"') return;
p++;
size_t i = 0;
while (*p && *p != '"' && i < dsz-1) {
if (*p == '\\' && *(p+1)) { p++; dst[i++] = *p++; }
else dst[i++] = *p++;
}
dst[i] = 0;
}
static int cfg_read_int(const char *json, const char *key, int def) {
char needle[128]; snprintf(needle, sizeof(needle), "\"%s\"", key);
const char *p = strstr(json, needle);
if (!p) return def;
p += strlen(needle);
while (*p == ' ' || *p == ':') p++;
if (*p < '0' || *p > '9') return def;
return atoi(p);
}
static long cfg_read_long(const char *json, const char *key, long def) {
char needle[128]; snprintf(needle, sizeof(needle), "\"%s\"", key);
const char *p = strstr(json, needle);
if (!p) return def;
p += strlen(needle);
while (*p == ' ' || *p == ':') p++;
if (*p < '0' || *p > '9') return def;
return atol(p);
}
static int try_load(const char *path) {
FILE *f = fopen(path, "r");
if (!f) return 0;
fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET);
char *buf = malloc(sz + 1);
if (!buf) { fclose(f); return 0; }
fread(buf, 1, sz, f); buf[sz] = 0; fclose(f);
cfg_read_str(buf, "bind_iface", g_cfg.bind_iface, sizeof(g_cfg.bind_iface));
cfg_read_str(buf, "iptv_api", g_cfg.iptv_api, sizeof(g_cfg.iptv_api));
cfg_read_str(buf, "iptv_user", g_cfg.iptv_user, sizeof(g_cfg.iptv_user));
cfg_read_str(buf, "iptv_pass", g_cfg.iptv_pass, sizeof(g_cfg.iptv_pass));
cfg_read_str(buf, "stream_base", g_cfg.stream_base, sizeof(g_cfg.stream_base));
cfg_read_str(buf, "dl_dir_tv", g_cfg.dl_dir_tv, sizeof(g_cfg.dl_dir_tv));
cfg_read_str(buf, "dl_dir_mov", g_cfg.dl_dir_mov, sizeof(g_cfg.dl_dir_mov));
cfg_read_str(buf, "discord_webhook", g_cfg.discord_webhook, sizeof(g_cfg.discord_webhook));
cfg_read_str(buf, "jellyfin_url", g_cfg.jellyfin_url, sizeof(g_cfg.jellyfin_url));
cfg_read_str(buf, "jellyfin_token", g_cfg.jellyfin_token, sizeof(g_cfg.jellyfin_token));
cfg_read_str(buf, "template_dir", g_cfg.template_dir, sizeof(g_cfg.template_dir));
cfg_read_str(buf, "data_dir", g_cfg.data_dir, sizeof(g_cfg.data_dir));
g_cfg.port = cfg_read_int(buf, "port", g_cfg.port);
g_cfg.max_recv_speed = cfg_read_long(buf, "max_recv_speed", g_cfg.max_recv_speed);
free(buf);
strncpy(g_cfg_path, path, sizeof(g_cfg_path)-1);
return 1;
}
const char *config_load(const char *explicit_path) {
set_defaults();
/* 1. explicit argument */
if (explicit_path && *explicit_path) {
if (try_load(explicit_path)) return g_cfg_path;
fprintf(stderr, "iptv-dl: warning: cannot read config '%s', using defaults\n", explicit_path);
return NULL;
}
/* 2. env var */
const char *env = getenv("IPTV_DL_CONFIG");
if (env && *env) {
if (try_load(env)) return g_cfg_path;
}
/* 3. ~/.iptv-downloader/config.json */
char home_cfg[512];
const char *home = getenv("HOME");
if (!home) {
struct passwd *pw = getpwuid(getuid());
if (pw) home = pw->pw_dir;
}
if (home) {
snprintf(home_cfg, sizeof(home_cfg), "%s/.iptv-downloader/config.json", home);
if (try_load(home_cfg)) return g_cfg_path;
}
/* 4. /etc/iptv-downloader/config.json */
if (try_load("/etc/iptv-downloader/config.json")) return g_cfg_path;
/* 5. defaults only */
fprintf(stderr, "iptv-dl: no config file found, using built-in defaults\n");
return NULL;
}
void config_save(void) {
if (!g_cfg_path[0]) {
fprintf(stderr, "iptv-dl: no config path set, cannot save\n");
return;
}
FILE *f = fopen(g_cfg_path, "w");
if (!f) { perror("config_save"); return; }
fprintf(f, "{\n");
fprintf(f, " \"port\": %d,\n", g_cfg.port);
fprintf(f, " \"bind_iface\": \"%s\",\n", g_cfg.bind_iface);
fprintf(f, " \"iptv_api\": \"%s\",\n", g_cfg.iptv_api);
fprintf(f, " \"iptv_user\": \"%s\",\n", g_cfg.iptv_user);
fprintf(f, " \"iptv_pass\": \"%s\",\n", g_cfg.iptv_pass);
fprintf(f, " \"stream_base\": \"%s\",\n", g_cfg.stream_base);
fprintf(f, " \"dl_dir_tv\": \"%s\",\n", g_cfg.dl_dir_tv);
fprintf(f, " \"dl_dir_mov\": \"%s\",\n", g_cfg.dl_dir_mov);
fprintf(f, " \"discord_webhook\": \"%s\",\n", g_cfg.discord_webhook);
fprintf(f, " \"jellyfin_url\": \"%s\",\n", g_cfg.jellyfin_url);
fprintf(f, " \"jellyfin_token\": \"%s\",\n", g_cfg.jellyfin_token);
fprintf(f, " \"template_dir\": \"%s\",\n", g_cfg.template_dir);
fprintf(f, " \"data_dir\": \"%s\",\n", g_cfg.data_dir);
fprintf(f, " \"max_recv_speed\": %ld\n", g_cfg.max_recv_speed);
fprintf(f, "}\n");
fclose(f);
}
void config_dump(void) {
fprintf(stderr,
"iptv-dl config:\n"
" port=%d bind_iface=%s\n"
" iptv_api=%s user=%s\n"
" stream_base=%s\n"
" dl_dir_tv=%s dl_dir_mov=%s\n"
" template_dir=%s data_dir=%s\n"
" max_recv_speed=%ld B/s\n",
g_cfg.port, g_cfg.bind_iface,
g_cfg.iptv_api, g_cfg.iptv_user,
g_cfg.stream_base,
g_cfg.dl_dir_tv, g_cfg.dl_dir_mov,
g_cfg.template_dir, g_cfg.data_dir,
g_cfg.max_recv_speed);
}
void config_history_path(char *buf, size_t n) {
snprintf(buf, n, "%s/history.json", g_cfg.data_dir);
}
void config_notif_path(char *buf, size_t n) {
snprintf(buf, n, "%s/notifications.json", g_cfg.data_dir);
}