#include #include #include #include #include #include #include #include #define CACHE "/mod/var/tvdb" #define SERIESDB CACHE "/series.db" #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) struct series { unsigned long id; char *imdb; char *name; char *overview; char *banner; }; struct episode { unsigned long id; char *name; char *overview; char *thumb; unsigned long series; unsigned long episode; }; #define HANDLE(str, c, n) \ do { \ if (!memcmp(p, str, (n + 1))) \ { \ *p = c; \ memmove(p + 1, p + (n + 1), l - (p - txt) - (n + 1)); \ l -= n; \ txt[l] = '\0'; \ } \ } while (0) void unescape(char *txt) { int l = strlen(txt); char *p = txt; for (; p = strchr(p, '&'); p++) { size_t ll; unsigned char icode; if (1 == sscanf( p, "&#%hhu;%n", &icode, &ll) || 1 == sscanf( p, "&#%*[xX]%hhx;%n", &icode, &ll)) { /* &#x;, &#; */ HANDLE(p, (char)icode, ll-1); } else { HANDLE("&", '&', 4); HANDLE(""", '"', 5); HANDLE("'", '\'', 5); HANDLE("<", '<', 3); HANDLE(">", '>', 3); } } for (p = txt; p = memchr(p, '\xe2', l - (p - txt)); p++) { /* curly apostrophe, en dash, curly quotes */ HANDLE("\xe2\x80\x99", '\'', 2); HANDLE("\xe2\x80\x93", '-', 2); HANDLE("\xe2\x80\x9c", '"', 2); HANDLE("\xe2\x80\x9d", '"', 2); } if ((p = strpbrk(txt, "\n\r"))) *p = '\0'; } int main(int argc, char **argv) { char *buf, *p, *q; struct series s; struct episode e; struct stat st; char *error; sqlite3_stmt *stmt; sqlite3 *db; int fd, epcnt = 0; int debug = 0; if (argc < 2) { printf("Syntax: %s [-d] \n", argv[0]); return 0; } 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) { perror("open"); return 0; } if (read(fd, buf, st.st_size) != st.st_size) { perror("read"); return 0; } if (debug) printf("Read - %lu bytes\n", st.st_size); sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); if (sqlite3_open_v2(SERIESDB, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) { printf("Failed to open series database: %s\n", sqlite3_errmsg(db)); return 0; } EXEC("pragma journal_mode = off"); EXEC("pragma synchronous = off"); EXEC( "create table if not exists series(" "[series_id] integer primary key, " "[imdb_id] text, " "[name] text, " "[overview] text, " "[banner] text, " "[dat] text" ")" ); if (sqlite3_prepare_v2(db, "insert or replace into series " "values(?,?,?,?,?,date('now'))", -1, &stmt, NULL) != SQLITE_OK) { fprintf(stderr, "Problem preparing series statement %s", sqlite3_errmsg(db)); exit(1); } p = strchr(buf, '<'); for (;;) { if (!p) break; p++; if (!strncmp(p, "Series>", 7)) { memset(&s, '\0', sizeof(s)); } if (!strncmp(p, "id>", 3)) { p += 3; s.id = strtoul(p, (char **)NULL, 10); } if (!strncmp(p, "IMDB_ID>", 8)) { p += 8; if ((q = strchr(p, '<'))) *q = '\0'; s.imdb = strdup(p); p = q; continue; } if (!strncmp(p, "SeriesName>", 11)) { p += 11; if ((q = strchr(p, '<'))) *q = '\0'; s.name = strdup(p); unescape(s.name); printf("Series: %s\n", s.name); p = q; continue; } if (!strncmp(p, "Overview>", 9)) { p += 9; if ((q = strchr(p, '<'))) *q = '\0'; s.overview = strdup(p); unescape(s.overview); if (debug) printf(" %s\n", s.overview); p = q; continue; } if (!strncmp(p, "banner>", 7)) { p += 7; if ((q = strchr(p, '<'))) *q = '\0'; s.banner = strdup(p); unescape(s.banner); p = q; continue; } if (!strncmp(p, "/Series>", 8)) { 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); sqlite3_bind_text(stmt, 5, s.banner, -1, NULL); sqlite3_step(stmt); } break; } p = strchr(p, '<'); } 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); EXEC("drop table if exists episode.episode"); EXEC( "create table if not exists episode.episode(" "[episode_id] integer primary key, " "[series] integer, " "[episode] integer, " "[name] text, " "[overview] text, " "[thumb] text" ")" ); if (sqlite3_prepare_v2(db, "insert or replace into episode.episode " "values(?,?,?,?,?,?)", -1, &stmt, NULL) != SQLITE_OK) { fprintf(stderr, "Problem preparing statement %s", sqlite3_errmsg(db)); exit(1); } for (;;) { if (!p) break; p++; if (!strncmp(p, "Episode>", 8)) { /* New episode */ memset(&e, '\0', sizeof(e)); } if (!strncmp(p, "id>", 3)) { 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); } if (!strncmp(p, "EpisodeName>", 12)) { p += 12; if ((q = strchr(p, '<'))) *q = '\0'; e.name = strdup(p); unescape(e.name); printf("Episode: %s\n", e.name); p = q; continue; } if (!strncmp(p, "Overview>", 9)) { p += 9; if ((q = strchr(p, '<'))) *q = '\0'; e.overview = strdup(p); unescape(e.overview); if (debug) printf(" %s\n", e.overview); p = q; continue; } if (!strncmp(p, "filename>", 9)) { p += 9; if ((q = strchr(p, '<'))) *q = '\0'; e.thumb = strdup(p); unescape(e.thumb); p = q; continue; } if (!strncmp(p, "/Episode>", 9)) { /* End of episode */ if (e.id && e.name && e.overview) { epcnt++; 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); sqlite3_bind_text(stmt, 6, e.thumb, -1, NULL); sqlite3_step(stmt); sqlite3_reset(stmt); sqlite3_clear_bindings(stmt); } if (e.name) free(e.name); if (e.overview) free(e.overview); if (e.thumb) free(e.thumb); memset(&e, '\0', sizeof(e)); } p = strchr(p, '<'); } close(fd); free(buf); sqlite3_finalize(stmt); sqlite3_close(db); printf("Episodes: %d\n", epcnt); return 0; }