178 lines
6.8 KiB
C
178 lines
6.8 KiB
C
#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);
|
|
strncpy(g_cfg.base_path, "", sizeof(g_cfg.base_path)-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));
|
|
cfg_read_str(buf, "base_path", g_cfg.base_path, sizeof(g_cfg.base_path));
|
|
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);
|
|
}
|