diff options
-rw-r--r-- | addons/skin.confluence/720p/DialogPVRGuideOSD.xml | 16 | ||||
-rw-r--r-- | addons/skin.confluence/720p/ViewsPVR.xml | 32 | ||||
-rw-r--r-- | addons/skin.confluence/media/PVR-HasRecording.png | bin | 0 -> 3629 bytes | |||
-rw-r--r-- | xbmc/GUIInfoManager.cpp | 5 | ||||
-rw-r--r-- | xbmc/GUIInfoManager.h | 1 | ||||
-rw-r--r-- | xbmc/addons/include/xbmc_epg_types.h | 1 | ||||
-rw-r--r-- | xbmc/epg/Epg.cpp | 20 | ||||
-rw-r--r-- | xbmc/epg/Epg.h | 4 | ||||
-rw-r--r-- | xbmc/epg/EpgContainer.cpp | 24 | ||||
-rw-r--r-- | xbmc/epg/EpgContainer.h | 6 | ||||
-rw-r--r-- | xbmc/epg/EpgDatabase.cpp | 19 | ||||
-rw-r--r-- | xbmc/epg/EpgDatabase.h | 2 | ||||
-rw-r--r-- | xbmc/epg/EpgInfoTag.cpp | 71 | ||||
-rw-r--r-- | xbmc/epg/EpgInfoTag.h | 47 | ||||
-rw-r--r-- | xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp | 15 | ||||
-rw-r--r-- | xbmc/pvr/recordings/PVRRecording.cpp | 44 | ||||
-rw-r--r-- | xbmc/pvr/recordings/PVRRecording.h | 25 | ||||
-rw-r--r-- | xbmc/pvr/recordings/PVRRecordings.cpp | 77 | ||||
-rw-r--r-- | xbmc/pvr/recordings/PVRRecordings.h | 6 | ||||
-rw-r--r-- | xbmc/pvr/windows/GUIWindowPVRCommon.cpp | 17 |
20 files changed, 357 insertions, 75 deletions
diff --git a/addons/skin.confluence/720p/DialogPVRGuideOSD.xml b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml index c5d6c3197a..a0951d5d1b 100644 --- a/addons/skin.confluence/720p/DialogPVRGuideOSD.xml +++ b/addons/skin.confluence/720p/DialogPVRGuideOSD.xml @@ -116,6 +116,14 @@ <texture>PVR-HasTimer.png</texture> <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> </control> + <control type="image"> + <left>60</left> + <top>7</top> + <width>16</width> + <height>16</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> <control type="label"> <left>100</left> <top>0</top> @@ -190,6 +198,14 @@ <texture>PVR-HasTimer.png</texture> <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> </control> + <control type="image"> + <left>60</left> + <top>7</top> + <width>16</width> + <height>16</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> <control type="label"> <left>100</left> <top>0</top> diff --git a/addons/skin.confluence/720p/ViewsPVR.xml b/addons/skin.confluence/720p/ViewsPVR.xml index de75379ef5..2c5a0cbf3f 100644 --- a/addons/skin.confluence/720p/ViewsPVR.xml +++ b/addons/skin.confluence/720p/ViewsPVR.xml @@ -1017,6 +1017,14 @@ <texture>PVR-HasTimer.png</texture> <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> </control> + <control type="image"> + <left>6</left> + <top>28</top> + <width>16</width> + <height>16</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> </itemlayout> <focusedlayout height="48" width="40"> <control type="image" id="14"> @@ -1061,6 +1069,14 @@ <texture>PVR-HasTimer.png</texture> <visible>ListItem.HasTimer + !ListItem.IsRecording</visible> </control> + <control type="image"> + <left>6</left> + <top>28</top> + <width>16</width> + <height>16</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> </focusedlayout> </control> <control type="group"> @@ -1579,6 +1595,14 @@ <label>31510</label> <visible>ListItem.HasTimer</visible> </control> + <control type="image"> + <left>980</left> + <top>10</top> + <width>20</width> + <height>20</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> </control> </itemlayout> <focusedlayout height="40"> @@ -1694,6 +1718,14 @@ <label>31510</label> <visible>ListItem.HasTimer</visible> </control> + <control type="image"> + <left>980</left> + <top>10</top> + <width>20</width> + <height>20</height> + <texture>PVR-HasRecording.png</texture> + <visible>ListItem.HasRecording + !ListItem.IsRecording + !ListItem.HasTimer</visible> + </control> </control> </focusedlayout> </control> diff --git a/addons/skin.confluence/media/PVR-HasRecording.png b/addons/skin.confluence/media/PVR-HasRecording.png Binary files differnew file mode 100644 index 0000000000..1b048d69fc --- /dev/null +++ b/addons/skin.confluence/media/PVR-HasRecording.png diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index a72367d0de..97835bd2d0 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -582,6 +582,7 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "channelgroup", LISTITEM_CHANNEL_GROUP }, { "hasepg", LISTITEM_HAS_EPG }, { "hastimer", LISTITEM_HASTIMER }, + { "hasrecording", LISTITEM_HASRECORDING }, { "isrecording", LISTITEM_ISRECORDING }, { "inprogress", LISTITEM_INPROGRESS }, { "isencrypted", LISTITEM_ISENCRYPTED }, @@ -5188,6 +5189,10 @@ bool CGUIInfoManager::GetItemBool(const CGUIListItem *item, int condition) const return timer->GetPVRTimerInfoTag()->IsActive(); } } + else if (condition == LISTITEM_HASRECORDING) + { + return pItem->HasEPGInfoTag() && pItem->GetEPGInfoTag()->HasRecording(); + } else if (condition == LISTITEM_HAS_EPG) { if (pItem->HasPVRChannelInfoTag()) diff --git a/xbmc/GUIInfoManager.h b/xbmc/GUIInfoManager.h index 4fdd302b52..2529eada81 100644 --- a/xbmc/GUIInfoManager.h +++ b/xbmc/GUIInfoManager.h @@ -646,6 +646,7 @@ namespace INFO #define LISTITEM_STEREOSCOPIC_MODE (LISTITEM_START + 140) #define LISTITEM_IS_STEREOSCOPIC (LISTITEM_START + 141) #define LISTITEM_INPROGRESS (LISTITEM_START + 142) +#define LISTITEM_HASRECORDING (LISTITEM_START + 143) #define LISTITEM_PROPERTY_START (LISTITEM_START + 200) #define LISTITEM_PROPERTY_END (LISTITEM_PROPERTY_START + 1000) diff --git a/xbmc/addons/include/xbmc_epg_types.h b/xbmc/addons/include/xbmc_epg_types.h index ceac0742ec..97cea40154 100644 --- a/xbmc/addons/include/xbmc_epg_types.h +++ b/xbmc/addons/include/xbmc_epg_types.h @@ -88,6 +88,7 @@ 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/epg/Epg.cpp b/xbmc/epg/Epg.cpp index 02ee4735ad..c69cc864d4 100644 --- a/xbmc/epg/Epg.cpp +++ b/xbmc/epg/Epg.cpp @@ -329,6 +329,7 @@ void CEpg::AddEntry(const CEpgInfoTag &tag) newTag->Update(tag); newTag->SetPVRChannel(m_pvrChannel); newTag->m_epg = this; + UpdateRecording(newTag); newTag->m_bChanged = false; } } @@ -355,6 +356,7 @@ bool CEpg::UpdateEntry(const CEpgInfoTag &tag, bool bUpdateDatabase /* = false * infoTag->Update(tag, bNewTag); infoTag->m_epg = this; infoTag->m_pvrChannel = m_pvrChannel; + UpdateRecording(infoTag); if (bUpdateDatabase) m_changedTags.insert(make_pair(infoTag->UniqueBroadcastID(), infoTag)); @@ -362,6 +364,24 @@ 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 3d2046994b..1e091447e5 100644 --- a/xbmc/epg/Epg.h +++ b/xbmc/epg/Epg.h @@ -293,7 +293,7 @@ namespace EPG bool NeedsSave(void) const; /*! - * @return True when this EPG is valid and can be updated, false otherwise + * @return True when this EPG is valid and can be updated, false otherwise. */ bool IsValid(void) const; protected: @@ -339,6 +339,8 @@ namespace EPG bool IsRemovableTag(const EPG::CEpgInfoTag &tag) const; + void UpdateRecording(CEpgInfoTagPtr tag); + std::map<CDateTime, CEpgInfoTagPtr> m_tags; std::map<int, CEpgInfoTagPtr> m_changedTags; std::map<int, CEpgInfoTagPtr> m_deletedTags; diff --git a/xbmc/epg/EpgContainer.cpp b/xbmc/epg/EpgContainer.cpp index 024c5c55d7..cc9afeb250 100644 --- a/xbmc/epg/EpgContainer.cpp +++ b/xbmc/epg/EpgContainer.cpp @@ -41,8 +41,6 @@ using namespace std; using namespace EPG; using namespace PVR; -typedef std::map<int, CEpg*>::iterator EPGITR; - CEpgContainer::CEpgContainer(void) : CThread("EPGUpdater") { @@ -100,7 +98,7 @@ void CEpgContainer::Clear(bool bClearDb /* = false */) { CSingleLock lock(m_critSection); /* clear all epg tables and remove pointers to epg tables on channels */ - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) { it->second->UnregisterObserver(this); delete it->second; @@ -214,7 +212,7 @@ void CEpgContainer::LoadFromDB(void) m_database.DeleteOldEpgEntries(); m_database.Get(*this); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) { if (m_bStop) break; @@ -245,7 +243,7 @@ bool CEpgContainer::PersistAll(void) std::map<unsigned int, CEpg*> copy = m_epgs; m_critSection.unlock(); - for (map<unsigned int, CEpg *>::iterator it = copy.begin(); it != copy.end() && !m_bStop; it++) + for (EPGMAP_CITR it = copy.begin(); it != copy.end() && !m_bStop; it++) { CEpg *epg = it->second; if (epg && epg->NeedsSave()) @@ -403,7 +401,7 @@ bool CEpgContainer::RemoveOldEntries(void) CDateTimeSpan(0, g_advancedSettings.m_iEpgLingerTime / 60, g_advancedSettings.m_iEpgLingerTime % 60, 0); /* call Cleanup() on all known EPG tables */ - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) it->second->Cleanup(now); /* remove the old entries from the database */ @@ -424,7 +422,7 @@ bool CEpgContainer::DeleteEpg(const CEpg &epg, bool bDeleteFromDatabase /* = fal CSingleLock lock(m_critSection); - map<unsigned int, CEpg *>::iterator it = m_epgs.find((unsigned int)epg.EpgID()); + EPGMAP_ITR it = m_epgs.find((unsigned int)epg.EpgID()); if (it == m_epgs.end()) return false; @@ -542,7 +540,7 @@ bool CEpgContainer::UpdateEPG(bool bOnlyPending /* = false */) /* load or update all EPG tables */ CEpg *epg; unsigned int iCounter(0); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) { if (InterruptUpdate()) { @@ -616,7 +614,7 @@ int CEpgContainer::GetEPGAll(CFileItemList &results) int iInitialSize = results.Size(); CSingleLock lock(m_critSection); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) it->second->Get(results); return results.Size() - iInitialSize; @@ -627,7 +625,7 @@ const CDateTime CEpgContainer::GetFirstEPGDate(void) CDateTime returnValue; CSingleLock lock(m_critSection); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) { lock.Leave(); CDateTime entry = it->second->GetFirstDate(); @@ -644,7 +642,7 @@ const CDateTime CEpgContainer::GetLastEPGDate(void) CDateTime returnValue; CSingleLock lock(m_critSection); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) { lock.Leave(); CDateTime entry = it->second->GetLastDate(); @@ -663,7 +661,7 @@ int CEpgContainer::GetEPGSearch(CFileItemList &results, const EpgSearchFilter &f /* get filtered results from all tables */ { CSingleLock lock(m_critSection); - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_CITR it = m_epgs.begin(); it != m_epgs.end(); it++) it->second->Get(results, filter); } @@ -685,7 +683,7 @@ bool CEpgContainer::CheckPlayingEvents(void) CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(iNow); if (iNow >= m_iNextEpgActiveTagCheck) { - for (map<unsigned int, CEpg *>::iterator it = m_epgs.begin(); it != m_epgs.end(); it++) + for (EPGMAP_ITR it = m_epgs.begin(); it != m_epgs.end(); it++) bFoundChanges = it->second->CheckPlayingEvent() || bFoundChanges; CDateTime::GetCurrentDateTime().GetAsUTCDateTime().GetAsTime(m_iNextEpgActiveTagCheck); m_iNextEpgActiveTagCheck += g_advancedSettings.m_iEpgActiveTagCheckInterval; diff --git a/xbmc/epg/EpgContainer.h b/xbmc/epg/EpgContainer.h index d50a7cd298..b6d00ab1d8 100644 --- a/xbmc/epg/EpgContainer.h +++ b/xbmc/epg/EpgContainer.h @@ -264,6 +264,10 @@ namespace EPG void InsertFromDatabase(int iEpgID, const CStdString &strName, const CStdString &strScraperName); + typedef std::map<unsigned int, CEpg*> EPGMAP; + typedef EPGMAP::iterator EPGMAP_ITR; + typedef EPGMAP::const_iterator EPGMAP_CITR; + CEpgDatabase m_database; /*!< the EPG database */ /** @name Configuration */ @@ -285,7 +289,7 @@ namespace EPG time_t m_iNextEpgUpdate; /*!< the time the EPG will be updated */ time_t m_iNextEpgActiveTagCheck; /*!< the time the EPG will be checked for active tag updates */ unsigned int m_iNextEpgId; /*!< the next epg ID that will be given to a new table when the db isn't being used */ - std::map<unsigned int, CEpg*> m_epgs; /*!< the EPGs in this container */ + EPGMAP m_epgs; /*!< the EPGs in this container */ //@} CGUIDialogProgressBarHandle * m_progressHandle; /*!< the progress dialog that is visible when updating the first time */ diff --git a/xbmc/epg/EpgDatabase.cpp b/xbmc/epg/EpgDatabase.cpp index 0d42ee2a04..b464b5ae60 100644 --- a/xbmc/epg/EpgDatabase.cpp +++ b/xbmc/epg/EpgDatabase.cpp @@ -71,7 +71,8 @@ void CEpgDatabase::CreateTables(void) "iSeriesId integer, " "iEpisodeId integer, " "iEpisodePart integer, " - "sEpisodeName varchar(128)" + "sEpisodeName varchar(128), " + "sRecordingId varchar(128)" ")" ); CLog::Log(LOGDEBUG, "EpgDB - %s - creating table 'lastepgscan'", __FUNCTION__); @@ -93,6 +94,9 @@ 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);"); } bool CEpgDatabase::DeleteEpg(void) @@ -221,6 +225,7 @@ 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(); epg.AddEntry(newTag); ++iReturn; @@ -328,26 +333,26 @@ int CEpgDatabase::Persist(const CEpgInfoTag &tag, bool bSingleUpdate /* = true * strQuery = PrepareSQL("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);", + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, sRecordingId) " + "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, '%s');", 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()); + tag.UniqueBroadcastID(), tag.RecordingId().c_str()); } else { strQuery = PrepareSQL("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);", + "iEpisodeId, iEpisodePart, sEpisodeName, iBroadcastUid, idBroadcast, sRecordingId) " + "VALUES (%u, %u, %u, '%s', '%s', '%s', %i, %i, '%s', %u, %i, %i, %i, %i, %i, %i, '%s', %i, %i, '%s');", 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); + tag.UniqueBroadcastID(), iBroadcastId, tag.RecordingId().c_str()); } if (bSingleUpdate) diff --git a/xbmc/epg/EpgDatabase.h b/xbmc/epg/EpgDatabase.h index 22970da550..5236d5774c 100644 --- a/xbmc/epg/EpgDatabase.h +++ b/xbmc/epg/EpgDatabase.h @@ -54,7 +54,7 @@ namespace EPG * @brief Get the minimal database version that is required to operate correctly. * @return The minimal database version. */ - virtual int GetSchemaVersion(void) const { return 7; }; + virtual int GetSchemaVersion(void) const { return 8; }; /*! * @brief Get the default sqlite database filename. diff --git a/xbmc/epg/EpgInfoTag.cpp b/xbmc/epg/EpgInfoTag.cpp index 9db4761d93..253f4bdabc 100644 --- a/xbmc/epg/EpgInfoTag.cpp +++ b/xbmc/epg/EpgInfoTag.cpp @@ -122,7 +122,9 @@ CEpgInfoTag::CEpgInfoTag(const CEpgInfoTag &tag) : m_startTime(tag.m_startTime), m_endTime(tag.m_endTime), m_firstAired(tag.m_firstAired), + m_strRecordingId(tag.m_strRecordingId), m_timer(tag.m_timer), + m_recording(tag.m_recording), m_epg(tag.m_epg), m_pvrChannel(tag.m_pvrChannel) { @@ -131,6 +133,7 @@ CEpgInfoTag::CEpgInfoTag(const CEpgInfoTag &tag) : CEpgInfoTag::~CEpgInfoTag() { ClearTimer(); + ClearRecording(); } bool CEpgInfoTag::operator ==(const CEpgInfoTag& right) const @@ -159,7 +162,8 @@ 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_pvrChannel == right.m_pvrChannel && + m_strRecordingId == right.m_strRecordingId); } bool CEpgInfoTag::operator !=(const CEpgInfoTag& right) const @@ -195,6 +199,8 @@ CEpgInfoTag &CEpgInfoTag::operator =(const CEpgInfoTag &other) m_endTime = other.m_endTime; m_firstAired = other.m_firstAired; m_timer = other.m_timer; + m_strRecordingId = other.m_strRecordingId; + m_recording = other.m_recording; m_epg = other.m_epg; m_pvrChannel = other.m_pvrChannel; @@ -221,6 +227,8 @@ 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(); } @@ -794,6 +802,29 @@ CStdString CEpgInfoTag::Path(void) const return retVal; } +void CEpgInfoTag::SetRecordingId(const std::string &strRecordingId) +{ + CSingleLock lock(m_critSection); + if (m_strRecordingId != strRecordingId) + { + m_strRecordingId = strRecordingId; + m_bChanged = true; + } +} + +const std::string& CEpgInfoTag::RecordingId(void) const +{ + CSingleLock lock(m_critSection); + return m_strRecordingId; +} + +bool CEpgInfoTag::HasRecordingId(void) const +{ + CSingleLock lock(m_critSection); + return !m_strRecordingId.empty(); +} + + //void CEpgInfoTag::SetTimer(CPVRTimerInfoTagPtr newTimer) //{ // CPVRTimerInfoTagPtr oldTimer; @@ -869,6 +900,7 @@ void CEpgInfoTag::Update(const EPG_TAG &tag) SetEpisodeName(tag.strEpisodeName); SetStarRating(tag.iStarRating); SetIcon(tag.strIconPath); + SetRecordingId(tag.strRecordingId); } bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = true */) @@ -895,7 +927,8 @@ bool CEpgInfoTag::Update(const CEpgInfoTag &tag, bool bUpdateBroadcastId /* = tr m_iUniqueBroadcastID != tag.m_iUniqueBroadcastID || EpgID() != tag.EpgID() || m_pvrChannel != tag.m_pvrChannel || - m_genre != tag.m_genre + m_genre != tag.m_genre || + m_strRecordingId != tag.m_strRecordingId ); if (bUpdateBroadcastId) bChanged = bChanged || m_iBroadcastId != tag.m_iBroadcastId; @@ -933,6 +966,7 @@ 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_bChanged = true; } @@ -1016,3 +1050,36 @@ void CEpgInfoTag::ClearTimer(void) if (previousTag) previousTag->ClearEpgTag(); } + +void CEpgInfoTag::SetRecording(CPVRRecordingPtr recording) +{ + CSingleLock lock(m_critSection); + if (m_recording != recording) + { + m_recording = recording; + m_bChanged = true; + } +} + +void CEpgInfoTag::ClearRecording(void) +{ + CSingleLock lock(m_critSection); + if (m_recording) + { + CPVRRecordingPtr emptyRecording; + m_recording = emptyRecording; + m_bChanged = true; + } +} + +bool CEpgInfoTag::HasRecording(void) const +{ + CSingleLock lock(m_critSection); + return m_recording != NULL; +} + +CPVRRecordingPtr CEpgInfoTag::Recording(void) const +{ + CSingleLock lock(m_critSection); + return m_recording; +} diff --git a/xbmc/epg/EpgInfoTag.h b/xbmc/epg/EpgInfoTag.h index 99d45f5d58..462a428cd2 100644 --- a/xbmc/epg/EpgInfoTag.h +++ b/xbmc/epg/EpgInfoTag.h @@ -26,6 +26,7 @@ #include "utils/StringUtils.h" #include "pvr/channels/PVRChannel.h" #include "pvr/timers/PVRTimerInfoTag.h" +#include "pvr/recordings/PVRRecording.h" #include <boost/shared_ptr.hpp> @@ -369,6 +370,24 @@ namespace EPG CStdString Path(void) const; /*! + * @brief Change the recording ID to this event. + * @param strRecordingId The new recording ID. + */ + void SetRecordingId(const std::string &strRecordingId); + + /*! + * @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. */ @@ -388,6 +407,29 @@ namespace EPG PVR::CPVRTimerInfoTagPtr Timer(void) const; /*! + * @brief Set a recording for this event or NULL to clear it. + * @param recording The recording value. + */ + void SetRecording(PVR::CPVRRecordingPtr recording); + + /*! + * @brief Clear a recording for this event. + */ + void ClearRecording(void); + + /*! + * @brief Check whether this event has a recording tag. + * @return True if it has a recording tag, false if not. + */ + bool HasRecording(void) const; + + /*! + * @brief Get a pointer to the recording for event or NULL if there is none. + * @return A pointer to the recording for event or NULL if there is none. + */ + PVR::CPVRRecordingPtr Recording(void) const; + + /*! * @brief Change the channel tag of this epg tag * @param channel The new channel */ @@ -449,15 +491,18 @@ namespace EPG CStdString m_strTitle; /*!< title */ CStdString m_strPlotOutline; /*!< plot outline */ CStdString m_strPlot; /*!< plot */ - std::vector<std::string> m_genre; /*!< genre */ + std::vector<std::string> m_genre; /*!< genre */ CStdString m_strEpisodeName; /*!< episode name */ CStdString m_strIconPath; /*!< the path to the icon */ CStdString m_strFileNameAndPath; /*!< the filename and path */ 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; + CEpg * m_epg; /*!< the schedule that this event belongs to */ PVR::CPVRChannelPtr m_pvrChannel; diff --git a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp index 2ae270745b..308309eedd 100644 --- a/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp +++ b/xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp @@ -169,8 +169,19 @@ bool CGUIDialogPVRGuideInfo::OnClickButtonSwitch(CGUIMessage &message) { Close(); PlayBackRet ret = PLAYBACK_CANCELED; - if (!m_progItem->GetEPGInfoTag()->HasPVRChannel() || - (ret = g_application.PlayFile(CFileItem(*m_progItem->GetEPGInfoTag()->ChannelTag()))) == PLAYBACK_FAIL) + CEpgInfoTag *epgTag = m_progItem->GetEPGInfoTag(); + + if (epgTag) + { + if (epgTag->HasRecording()) + ret = g_application.PlayFile(CFileItem(*epgTag->Recording())); + else if (epgTag->HasPVRChannel()) + ret = g_application.PlayFile(CFileItem(*epgTag->ChannelTag())); + } + else + ret = PLAYBACK_FAIL; + + if (ret == PLAYBACK_FAIL) { CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), g_localizeStrings.Get(19029).c_str()); // Channel could not be played. Check the log for details. CGUIDialogOK::ShowAndGetInput(19033, 0, msg, 0); diff --git a/xbmc/pvr/recordings/PVRRecording.cpp b/xbmc/pvr/recordings/PVRRecording.cpp index 5b60007900..4165bf8b47 100644 --- a/xbmc/pvr/recordings/PVRRecording.cpp +++ b/xbmc/pvr/recordings/PVRRecording.cpp @@ -33,6 +33,48 @@ using namespace PVR; using namespace EPG; +CPVRRecordingUid::CPVRRecordingUid() : + m_iClientId(PVR_VIRTUAL_CLIENT_ID) +{ +} + +CPVRRecordingUid::CPVRRecordingUid(const CPVRRecordingUid &recordingId) +{ + m_iClientId = recordingId.m_iClientId; + m_strRecordingId = recordingId.m_strRecordingId; +} + +CPVRRecordingUid::CPVRRecordingUid(int iClientId, const std::string& strRecordingId) +{ + m_iClientId = iClientId; + m_strRecordingId = strRecordingId; +} + +bool CPVRRecordingUid::operator >(const CPVRRecordingUid& right) const +{ + return (m_iClientId == right.m_iClientId) ? + m_strRecordingId > right.m_strRecordingId : + m_iClientId > right.m_iClientId; +} + +bool CPVRRecordingUid::operator <(const CPVRRecordingUid& right) const +{ + return (m_iClientId == right.m_iClientId) ? + m_strRecordingId < right.m_strRecordingId : + m_iClientId < right.m_iClientId; +} + +bool CPVRRecordingUid::operator ==(const CPVRRecordingUid& right) const +{ + return m_iClientId == right.m_iClientId && m_strRecordingId == right.m_strRecordingId; +} + +bool CPVRRecordingUid::operator !=(const CPVRRecordingUid& right) const +{ + return m_iClientId != right.m_iClientId || m_strRecordingId != right.m_strRecordingId; +} + + CPVRRecording::CPVRRecording() { Reset(); @@ -114,7 +156,7 @@ void CPVRRecording::Serialize(CVariant& value) const void CPVRRecording::Reset(void) { - m_strRecordingId = StringUtils::EmptyString; + m_strRecordingId = StringUtils::Empty; m_iClientId = 0; m_strChannelName = StringUtils::EmptyString; m_strDirectory = StringUtils::EmptyString; diff --git a/xbmc/pvr/recordings/PVRRecording.h b/xbmc/pvr/recordings/PVRRecording.h index 814b36dbfe..a6d69a6764 100644 --- a/xbmc/pvr/recordings/PVRRecording.h +++ b/xbmc/pvr/recordings/PVRRecording.h @@ -41,11 +41,34 @@ namespace PVR { + class CPVRRecording; + + typedef boost::shared_ptr<PVR::CPVRRecording> CPVRRecordingPtr; + + /*! + * @brief Representation of a CPVRRecording unique ID. + */ + class CPVRRecordingUid + { + public: + int m_iClientId; /*!< ID of the backend */ + std::string m_strRecordingId; /*!< unique ID of the recording on the client */ + + CPVRRecordingUid(); + CPVRRecordingUid(const CPVRRecordingUid& recordingId); + CPVRRecordingUid(int iClientId, const std::string &strRecordingId); + + bool operator >(const CPVRRecordingUid& right) const; + bool operator <(const CPVRRecordingUid& right) const; + bool operator ==(const CPVRRecordingUid& right) const; + bool operator !=(const CPVRRecordingUid& right) const; + }; + class CPVRRecording : public CVideoInfoTag { public: int m_iClientId; /*!< ID of the backend */ - CStdString m_strRecordingId; /*!< unique id of the recording on the client */ + std::string m_strRecordingId; /*!< unique ID of the recording on the client */ CStdString m_strChannelName; /*!< name of the channel this was recorded from */ CDateTimeSpan m_duration; /*!< duration of this recording */ int m_iPriority; /*!< priority of this recording */ diff --git a/xbmc/pvr/recordings/PVRRecordings.cpp b/xbmc/pvr/recordings/PVRRecordings.cpp index c5201203ff..a574bc1cd7 100644 --- a/xbmc/pvr/recordings/PVRRecordings.cpp +++ b/xbmc/pvr/recordings/PVRRecordings.cpp @@ -100,9 +100,9 @@ bool CPVRRecordings::IsDirectoryMember(const CStdString &strDirectory, const CSt void CPVRRecordings::GetContents(const CStdString &strDirectory, CFileItemList *results) { - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - CPVRRecording *current = m_recordings.at(iRecordingPtr); + CPVRRecordingPtr current = it->second; bool directMember = !HasAllRecordingsPathExtension(strDirectory); if (!IsDirectoryMember(RemoveAllRecordingsPathExtension(strDirectory), current->m_strDirectory, directMember)) continue; @@ -141,9 +141,9 @@ void CPVRRecordings::GetSubDirectories(const CStdString &strBase, CFileItemList std::set<CStdString> unwatchedFolders; - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - CPVRRecording *current = m_recordings.at(iRecordingPtr); + CPVRRecordingPtr current = it->second; const CStdString strCurrent = GetDirectoryFromPath(current->m_strDirectory, strUseBase); if (strCurrent.empty()) continue; @@ -308,9 +308,9 @@ int CPVRRecordings::GetRecordings(CFileItemList* results) { CSingleLock lock(m_critSection); - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - CFileItemPtr pFileItem(new CFileItem(*m_recordings.at(iRecordingPtr))); + CFileItemPtr pFileItem(new CFileItem(*it->second)); results->Add(pFileItem); } @@ -450,24 +450,20 @@ void CPVRRecordings::SetPlayCount(const CFileItem &item, int iPlayCount) return; const CPVRRecording *recording = item.GetPVRRecordingInfoTag(); - CSingleLock lock(m_critSection); - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + CPVRRecordingPtr foundRecording = GetById(recording->m_iClientId, recording->m_strRecordingId); + if (foundRecording) { - CPVRRecording *current = m_recordings.at(iRecordingPtr); - if (current->m_iClientId == recording->m_iClientId && current->m_strRecordingId.Equals(recording->m_strRecordingId)) - { - current->SetPlayCount(iPlayCount); - break; - } + CSingleLock lock(m_critSection); + foundRecording->SetPlayCount(iPlayCount); } } void CPVRRecordings::GetAll(CFileItemList &items) { CSingleLock lock(m_critSection); - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - CPVRRecording *current = m_recordings.at(iRecordingPtr); + CPVRRecordingPtr current = it->second; current->UpdateMetadata(); CFileItemPtr pFileItem(new CFileItem(*current)); @@ -484,10 +480,10 @@ CFileItemPtr CPVRRecordings::GetById(unsigned int iId) const { CFileItemPtr item; CSingleLock lock(m_critSection); - for (std::vector<CPVRRecording *>::const_iterator it = m_recordings.begin(); !item && it != m_recordings.end(); ++it) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - if (iId == (*it)->m_iRecordingId) - item = CFileItemPtr(new CFileItem(**it)); + if (iId == it->second->m_iRecordingId) + item = CFileItemPtr(new CFileItem(*(it->second))); } return item; @@ -503,11 +499,12 @@ CFileItemPtr CPVRRecordings::GetByPath(const CStdString &path) if (StringUtils::StartsWith(fileName, "recordings/")) { - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++) { - if(path.Equals(m_recordings.at(iRecordingPtr)->m_strFileNameAndPath)) + CPVRRecordingPtr current = it->second; + if (path.Equals(current->m_strFileNameAndPath)) { - CFileItemPtr fileItem(new CFileItem(*m_recordings.at(iRecordingPtr))); + CFileItemPtr fileItem(new CFileItem(*current)); return fileItem; } } @@ -517,37 +514,37 @@ CFileItemPtr CPVRRecordings::GetByPath(const CStdString &path) return fileItem; } -void CPVRRecordings::Clear() +CPVRRecordingPtr CPVRRecordings::GetById(int iClientId, const std::string &strRecordingId) const { + CPVRRecordingPtr retVal; CSingleLock lock(m_critSection); + PVR_RECORDINGMAP_CITR it = m_recordings.find(CPVRRecordingUid(iClientId, strRecordingId)); + if (it != m_recordings.end()) + retVal = it->second; - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) - delete m_recordings.at(iRecordingPtr); - m_recordings.erase(m_recordings.begin(), m_recordings.end()); + return retVal; +} + +void CPVRRecordings::Clear() +{ + CSingleLock lock(m_critSection); + m_recordings.clear(); } void CPVRRecordings::UpdateEntry(const CPVRRecording &tag) { - bool bFound = false; CSingleLock lock(m_critSection); - for (unsigned int iRecordingPtr = 0; iRecordingPtr < m_recordings.size(); iRecordingPtr++) + CPVRRecordingPtr newTag = GetById(tag.m_iClientId, tag.m_strRecordingId); + if (newTag) { - CPVRRecording *currentTag = m_recordings.at(iRecordingPtr); - if (currentTag->m_iClientId == tag.m_iClientId && - currentTag->m_strRecordingId.Equals(tag.m_strRecordingId)) - { - currentTag->Update(tag); - bFound = true; - break; - } + newTag->Update(tag); } - - if (!bFound) + else { - CPVRRecording *newTag = new CPVRRecording(); + newTag = CPVRRecordingPtr(new CPVRRecording); newTag->Update(tag); newTag->m_iRecordingId = ++m_iLastId; - m_recordings.push_back(newTag); + m_recordings.insert(std::make_pair(CPVRRecordingUid(newTag->m_iClientId, newTag->m_strRecordingId), newTag)); } } diff --git a/xbmc/pvr/recordings/PVRRecordings.h b/xbmc/pvr/recordings/PVRRecordings.h index 03787a2233..11b96164c4 100644 --- a/xbmc/pvr/recordings/PVRRecordings.h +++ b/xbmc/pvr/recordings/PVRRecordings.h @@ -32,9 +32,12 @@ namespace PVR class CPVRRecordings : public Observable { private: + typedef std::map<CPVRRecordingUid, CPVRRecordingPtr> PVR_RECORDINGMAP; + typedef PVR_RECORDINGMAP::const_iterator PVR_RECORDINGMAP_CITR; + CCriticalSection m_critSection; bool m_bIsUpdating; - std::vector<CPVRRecording *> m_recordings; + PVR_RECORDINGMAP m_recordings; unsigned int m_iLastId; virtual void UpdateFromClients(void); @@ -85,6 +88,7 @@ namespace PVR bool IsAllRecordingsDirectory(const CFileItem &item) const; bool GetDirectory(const CStdString& strPath, CFileItemList &items); CFileItemPtr GetByPath(const CStdString &path); + CPVRRecordingPtr GetById(int iClientId, const std::string &strRecordingId) const; void SetPlayCount(const CFileItem &item, int iPlayCount); void GetAll(CFileItemList &items); CFileItemPtr GetById(unsigned int iId) const; diff --git a/xbmc/pvr/windows/GUIWindowPVRCommon.cpp b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp index f4ddf1b13e..0f87b143f1 100644 --- a/xbmc/pvr/windows/GUIWindowPVRCommon.cpp +++ b/xbmc/pvr/windows/GUIWindowPVRCommon.cpp @@ -521,16 +521,25 @@ bool CGUIWindowPVRCommon::ActionPlayChannel(CFileItem *item) bool CGUIWindowPVRCommon::ActionPlayEpg(CFileItem *item) { + if (!item || !item->HasEPGInfoTag()) + return false; + CPVRChannelPtr channel; - if (item && item->HasEPGInfoTag() && item->GetEPGInfoTag()->HasPVRChannel()) - channel = item->GetEPGInfoTag()->ChannelTag(); + CEpgInfoTag *epgTag = item->GetEPGInfoTag(); + if (epgTag->HasPVRChannel()) + channel = epgTag->ChannelTag(); if (!channel || !g_PVRManager.CheckParentalLock(*channel)) return false; - CFileItem channelItem = CFileItem(*channel); + CFileItem fileItem; + if (epgTag->HasRecording()) + fileItem = CFileItem(*epgTag->Recording()); + else + fileItem = CFileItem(*channel); + g_application.SwitchToFullScreen(); - if (!PlayFile(&channelItem)) + if (!PlayFile(&fileItem)) { // CHANNELNAME could not be played. Check the log for details. CStdString msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channel->ChannelName().c_str()); |