/* * Copyright (C) 2012 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, see * . * */ #include "dbwrappers/dataset.h" #include "settings/AdvancedSettings.h" #include "settings/VideoSettings.h" #include "utils/log.h" #include "addons/include/xbmc_pvr_types.h" #include "EpgDatabase.h" #include "EpgContainer.h" using namespace std; using namespace dbiplus; using namespace EPG; bool CEpgDatabase::Open(void) { return CDatabase::Open(g_advancedSettings.m_databaseEpg); } bool CEpgDatabase::CreateTables(void) { bool bReturn(false); try { CDatabase::CreateTables(); BeginTransaction(); CLog::Log(LOGINFO, "EpgDB - %s - creating tables", __FUNCTION__); CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epg'", __FUNCTION__); m_pDS->exec( "CREATE TABLE epg (" "idEpg integer primary key, " "sName varchar(64)," "sScraperName varchar(32)" ")" ); CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'epgtags'", __FUNCTION__); m_pDS->exec( "CREATE TABLE epgtags (" "idBroadcast integer primary key, " "iBroadcastUid integer, " "idEpg integer, " "sTitle varchar(128), " "sPlotOutline text, " "sPlot text, " "iStartTime integer, " "iEndTime integer, " "iGenreType integer, " "iGenreSubType integer, " "sGenre varchar(128), " "iFirstAired integer, " "iParentalRating integer, " "iStarRating integer, " "bNotify bool, " "iSeriesId integer, " "iEpisodeId integer, " "iEpisodePart integer, " "sEpisodeName varchar(128)" ")" ); m_pDS->exec("CREATE UNIQUE INDEX idx_epg_idEpg_iStartTime on epgtags(idEpg, iStartTime desc);"); m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);"); CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'lastepgscan'", __FUNCTION__); m_pDS->exec("CREATE TABLE lastepgscan (" "idEpg integer primary key, " "sLastScan varchar(20)" ")" ); CommitTransaction(); bReturn = true; } catch (...) { CLog::Log(LOGERROR, "EpgDB - %s - unable to create EPG tables:%i", __FUNCTION__, (int)GetLastError()); RollbackTransaction(); bReturn = false; } return bReturn; } bool CEpgDatabase::UpdateOldVersion(int iVersion) { bool bReturn = true; if (iVersion < 4) { CLog::Log(LOGERROR, "EpgDB - %s - updating from table versions < 4 not supported. please delete '%s'", __FUNCTION__, GetBaseDBName()); return false; } BeginTransaction(); try { if (iVersion < 5) m_pDS->exec("ALTER TABLE epgtags ADD sGenre varchar(128);"); if (iVersion < 6) { m_pDS->exec("DROP INDEX idx_epg_iBroadcastUid;"); m_pDS->exec("DROP INDEX idx_epg_idEpg;"); m_pDS->exec("DROP INDEX idx_epg_iStartTime;"); m_pDS->exec("DROP INDEX idx_epg_iEndTime;"); } if (iVersion < 7) { m_pDS->exec("CREATE INDEX idx_epg_iEndTime on epgtags(iEndTime);"); } } catch (...) { CLog::Log(LOGERROR, "Error attempting to update the database version!"); bReturn = false; } if (bReturn) CommitTransaction(); else RollbackTransaction(); return bReturn; } bool CEpgDatabase::DeleteEpg(void) { bool bReturn(false); CLog::Log(LOGDEBUG, "EpgDB - %s - deleting all EPG data from the database", __FUNCTION__); bReturn = DeleteValues("epg") || bReturn; bReturn = DeleteValues("epgtags") || bReturn; bReturn = DeleteValues("lastepgscan") || bReturn; return bReturn; } bool CEpgDatabase::Delete(const CEpg &table) { /* invalid channel */ if (table.EpgID() <= 0) { CLog::Log(LOGERROR, "EpgDB - %s - invalid channel id: %d", __FUNCTION__, table.EpgID()); return false; } CStdString strWhereClause; strWhereClause = FormatSQL("idEpg = %u", table.EpgID()); return DeleteValues("epg", strWhereClause); } bool CEpgDatabase::DeleteOldEpgEntries(void) { time_t iCleanupTime; CDateTime cleanupTime = CDateTime::GetCurrentDateTime().GetAsUTCDateTime() - CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); cleanupTime.GetAsTime(iCleanupTime); CStdString strWhereClause = FormatSQL("iEndTime < %u", iCleanupTime); return DeleteValues("epgtags", strWhereClause); } bool CEpgDatabase::Delete(const CEpgInfoTag &tag) { /* tag without a database ID was not persisted */ if (tag.BroadcastId() <= 0) return false; CStdString strWhereClause = FormatSQL("idBroadcast = %u", tag.BroadcastId()); return DeleteValues("epgtags", strWhereClause); } int CEpgDatabase::Get(CEpgContainer &container) { int iReturn(-1); CStdString strQuery = FormatSQL("SELECT idEpg, sName, sScraperName FROM epg;"); if (ResultQuery(strQuery)) { iReturn = 0; try { while (!m_pDS->eof()) { int iEpgID = m_pDS->fv("idEpg").get_asInt(); CStdString strName = m_pDS->fv("sName").get_asString().c_str(); CStdString strScraperName = m_pDS->fv("sScraperName").get_asString().c_str(); container.InsertFromDatabase(iEpgID, strName, strScraperName); ++iReturn; m_pDS->next(); } m_pDS->close(); } catch (...) { CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__); } } return iReturn; } int CEpgDatabase::Get(CEpg &epg) { int iReturn(-1); CStdString strQuery = FormatSQL("SELECT * FROM epgtags WHERE idEpg = %u;", epg.EpgID()); if (ResultQuery(strQuery)) { iReturn = 0; try { while (!m_pDS->eof()) { CEpgInfoTag newTag; time_t iStartTime, iEndTime, iFirstAired; iStartTime = (time_t) m_pDS->fv("iStartTime").get_asInt(); CDateTime startTime(iStartTime); newTag.m_startTime = startTime; iEndTime = (time_t) m_pDS->fv("iEndTime").get_asInt(); CDateTime endTime(iEndTime); newTag.m_endTime = endTime; iFirstAired = (time_t) m_pDS->fv("iFirstAired").get_asInt(); CDateTime firstAired(iFirstAired); newTag.m_firstAired = firstAired; newTag.m_iUniqueBroadcastID = m_pDS->fv("iBroadcastUid").get_asInt(); newTag.m_iBroadcastId = m_pDS->fv("idBroadcast").get_asInt(); newTag.m_strTitle = m_pDS->fv("sTitle").get_asString().c_str(); newTag.m_strPlotOutline = m_pDS->fv("sPlotOutline").get_asString().c_str(); newTag.m_strPlot = m_pDS->fv("sPlot").get_asString().c_str(); newTag.m_iGenreType = m_pDS->fv("iGenreType").get_asInt(); newTag.m_iGenreSubType = m_pDS->fv("iGenreSubType").get_asInt(); newTag.m_genre = StringUtils::Split(m_pDS->fv("sGenre").get_asString().c_str(), g_advancedSettings.m_videoItemSeparator); newTag.m_iParentalRating = m_pDS->fv("iParentalRating").get_asInt(); newTag.m_iStarRating = m_pDS->fv("iStarRating").get_asInt(); newTag.m_bNotify = m_pDS->fv("bNotify").get_asBool(); newTag.m_iEpisodeNumber = m_pDS->fv("iEpisodeId").get_asInt(); newTag.m_iEpisodePart = m_pDS->fv("iEpisodePart").get_asInt(); newTag.m_strEpisodeName = m_pDS->fv("sEpisodeName").get_asString().c_str(); newTag.m_iSeriesNumber = m_pDS->fv("iSeriesId").get_asInt(); epg.AddEntry(newTag); ++iReturn; m_pDS->next(); } m_pDS->close(); } catch (...) { CLog::Log(LOGERROR, "%s - couldn't load EPG data from the database", __FUNCTION__); } } return iReturn; } bool CEpgDatabase::GetLastEpgScanTime(int iEpgId, CDateTime *lastScan) { bool bReturn = false; CStdString strWhereClause = FormatSQL("idEpg = %u", iEpgId); CStdString strValue = GetSingleValue("lastepgscan", "sLastScan", strWhereClause); if (!strValue.IsEmpty()) { lastScan->SetFromDBDateTime(strValue.c_str()); bReturn = true; } else { lastScan->SetValid(false); } return bReturn; } bool CEpgDatabase::PersistLastEpgScanTime(int iEpgId /* = 0 */, bool bQueueWrite /* = false */) { CStdString strQuery = FormatSQL("REPLACE INTO lastepgscan(idEpg, sLastScan) VALUES (%u, '%s');", iEpgId, CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsDBDateTime().c_str()); return bQueueWrite ? QueueInsertQuery(strQuery) : ExecuteQuery(strQuery); } bool CEpgDatabase::Persist(const CEpgContainer &epg) { for (map::const_iterator it = epg.m_epgs.begin(); it != epg.m_epgs.end(); it++) { CEpg *epg = it->second; if (epg) Persist(*epg, true); } return CommitInsertQueries(); } int CEpgDatabase::Persist(const CEpg &epg, bool bQueueWrite /* = false */) { int iReturn(-1); CStdString strQuery; if (epg.EpgID() > 0) strQuery = FormatSQL("REPLACE INTO epg (idEpg, sName, sScraperName) " "VALUES (%u, '%s', '%s');", epg.EpgID(), epg.Name().c_str(), epg.ScraperName().c_str()); else strQuery = FormatSQL("INSERT INTO epg (sName, sScraperName) " "VALUES ('%s', '%s');", epg.Name().c_str(), epg.ScraperName().c_str()); if (bQueueWrite) { if (QueueInsertQuery(strQuery)) iReturn = epg.EpgID() <= 0 ? 0 : epg.EpgID(); } else { if (ExecuteQuery(strQuery)) iReturn = epg.EpgID() <= 0 ? (int) m_pDS->lastinsertid() : epg.EpgID(); } return iReturn; } int CEpgDatabase::Persist(const CEpgInfoTag &tag, bool bSingleUpdate /* = true */) { int iReturn(-1); if (tag.EpgID() <= 0) { CLog::Log(LOGERROR, "%s - tag '%s' does not have a valid table", __FUNCTION__, tag.Title(true).c_str()); return iReturn; } time_t iStartTime, iEndTime, iFirstAired; tag.StartAsUTC().GetAsTime(iStartTime); tag.EndAsUTC().GetAsTime(iEndTime); tag.FirstAiredAsUTC().GetAsTime(iFirstAired); int iBroadcastId = tag.BroadcastId(); CStdString strQuery; /* Only store the genre string when needed */ CStdString strGenre = (tag.GenreType() == EPG_GENRE_USE_STRING) ? StringUtils::Join(tag.Genre(), g_advancedSettings.m_videoItemSeparator) : ""; if (iBroadcastId < 0) { strQuery = FormatSQL("REPLACE INTO epgtags (idEpg, iStartTime, " "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, " "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid) " "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i);", tag.EpgID(), iStartTime, iEndTime, tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.UniqueBroadcastID()); } else { strQuery = FormatSQL("REPLACE INTO epgtags (idEpg, iStartTime, " "iEndTime, sTitle, sPlotOutline, sPlot, iGenreType, iGenreSubType, sGenre, " "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast) " "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, %i);", tag.EpgID(), iStartTime, iEndTime, tag.Title(true).c_str(), tag.PlotOutline(true).c_str(), tag.Plot(true).c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), tag.SeriesNum(), tag.EpisodeNum(), tag.EpisodePart(), tag.EpisodeName().c_str(), tag.UniqueBroadcastID(), iBroadcastId); } if (bSingleUpdate) { if (ExecuteQuery(strQuery)) iReturn = (int) m_pDS->lastinsertid(); } else { QueueInsertQuery(strQuery); iReturn = 0; } return iReturn; }