summaryrefslogtreecommitdiff
path: root/update.c
diff options
context:
space:
mode:
Diffstat (limited to 'update.c')
-rw-r--r--update.c1323
1 files changed, 1323 insertions, 0 deletions
diff --git a/update.c b/update.c
new file mode 100644
index 0000000..8c56055
--- /dev/null
+++ b/update.c
@@ -0,0 +1,1323 @@
+#define __STL_CONFIG_H
+#include <locale.h>
+
+#include <vdr/videodir.h>
+#include <vdr/tools.h>
+#include <vdr/plugin.h>
+
+#include "config.h"
+#include "tools.h"
+#include "update.h"
+
+extern cScraper2VdrConfig config;
+
+cUpdate::cUpdate(cScrapManager *manager) : cThread("update thread started") {
+ connection = NULL;
+ vdrDb = NULL;
+ tEvents = NULL;
+ tSeries = NULL;
+ tEpisodes = NULL;
+ tSeriesMedia = NULL;
+ tSeriesActors = NULL;
+ tMovies = NULL;
+ tMovieActor = NULL;
+ tMovieActors = NULL;
+ tMovieMedia = NULL;
+ tRecordings = NULL;
+ scrapManager = manager;
+ imgPathSeries = config.imageDir + "/series";
+ imgPathMovies = config.imageDir + "/movies";
+ lastScrap = 0;
+ forceUpdate = false;
+ forceRecordingUpdate = false;
+ forceVideoDirUpdate = false;
+ forceScrapInfoUpdate = false;
+ forceCleanupRecordingDb = false;
+ char* lang;
+ lang = setlocale(LC_CTYPE, 0);
+ if (lang) {
+ tell(0, "Set locale to '%s'", lang);
+ if ((strcasestr(lang, "UTF-8") != 0) || (strcasestr(lang, "UTF8") != 0)){
+ tell(0, "detected UTF-8");
+ withutf8 = yes;
+ }
+ } else {
+ tell(0, "Reseting locale for LC_CTYPE failed.");
+ }
+ cDbConnection::setEncoding(withutf8 ? "utf8": "latin1");
+ cDbConnection::setHost(config.mysqlHost.c_str());
+ cDbConnection::setPort(config.mysqlPort);
+ cDbConnection::setName(config.mysqlDBName.c_str());
+ cDbConnection::setUser(config.mysqlDBUser.c_str());
+ cDbConnection::setPass(config.mysqlDBPass.c_str());
+ cDbTable::setConfPath(cPlugin::ConfigDirectory("epg2vdr/"));
+}
+
+cUpdate::~cUpdate() {
+ if (loopActive)
+ Stop();
+ if (vdrDb)
+ delete vdrDb;
+ if (tEvents)
+ delete tEvents;
+ if (tSeries)
+ delete tSeries;
+ if (tEpisodes)
+ tEpisodes;
+ if (tSeriesMedia)
+ delete tSeriesMedia;
+ if (tSeriesActors)
+ delete tSeriesActors;
+ if (tMovies)
+ delete tMovies;
+ if (tMovieActor)
+ delete tMovieActor;
+ if (tMovieActors)
+ delete tMovieActors;
+ if (tMovieMedia)
+ delete tMovieMedia;
+ if (tRecordings)
+ delete tRecordings;
+ exitDb();
+}
+
+int cUpdate::initDb() {
+ int status = success;
+ if (!connection)
+ connection = new cDbConnection();
+ if (!connection)
+ return fail;
+ vdrDb = new cTableVdrs(connection);
+ if (vdrDb->open() != success)
+ return fail;
+ tEvents = new cTableEvents(connection);
+ if (tEvents->open() != success)
+ return fail;
+ tSeries = new cTableSeries(connection);
+ if (tSeries->open() != success)
+ return fail;
+ tEpisodes = new cTableSeriesEpisode(connection);
+ if (tEpisodes->open() != success)
+ return fail;
+ tSeriesMedia = new cTableSeriesMedia(connection);
+ if (tSeriesMedia->open() != success)
+ return fail;
+ tSeriesActors = new cTableSeriesActor(connection);
+ if (tSeriesActors->open() != success)
+ return fail;
+ tMovies = new cTableMovies(connection);
+ if (tMovies->open() != success)
+ return fail;
+ tMovieActor = new cTableMovieActor(connection);
+ if (tMovieActor->open() != success)
+ return fail;
+ tMovieActors = new cTableMovieActors(connection);
+ if (tMovieActors->open() != success)
+ return fail;
+ tMovieMedia = new cTableMovieMedia(connection);
+ if (tMovieMedia->open() != success)
+ return fail;
+ tRecordings = new cTableRecordings(connection);
+ if (tRecordings->open() != success)
+ return fail;
+ return status;
+}
+
+int cUpdate::exitDb() {
+ delete connection;
+ connection = 0;
+ return done;
+}
+
+void cUpdate::Stop() {
+ loopActive = false;
+ waitCondition.Broadcast();
+ Cancel(3);
+}
+
+int cUpdate::CheckConnection(int& timeout) {
+ static int retry = 0;
+ timeout = retry < 5 ? 10 : 60;
+ // check connection
+ if (!dbConnected(yes)) {
+ // try to connect
+ tell(0, "Trying to re-connect to database!");
+ retry++;
+ if (initDb() != success) {
+ tell(0, "Retry #%d failed, retrying in %d seconds!", retry, timeout);
+ exitDb();
+ return fail;
+ }
+ retry = 0;
+ tell(0, "Connection established successfull!");
+ }
+ return success;
+}
+
+bool cUpdate::CheckEpgdBusy(void) {
+ vdrDb->clear();
+ vdrDb->setValue(cTableVdrs::fiUuid, EPGDNAME);
+ if (vdrDb->find()) {
+ Es::State epgdState = cEpgdState::toState(vdrDb->getStrValue(cTableVdrs::fiState));
+ if (epgdState >= cEpgdState::esBusy)
+ return true;
+ }
+ return false;
+}
+
+int cUpdate::ReadScrapedEvents(void) {
+ int status = success;
+ cDbStatement *select = new cDbStatement(tEvents);
+ select->build("select ");
+ select->bind(cTableEvents::fiEventId, cDBS::bndOut);
+ select->bind(cTableEvents::fiChannelId, cDBS::bndOut, ", ");
+ select->bind(cTableEvents::fiUseId, cDBS::bndOut, ", ");
+ select->bind(cTableEvents::fiScrSeriesId, cDBS::bndOut, ", ");
+ select->bind(cTableEvents::fiScrSeriesEpisode, cDBS::bndOut, ", ");
+ select->bind(cTableEvents::fiScrMovieId, cDBS::bndOut, ", ");
+ select->bind(cTableEvents::fiScrSp, cDBS::bndOut, ", ");
+ select->build(" from %s where ", tEvents->TableName());
+ select->build(" ((%s is not null and %s > 0) ",
+ tEvents->getField(cTableEvents::fiScrSeriesId)->name,
+ tEvents->getField(cTableEvents::fiScrSeriesId)->name);
+ select->build(" or (%s is not null and %s > 0)) ",
+ tEvents->getField(cTableEvents::fiScrMovieId)->name,
+ tEvents->getField(cTableEvents::fiScrMovieId)->name);
+ if (lastScrap > 0) {
+ select->build(" and %s > %d",
+ tEvents->getField(cTableEvents::fiScrSp)->name,
+ lastScrap);
+ }
+ status += select->prepare();
+ if (status != success) {
+ delete select;
+ return 0;
+ }
+
+ int eventId = 0;
+ int seriesId = 0;
+ int episodeId = 0;
+ int movieId = 0;
+ string channelId = "";
+
+ int numNew = 0;
+ for (int res = select->find(); res; res = select->fetch()) {
+ eventId = tEvents->getIntValue(cTableEvents::fiUseId);
+ channelId = tEvents->getStrValue(cTableEvents::fiChannelId);
+ seriesId = tEvents->getIntValue(cTableEvents::fiScrSeriesId);
+ episodeId = tEvents->getIntValue(cTableEvents::fiScrSeriesEpisode);
+ movieId = tEvents->getIntValue(cTableEvents::fiScrMovieId);
+ scrapManager->AddEvent(eventId, channelId, seriesId, episodeId, movieId);
+ lastScrap = max(lastScrap, (int)tEvents->getIntValue(cTableEvents::fiScrSp));
+ numNew++;
+ }
+ select->freeResult();
+ delete select;
+ return numNew;
+}
+
+//***************************************************************************
+// SERIES
+//***************************************************************************
+
+int cUpdate::ReadSeries(bool isRec) {
+ scrapManager->InitIterator(isRec);
+ int seriesId = 0;
+ int episodeId = 0;
+
+ if (!CreateDirectory(config.imageDir))
+ return 0;
+ if (!CreateDirectory(imgPathSeries))
+ return 0;
+
+ bool isNew = false;
+ int numNew = 0;
+ while (scrapManager->GetNextSeries(isRec, seriesId, episodeId)) {
+ cTVDBSeries *series = scrapManager->GetSeries(seriesId);
+ if (!series) {
+ tSeries->clear();
+ tSeries->setValue(cTableSeries::fiSeriesId, seriesId);
+ int res = tSeries->find();
+ if (res) {
+ series = scrapManager->AddSeries(tSeries);
+ }
+ isNew = true;
+ } else {
+ isNew = false;
+ }
+ if (series) {
+ stringstream sPath;
+ sPath << imgPathSeries << "/" << seriesId;
+ string seriesPath = sPath.str();
+ if (episodeId) {
+ ReadEpisode(episodeId, series, seriesPath);
+ }
+ if (isNew) {
+ ReadSeriesActors(series, seriesPath);
+ LoadSeriesMedia(series, seriesPath);
+ }
+ }
+ numNew++;
+ }
+ return numNew;
+}
+
+void cUpdate::ReadEpisode(int episodeId, cTVDBSeries *series, string path) {
+ int status = success;
+ tEpisodes->clear();
+ tEpisodes->setValue(cTableSeriesEpisode::fiEpisodeId, episodeId);
+ cDbStatement *selectEpisode = new cDbStatement(tEpisodes);
+ selectEpisode->build("select ");
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeName, cDBS::bndOut);
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeNumber, cDBS::bndOut, ", ");
+ selectEpisode->bind(cTableSeriesEpisode::fiSeasonNumber, cDBS::bndOut, ", ");
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeOverview, cDBS::bndOut, ", ");
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeFirstAired, cDBS::bndOut, ", ");
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeGuestStars, cDBS::bndOut, ", ");
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeRating, cDBS::bndOut, ", ");
+ selectEpisode->build(" from %s where ", tEpisodes->TableName());
+ selectEpisode->bind(cTableSeriesEpisode::fiEpisodeId, cDBS::bndIn | cDBS::bndSet);
+ status += selectEpisode->prepare();
+ if (status != success) {
+ delete selectEpisode;
+ return;
+ }
+ int res = selectEpisode->find();
+ if (res) {
+ scrapManager->AddSeriesEpisode(series, tEpisodes);
+ LoadEpisodeImage(series, episodeId, path);
+ int season = tEpisodes->getIntValue(cTableSeriesEpisode::fiSeasonNumber);
+ if (season > 0)
+ LoadSeasonPoster(series, season, path);
+ }
+ selectEpisode->freeResult();
+ delete selectEpisode;
+ return;
+}
+
+void cUpdate::LoadEpisodeImage(cTVDBSeries *series, int episodeId, string path) {
+ int status = success;
+ stringstream iPath;
+ iPath << path << "/" << "episode_" << episodeId << ".jpg";
+ string imgPath = iPath.str();
+ bool imgExists = FileExists(imgPath);
+ if (!CreateDirectory(path))
+ return;
+ tSeriesMedia->clear();
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ cDbStatement *selectImg = new cDbStatement(tSeriesMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableSeriesMedia::fiMediaWidth, cDBS::bndOut);
+ selectImg->bind(cTableSeriesMedia::fiMediaHeight, cDBS::bndOut, ", ");
+ if (!imgExists) {
+ selectImg->bind(cTableSeriesMedia::fiMediaContent, cDBS::bndOut, ", ");
+ selectImg->build(", length(");
+ selectImg->bind(&imageSize, cDBS::bndOut);
+ selectImg->build(")");
+ }
+ selectImg->build(" from %s where ", tSeriesMedia->TableName());
+ selectImg->bind(cTableSeriesMedia::fiSeriesId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->bind(cTableSeriesMedia::fiEpisodeId, cDBS::bndIn | cDBS::bndSet, " and ");
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return;
+ }
+ tSeriesMedia->setValue(cTableSeriesMedia::fiSeriesId, series->id);
+ tSeriesMedia->setValue(cTableSeriesMedia::fiEpisodeId, episodeId);
+ int res = selectImg->find();
+ if (res) {
+ if (!imgExists) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(imgPath.c_str(), "w")) {
+ fwrite(tSeriesMedia->getStrValue(cTableSeriesMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ }
+ int imgWidth = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaWidth);
+ int imgHeight = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaHeight);
+ series->InsertEpisodeImage(episodeId, imgWidth, imgHeight, imgPath);
+ }
+ selectImg->freeResult();
+ delete selectImg;
+}
+
+void cUpdate::LoadSeasonPoster(cTVDBSeries *series, int season, string path) {
+ int status = success;
+ stringstream iPath;
+ iPath << path << "/" << "season_" << season << ".jpg";
+ stringstream tPath;
+ tPath << path << "/" << "season_" << season << "_thumb.jpg";
+ string imgPath = iPath.str();
+ string thumbPath = tPath.str();
+ bool imgExists = FileExists(imgPath);
+ if (!CreateDirectory(path))
+ return;
+ tSeriesMedia->clear();
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ cDbStatement *selectImg = new cDbStatement(tSeriesMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableSeriesMedia::fiMediaWidth, cDBS::bndOut);
+ selectImg->bind(cTableSeriesMedia::fiMediaHeight, cDBS::bndOut, ", ");
+ if (!imgExists) {
+ selectImg->bind(cTableSeriesMedia::fiMediaContent, cDBS::bndOut, ", ");
+ selectImg->build(", length(");
+ selectImg->bind(&imageSize, cDBS::bndOut);
+ selectImg->build(")");
+ }
+ selectImg->build(" from %s where ", tSeriesMedia->TableName());
+ selectImg->bind(cTableSeriesMedia::fiSeriesId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->bind(cTableSeriesMedia::fiSeasonNumber, cDBS::bndIn | cDBS::bndSet, " and ");
+ selectImg->bind(cTableSeriesMedia::fiMediaType, cDBS::bndIn | cDBS::bndSet, " and ");
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return;
+ }
+ tSeriesMedia->setValue(cTableSeriesMedia::fiSeriesId, series->id);
+ tSeriesMedia->setValue(cTableSeriesMedia::fiSeasonNumber, season);
+ tSeriesMedia->setValue(cTableSeriesMedia::fiMediaType, msSeasonPoster);
+ int res = selectImg->find();
+ if (res) {
+ if (!imgExists) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(imgPath.c_str(), "w")) {
+ fwrite(tSeriesMedia->getStrValue(cTableSeriesMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ }
+ int imgWidth = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaWidth);
+ int imgHeight = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaHeight);
+ if (!FileExists(thumbPath)) {
+ CreateThumbnail(imgPath, thumbPath, imgWidth, imgHeight, 2);
+ }
+ series->InsertMedia(msSeasonPoster, imgWidth, imgHeight, imgPath, season);
+ series->InsertMedia(msSeasonPosterThumb, imgWidth/2, imgHeight/2, thumbPath, season);
+ }
+ selectImg->freeResult();
+ delete selectImg;
+}
+
+void cUpdate::ReadSeriesActors(cTVDBSeries *series, string path) {
+ int status = success;
+ tSeriesActors->clear();
+ cDbValue series_id;
+ series_id.setField(tSeriesMedia->getField(cTableSeriesMedia::fiSeriesId));
+ series_id.setValue(series->id);
+ cDbStatement *selectActors = new cDbStatement(tSeriesActors);
+ selectActors->build("select ");
+ selectActors->setBindPrefix("series_actor.");
+ selectActors->bind(cTableSeriesActor::fiActorId, cDBS::bndOut);
+ selectActors->bind(cTableSeriesActor::fiActorName, cDBS::bndOut, ", ");
+ selectActors->bind(cTableSeriesActor::fiActorRole, cDBS::bndOut, ", ");
+ selectActors->clrBindPrefix();
+ selectActors->build(" from %s, %s where ", tSeriesActors->TableName(), tSeriesMedia->TableName());
+ selectActors->build(" %s.%s = %s.%s ", tSeriesActors->TableName(),
+ tSeriesActors->getField(cTableSeriesActor::fiActorId)->name,
+ tSeriesMedia->TableName(),
+ tSeriesMedia->getField(cTableSeriesMedia::fiActorId)->name);
+ selectActors->setBindPrefix("series_media.");
+ selectActors->bind(&series_id, cDBS::bndIn | cDBS::bndSet, " and ");
+ selectActors->build(" order by %s, %s asc", tSeriesActors->getField(cTableSeriesActor::fiSortOrder)->name,
+ tSeriesActors->getField(cTableSeriesActor::fiActorRole)->name);
+ status += selectActors->prepare();
+ if (status != success) {
+ delete selectActors;
+ return;
+ }
+ for (int res = selectActors->find(); res; res = selectActors->fetch()) {
+ scrapManager->AddSeriesActor(series, tSeriesActors);
+ LoadSeriesActorThumb(series, tSeriesActors->getIntValue(cTableSeriesActor::fiActorId), path);
+ }
+ selectActors->freeResult();
+ delete selectActors;
+}
+
+void cUpdate::LoadSeriesActorThumb(cTVDBSeries *series, int actorId, string path) {
+ int status = success;
+ stringstream iPath;
+ iPath << path << "/" << "actor_" << actorId << ".jpg";
+ string imgPath = iPath.str();
+ bool imgExists = FileExists(imgPath);
+ if (!CreateDirectory(path))
+ return;
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ tSeriesMedia->clear();
+ cDbStatement *selectActorThumbs = new cDbStatement(tSeriesMedia);
+ selectActorThumbs->build("select ");
+ selectActorThumbs->bind(cTableSeriesMedia::fiMediaWidth, cDBS::bndOut);
+ selectActorThumbs->bind(cTableSeriesMedia::fiMediaHeight, cDBS::bndOut, ", ");
+ if (!imgExists) {
+ selectActorThumbs->bind(cTableSeriesMedia::fiMediaContent, cDBS::bndOut, ", ");
+ selectActorThumbs->build(", length(");
+ selectActorThumbs->bind(&imageSize, cDBS::bndOut);
+ selectActorThumbs->build(")");
+ }
+ selectActorThumbs->build(" from %s where ", tSeriesMedia->TableName());
+ selectActorThumbs->bind(cTableSeriesMedia::fiActorId, cDBS::bndIn | cDBS::bndSet);
+ status += selectActorThumbs->prepare();
+ if (status != success) {
+ delete selectActorThumbs;
+ return;
+ }
+ tSeriesMedia->setValue(cTableSeriesMedia::fiActorId, actorId);
+ int res = selectActorThumbs->find();
+ if (res) {
+ if (!imgExists) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(imgPath.c_str(), "w")) {
+ fwrite(tSeriesMedia->getStrValue(cTableSeriesMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ }
+ int tmbWidth = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaWidth);
+ int tmbHeight = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaHeight);
+ series->InsertActorThumb(actorId, tmbWidth, tmbHeight, imgPath);
+ }
+ selectActorThumbs->freeResult();
+ delete selectActorThumbs;
+}
+
+void cUpdate::LoadSeriesMedia(cTVDBSeries *series, string path) {
+ int status = success;
+ tSeriesMedia->clear();
+ cDbStatement *selectImg = new cDbStatement(tSeriesMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableSeriesMedia::fiMediaWidth, cDBS::bndOut);
+ selectImg->bind(cTableSeriesMedia::fiMediaHeight, cDBS::bndOut, ", ");
+ selectImg->bind(cTableSeriesMedia::fiMediaType, cDBS::bndOut, ", ");
+ selectImg->build(" from %s where ", tSeriesMedia->TableName());
+ selectImg->bind(cTableSeriesMedia::fiSeriesId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->build(" and %s in (%d, %d, %d, %d, %d, %d, %d, %d, %d)",
+ tSeriesMedia->getField(cTableSeriesMedia::fiMediaType)->name,
+ msPoster1, msPoster2, msPoster3,
+ msFanart1, msFanart2, msFanart3,
+ msBanner1, msBanner2, msBanner3);
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return;
+ }
+ tSeriesMedia->setValue(cTableSeriesMedia::fiSeriesId, series->id);
+ for (int res = selectImg->find(); res; res = selectImg->fetch()) {
+ int mediaType = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaType);
+ int mediaWidth = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaWidth);
+ int mediaHeight = tSeriesMedia->getIntValue(cTableSeriesMedia::fiMediaHeight);
+ string mediaPath = LoadMediaSeries(series->id, mediaType, path, mediaWidth, mediaHeight);
+ series->InsertMedia(mediaType, mediaWidth, mediaHeight, mediaPath);
+ if (mediaType == msPoster1) {
+ string thumbPath = path + "/poster_thumb.jpg";
+ series->InsertMedia(msPosterThumb, mediaWidth/5, mediaHeight/5, thumbPath);
+ }
+ }
+ selectImg->freeResult();
+ delete selectImg;
+}
+
+string cUpdate::LoadMediaSeries(int seriesId, int mediaType, string path, int width, int height) {
+ int status = success;
+ stringstream iPath;
+ iPath << path << "/";
+ bool createThumb = false;
+ stringstream tPath;
+ tPath << path << "/";
+ switch (mediaType) {
+ case msPoster1:
+ iPath << "poster1.jpg";
+ createThumb = true;
+ tPath << "poster_thumb.jpg";
+ break;
+ case msPoster2:
+ iPath << "poster2.jpg";
+ break;
+ case msPoster3:
+ iPath << "poster3.jpg";
+ break;
+ case msFanart1:
+ iPath << "fanart1.jpg";
+ break;
+ case msFanart2:
+ iPath << "fanart2.jpg";
+ break;
+ case msFanart3:
+ iPath << "fanart3.jpg";
+ break;
+ case msBanner1:
+ iPath << "banner1.jpg";
+ break;
+ case msBanner2:
+ iPath << "banner2.jpg";
+ break;
+ case msBanner3:
+ iPath << "banner3.jpg";
+ break;
+ default:
+ break;
+ }
+ string imgPath = iPath.str();
+ string thumbPath = tPath.str();
+ if (FileExists(imgPath)) {
+ if (createThumb && !FileExists(thumbPath)) {
+ CreateThumbnail(imgPath, thumbPath, width, height, 5);
+ }
+ return imgPath;
+ }
+ if (!CreateDirectory(path))
+ return "";
+ tSeriesMedia->clear();
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ cDbStatement *selectImg = new cDbStatement(tSeriesMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableSeriesMedia::fiMediaContent, cDBS::bndOut);
+ selectImg->build(", length(");
+ selectImg->bind(&imageSize, cDBS::bndOut);
+ selectImg->build(")");
+ selectImg->build(" from %s where ", tSeriesMedia->TableName());
+ selectImg->bind(cTableSeriesMedia::fiSeriesId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->bind(cTableSeriesMedia::fiMediaType, cDBS::bndIn | cDBS::bndSet, " and ");
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return "";
+ }
+ tSeriesMedia->setValue(cTableSeriesMedia::fiSeriesId, seriesId);
+ tSeriesMedia->setValue(cTableSeriesMedia::fiMediaType, mediaType);
+ int res = selectImg->find();
+ if (res) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(imgPath.c_str(), "w")) {
+ fwrite(tSeriesMedia->getStrValue(cTableSeriesMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ if (createThumb && !FileExists(thumbPath)) {
+ CreateThumbnail(imgPath, thumbPath, width, height, 5);
+ }
+ }
+ selectImg->freeResult();
+ delete selectImg;
+ return imgPath;
+}
+
+//***************************************************************************
+// MOVIES
+//***************************************************************************
+
+int cUpdate::ReadMovies(bool isRec) {
+ scrapManager->InitIterator(isRec);
+ int movieId = 0;
+ int i=0;
+
+ if (!CreateDirectory(config.imageDir))
+ return 0;
+ if (!CreateDirectory(imgPathMovies))
+ return 0;
+
+ int numNew = 0;
+ while (scrapManager->GetNextMovie(isRec, movieId)) {
+ cMovieDbMovie *movie = scrapManager->GetMovie(movieId);
+ if (movie)
+ continue;
+ tMovies->clear();
+ tMovies->setValue(cTableMovies::fiMovieId, movieId);
+ int res = tMovies->find();
+ if (!res)
+ continue;
+ movie = scrapManager->AddMovie(tMovies);
+ stringstream mPath;
+ mPath << imgPathMovies << "/" << movieId;
+ string moviePath = mPath.str();
+ ReadMovieActors(movie);
+ LoadMovieActorThumbs(movie);
+ LoadMovieMedia(movie, moviePath);
+ numNew++;
+ }
+ return numNew;
+}
+
+void cUpdate::ReadMovieActors(cMovieDbMovie *movie) {
+ int status = success;
+ cDbValue actorRole;
+ cDbValue actorMovie;
+ cDbValue thbWidth;
+ cDbValue thbHeight;
+ actorRole.setField(tMovieActors->getField(cTableMovieActors::fiRole));
+ actorMovie.setField(tMovieActors->getField(cTableMovieActors::fiMovieId));
+ thbWidth.setField(tMovieMedia->getField(cTableMovieMedia::fiMediaWidth));
+ thbHeight.setField(tMovieMedia->getField(cTableMovieMedia::fiMediaHeight));
+ cDbStatement *selectActors = new cDbStatement(tMovieActor);
+ selectActors->build("select ");
+ selectActors->setBindPrefix("act.");
+ selectActors->bind(cTableMovieActor::fiActorId, cDBS::bndOut);
+ selectActors->bind(cTableMovieActor::fiActorName, cDBS::bndOut, ", ");
+ selectActors->setBindPrefix("role.");
+ selectActors->bind(&actorRole, cDBS::bndOut, ", ");
+ selectActors->setBindPrefix("thumb.");
+ selectActors->bind(&thbWidth, cDBS::bndOut, ", ");
+ selectActors->bind(&thbHeight, cDBS::bndOut, ", ");
+ selectActors->clrBindPrefix();
+ selectActors->build(" from %s act, %s role, %s thumb where ",
+ tMovieActor->TableName(), tMovieActors->TableName(), tMovieMedia->TableName());
+ selectActors->build("act.%s = role.%s ",
+ tMovieActor->getField(cTableMovieActor::fiActorId)->name,
+ tMovieActors->getField(cTableMovieActors::fiActorId)->name);
+ selectActors->build(" and role.%s = thumb.%s ",
+ tMovieActors->getField(cTableMovieActors::fiActorId)->name,
+ tMovieMedia->getField(cTableMovieMedia::fiActorId)->name);
+ selectActors->setBindPrefix("role.");
+ selectActors->bind(&actorMovie, cDBS::bndIn | cDBS::bndSet, " and ");
+ status += selectActors->prepare();
+ if (status != success) {
+ delete selectActors;
+ return;
+ }
+ actorMovie.setValue(movie->id);
+ for (int res = selectActors->find(); res; res = selectActors->fetch()) {
+ scrapManager->AddMovieActor(movie, tMovieActor, actorRole.getStrValue());
+ int tmbWidth = thbWidth.getIntValue();
+ int tmbHeight = thbHeight.getIntValue();
+ movie->SetActorThumbSize(tMovieActor->getIntValue(cTableMovieActor::fiActorId), tmbWidth, tmbHeight);
+ }
+ selectActors->freeResult();
+ delete selectActors;
+}
+
+void cUpdate::LoadMovieActorThumbs(cMovieDbMovie *movie) {
+ int status = success;
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ tMovieMedia->clear();
+ cDbStatement *selectActorThumbs = new cDbStatement(tMovieMedia);
+ selectActorThumbs->build("select ");
+ selectActorThumbs->bind(cTableMovieMedia::fiMediaContent, cDBS::bndOut);
+ selectActorThumbs->build(", length(");
+ selectActorThumbs->bind(&imageSize, cDBS::bndOut);
+ selectActorThumbs->build(")");
+ selectActorThumbs->build(" from %s where ", tMovieMedia->TableName());
+ selectActorThumbs->bind(cTableMovieMedia::fiActorId, cDBS::bndIn | cDBS::bndSet);
+ status += selectActorThumbs->prepare();
+ if (status != success) {
+ delete selectActorThumbs;
+ return;
+ }
+ string movieActorsPath = imgPathMovies + "/actors";
+ if (!CreateDirectory(movieActorsPath))
+ return;
+
+ vector<int> IDs = movie->GetActorIDs();
+ for (vector<int>::iterator it = IDs.begin(); it != IDs.end(); it++) {
+ int actorId = (int)*it;
+ stringstream tName;
+ tName << "actor_" << actorId << ".jpg";
+ string thumbName = tName.str();
+ string thumbFullPath = movieActorsPath + "/" + thumbName;
+ if (!FileExists(thumbFullPath)) {
+ tMovieMedia->setValue(cTableMovieMedia::fiActorId, actorId);
+ int res = selectActorThumbs->find();
+ if (res) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(thumbFullPath.c_str(), "w")) {
+ fwrite(tMovieMedia->getStrValue(cTableMovieMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ movie->SetActorPath(actorId, thumbFullPath);
+ }
+ } else {
+ movie->SetActorPath(actorId, thumbFullPath);
+ }
+ }
+ selectActorThumbs->freeResult();
+ delete selectActorThumbs;
+}
+
+void cUpdate::LoadMovieMedia(cMovieDbMovie *movie, string moviePath) {
+ int status = success;
+ tMovieMedia->clear();
+ cDbStatement *selectImg = new cDbStatement(tMovieMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableMovieMedia::fiMediaWidth, cDBS::bndOut);
+ selectImg->bind(cTableMovieMedia::fiMediaHeight, cDBS::bndOut, ", ");
+ selectImg->bind(cTableMovieMedia::fiMediaType, cDBS::bndOut, ", ");
+ selectImg->build(" from %s where ", tMovieMedia->TableName());
+ selectImg->bind(cTableMovieMedia::fiMovieId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->build(" and %s in (%d, %d, %d, %d)",
+ tMovieMedia->getField(cTableMovieMedia::fiMediaType)->name,
+ mmPoster,
+ mmFanart,
+ mmCollectionPoster,
+ mmCollectionFanart);
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return;
+ }
+ tMovieMedia->setValue(cTableMovieMedia::fiMovieId, movie->id);
+ for (int res = selectImg->find(); res; res = selectImg->fetch()) {
+ int mediaType = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaType);
+ int mediaWidth = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaWidth);
+ int mediaHeight = tMovieMedia->getIntValue(cTableMovieMedia::fiMediaHeight);
+ string imgPath = LoadMediaMovie(movie->id, mediaType, moviePath, mediaWidth, mediaHeight);
+ if (imgPath.size() > 0)
+ scrapManager->AddMovieMedia(movie, tMovieMedia, imgPath);
+ if (mediaType == mmPoster) {
+ cMovieMedia *m = new cMovieMedia();
+ m->mediaType = mmPosterThumb;
+ m->width = mediaWidth/4;
+ m->height = mediaHeight/4;
+ m->path = moviePath + "/poster_thumb.jpg";
+ movie->InsertMedia(m);
+ }
+
+ }
+ selectImg->freeResult();
+ delete selectImg;
+}
+
+string cUpdate::LoadMediaMovie(int movieId, int mediaType, string path, int width, int height) {
+ int status = success;
+ stringstream iPath;
+ iPath << path << "/";
+ bool createThumb = false;
+ stringstream tPath;
+ tPath << path << "/";
+ switch (mediaType) {
+ case mmPoster:
+ iPath << "poster.jpg";
+ createThumb = true;
+ tPath << "poster_thumb.jpg";
+ break;
+ case mmFanart:
+ iPath << "fanart.jpg";
+ break;
+ case mmCollectionPoster:
+ iPath << "collectionPoster.jpg";
+ break;
+ case mmCollectionFanart:
+ iPath << "collectionFanart.jpg";
+ break;
+ default:
+ break;
+ }
+ string imgPath = iPath.str();
+ string thumbPath = tPath.str();
+ if (FileExists(imgPath)) {
+ if (createThumb && !FileExists(thumbPath)) {
+ CreateThumbnail(imgPath, thumbPath, width, height, 4);
+ }
+ return imgPath;
+ }
+ if (!CreateDirectory(path))
+ return imgPath;
+ tMovieMedia->clear();
+ cDbValue imageSize;
+ cDBS::FieldDef imageSizeDef = { "media_content", cDBS::ffUInt, 0, 999, cDBS::ftData };
+ imageSize.setField(&imageSizeDef);
+ cDbStatement *selectImg = new cDbStatement(tMovieMedia);
+ selectImg->build("select ");
+ selectImg->bind(cTableMovieMedia::fiMediaContent, cDBS::bndOut);
+ selectImg->build(", length(");
+ selectImg->bind(&imageSize, cDBS::bndOut);
+ selectImg->build(")");
+ selectImg->build(" from %s where ", tMovieMedia->TableName());
+ selectImg->bind(cTableMovieMedia::fiMovieId, cDBS::bndIn | cDBS::bndSet);
+ selectImg->bind(cTableMovieMedia::fiMediaType, cDBS::bndIn | cDBS::bndSet, " and ");
+ status += selectImg->prepare();
+ if (status != success) {
+ delete selectImg;
+ return "";
+ }
+ tMovieMedia->setValue(cTableMovieMedia::fiMovieId, movieId);
+ tMovieMedia->setValue(cTableMovieMedia::fiMediaType, mediaType);
+ int res = selectImg->find();
+ if (res) {
+ int size = imageSize.getIntValue();
+ if (FILE* fh = fopen(imgPath.c_str(), "w")) {
+ fwrite(tMovieMedia->getStrValue(cTableMovieMedia::fiMediaContent), 1, size, fh);
+ fclose(fh);
+ }
+ if (createThumb && !FileExists(thumbPath)) {
+ CreateThumbnail(imgPath, thumbPath, width, height, 4);
+ }
+ }
+ selectImg->freeResult();
+ delete selectImg;
+ return imgPath;
+}
+
+//***************************************************************************
+// RECORDINGS
+//***************************************************************************
+int cUpdate::ReadRecordings(void) {
+ int status = success;
+ cDbStatement *select = new cDbStatement(tRecordings);
+ select->build("select ");
+ select->bind(cTableRecordings::fiRecPath, cDBS::bndOut);
+ select->bind(cTableRecordings::fiRecStart, cDBS::bndOut, ", ");
+ select->bind(cTableRecordings::fiMovieId, cDBS::bndOut, ", ");
+ select->bind(cTableRecordings::fiSeriesId, cDBS::bndOut, ", ");
+ select->bind(cTableRecordings::fiEpisodeId, cDBS::bndOut, ", ");
+ select->build(" from %s where ", tRecordings->TableName());
+ select->bind(cTableRecordings::fiUuid, cDBS::bndIn | cDBS::bndSet);
+ select->build(" and %s = 0", tRecordings->getField(cTableRecordings::fiScrapNew)->name);
+
+ status += select->prepare();
+ if (status != success) {
+ delete select;
+ return 0;
+ }
+
+ tRecordings->clear();
+ tRecordings->setValue(cTableRecordings::fiUuid, config.uuid.c_str());
+ int numRecs = 0;
+ for (int res = select->find(); res; res = select->fetch()) {
+ int recStart = tRecordings->getIntValue(cTableRecordings::fiRecStart);
+ string recPath = tRecordings->getStrValue(cTableRecordings::fiRecPath);
+ int movieId = tRecordings->getIntValue(cTableRecordings::fiMovieId);
+ int seriesId = tRecordings->getIntValue(cTableRecordings::fiSeriesId);
+ int episodeId = tRecordings->getIntValue(cTableRecordings::fiEpisodeId);
+ bool isNew = scrapManager->AddRecording(recStart, recPath, seriesId, episodeId, movieId);
+ if (isNew)
+ numRecs++;
+ }
+ select->freeResult();
+ delete select;
+ return numRecs;
+}
+
+int cUpdate::ScanVideoDir(void) {
+ int newRecs = 0;
+ for (cRecording *rec = Recordings.First(); rec; rec = Recordings.Next(rec)) {
+ string recPath = rec->FileName();
+ int recStart = rec->Start();
+ if (!scrapManager->RecordingExists(recStart, recPath)) {
+ newRecs++;
+ int scrapInfoMovieID = 0;
+ int scrapInfoSeriesID = 0;
+ int scrapInfoEpisodeID = 0;
+ ReadScrapInfo(rec->FileName(), scrapInfoMovieID, scrapInfoSeriesID, scrapInfoEpisodeID);
+ int eventId = 0;
+ string channelId = "";
+ string title = *(rec->BaseName());
+ string subTitle = "";
+ const cRecordingInfo *recInfo = rec->Info();
+ if (recInfo) {
+ const cEvent *recEvent = recInfo->GetEvent();
+ if (recEvent) {
+ eventId = recEvent->EventID();
+ channelId = *(recInfo->ChannelID().ToString());
+ subTitle = (recInfo->ShortText())?(recInfo->ShortText()):"";
+ }
+ }
+ tRecordings->clear();
+ tRecordings->setValue(cTableRecordings::fiUuid, config.uuid.c_str());
+ tRecordings->setValue(cTableRecordings::fiRecPath, recPath.c_str());
+ tRecordings->setValue(cTableRecordings::fiRecStart, recStart);
+
+ tRecordings->setValue(cTableRecordings::fiEventId, eventId);
+ tRecordings->setValue(cTableRecordings::fiChannelId, channelId.c_str());
+ tRecordings->setValue(cTableRecordings::fiScrapInfoMovieId, scrapInfoMovieID);
+ tRecordings->setValue(cTableRecordings::fiScrapInfoSeriesId, scrapInfoSeriesID);
+ tRecordings->setValue(cTableRecordings::fiScrapInfoEpisodeId, scrapInfoEpisodeID);
+ tRecordings->setValue(cTableRecordings::fiScrapNew, 1);
+ tRecordings->setValue(cTableRecordings::fiRecTitle, title.c_str());
+ tRecordings->setValue(cTableRecordings::fiRecSubTitle, subTitle.c_str());
+ tRecordings->setValue(cTableRecordings::fiRecDuration, rec->LengthInSeconds()/60);
+ tRecordings->store();
+ }
+ }
+ return newRecs;
+}
+
+int cUpdate::ScanVideoDirScrapInfo(void) {
+ int numUpdated = 0;
+ for (cRecording *rec = Recordings.First(); rec; rec = Recordings.Next(rec)) {
+ int recStart = rec->Start();
+ string recPath = rec->FileName();
+ bool recExists = LoadRecording(recStart, recPath);
+ int scrapInfoMovieID = 0;
+ int scrapInfoSeriesID = 0;
+ int scrapInfoEpisodeID = 0;
+ ReadScrapInfo(rec->FileName(), scrapInfoMovieID, scrapInfoSeriesID, scrapInfoEpisodeID);
+ if (ScrapInfoChanged(scrapInfoMovieID, scrapInfoSeriesID, scrapInfoEpisodeID)) {
+ tRecordings->setValue(cTableRecordings::fiScrapNew, 1);
+ tRecordings->setValue(cTableRecordings::fiScrapInfoMovieId, scrapInfoMovieID);
+ tRecordings->setValue(cTableRecordings::fiScrapInfoSeriesId, scrapInfoSeriesID);
+ tRecordings->setValue(cTableRecordings::fiScrapInfoEpisodeId, scrapInfoEpisodeID);
+ tRecordings->update();
+ numUpdated++;
+ }
+ }
+ return numUpdated;
+}
+
+bool cUpdate::LoadRecording(int recStart, string recPath) {
+ tRecordings->clear();
+ tRecordings->setValue(cTableRecordings::fiUuid, config.uuid.c_str());
+ tRecordings->setValue(cTableRecordings::fiRecStart, recStart);
+ tRecordings->setValue(cTableRecordings::fiRecPath, recPath.c_str());
+ int found = tRecordings->find();
+ if (found == yes) {
+ return true;
+ }
+ return false;
+}
+
+bool cUpdate::ScrapInfoChanged(int scrapInfoMovieID, int scrapInfoSeriesID, int scrapInfoEpisodeID) {
+ int movieIdCurrent = tRecordings->getIntValue(cTableRecordings::fiScrapInfoMovieId);
+ int seriesIdCurrent = tRecordings->getIntValue(cTableRecordings::fiScrapInfoSeriesId);
+ int episodeIdCurrent = tRecordings->getIntValue(cTableRecordings::fiScrapInfoEpisodeId);
+ if ((movieIdCurrent != scrapInfoMovieID) ||
+ (seriesIdCurrent != scrapInfoSeriesID) ||
+ (episodeIdCurrent != scrapInfoEpisodeID))
+ return true;
+ return false;
+}
+
+void cUpdate::ReadScrapInfo(string recDir, int &scrapInfoMovieID, int &scrapInfoSeriesID, int &scrapInfoEpisodeID) {
+ stringstream sInfoName;
+ sInfoName << recDir << "/" << config.recScrapInfoName;
+ string scrapInfoName = sInfoName.str();
+ if (!FileExists(scrapInfoName, false)) {
+ string twoHigher = TwoFoldersHigher(recDir);
+ if (twoHigher.size() > 0) {
+ stringstream sInfoNameAlt;
+ sInfoNameAlt << twoHigher << "/" << config.recScrapInfoName;
+ scrapInfoName = sInfoNameAlt.str();
+ if (!FileExists(scrapInfoName, false)) {
+ return;
+ }
+ } else
+ return;
+ }
+ vector<string> scrapInfoLines;
+ FILE *f = fopen(scrapInfoName.c_str(), "r");
+ if (!f)
+ return;
+ cReadLine ReadLine;
+ char *line;
+ while ((line = ReadLine.Read(f)) != NULL) {
+ scrapInfoLines.push_back(line);
+ }
+ fclose(f);
+ int numLines = scrapInfoLines.size();
+ if (numLines < 2) {
+ tell(0, "invalid scrapinfo file in %s", recDir.c_str());
+ return;
+ }
+ for (int line=0; line < numLines; line++) {
+ scrapInfoLines[line] = trim(scrapInfoLines[line]);
+ toLower(scrapInfoLines[line]);
+ }
+ bool isMovie = false;
+ bool isSeries = false;
+ if (!scrapInfoLines[0].compare("movie"))
+ isMovie = true;
+ else if (!scrapInfoLines[0].compare("series"))
+ isSeries = true;
+ else
+ tell(0, "invalid scrapinfo file in %s", recDir.c_str());
+
+ int id1 = 0, id2 = 0;
+ string key = "", value = "";
+
+ splitstring s(scrapInfoLines[1].c_str());
+ vector<string> flds = s.split('=');
+ if (flds.size() == 2) {
+ key = trim(flds[0]);
+ value = trim(flds[1]);
+ } else {
+ tell(0, "invalid scrapinfo file in %s", recDir.c_str());
+ }
+ if (!key.compare("id")) {
+ id1 = atoi(value.c_str());
+ if (numLines > 2) {
+ splitstring s2(scrapInfoLines[2].c_str());
+ vector<string> flds2 = s2.split('=');
+ if (flds2.size() == 2) {
+ key = trim(flds2[0]);
+ toLower(key);
+ value = trim(flds2[1]);
+ }
+ if (!key.compare("episode")) {
+ id2 = atoi(value.c_str());
+ }
+ }
+ } else
+ tell(0, "invalid scrapinfo file in %s", recDir.c_str());
+ if (isSeries) {
+ scrapInfoSeriesID = id1;
+ scrapInfoEpisodeID = id2;
+ }
+ if (isMovie) {
+ scrapInfoMovieID = id1;
+ }
+}
+
+//***************************************************************************
+// Cleanup
+//***************************************************************************
+
+int cUpdate::CleanupSeries(void) {
+ //read existing series in file system
+ vector<string> storedSeries;
+ DIR *dir;
+ struct dirent *entry;
+ if ((dir = opendir (imgPathSeries.c_str())) != NULL) {
+ while ((entry = readdir (dir)) != NULL) {
+ string dirName = entry->d_name;
+ if (isNumber(dirName)) {
+ storedSeries.push_back(dirName);
+ }
+ }
+ closedir (dir);
+ } else return 0;
+ int deletedSeries = 0;
+ //aviod complete delete if no series are available
+ int numSeriesAvailable = scrapManager->GetNumSeries();
+ if (numSeriesAvailable == 0)
+ return 0;
+ for (vector<string>::iterator seriesDir = storedSeries.begin(); seriesDir != storedSeries.end(); seriesDir++) {
+ int seriesId = atoi(((string)*seriesDir).c_str());
+ if (!scrapManager->SeriesInUse(seriesId)) {
+ string delDir = imgPathSeries + "/" + ((string)*seriesDir);
+ DeleteDirectory(delDir);
+ deletedSeries++;
+ }
+ }
+ return deletedSeries;
+}
+
+int cUpdate::CleanupMovies(void) {
+ //read existing movies in file system
+ vector<string> storedMovies;
+ DIR *dir;
+ struct dirent *entry;
+ if ((dir = opendir (imgPathMovies.c_str())) != NULL) {
+ while ((entry = readdir (dir)) != NULL) {
+ string dirName = entry->d_name;
+ if (isNumber(dirName)) {
+ storedMovies.push_back(dirName);
+ }
+ }
+ closedir (dir);
+ } else return 0;
+ int deletedMovies = 0;
+ //aviod complete delete if no movies are available
+ int numMoviesAvailable = scrapManager->GetNumMovies();
+ if (numMoviesAvailable == 0)
+ return 0;
+ for (vector<string>::iterator movieDir = storedMovies.begin(); movieDir != storedMovies.end(); movieDir++) {
+ int movieId = atoi(((string)*movieDir).c_str());
+ if (!scrapManager->MovieInUse(movieId)) {
+ string delDir = imgPathMovies + "/" + ((string)*movieDir);
+ DeleteDirectory(delDir);
+ deletedMovies++;
+ }
+ }
+ return deletedMovies;
+}
+
+int cUpdate::CleanupRecordings(void) {
+ //delete all not anymore existing recordings in database
+ int status = success;
+ cDbStatement *select = new cDbStatement(tRecordings);
+ select->build("select ");
+ select->bind(cTableRecordings::fiRecPath, cDBS::bndOut);
+ select->bind(cTableRecordings::fiRecStart, cDBS::bndOut, ", ");
+ select->build(" from %s where ", tRecordings->TableName());
+ select->bind(cTableRecordings::fiUuid, cDBS::bndIn | cDBS::bndSet);
+
+ status += select->prepare();
+ if (status != success) {
+ delete select;
+ return 0;
+ }
+
+ tRecordings->clear();
+ tRecordings->setValue(cTableRecordings::fiUuid, config.uuid.c_str());
+ int numRecsDeleted = 0;
+ for (int res = select->find(); res; res = select->fetch()) {
+ int recStart = tRecordings->getIntValue(cTableRecordings::fiRecStart);
+ string recPath = tRecordings->getStrValue(cTableRecordings::fiRecPath);
+ if (!Recordings.GetByName(recPath.c_str())) {
+ stringstream delWhere;
+ delWhere << "uuid = '" << config.uuid << "' and rec_path = '" << recPath << "' and rec_start = " << recStart;
+ tRecordings->deleteWhere(delWhere.str().c_str());
+ numRecsDeleted++;
+ }
+ }
+ select->freeResult();
+ delete select;
+ return numRecsDeleted;
+}
+
+
+//***************************************************************************
+// Action
+//***************************************************************************
+
+void cUpdate::Action() {
+ tell(0, "Update thread started (pid=%d)", getpid());
+ mutex.Lock();
+ loopActive = yes;
+ int sleep = 10;
+ int scanFreq = 60;
+ int scanNewRecFreq = 60 * 5;
+ int scanNewRecDBFreq = 60 * 5;
+ int cleanUpFreq = 60 * 5;
+ forceUpdate = true;
+ forceRecordingUpdate = true;
+ time_t lastScan = time(0);
+ time_t lastScanNewRec = time(0);
+ time_t lastScanNewRecDB = time(0);
+ time_t lastCleanup = time(0);
+ bool init = true;
+ while (loopActive && Running()) {
+ int reconnectTimeout; //set by checkConnection
+ if (CheckConnection(reconnectTimeout) != success) {
+ waitCondition.TimedWait(mutex, reconnectTimeout*1000);
+ continue;
+ }
+ //Update Events
+ if (!config.headless && (forceUpdate || (time(0) - lastScan > scanFreq))) {
+ if (!init && CheckEpgdBusy())
+ continue;
+ int numNewEvents = ReadScrapedEvents();
+ if (numNewEvents > 0) {
+ tell(0, "Loaded %d new scraped Events from Database", numNewEvents);
+ } else {
+ lastScan = time(0);
+ forceUpdate = false;
+ init = false;
+ continue;
+ }
+ tell(0, "Loading new Series and Episodes from Database...");
+ time_t now = time(0);
+ int numNewSeries = ReadSeries(false);
+ int dur = time(0) - now;
+ tell(0, "Loaded %d new Series and Episodes in %ds from Database", numNewSeries, dur);
+ //scrapManager->DumpSeries(5);
+
+ tell(0, "Loading new Movies from Database...");
+ now = time(0);
+ int numNewMovies = ReadMovies(false);
+ dur = time(0) - now;
+ tell(0, "Loaded %d new Movies in %ds from Database", numNewMovies, dur);
+ //scrapManager->DumpMovies(5);
+ lastScan = time(0);
+ forceUpdate = false;
+ }
+ //Update Recordings from Database
+ if (forceRecordingUpdate || (time(0) - lastScanNewRecDB > scanNewRecDBFreq)) {
+ if (!init && CheckEpgdBusy())
+ continue;
+ int numNewRecs = ReadRecordings();
+ if (numNewRecs > 0) {
+ int numSeries = ReadSeries(true);
+ int numMovies = ReadMovies(true);
+ tell(0, "Loaded %d new Recordings from Database, %d series, %d movies", numNewRecs, numSeries, numMovies);
+ }
+ forceRecordingUpdate = false;
+ }
+
+ //Scan new recordings
+ if (init || forceVideoDirUpdate || (time(0) - lastScanNewRec > scanNewRecFreq)) {
+ if (CheckEpgdBusy()) {
+ waitCondition.TimedWait(mutex, 1000);
+ continue;
+ }
+ static int recState = 0;
+ if (Recordings.StateChanged(recState)) {
+ tell(0, "Searching for new recordings because of Recordings State Change...");
+ int newRecs = ScanVideoDir();
+ tell(0, "found %d new recordings", newRecs);
+ }
+ lastScanNewRec = time(0);
+ forceVideoDirUpdate = false;
+ }
+
+ init = false;
+
+ //Scan Video dir for scrapinfo files
+ if (forceScrapInfoUpdate) {
+ if (CheckEpgdBusy()) {
+ tell(0, "epgd busy, try again in 1s...");
+ waitCondition.TimedWait(mutex, 1000);
+ continue;
+ }
+ tell(0, "Checking for new or updated scrapinfo files in recordings...");
+ int numUpdated = ScanVideoDirScrapInfo();
+ tell(0, "found %d new or updated scrapinfo files", numUpdated);
+ forceScrapInfoUpdate = false;
+ }
+
+ //Cleanup
+ if (time(0) - lastCleanup > cleanUpFreq) {
+ if (CheckEpgdBusy()) {
+ waitCondition.TimedWait(mutex, 1000);
+ continue;
+ }
+ int seriesDeleted = CleanupSeries();
+ int moviesDeleted = CleanupMovies();
+ if (seriesDeleted > 0 || moviesDeleted > 0) {
+ tell(0, "Deleted %d outdated series image folders", seriesDeleted);
+ tell(0, "Deleted %d outdated movie image folders", moviesDeleted);
+ }
+ lastCleanup = time(0);
+ }
+
+ //Cleanup Recording DB
+ if (forceCleanupRecordingDb) {
+ if (CheckEpgdBusy()) {
+ waitCondition.TimedWait(mutex, 1000);
+ continue;
+ }
+ tell(0, "Cleaning up recordings in database...");
+ int recsDeleted = CleanupRecordings();
+ tell(0, "Deleted %d not anymore existing recordings in database", recsDeleted);
+ forceCleanupRecordingDb = false;
+ }
+
+ waitCondition.TimedWait(mutex, sleep*1000);
+
+ }
+
+ loopActive = no;
+ tell(0, "Update thread ended (pid=%d)", getpid());
+}
+
+//***************************************************************************
+// External trigggering of Actions
+//***************************************************************************
+void cUpdate::ForceUpdate(void) {
+ tell(0, "full update from database forced");
+ forceUpdate = true;
+}
+
+void cUpdate::ForceRecordingUpdate(void) {
+ tell(0, "scanning of recordings in database triggered");
+ forceRecordingUpdate = true;
+}
+
+void cUpdate::ForceVideoDirUpdate(void) {
+ tell(0, "scanning for new recordings in video directory triggered");
+ forceVideoDirUpdate = true;
+}
+
+void cUpdate::ForceScrapInfoUpdate(void) {
+ tell(0, "scanning of recording scrapinfo files triggered");
+ forceScrapInfoUpdate = true;
+}
+
+void cUpdate::TriggerCleanRecordingsDB(void) {
+ tell(0, "cleanup of recording DB triggered");
+ forceCleanupRecordingDb = true;
+}