2015-03-08 00:16:18 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
2019-11-27 15:21:41 +00:00
|
|
|
#include <fcntl.h>
|
2015-03-08 00:16:18 +00:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sqlite3.h>
|
|
|
|
|
2015-03-09 20:48:47 +00:00
|
|
|
#define CACHE "/mod/var/tvdb"
|
|
|
|
#define SERIESDB CACHE "/series.db"
|
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
#define EXEC(xx) if (sqlite3_exec(db, xx, NULL, NULL, &error) != SQLITE_OK) \
|
|
|
|
do { \
|
|
|
|
printf("Failed: %s - %s (%s)\n", xx, error, sqlite3_errmsg(db)); \
|
|
|
|
sqlite3_free(error); \
|
|
|
|
exit(0); \
|
|
|
|
} while (0)
|
|
|
|
|
2015-03-09 20:48:47 +00:00
|
|
|
struct series {
|
|
|
|
unsigned long id;
|
|
|
|
char *imdb;
|
|
|
|
char *name;
|
|
|
|
char *overview;
|
2015-10-12 14:46:17 +00:00
|
|
|
char *banner;
|
2015-03-09 20:48:47 +00:00
|
|
|
};
|
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
struct episode {
|
|
|
|
unsigned long id;
|
|
|
|
char *name;
|
|
|
|
char *overview;
|
2015-11-02 23:05:32 +00:00
|
|
|
char *thumb;
|
2015-03-08 00:16:18 +00:00
|
|
|
unsigned long series;
|
|
|
|
unsigned long episode;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define HANDLE(str, c, n) \
|
|
|
|
do { \
|
2015-03-09 20:48:47 +00:00
|
|
|
if (!memcmp(p, str, (n + 1))) \
|
2015-03-08 00:16:18 +00:00
|
|
|
{ \
|
|
|
|
*p = c; \
|
|
|
|
memmove(p + 1, p + (n + 1), l - (p - txt) - (n + 1)); \
|
|
|
|
l -= n; \
|
|
|
|
txt[l] = '\0'; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
void
|
|
|
|
unescape(char *txt)
|
|
|
|
{
|
2021-01-14 23:55:08 +00:00
|
|
|
char *p = txt;
|
2021-01-15 00:31:20 +00:00
|
|
|
int l = strlen(txt);
|
2015-03-08 00:16:18 +00:00
|
|
|
|
2021-01-15 00:31:20 +00:00
|
|
|
for (; (p = strchr(p, '&')); p++)
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
2021-01-15 00:58:18 +00:00
|
|
|
int ll = 0;
|
2021-01-14 23:55:08 +00:00
|
|
|
unsigned char icode;
|
2021-01-15 00:58:18 +00:00
|
|
|
/* sscanf -> 1: the code was read; ll>0: ';' came next */
|
|
|
|
if ((1 == sscanf( p, "&#%hhu;%n", &icode, &ll) ||
|
|
|
|
1 == sscanf( p, "&#%*[xX]%hhx;%n", &icode, &ll)) &&
|
|
|
|
ll > 0) {
|
2021-01-14 23:55:08 +00:00
|
|
|
/* &#x<hex>;, &#<decimal>; */
|
|
|
|
HANDLE(p, (char)icode, ll-1);
|
|
|
|
} else {
|
|
|
|
HANDLE("&", '&', 4);
|
|
|
|
HANDLE(""", '"', 5);
|
|
|
|
HANDLE("'", '\'', 5);
|
|
|
|
HANDLE("<", '<', 3);
|
|
|
|
HANDLE(">", '>', 3);
|
|
|
|
}
|
2015-03-08 00:16:18 +00:00
|
|
|
}
|
2015-03-09 20:48:47 +00:00
|
|
|
|
2021-01-15 00:31:20 +00:00
|
|
|
for (p = txt; (p = memchr(p, '\xe2', l - (p - txt))); p++)
|
2021-01-14 23:55:08 +00:00
|
|
|
{ /* curly apostrophe, en dash, curly quotes */
|
2015-03-09 20:48:47 +00:00
|
|
|
HANDLE("\xe2\x80\x99", '\'', 2);
|
|
|
|
HANDLE("\xe2\x80\x93", '-', 2);
|
|
|
|
HANDLE("\xe2\x80\x9c", '"', 2);
|
|
|
|
HANDLE("\xe2\x80\x9d", '"', 2);
|
|
|
|
}
|
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
if ((p = strpbrk(txt, "\n\r")))
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
char *buf, *p, *q;
|
2015-03-09 20:48:47 +00:00
|
|
|
struct series s;
|
2015-03-08 00:16:18 +00:00
|
|
|
struct episode e;
|
2019-11-27 15:21:41 +00:00
|
|
|
struct stat st;
|
2015-03-08 00:16:18 +00:00
|
|
|
char *error;
|
|
|
|
sqlite3_stmt *stmt;
|
|
|
|
sqlite3 *db;
|
2019-11-27 15:21:41 +00:00
|
|
|
int fd, epcnt = 0;
|
|
|
|
int debug = 0;
|
2015-03-08 00:16:18 +00:00
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
if (argc < 2)
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
printf("Syntax: %s [-d] <xml file>\n", argv[0]);
|
2015-03-08 00:16:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
if (!strcmp(argv[1], "-d"))
|
|
|
|
{
|
|
|
|
debug = 1;
|
|
|
|
argc--, argv++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(argv[1], &st) == -1)
|
|
|
|
{
|
|
|
|
perror("stat");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(buf = malloc(st.st_size)))
|
|
|
|
{
|
|
|
|
perror("malloc");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = open(argv[1], O_RDONLY)) == -1)
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
|
|
|
perror("open");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
if (read(fd, buf, st.st_size) != st.st_size)
|
|
|
|
{
|
|
|
|
perror("read");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (debug)
|
|
|
|
printf("Read - %lu bytes\n", st.st_size);
|
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
|
|
|
|
|
2015-03-09 20:48:47 +00:00
|
|
|
if (sqlite3_open_v2(SERIESDB, &db,
|
|
|
|
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)
|
2015-03-08 00:16:18 +00:00
|
|
|
!= SQLITE_OK)
|
|
|
|
{
|
2015-03-09 20:48:47 +00:00
|
|
|
printf("Failed to open series database: %s\n",
|
|
|
|
sqlite3_errmsg(db));
|
2015-03-08 00:16:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXEC("pragma journal_mode = off");
|
|
|
|
EXEC("pragma synchronous = off");
|
2015-03-09 20:48:47 +00:00
|
|
|
|
|
|
|
EXEC(
|
|
|
|
"create table if not exists series("
|
|
|
|
"[series_id] integer primary key, "
|
|
|
|
"[imdb_id] text, "
|
|
|
|
"[name] text, "
|
|
|
|
"[overview] text, "
|
2015-10-12 14:46:17 +00:00
|
|
|
"[banner] text, "
|
2015-03-09 20:48:47 +00:00
|
|
|
"[dat] text"
|
|
|
|
")"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (sqlite3_prepare_v2(db,
|
|
|
|
"insert or replace into series "
|
2015-10-12 14:46:17 +00:00
|
|
|
"values(?,?,?,?,?,date('now'))",
|
2015-03-09 20:48:47 +00:00
|
|
|
-1, &stmt, NULL) != SQLITE_OK)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"Problem preparing series statement %s",
|
|
|
|
sqlite3_errmsg(db));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
p = strchr(buf, '<');
|
|
|
|
for (;;)
|
2015-03-09 20:48:47 +00:00
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
|
2015-03-09 20:48:47 +00:00
|
|
|
if (!strncmp(p, "Series>", 7))
|
|
|
|
{
|
|
|
|
memset(&s, '\0', sizeof(s));
|
|
|
|
}
|
|
|
|
if (!strncmp(p, "id>", 3))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 3;
|
|
|
|
s.id = strtoul(p, (char **)NULL, 10);
|
2015-03-09 20:48:47 +00:00
|
|
|
}
|
|
|
|
if (!strncmp(p, "IMDB_ID>", 8))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 8;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-03-09 20:48:47 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
s.imdb = strdup(p);
|
|
|
|
p = q;
|
2015-03-09 20:48:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(p, "SeriesName>", 11))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 11;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-03-09 20:48:47 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
s.name = strdup(p);
|
2015-03-09 20:48:47 +00:00
|
|
|
unescape(s.name);
|
2019-11-27 15:21:41 +00:00
|
|
|
printf("Series: %s\n", s.name);
|
|
|
|
p = q;
|
2015-03-09 20:48:47 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!strncmp(p, "Overview>", 9))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 9;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-03-09 20:48:47 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
s.overview = strdup(p);
|
2015-03-09 20:48:47 +00:00
|
|
|
unescape(s.overview);
|
2019-11-27 15:21:41 +00:00
|
|
|
if (debug)
|
|
|
|
printf(" %s\n", s.overview);
|
|
|
|
p = q;
|
|
|
|
continue;
|
2015-03-09 20:48:47 +00:00
|
|
|
}
|
2015-10-12 14:46:17 +00:00
|
|
|
if (!strncmp(p, "banner>", 7))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 7;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-10-12 14:46:17 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
s.banner = strdup(p);
|
2015-10-12 14:46:17 +00:00
|
|
|
unescape(s.banner);
|
2019-11-27 15:21:41 +00:00
|
|
|
p = q;
|
|
|
|
continue;
|
2015-10-12 14:46:17 +00:00
|
|
|
}
|
2015-03-09 20:48:47 +00:00
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
if (!strncmp(p, "/Series>", 8))
|
2015-03-09 20:48:47 +00:00
|
|
|
{
|
|
|
|
if (s.id && s.name && s.overview)
|
|
|
|
{
|
|
|
|
sqlite3_bind_int(stmt, 1, s.id);
|
|
|
|
sqlite3_bind_text(stmt, 2,
|
|
|
|
s.imdb ? s.imdb : "", -1, NULL);
|
|
|
|
sqlite3_bind_text(stmt, 3, s.name, -1, NULL);
|
|
|
|
sqlite3_bind_text(stmt, 4, s.overview,
|
|
|
|
-1, NULL);
|
2015-10-12 14:46:17 +00:00
|
|
|
sqlite3_bind_text(stmt, 5, s.banner, -1, NULL);
|
2015-03-09 20:48:47 +00:00
|
|
|
sqlite3_step(stmt);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-11-27 15:21:41 +00:00
|
|
|
p = strchr(p, '<');
|
2015-03-09 20:48:47 +00:00
|
|
|
}
|
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
|
|
|
|
if (!s.id)
|
|
|
|
{
|
|
|
|
printf("No series found.\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Attach the Episode database for this series. */
|
|
|
|
sprintf(buf, "attach '%s/%lu.db' as episode", CACHE, s.id);
|
|
|
|
EXEC(buf);
|
|
|
|
|
2015-11-02 23:05:32 +00:00
|
|
|
EXEC("drop table if exists episode.episode");
|
2015-03-08 00:16:18 +00:00
|
|
|
EXEC(
|
2015-03-09 20:48:47 +00:00
|
|
|
"create table if not exists episode.episode("
|
2015-03-08 00:16:18 +00:00
|
|
|
"[episode_id] integer primary key, "
|
|
|
|
"[series] integer, "
|
|
|
|
"[episode] integer, "
|
|
|
|
"[name] text, "
|
2015-11-02 23:05:32 +00:00
|
|
|
"[overview] text, "
|
|
|
|
"[thumb] text"
|
2015-03-08 00:16:18 +00:00
|
|
|
")"
|
|
|
|
);
|
|
|
|
|
|
|
|
if (sqlite3_prepare_v2(db,
|
2015-03-09 20:48:47 +00:00
|
|
|
"insert or replace into episode.episode "
|
2015-11-02 23:05:32 +00:00
|
|
|
"values(?,?,?,?,?,?)",
|
2015-03-08 00:16:18 +00:00
|
|
|
-1, &stmt, NULL) != SQLITE_OK)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"Problem preparing statement %s", sqlite3_errmsg(db));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
for (;;)
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
p++;
|
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
if (!strncmp(p, "Episode>", 8))
|
|
|
|
{
|
|
|
|
/* New episode */
|
|
|
|
memset(&e, '\0', sizeof(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strncmp(p, "id>", 3))
|
2019-11-27 15:21:41 +00:00
|
|
|
{
|
|
|
|
p += 3;
|
|
|
|
e.id = strtoul(p, (char **)NULL, 10);
|
|
|
|
}
|
|
|
|
if (!strncmp(p, "Combined_episodenumber>", 23)) {
|
|
|
|
p += 23;
|
|
|
|
e.episode = strtoul(p, (char **)NULL, 10);
|
|
|
|
}
|
|
|
|
if (!strncmp(p, "Combined_season>", 16)) {
|
|
|
|
p += 16;
|
|
|
|
e.series = strtoul(p, (char **)NULL, 10);
|
|
|
|
}
|
2015-03-08 00:16:18 +00:00
|
|
|
if (!strncmp(p, "EpisodeName>", 12))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 12;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-03-08 00:16:18 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
e.name = strdup(p);
|
2015-03-08 00:16:18 +00:00
|
|
|
unescape(e.name);
|
2019-11-27 15:21:41 +00:00
|
|
|
printf("Episode: %s\n", e.name);
|
|
|
|
p = q;
|
|
|
|
continue;
|
2015-03-08 00:16:18 +00:00
|
|
|
}
|
|
|
|
if (!strncmp(p, "Overview>", 9))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 9;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-03-08 00:16:18 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
e.overview = strdup(p);
|
2015-03-08 00:16:18 +00:00
|
|
|
unescape(e.overview);
|
2019-11-27 15:21:41 +00:00
|
|
|
if (debug)
|
|
|
|
printf(" %s\n", e.overview);
|
|
|
|
p = q;
|
|
|
|
continue;
|
2015-03-08 00:16:18 +00:00
|
|
|
}
|
2015-11-02 23:05:32 +00:00
|
|
|
if (!strncmp(p, "filename>", 9))
|
|
|
|
{
|
2019-11-27 15:21:41 +00:00
|
|
|
p += 9;
|
|
|
|
if ((q = strchr(p, '<')))
|
2015-11-02 23:05:32 +00:00
|
|
|
*q = '\0';
|
2019-11-27 15:21:41 +00:00
|
|
|
e.thumb = strdup(p);
|
2015-11-02 23:05:32 +00:00
|
|
|
unescape(e.thumb);
|
2019-11-27 15:21:41 +00:00
|
|
|
p = q;
|
|
|
|
continue;
|
2015-11-02 23:05:32 +00:00
|
|
|
}
|
2015-03-08 00:16:18 +00:00
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
if (!strncmp(p, "/Episode>", 9))
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
|
|
|
/* End of episode */
|
2015-03-09 20:48:47 +00:00
|
|
|
if (e.id && e.name && e.overview)
|
2015-03-08 00:16:18 +00:00
|
|
|
{
|
2015-03-09 20:48:47 +00:00
|
|
|
epcnt++;
|
2015-03-08 00:16:18 +00:00
|
|
|
sqlite3_bind_int(stmt, 1, e.id);
|
|
|
|
sqlite3_bind_int(stmt, 2, e.series);
|
|
|
|
sqlite3_bind_int(stmt, 3, e.episode);
|
|
|
|
sqlite3_bind_text(stmt, 4, e.name, -1, NULL);
|
|
|
|
sqlite3_bind_text(stmt, 5, e.overview,
|
|
|
|
-1, NULL);
|
2015-11-02 23:05:32 +00:00
|
|
|
sqlite3_bind_text(stmt, 6, e.thumb, -1, NULL);
|
2015-03-08 00:16:18 +00:00
|
|
|
sqlite3_step(stmt);
|
|
|
|
sqlite3_reset(stmt);
|
|
|
|
sqlite3_clear_bindings(stmt);
|
|
|
|
}
|
|
|
|
if (e.name) free(e.name);
|
|
|
|
if (e.overview) free(e.overview);
|
2015-11-02 23:05:32 +00:00
|
|
|
if (e.thumb) free(e.thumb);
|
2015-03-09 20:48:47 +00:00
|
|
|
memset(&e, '\0', sizeof(e));
|
2015-03-08 00:16:18 +00:00
|
|
|
}
|
2019-11-27 15:21:41 +00:00
|
|
|
|
|
|
|
p = strchr(p, '<');
|
2015-03-08 00:16:18 +00:00
|
|
|
}
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
close(fd);
|
|
|
|
free(buf);
|
2015-03-08 00:16:18 +00:00
|
|
|
sqlite3_finalize(stmt);
|
|
|
|
sqlite3_close(db);
|
|
|
|
|
2019-11-27 15:21:41 +00:00
|
|
|
printf("Episodes: %d\n", epcnt);
|
2015-03-09 20:48:47 +00:00
|
|
|
|
2015-03-08 00:16:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|