vdr-plugin-scraper2vdr/update.c

1318 lines
49 KiB
C

#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(1);
while (Active())
cCondWait::SleepMs(10);
}
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);
}
select->build(" order by %s", tEvents->getField(cTableEvents::fiInsSp)->name);
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() && Running()) {
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) && Running()) {
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);
int res = tEpisodes->find();
if (res) {
scrapManager->AddSeriesEpisode(series, tEpisodes);
LoadEpisodeImage(series, episodeId, path);
int season = tEpisodes->getIntValue(cTableSeriesEpisode::fiSeasonNumber);
if (season > 0)
LoadSeasonPoster(series, season, path);
}
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 (!imgExists)
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 (!imgExists)
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 (!imgExists)
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) && Running()) {
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 = getRecPath(rec);
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->Name();
//remove directory
size_t posDelim = title.find_last_of('~');
if (posDelim != string::npos) {
title = title.substr(posDelim+1);
}
string subTitle = "";
const cRecordingInfo *recInfo = rec->Info();
if (recInfo) {
const cEvent *recEvent = recInfo->GetEvent();
if (recEvent) {
eventId = recEvent->EventID();
channelId = *(recInfo->ChannelID().ToString());
if (recInfo->Title())
title = recInfo->Title();
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 = getRecPath(rec);
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 * 2;
int scanNewRecFreq = 60 * 5;
int scanNewRecDBFreq = 60 * 5;
int cleanUpFreq = 60 * 10;
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 Recordings from Database
if (forceRecordingUpdate || (time(0) - lastScanNewRecDB > scanNewRecDBFreq) && Running()) {
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;
}
//Update Events
if (!config.headless && (forceUpdate || (time(0) - lastScan > scanFreq)) && Running()) {
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 Movies from Database...");
time_t now = time(0);
int numNewMovies = ReadMovies(false);
int dur = time(0) - now;
tell(0, "Loaded %d new Movies in %ds from Database", numNewMovies, dur);
tell(0, "Loading new Series and Episodes from Database...");
now = time(0);
int numNewSeries = ReadSeries(false);
dur = time(0) - now;
tell(0, "Loaded %d new Series and Episodes in %ds from Database", numNewSeries, dur);
lastScan = time(0);
forceUpdate = false;
}
//Scan new recordings
if ((init || forceVideoDirUpdate || (time(0) - lastScanNewRec > scanNewRecFreq)) && Running()) {
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) && Running()){
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;
}