Skip to content

Commit a018bcd

Browse files
authored
Merge pull request xbmc#24528 from ksooo/pvr-epg-tag-playlist
[PVR] "Play EPG Tags as Movies"
2 parents 6563071 + 360c463 commit a018bcd

File tree

10 files changed

+243
-82
lines changed

10 files changed

+243
-82
lines changed

addons/resource.language.en_gb/resources/strings.po

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11620,13 +11620,37 @@ msgctxt "#19349"
1162011620
msgid "User preference"
1162111621
msgstr ""
1162211622

11623-
#. label for PVR settings use backend channel group order control
11623+
#. label for PVR setting use backend channel group order control
1162411624
#: system/settings/settings.xml
1162511625
msgctxt "#19350"
1162611626
msgid "Use channel group order from backend(s)"
1162711627
msgstr ""
1162811628

11629-
#empty strings from id 19351 to 19498
11629+
#. label for PVR settings category for live tv catchup and video on demand
11630+
#: system/settings/settings.xml
11631+
msgctxt "#19351"
11632+
msgid "Catchup / Video On Demand"
11633+
msgstr ""
11634+
11635+
#. label for PVR setting play next programme automatically in catchup / vod mode
11636+
#: system/settings/settings.xml
11637+
msgctxt "#19352"
11638+
msgid "Play next programme automatically"
11639+
msgstr ""
11640+
11641+
#. Label of a context menu entry to kick catchup / VOD playback, starting with the selected item, auto playing all following programmes
11642+
#: xbmc/pvr/PVRContextMenuItem.cpp
11643+
msgctxt "#19353"
11644+
msgid "Play programmes from here"
11645+
msgstr ""
11646+
11647+
#. Label of a context menu entry to kick catchup / VOD playback of only the selected item, not auto playing any following programmes
11648+
#: xbmc/pvr/PVRContextMenuItem.cpp
11649+
msgctxt "#19354"
11650+
msgid "Play only this programme"
11651+
msgstr ""
11652+
11653+
#empty strings from id 19355 to 19498
1163011654

1163111655
#. label for epg genre value
1163211656
#: xbmc/pvr/epg/Epg.cpp
@@ -21052,7 +21076,13 @@ msgctxt "#36438"
2105221076
msgid "Any repositories"
2105321077
msgstr ""
2105421078

21055-
#empty strings from id 36439 to 36441
21079+
#. Description of setting with label #19352 "Play next programme automatically"
21080+
#: system/settings/settings.xml
21081+
msgctxt "#36439"
21082+
msgid "Enable automatic playback of the next programme when playing past programmes (catchup) or for Video On Demand (VOD) channels. Please note that catchup and VOD only work if supported by the channel provider."
21083+
msgstr ""
21084+
21085+
#empty strings from id 36440 to 36441
2105621086

2105721087
#. Description of setting "System -> Audio output -> Volume control steps" with label #1302
2105821088
#: system/settings/settings.xml

system/settings/settings.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1861,7 +1861,14 @@
18611861
<control type="list" format="string" />
18621862
</setting>
18631863
</group>
1864-
<group id="2" label="14304">
1864+
<group id="2" label="19351">
1865+
<setting id="pvrplayback.autoplaynextprogramme" type="boolean" label="19352" help="36439">
1866+
<level>2</level>
1867+
<default>true</default>
1868+
<control type="toggle" />
1869+
</setting>
1870+
</group>
1871+
<group id="3" label="14304">
18651872
<setting id="pvrplayback.enableradiords" type="boolean" label="29980" help="29981">
18661873
<level>1</level>
18671874
<default>true</default>

xbmc/application/Application.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2932,11 +2932,20 @@ bool CApplication::OnMessage(CGUIMessage& message)
29322932
PlayFile(stackHelper->SetNextStackPartCurrentFileItem(), "", true);
29332933
return true;
29342934
}
2935+
2936+
// For EPG playlist items we keep the player open to ensure continuous viewing experience.
2937+
const bool isEpgPlaylistItem{
2938+
m_itemCurrentFile->GetProperty("epg_playlist_item").asBoolean(false)};
2939+
29352940
ResetCurrentItem();
2936-
if (!CServiceBroker::GetPlaylistPlayer().PlayNext(1, true))
2937-
GetComponent<CApplicationPlayer>()->ClosePlayer();
29382941

2939-
PlaybackCleanup();
2942+
if (!isEpgPlaylistItem)
2943+
{
2944+
if (!CServiceBroker::GetPlaylistPlayer().PlayNext(1, true))
2945+
GetComponent<CApplicationPlayer>()->ClosePlayer();
2946+
2947+
PlaybackCleanup();
2948+
}
29402949

29412950
#ifdef HAS_PYTHON
29422951
CServiceBroker::GetXBPython().OnPlayBackEnded();

xbmc/pvr/PVRContextMenus.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#include "pvr/timers/PVRTimerInfoTag.h"
3030
#include "pvr/timers/PVRTimers.h"
3131
#include "pvr/timers/PVRTimersPath.h"
32+
#include "settings/Settings.h"
33+
#include "settings/SettingsComponent.h"
3234
#include "utils/URIUtils.h"
3335

3436
#include <memory>
@@ -57,6 +59,7 @@ namespace CONTEXTMENUITEM
5759
};
5860

5961
DECL_STATICCONTEXTMENUITEM(PlayEpgTag);
62+
DECL_CONTEXTMENUITEM(PlayEpgTagFromHere);
6063
DECL_STATICCONTEXTMENUITEM(PlayRecording);
6164
DECL_CONTEXTMENUITEM(ShowInformation);
6265
DECL_STATICCONTEXTMENUITEM(ShowChannelGuide);
@@ -125,6 +128,37 @@ bool PlayEpgTag::Execute(const CFileItemPtr& item) const
125128
return CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(*item);
126129
}
127130

131+
///////////////////////////////////////////////////////////////////////////////
132+
// Play epg tag from here
133+
134+
std::string PlayEpgTagFromHere::GetLabel(const CFileItem& item) const
135+
{
136+
if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
137+
CSettings::SETTING_PVRPLAYBACK_AUTOPLAYNEXTPROGRAMME))
138+
return g_localizeStrings.Get(19354); /* Play only this programme */
139+
140+
return g_localizeStrings.Get(19353); /* Play programmes from here */
141+
}
142+
143+
bool PlayEpgTagFromHere::IsVisible(const CFileItem& item) const
144+
{
145+
const std::shared_ptr<const CPVREpgInfoTag> epg(item.GetEPGInfoTag());
146+
if (epg)
147+
return epg->IsPlayable();
148+
149+
return false;
150+
}
151+
152+
bool PlayEpgTagFromHere::Execute(const CFileItemPtr& item) const
153+
{
154+
ContentUtils::PlayMode mode{ContentUtils::PlayMode::PLAY_FROM_HERE};
155+
if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
156+
CSettings::SETTING_PVRPLAYBACK_AUTOPLAYNEXTPROGRAMME))
157+
mode = ContentUtils::PlayMode::PLAY_ONLY_THIS;
158+
159+
return CServiceBroker::GetPVRManager().Get<PVR::GUI::Playback>().PlayEpgTag(*item, mode);
160+
}
161+
128162
///////////////////////////////////////////////////////////////////////////////
129163
// Play recording
130164

@@ -724,6 +758,7 @@ CPVRContextMenuManager& CPVRContextMenuManager::GetInstance()
724758
CPVRContextMenuManager::CPVRContextMenuManager()
725759
: m_items({
726760
std::make_shared<CONTEXTMENUITEM::PlayEpgTag>(19190), /* Play programme */
761+
std::make_shared<CONTEXTMENUITEM::PlayEpgTagFromHere>(),
727762
std::make_shared<CONTEXTMENUITEM::PlayRecording>(19687), /* Play recording */
728763
std::make_shared<CONTEXTMENUITEM::ShowInformation>(),
729764
std::make_shared<CONTEXTMENUITEM::ShowChannelGuide>(19686), /* Channel guide */

xbmc/pvr/PVRManager.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,11 @@ void CPVRManager::OnPlaybackStopped(const CFileItem& item)
850850
void CPVRManager::OnPlaybackEnded(const CFileItem& item)
851851
{
852852
// Playback ended, but not due to user interaction
853-
OnPlaybackStopped(item);
853+
if (m_playbackState->OnPlaybackEnded(item))
854+
PublishEvent(PVREvent::ChannelPlaybackStopped);
855+
856+
Get<PVR::GUI::Channels>().OnPlaybackStopped(item);
857+
m_epgContainer->OnPlaybackStopped();
854858
}
855859

856860
void CPVRManager::LocalizationChanged()

xbmc/pvr/PVRPlaybackState.cpp

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
#include "ServiceBroker.h"
1313
#include "XBDateTime.h"
1414
#include "cores/DataCacheCore.h"
15+
#include "messaging/ApplicationMessenger.h"
1516
#include "pvr/PVRManager.h"
17+
#include "pvr/PVRStreamProperties.h"
1618
#include "pvr/addons/PVRClient.h"
1719
#include "pvr/channels/PVRChannel.h"
1820
#include "pvr/channels/PVRChannelGroup.h"
@@ -283,10 +285,120 @@ bool CPVRPlaybackState::OnPlaybackStopped(const CFileItem& item)
283285
return bChanged;
284286
}
285287

286-
void CPVRPlaybackState::OnPlaybackEnded(const CFileItem& item)
288+
std::unique_ptr<CFileItem> CPVRPlaybackState::GetNextAutoplayItem(const CFileItem& item)
289+
{
290+
if (!item.GetProperty("epg_playlist_item").asBoolean(false))
291+
return {};
292+
293+
std::unique_lock<CCriticalSection> lock(m_critSection);
294+
295+
if (item.HasEPGInfoTag() && item.GetEPGInfoTag()->ClientID() == m_playingClientId &&
296+
item.GetEPGInfoTag()->UniqueChannelID() == m_playingEpgTagChannelUniqueId &&
297+
item.GetEPGInfoTag()->UniqueBroadcastID() == m_playingEpgTagUniqueId)
298+
{
299+
auto& pvrMgr{CServiceBroker::GetPVRManager()};
300+
const std::shared_ptr<const CPVREpg> epg{
301+
pvrMgr.EpgContainer().GetByChannelUid(m_playingClientId, m_playingEpgTagChannelUniqueId)};
302+
const std::shared_ptr<const CPVRClient> client{pvrMgr.GetClient(m_playingClientId)};
303+
if (epg && client)
304+
{
305+
const std::vector<std::shared_ptr<CPVREpgInfoTag>> tags{epg->GetTags()};
306+
bool nextIsMatch{false};
307+
for (const auto& tag : tags)
308+
{
309+
if (nextIsMatch)
310+
{
311+
// Next to play is successor of given item in channel's timeline.
312+
return std::make_unique<CFileItem>(tag);
313+
}
314+
315+
if (tag != tags.back() && tag->StartAsUTC() == m_playingEpgTag->StartAsUTC() &&
316+
tag->EndAsUTC() == m_playingEpgTag->EndAsUTC())
317+
nextIsMatch = true;
318+
}
319+
320+
if (!nextIsMatch)
321+
{
322+
// No more non-live epg items in channel's timeline. Next to play is the live channel.
323+
const std::shared_ptr<CPVRChannelGroup> group{
324+
pvrMgr.ChannelGroups()->Get(m_playingEpgTag->IsRadio())->GetGroupAll()};
325+
if (group)
326+
{
327+
const std::shared_ptr<CPVRChannelGroupMember> groupMember{
328+
group->GetByUniqueID({m_playingClientId, m_playingEpgTagChannelUniqueId})};
329+
if (groupMember)
330+
return std::make_unique<CFileItem>(groupMember);
331+
}
332+
}
333+
}
334+
}
335+
return {};
336+
}
337+
338+
bool CPVRPlaybackState::OnPlaybackEnded(const CFileItem& item)
287339
{
288340
// Playback ended, but not due to user interaction
289-
OnPlaybackStopped(item);
341+
342+
std::unique_ptr<CFileItem> nextToPlay{GetNextAutoplayItem(item)};
343+
if (nextToPlay)
344+
StartPlayback(nextToPlay.release());
345+
346+
return OnPlaybackStopped(item);
347+
}
348+
349+
void CPVRPlaybackState::StartPlayback(
350+
CFileItem* item,
351+
ContentUtils::PlayMode mode /* = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM */) const
352+
{
353+
// Obtain dynamic playback url and properties from the respective pvr client
354+
const std::shared_ptr<const CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item);
355+
if (client)
356+
{
357+
CPVRStreamProperties props;
358+
359+
if (item->IsPVRChannel())
360+
{
361+
client->GetChannelStreamProperties(item->GetPVRChannelInfoTag(), props);
362+
}
363+
else if (item->IsPVRRecording())
364+
{
365+
client->GetRecordingStreamProperties(item->GetPVRRecordingInfoTag(), props);
366+
}
367+
else if (item->IsEPG())
368+
{
369+
client->GetEpgTagStreamProperties(item->GetEPGInfoTag(), props);
370+
371+
if (mode == ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM)
372+
{
373+
if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
374+
CSettings::SETTING_PVRPLAYBACK_AUTOPLAYNEXTPROGRAMME))
375+
item->SetProperty("epg_playlist_item", true);
376+
}
377+
else if (mode == ContentUtils::PlayMode::PLAY_FROM_HERE)
378+
{
379+
item->SetProperty("epg_playlist_item", true);
380+
}
381+
}
382+
383+
if (props.size())
384+
{
385+
const std::string url = props.GetStreamURL();
386+
if (!url.empty())
387+
item->SetDynPath(url);
388+
389+
const std::string mime = props.GetStreamMimeType();
390+
if (!mime.empty())
391+
{
392+
item->SetMimeType(mime);
393+
item->SetContentLookup(false);
394+
}
395+
396+
for (const auto& prop : props)
397+
item->SetProperty(prop.first, prop.second);
398+
}
399+
}
400+
401+
CServiceBroker::GetAppMessenger()->PostMsg(TMSG_MEDIA_PLAY, 0, 0, static_cast<void*>(item));
290402
}
291403

292404
bool CPVRPlaybackState::IsPlaying() const

xbmc/pvr/PVRPlaybackState.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#pragma once
1010

1111
#include "threads/CriticalSection.h"
12+
#include "utils/ContentUtils.h"
1213

1314
#include <memory>
1415
#include <string>
@@ -23,6 +24,7 @@ class CPVRChannelGroup;
2324
class CPVRChannelGroupMember;
2425
class CPVREpgInfoTag;
2526
class CPVRRecording;
27+
class CPVRStreamProperties;
2628

2729
class CPVRPlaybackState
2830
{
@@ -63,8 +65,18 @@ class CPVRPlaybackState
6365
/*!
6466
* @brief Inform that playback of an item has stopped without user interaction.
6567
* @param item The item that ended to play.
68+
* @return True, if the state has changed, false otherwise
69+
*/
70+
bool OnPlaybackEnded(const CFileItem& item);
71+
72+
/*!
73+
* @brief Start playback of the given item.
74+
* @param item containing a channel, a recording or an epg tag.
75+
* @param mode playback mode.
6676
*/
67-
void OnPlaybackEnded(const CFileItem& item);
77+
void StartPlayback(
78+
CFileItem* item,
79+
ContentUtils::PlayMode mode = ContentUtils::PlayMode::CHECK_AUTO_PLAY_NEXT_ITEM) const;
6880

6981
/*!
7082
* @brief Check if a TV channel, radio channel or recording is playing.
@@ -245,6 +257,13 @@ class CPVRPlaybackState
245257
private:
246258
void ClearData();
247259

260+
/*!
261+
* @brief Return the next item to play automatically, if any.
262+
* @param item The item which just finished playback.
263+
* @return The item to play next, if any, nullptr otherwise.
264+
*/
265+
std::unique_ptr<CFileItem> GetNextAutoplayItem(const CFileItem& item);
266+
248267
/*!
249268
* @brief Set the active group to the group of the supplied channel group member.
250269
* @param channel The channel group member

0 commit comments

Comments
 (0)