From 5f76e327a56dd6df31187abd56dca7270c1751df Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Sat, 28 Feb 2015 13:50:48 +0100 Subject: [PATCH] [pvr] changed: instead of having a recording id set for epg tags, have an epg id set for recordings epg tags are only refreshed based on a timeout, or when an update is forced fixes playing recordings in progress from the timeline --- xbmc/addons/include/xbmc_epg_types.h | 1 - xbmc/addons/include/xbmc_pvr_types.h | 5 +++-- xbmc/epg/Epg.cpp | 31 ++++++++++----------------- xbmc/epg/Epg.h | 17 ++++++++++----- xbmc/epg/EpgContainer.cpp | 22 ++++++++++++++++--- xbmc/epg/EpgContainer.h | 7 ++++++ xbmc/epg/EpgDatabase.cpp | 19 ++++++---------- xbmc/epg/EpgInfoTag.cpp | 18 +--------------- xbmc/epg/EpgInfoTag.h | 13 ----------- xbmc/pvr/recordings/PVRRecording.cpp | 7 +++++- xbmc/pvr/recordings/PVRRecording.h | 6 ++++++ xbmc/pvr/recordings/PVRRecordings.cpp | 23 ++++++++++++++++++++ xbmc/pvr/recordings/PVRRecordings.h | 2 ++ 13 files changed, 97 insertions(+), 74 deletions(-) diff --git a/xbmc/addons/include/xbmc_epg_types.h b/xbmc/addons/include/xbmc_epg_types.h index 97cea40154c19..ceac0742ec96e 100644 --- a/xbmc/addons/include/xbmc_epg_types.h +++ b/xbmc/addons/include/xbmc_epg_types.h @@ -88,7 +88,6 @@ extern "C" { int iEpisodeNumber; /*!< @brief (optional) episode number */ int iEpisodePartNumber; /*!< @brief (optional) episode part number */ const char * strEpisodeName; /*!< @brief (optional) episode name */ - const char * strRecordingId; /*!< @brief (optional) unique id of the recording on the client which represents this event */ } ATTRIBUTE_PACKED EPG_TAG; #ifdef __cplusplus diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h index 5285bd1551b04..5716e518ab371 100644 --- a/xbmc/addons/include/xbmc_pvr_types.h +++ b/xbmc/addons/include/xbmc_pvr_types.h @@ -75,10 +75,10 @@ struct DemuxPacket; #define PVR_STREAM_MAX_STREAMS 20 /* current PVR API version */ -#define XBMC_PVR_API_VERSION "1.9.4" +#define XBMC_PVR_API_VERSION "1.9.5" /* min. PVR API version */ -#define XBMC_PVR_MIN_API_VERSION "1.9.4" +#define XBMC_PVR_MIN_API_VERSION "1.9.5" #ifdef __cplusplus extern "C" { @@ -299,6 +299,7 @@ extern "C" { int iPlayCount; /*!< @brief (optional) play count of this recording on the client */ int iLastPlayedPosition; /*!< @brief (optional) last played position of this recording on the client */ bool bIsDeleted; /*!< @brief (optional) shows this recording is deleted and can be undelete */ + unsigned int iEpgEventId; /*!< @brief (optional) EPG event id associated with this recording */ } ATTRIBUTE_PACKED PVR_RECORDING; /*! diff --git a/xbmc/epg/Epg.cpp b/xbmc/epg/Epg.cpp index e8a6397f0b40c..d47a54658ca32 100644 --- a/xbmc/epg/Epg.cpp +++ b/xbmc/epg/Epg.cpp @@ -261,6 +261,17 @@ CEpgInfoTagPtr CEpg::GetTag(const CDateTime &StartTime) const return CEpgInfoTagPtr(); } +CEpgInfoTagPtr CEpg::GetTag(int uniqueID) const +{ + CEpgInfoTagPtr retval; + CSingleLock lock(m_critSection); + for (map::const_iterator it = m_tags.begin(); !retval && it != m_tags.end(); ++it) + if (it->second->UniqueBroadcastID() == uniqueID) + retval = it->second; + + return retval; +} + CEpgInfoTagPtr CEpg::GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const { CSingleLock lock(m_critSection); @@ -303,7 +314,6 @@ void CEpg::AddEntry(const CEpgInfoTag &tag) newTag->Update(tag); newTag->SetPVRChannel(m_pvrChannel); newTag->SetEpg(this); - UpdateRecording(newTag); } } @@ -329,7 +339,6 @@ bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false * infoTag->Update(tag, bNewTag); infoTag->SetEpg(this); infoTag->SetPVRChannel(m_pvrChannel); - UpdateRecording(infoTag); if (bUpdateDatabase) m_changedTags.insert(make_pair(infoTag->UniqueBroadcastID(), infoTag)); @@ -337,24 +346,6 @@ bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false * return true; } -void CEpg::UpdateRecording(CEpgInfoTagPtr &tag) -{ - if (!tag) - return; - - if (tag->HasPVRChannel() && tag->HasRecordingId()) - { - CPVRRecordingPtr recording = g_PVRRecordings->GetById(tag->ChannelTag()->ClientID(), tag->RecordingId()); - if (recording) - { - tag->SetRecording(recording); - return; - } - } - - tag->ClearRecording(); -} - bool CEpg::Load(void) { bool bReturn(false); diff --git a/xbmc/epg/Epg.h b/xbmc/epg/Epg.h index c3fdcab4cc4a5..3d01a4231d829 100644 --- a/xbmc/epg/Epg.h +++ b/xbmc/epg/Epg.h @@ -191,16 +191,25 @@ namespace EPG CEpgInfoTagPtr GetTagBetween(const CDateTime &beginTime, const CDateTime &endTime) const; /*! - * @brief Get the infotag with the given ID. + * @brief Get the infotag with the given begin time. * * Get the infotag with the given ID. * If it wasn't found, try finding the event with the given start time * - * @param uniqueID The unique ID of the event to find. * @param beginTime The start time in UTC of the event to find if it wasn't found by it's unique ID. - * @return The found tag or NULL if it wasn't found. + * @return The found tag or an empty tag if it wasn't found. */ CEpgInfoTagPtr GetTag(const CDateTime &beginTime) const; + /*! + * @brief Get the infotag with the given ID. + * + * Get the infotag with the given ID. + * If it wasn't found, try finding the event with the given start time + * + * @param uniqueID The unique ID of the event to find. + * @return The found tag or an empty tag if it wasn't found. + */ + CEpgInfoTagPtr GetTag(int uniqueID) const; /*! * @brief Update an entry in this EPG. @@ -339,8 +348,6 @@ namespace EPG bool IsRemovableTag(const EPG::CEpgInfoTag &tag) const; - void UpdateRecording(CEpgInfoTagPtr &tag); - std::map m_tags; std::map m_changedTags; std::map m_deletedTags; diff --git a/xbmc/epg/EpgContainer.cpp b/xbmc/epg/EpgContainer.cpp index c42118414ce56..d425c62debe88 100644 --- a/xbmc/epg/EpgContainer.cpp +++ b/xbmc/epg/EpgContainer.cpp @@ -30,6 +30,7 @@ #include "utils/log.h" #include "pvr/PVRManager.h" #include "pvr/channels/PVRChannelGroupsContainer.h" +#include "pvr/recordings/PVRRecordings.h" #include "pvr/timers/PVRTimers.h" #include "EpgContainer.h" @@ -146,6 +147,8 @@ void CEpgContainer::Start(void) } LoadFromDB(); + if (g_PVRManager.IsStarted()) + g_PVRManager.Recordings()->UpdateEpgTags(); CSingleLock lock(m_critSection); if (!m_bStop) @@ -339,6 +342,15 @@ CEpg *CEpgContainer::GetById(int iEpgId) const return it != m_epgs.end() ? it->second : NULL; } +CEpgInfoTagPtr CEpgContainer::GetTagById(int iBroadcastId) const +{ + CEpgInfoTagPtr retval; + CSingleLock lock(m_critSection); + for (map::const_iterator it = m_epgs.begin(); !retval && it != m_epgs.end(); ++it) + retval = it->second->GetTag(iBroadcastId); + return retval; +} + CEpg *CEpgContainer::GetByChannel(const CPVRChannel &channel) const { CSingleLock lock(m_critSection); @@ -550,9 +562,13 @@ bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */) { CLog::Log(LOGERROR, "EpgContainer - %s - could not open the database", __FUNCTION__); - CSingleLock lock(m_critSection); - m_bIsUpdating = false; - m_updateEvent.Set(); + { + CSingleLock lock(m_critSection); + m_bIsUpdating = false; + m_updateEvent.Set(); + } + + g_PVRManager.Recordings()->UpdateEpgTags(); if (bShowProgress && !bOnlyPending) CloseProgressDialog(); diff --git a/xbmc/epg/EpgContainer.h b/xbmc/epg/EpgContainer.h index 4d0dd87957fbf..e5c9c1e961af9 100644 --- a/xbmc/epg/EpgContainer.h +++ b/xbmc/epg/EpgContainer.h @@ -159,6 +159,13 @@ namespace EPG */ virtual CEpg *GetById(int iEpgId) const; + /*! + * @brief Get the EPG event with the given event id + * @param iBroadcastId The event id to get + * @return The requested event, or an empty tag when not found + */ + virtual CEpgInfoTagPtr GetTagById(int iBroadcastId) const; + /*! * @brief Get an EPG table given a PVR channel. * @param channel The channel to get the EPG table for. diff --git a/xbmc/epg/EpgDatabase.cpp b/xbmc/epg/EpgDatabase.cpp index dd30feacee313..70278f5ddab9a 100644 --- a/xbmc/epg/EpgDatabase.cpp +++ b/xbmc/epg/EpgDatabase.cpp @@ -74,8 +74,7 @@ void CEpgDatabase::CreateTables(void) "iSeriesId integer, " "iEpisodeId integer, " "iEpisodePart integer, " - "sEpisodeName varchar(128), " - "sRecordingId varchar(128)" + "sEpisodeName varchar(128)" ")" ); CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'lastepgscan'", __FUNCTION__); @@ -98,9 +97,6 @@ void CEpgDatabase::UpdateTables(int iVersion) if (iVersion < 5) m_pDS->exec("ALTER TABLE epgtags ADD sGenre varchar(128);"); - if (iVersion < 8) - m_pDS->exec("ALTER TABLE epgtags ADD sRecordingId varchar(128);"); - if (iVersion < 9) m_pDS->exec("ALTER TABLE epgtags ADD sIconPath varchar(255);"); } @@ -231,7 +227,6 @@ int CEpgDatabase::Get(CEpg &epg) 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(); - newTag->m_strRecordingId = m_pDS->fv("sRecordingId").get_asString().c_str(); newTag->m_strIconPath = m_pDS->fv("sIconPath").get_asString().c_str(); epg.AddEntry(*newTag); @@ -340,26 +335,26 @@ int CEpgDatabase::Persist(const CEpgInfoTag &tag, bool bSingleUpdate /* = true * strQuery = PrepareSQL("REPLACE INTO epgtags (idEpg, iStartTime, " "iEndTime, sTitle, sPlotOutline, sPlot, sIconPath, iGenreType, iGenreSubType, sGenre, " "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " - "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, sRecordingId) " - "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, '%s');", + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid) " + "VALUES (%u, %u, %u, '%s', '%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.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), - tag.UniqueBroadcastID(), tag.RecordingId().c_str()); + tag.UniqueBroadcastID()); } else { strQuery = PrepareSQL("REPLACE INTO epgtags (idEpg, iStartTime, " "iEndTime, sTitle, sPlotOutline, sPlot, sIconPath, iGenreType, iGenreSubType, sGenre, " "iFirstAired, iParentalRating, iStarRating, bNotify, iSeriesId, " - "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast, sRecordingId) " - "VALUES (%u, %u, %u, '%s', '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, %i, '%s');", + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast) " + "VALUES (%u, %u, %u, '%s', '%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.Icon().c_str(), tag.GenreType(), tag.GenreSubType(), strGenre.c_str(), iFirstAired, tag.ParentalRating(), tag.StarRating(), tag.Notify(), tag.SeriesNumber(), tag.EpisodeNumber(), tag.EpisodePart(), tag.EpisodeName().c_str(), - tag.UniqueBroadcastID(), iBroadcastId, tag.RecordingId().c_str()); + tag.UniqueBroadcastID(), iBroadcastId); } if (bSingleUpdate) diff --git a/xbmc/epg/EpgInfoTag.cpp b/xbmc/epg/EpgInfoTag.cpp index 4755b38cb4ad4..7ee7c224aa324 100644 --- a/xbmc/epg/EpgInfoTag.cpp +++ b/xbmc/epg/EpgInfoTag.cpp @@ -113,8 +113,6 @@ CEpgInfoTag::CEpgInfoTag(const EPG_TAG &data) : m_strEpisodeName = data.strEpisodeName; if (data.strIconPath) m_strIconPath = data.strIconPath; - if (data.strRecordingId) - m_strRecordingId = data.strRecordingId; UpdatePath(); } @@ -149,8 +147,7 @@ bool CEpgInfoTag::operator ==(const CEpgInfoTag& right) const m_strFileNameAndPath == right.m_strFileNameAndPath && m_startTime == right.m_startTime && m_endTime == right.m_endTime && - m_pvrChannel == right.m_pvrChannel && - m_strRecordingId == right.m_strRecordingId); + m_pvrChannel == right.m_pvrChannel); } bool CEpgInfoTag::operator !=(const CEpgInfoTag& right) const @@ -180,7 +177,6 @@ void CEpgInfoTag::Serialize(CVariant &value) const value["episodenum"] = m_iEpisodeNumber; value["episodepart"] = m_iEpisodePart; value["hastimer"] = HasTimer(); - value["recordingid"] = m_strRecordingId; value["hasrecording"] = HasRecording(); value["isactive"] = IsActive(); value["wasactive"] = WasActive(); @@ -442,16 +438,6 @@ std::string CEpgInfoTag::Path(void) const return m_strFileNameAndPath; } -const std::string& CEpgInfoTag::RecordingId(void) const -{ - return m_strRecordingId; -} - -bool CEpgInfoTag::HasRecordingId(void) const -{ - return !m_strRecordingId.empty(); -} - bool CEpgInfoTag::HasTimer(void) const { return m_timer != NULL; @@ -514,7 +500,6 @@ bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = tr EpgID() != tag.EpgID() || m_pvrChannel != tag.m_pvrChannel || m_genre != tag.m_genre || - m_strRecordingId != tag.m_strRecordingId || m_strIconPath != tag.m_strIconPath ); if (bUpdateBroadcastId) @@ -553,7 +538,6 @@ bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = tr m_iSeriesNumber = tag.m_iSeriesNumber; m_strEpisodeName = tag.m_strEpisodeName; m_iUniqueBroadcastID = tag.m_iUniqueBroadcastID; - m_strRecordingId = tag.m_strRecordingId; m_strIconPath = tag.m_strIconPath; } } diff --git a/xbmc/epg/EpgInfoTag.h b/xbmc/epg/EpgInfoTag.h index d3c20d2fc7543..64e40e82ba60d 100644 --- a/xbmc/epg/EpgInfoTag.h +++ b/xbmc/epg/EpgInfoTag.h @@ -284,18 +284,6 @@ namespace EPG */ std::string Path(void) const; - /*! - * @brief The recording ID to this event. - * @return The recording ID. - */ - const std::string& RecordingId(void) const; - - /*! - * @brief Check whether this event has a recording ID. - * @return True if it has a recording ID, false if not. - */ - bool HasRecordingId(void) const; - /*! * @brief Set a timer for this event or NULL to clear it. * @param newTimer The new timer value. @@ -414,7 +402,6 @@ namespace EPG CDateTime m_startTime; /*!< event start time */ CDateTime m_endTime; /*!< event end time */ CDateTime m_firstAired; /*!< first airdate */ - std::string m_strRecordingId; /*!< linked recording ID */ PVR::CPVRTimerInfoTagPtr m_timer; PVR::CPVRRecordingPtr m_recording; diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp index 02fca6e4553b9..4327b67b699a1 100644 --- a/xbmc/pvr/recordings/PVRRecording.cpp +++ b/xbmc/pvr/recordings/PVRRecording.cpp @@ -105,6 +105,7 @@ CPVRRecording::CPVRRecording(const PVR_RECORDING &recording, unsigned int iClien m_strThumbnailPath = recording.strThumbnailPath; m_strFanartPath = recording.strFanartPath; m_bIsDeleted = recording.bIsDeleted; + m_iEpgEventId = recording.iEpgEventId; } bool CPVRRecording::operator ==(const CPVRRecording& right) const @@ -127,7 +128,8 @@ bool CPVRRecording::operator ==(const CPVRRecording& right) const m_strThumbnailPath == right.m_strThumbnailPath && m_strFanartPath == right.m_strFanartPath && m_iRecordingId == right.m_iRecordingId && - m_bIsDeleted == right.m_bIsDeleted); + m_bIsDeleted == right.m_bIsDeleted && + m_iEpgEventId == right.m_iEpgEventId); } bool CPVRRecording::operator !=(const CPVRRecording& right) const @@ -149,6 +151,7 @@ void CPVRRecording::Serialize(CVariant& value) const value["endtime"] = m_recordingTime.IsValid() ? (m_recordingTime + m_duration).GetAsDBDateTime() : ""; value["recordingid"] = m_iRecordingId; value["deleted"] = m_bIsDeleted; + value["epgevent"] = m_iEpgEventId; if (!value.isMember("art")) value["art"] = CVariant(CVariant::VariantTypeObject); @@ -174,6 +177,7 @@ void CPVRRecording::Reset(void) m_bGotMetaData = false; m_iRecordingId = 0; m_bIsDeleted = false; + m_iEpgEventId = -1; m_recordingTime.Reset(); CVideoInfoTag::Reset(); @@ -333,6 +337,7 @@ void CPVRRecording::Update(const CPVRRecording &tag) m_strThumbnailPath = tag.m_strThumbnailPath; m_strFanartPath = tag.m_strFanartPath; m_bIsDeleted = tag.m_bIsDeleted; + m_iEpgEventId = tag.m_iEpgEventId; if (g_PVRClients->SupportsRecordingPlayCount(m_iClientId)) m_playCount = tag.m_playCount; diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h index 86e396244c182..513d33df37d72 100644 --- a/xbmc/pvr/recordings/PVRRecording.h +++ b/xbmc/pvr/recordings/PVRRecording.h @@ -198,10 +198,16 @@ namespace PVR */ bool IsDeleted() const { return m_bIsDeleted; } + /*! + * @return Broadcast id of the EPG event associated with this recording + */ + int EpgEvent(void) const { return m_iEpgEventId; } + private: CDateTime m_recordingTime; /*!< start time of the recording */ bool m_bGotMetaData; bool m_bIsDeleted; /*!< set if entry is a deleted recording which can be undelete */ + int m_iEpgEventId; /*!< epg broadcast id associated with this recording */ void UpdatePath(void); void DisplayError(PVR_ERROR err) const; diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp index 09dfda0ee2b81..93bc5a03312d4 100644 --- a/xbmc/pvr/recordings/PVRRecordings.cpp +++ b/xbmc/pvr/recordings/PVRRecordings.cpp @@ -20,6 +20,7 @@ #include "FileItem.h" #include "dialogs/GUIDialogOK.h" +#include "epg/EpgContainer.h" #include "guilib/GUIWindowManager.h" #include "guilib/LocalizeStrings.h" #include "Util.h" @@ -504,7 +505,29 @@ void CPVRRecordings::UpdateFromClient(const CPVRRecordingPtr &tag) { newTag = CPVRRecordingPtr(new CPVRRecording); newTag->Update(*tag); + if (newTag->EpgEvent() > 0) + { + EPG::CEpgInfoTagPtr epgTag = EPG::CEpgContainer::Get().GetTagById(newTag->EpgEvent()); + if (epgTag) + epgTag->SetRecording(newTag); + } newTag->m_iRecordingId = ++m_iLastId; m_recordings.insert(std::make_pair(CPVRRecordingUid(newTag->m_iClientId, newTag->m_strRecordingId), newTag)); } } + +void CPVRRecordings::UpdateEpgTags(void) +{ + CSingleLock lock(m_critSection); + int iEpgEvent; + for (PVR_RECORDINGMAP_ITR it = m_recordings.begin(); it != m_recordings.end(); ++it) + { + iEpgEvent = it->second->EpgEvent(); + if (iEpgEvent > 0) + { + EPG::CEpgInfoTagPtr epgTag = EPG::CEpgContainer::Get().GetTagById(iEpgEvent); + if (epgTag) + epgTag->SetRecording(it->second); + } + } +} diff --git a/xbmc/pvr/recordings/PVRRecordings.h b/xbmc/pvr/recordings/PVRRecordings.h index 3df4f671b396d..38949036e0ee4 100644 --- a/xbmc/pvr/recordings/PVRRecordings.h +++ b/xbmc/pvr/recordings/PVRRecordings.h @@ -34,6 +34,7 @@ namespace PVR { private: typedef std::map PVR_RECORDINGMAP; + typedef PVR_RECORDINGMAP::iterator PVR_RECORDINGMAP_ITR; typedef PVR_RECORDINGMAP::const_iterator PVR_RECORDINGMAP_CITR; CCriticalSection m_critSection; @@ -66,6 +67,7 @@ namespace PVR void Unload(); void Clear(); void UpdateFromClient(const CPVRRecordingPtr &tag); + void UpdateEpgTags(void); /** * @brief refresh the recordings list from the clients.