aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/skin.confluence/720p/DialogPVRGuideOSD.xml16
-rw-r--r--addons/skin.confluence/720p/ViewsPVR.xml32
-rw-r--r--addons/skin.confluence/media/PVR-HasRecording.pngbin0 -> 3629 bytes
-rw-r--r--xbmc/GUIInfoManager.cpp5
-rw-r--r--xbmc/GUIInfoManager.h1
-rw-r--r--xbmc/addons/include/xbmc_epg_types.h1
-rw-r--r--xbmc/epg/Epg.cpp20
-rw-r--r--xbmc/epg/Epg.h4
-rw-r--r--xbmc/epg/EpgContainer.cpp24
-rw-r--r--xbmc/epg/EpgContainer.h6
-rw-r--r--xbmc/epg/EpgDatabase.cpp19
-rw-r--r--xbmc/epg/EpgDatabase.h2
-rw-r--r--xbmc/epg/EpgInfoTag.cpp71
-rw-r--r--xbmc/epg/EpgInfoTag.h47
-rw-r--r--xbmc/pvr/dialogs/GUIDialogPVRGuideInfo.cpp15
-rw-r--r--xbmc/pvr/recordings/PVRRecording.cpp44
-rw-r--r--xbmc/pvr/recordings/PVRRecording.h25
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.cpp77
-rw-r--r--xbmc/pvr/recordings/PVRRecordings.h6
-rw-r--r--xbmc/pvr/windows/GUIWindowPVRCommon.cpp17
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
new file mode 100644
index 0000000000..1b048d69fc
--- /dev/null
+++ b/addons/skin.confluence/media/PVR-HasRecording.png
Binary files differ
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());