Merge pull request #26386 from ksooo/guiinfo-fix-listitem-filenamenoextension
[xbmc.git] / xbmc / pvr / guilib / guiinfo / PVRGUITimesInfo.cpp
blob4559d8715b8f4ab36ac388ddba8da0537c989965
1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "PVRGUITimesInfo.h"
11 #include "ServiceBroker.h"
12 #include "cores/DataCacheCore.h"
13 #include "pvr/PVRManager.h"
14 #include "pvr/PVRPlaybackState.h"
15 #include "pvr/channels/PVRChannel.h"
16 #include "pvr/channels/PVRChannelGroupsContainer.h"
17 #include "pvr/epg/EpgInfoTag.h"
18 #include "pvr/recordings/PVRRecording.h"
19 #include "settings/AdvancedSettings.h"
20 #include "settings/SettingsComponent.h"
21 #include "utils/MathUtils.h"
22 #include "utils/StringUtils.h"
24 #include <chrono>
25 #include <cmath>
26 #include <ctime>
27 #include <memory>
28 #include <mutex>
30 using namespace PVR;
32 CPVRGUITimesInfo::CPVRGUITimesInfo()
34 Reset();
37 void CPVRGUITimesInfo::Reset()
39 std::unique_lock<CCriticalSection> lock(m_critSection);
41 m_iStartTime = 0;
42 m_iDuration = 0;
43 m_iTimeshiftStartTime = 0;
44 m_iTimeshiftEndTime = 0;
45 m_iTimeshiftPlayTime = 0;
46 m_iTimeshiftOffset = 0;
48 m_iTimeshiftProgressStartTime = 0;
49 m_iTimeshiftProgressEndTime = 0;
50 m_iTimeshiftProgressDuration = 0;
52 m_playingEpgTag.reset();
53 m_playingChannel.reset();
56 void CPVRGUITimesInfo::UpdatePlayingTag()
58 const std::shared_ptr<const CPVRChannel> currentChannel =
59 CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
60 std::shared_ptr<CPVREpgInfoTag> currentTag = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingEpgTag();
62 if (currentChannel || currentTag)
64 if (currentChannel && !currentTag)
65 currentTag = currentChannel->GetEPGNow();
67 const std::shared_ptr<const CPVRChannelGroupsContainer> groups =
68 CServiceBroker::GetPVRManager().ChannelGroups();
70 std::unique_lock<CCriticalSection> lock(m_critSection);
72 const std::shared_ptr<const CPVRChannel> playingChannel =
73 m_playingEpgTag ? groups->GetChannelForEpgTag(m_playingEpgTag) : nullptr;
75 if (!m_playingEpgTag || !currentTag || !playingChannel || !currentChannel ||
76 m_playingEpgTag->StartAsUTC() != currentTag->StartAsUTC() ||
77 m_playingEpgTag->EndAsUTC() != currentTag->EndAsUTC() || *playingChannel != *currentChannel)
79 if (currentTag)
81 m_playingEpgTag = currentTag;
82 m_iDuration = m_playingEpgTag->GetDuration();
84 else if (m_iTimeshiftEndTime > m_iTimeshiftStartTime)
86 m_playingEpgTag.reset();
87 std::chrono::duration<unsigned int> duration{m_iTimeshiftEndTime - m_iTimeshiftStartTime};
88 m_iDuration = duration.count();
90 else
92 m_playingEpgTag.reset();
93 m_iDuration = 0;
97 else
99 const std::shared_ptr<const CPVRRecording> recording =
100 CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingRecording();
101 if (recording)
103 std::unique_lock<CCriticalSection> lock(m_critSection);
104 m_playingEpgTag.reset();
105 m_iDuration = recording->GetDuration();
110 void CPVRGUITimesInfo::UpdateTimeshiftData()
112 if (!CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV() && !CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio())
114 // If nothing is playing (anymore), there is no need to update data.
115 Reset();
116 return;
119 time_t now = std::time(nullptr);
120 time_t iStartTime;
121 int64_t iPlayTime, iMinTime, iMaxTime;
122 CServiceBroker::GetDataCacheCore().GetPlayTimes(iStartTime, iPlayTime, iMinTime, iMaxTime);
123 bool bPlaying = CServiceBroker::GetDataCacheCore().GetSpeed() == 1.0f;
124 const std::shared_ptr<const CPVRChannel> playingChannel =
125 CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel();
127 std::unique_lock<CCriticalSection> lock(m_critSection);
129 if (playingChannel != m_playingChannel)
131 // playing channel changed. we need to reset offset and playtime.
132 m_iTimeshiftOffset = 0;
133 m_iTimeshiftPlayTime = 0;
134 m_playingChannel = playingChannel;
137 if (!iStartTime)
139 if (m_iStartTime == 0)
140 iStartTime = now;
141 else
142 iStartTime = m_iStartTime;
144 iMinTime = iPlayTime;
145 iMaxTime = iPlayTime;
148 m_iStartTime = iStartTime;
149 m_iTimeshiftStartTime = iStartTime + iMinTime / 1000;
150 m_iTimeshiftEndTime = iStartTime + iMaxTime / 1000;
152 if (m_iTimeshiftEndTime > m_iTimeshiftStartTime)
154 // timeshifting supported
155 m_iTimeshiftPlayTime = iStartTime + iPlayTime / 1000;
156 if (iMaxTime > iPlayTime)
157 m_iTimeshiftOffset = (iMaxTime - iPlayTime) / 1000;
158 else
159 m_iTimeshiftOffset = 0;
161 else
163 // timeshifting not supported
164 if (bPlaying)
165 m_iTimeshiftPlayTime = now - m_iTimeshiftOffset;
167 m_iTimeshiftOffset = now - m_iTimeshiftPlayTime;
170 UpdateTimeshiftProgressData();
173 void CPVRGUITimesInfo::UpdateTimeshiftProgressData()
175 // Note: General idea of the ts progress is always to be able to visualise both the complete
176 // ts buffer and the complete playing epg event (if any) side by side with the same time
177 // scale. TS progress start and end times will be calculated accordingly.
178 // + Start is usually ts buffer start, except if start time of playing epg event is
179 // before ts buffer start, then progress start is epg event start.
180 // + End is usually ts buffer end, except if end time of playing epg event is
181 // after ts buffer end, then progress end is epg event end.
182 // In simple timeshift mode (settings value), progress start is always the start time of
183 // playing epg event and progress end is always the end time of playing epg event.
185 std::unique_lock<CCriticalSection> lock(m_critSection);
187 //////////////////////////////////////////////////////////////////////////////////////
188 // start time
189 //////////////////////////////////////////////////////////////////////////////////////
190 bool bUpdatedStartTime = false;
191 if (m_playingEpgTag)
193 time_t start = 0;
194 m_playingEpgTag->StartAsUTC().GetAsTime(start);
195 if (start < m_iTimeshiftStartTime ||
196 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bPVRTimeshiftSimpleOSD)
198 // playing event started before start of ts buffer or simple ts osd to be used
199 m_iTimeshiftProgressStartTime = start;
200 bUpdatedStartTime = true;
204 if (!bUpdatedStartTime)
206 // default to ts buffer start
207 m_iTimeshiftProgressStartTime = m_iTimeshiftStartTime;
210 //////////////////////////////////////////////////////////////////////////////////////
211 // end time
212 //////////////////////////////////////////////////////////////////////////////////////
213 bool bUpdatedEndTime = false;
214 if (m_playingEpgTag)
216 time_t end = 0;
217 m_playingEpgTag->EndAsUTC().GetAsTime(end);
218 if (end > m_iTimeshiftEndTime ||
219 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_bPVRTimeshiftSimpleOSD)
221 // playing event will end after end of ts buffer or simple ts osd to be used
222 m_iTimeshiftProgressEndTime = end;
223 bUpdatedEndTime = true;
227 if (!bUpdatedEndTime)
229 // default to ts buffer end
230 m_iTimeshiftProgressEndTime = m_iTimeshiftEndTime;
233 //////////////////////////////////////////////////////////////////////////////////////
234 // duration
235 //////////////////////////////////////////////////////////////////////////////////////
236 std::chrono::duration<unsigned int> duration{m_iTimeshiftProgressEndTime -
237 m_iTimeshiftProgressStartTime};
238 m_iTimeshiftProgressDuration = duration.count();
241 void CPVRGUITimesInfo::Update()
243 UpdatePlayingTag();
244 UpdateTimeshiftData();
247 std::string CPVRGUITimesInfo::TimeToTimeString(time_t datetime, TIME_FORMAT format, bool withSeconds)
249 CDateTime time;
250 time.SetFromUTCDateTime(datetime);
251 return time.GetAsLocalizedTime(format, withSeconds);
254 std::string CPVRGUITimesInfo::GetTimeshiftStartTime(TIME_FORMAT format) const
256 std::unique_lock<CCriticalSection> lock(m_critSection);
257 return TimeToTimeString(m_iTimeshiftStartTime, format, false);
260 std::string CPVRGUITimesInfo::GetTimeshiftEndTime(TIME_FORMAT format) const
262 std::unique_lock<CCriticalSection> lock(m_critSection);
263 return TimeToTimeString(m_iTimeshiftEndTime, format, false);
266 std::string CPVRGUITimesInfo::GetTimeshiftPlayTime(TIME_FORMAT format) const
268 std::unique_lock<CCriticalSection> lock(m_critSection);
269 return TimeToTimeString(m_iTimeshiftPlayTime, format, true);
272 std::string CPVRGUITimesInfo::GetTimeshiftOffset(TIME_FORMAT format) const
274 std::unique_lock<CCriticalSection> lock(m_critSection);
275 return StringUtils::SecondsToTimeString(m_iTimeshiftOffset, format);
278 std::string CPVRGUITimesInfo::GetTimeshiftProgressDuration(TIME_FORMAT format) const
280 std::unique_lock<CCriticalSection> lock(m_critSection);
281 return StringUtils::SecondsToTimeString(m_iTimeshiftProgressDuration, format);
284 std::string CPVRGUITimesInfo::GetTimeshiftProgressStartTime(TIME_FORMAT format) const
286 std::unique_lock<CCriticalSection> lock(m_critSection);
287 return TimeToTimeString(m_iTimeshiftProgressStartTime, format, false);
290 std::string CPVRGUITimesInfo::GetTimeshiftProgressEndTime(TIME_FORMAT format) const
292 std::unique_lock<CCriticalSection> lock(m_critSection);
293 return TimeToTimeString(m_iTimeshiftProgressEndTime, format, false);
296 std::string CPVRGUITimesInfo::GetEpgEventDuration(
297 const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
299 std::unique_lock<CCriticalSection> lock(m_critSection);
300 return StringUtils::SecondsToTimeString(GetEpgEventDuration(epgTag), format);
303 std::string CPVRGUITimesInfo::GetEpgEventElapsedTime(
304 const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
306 int iElapsed = 0;
307 std::unique_lock<CCriticalSection> lock(m_critSection);
308 if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
309 iElapsed = epgTag->Progress();
310 else
311 iElapsed = GetElapsedTime();
313 return StringUtils::SecondsToTimeString(iElapsed, format);
316 std::string CPVRGUITimesInfo::GetEpgEventRemainingTime(
317 const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
319 std::unique_lock<CCriticalSection> lock(m_critSection);
320 return StringUtils::SecondsToTimeString(GetRemainingTime(epgTag), format);
323 std::string CPVRGUITimesInfo::GetEpgEventFinishTime(
324 const std::shared_ptr<const CPVREpgInfoTag>& epgTag, TIME_FORMAT format) const
326 CDateTime finish = CDateTime::GetCurrentDateTime();
327 finish += CDateTimeSpan(0, 0, 0, GetRemainingTime(epgTag));
328 return finish.GetAsLocalizedTime(format);
331 std::string CPVRGUITimesInfo::GetEpgEventSeekTime(int iSeekSize, TIME_FORMAT format) const
333 return StringUtils::SecondsToTimeString(GetElapsedTime() + iSeekSize, format);
336 int CPVRGUITimesInfo::GetElapsedTime() const
338 std::unique_lock<CCriticalSection> lock(m_critSection);
339 if (m_playingEpgTag || m_iTimeshiftStartTime)
341 CDateTime current(m_iTimeshiftPlayTime);
342 CDateTime start = m_playingEpgTag ? m_playingEpgTag->StartAsUTC() : CDateTime(m_iTimeshiftStartTime);
343 CDateTimeSpan time = current > start ? current - start : CDateTimeSpan(0, 0, 0, 0);
344 return time.GetSecondsTotal();
346 else
348 return 0;
352 int CPVRGUITimesInfo::GetRemainingTime(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
354 std::unique_lock<CCriticalSection> lock(m_critSection);
355 if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
356 return epgTag->GetDuration() - epgTag->Progress();
357 else
358 return static_cast<int>(m_iDuration - GetElapsedTime());
361 int CPVRGUITimesInfo::GetTimeshiftProgress() const
363 std::unique_lock<CCriticalSection> lock(m_critSection);
364 const std::chrono::duration<double> total{m_iTimeshiftEndTime - m_iTimeshiftStartTime};
365 if (total.count())
367 const std::chrono::duration<double> current{m_iTimeshiftPlayTime - m_iTimeshiftStartTime};
368 return MathUtils::round_int(current.count() / total.count() * 100.0);
370 return 0;
373 int CPVRGUITimesInfo::GetTimeshiftProgressDuration() const
375 std::unique_lock<CCriticalSection> lock(m_critSection);
376 return static_cast<int>(m_iTimeshiftProgressDuration);
379 int CPVRGUITimesInfo::GetTimeshiftProgressPlayPosition() const
381 std::unique_lock<CCriticalSection> lock(m_critSection);
382 if (m_iTimeshiftProgressDuration)
384 const std::chrono::duration<double> duration{m_iTimeshiftPlayTime -
385 m_iTimeshiftProgressStartTime};
386 return MathUtils::round_int(duration.count() / m_iTimeshiftProgressDuration * 100.0);
388 return 0;
391 int CPVRGUITimesInfo::GetTimeshiftProgressEpgStart() const
393 std::unique_lock<CCriticalSection> lock(m_critSection);
394 if (m_playingEpgTag && m_iTimeshiftProgressDuration)
396 time_t epgStart = 0;
397 m_playingEpgTag->StartAsUTC().GetAsTime(epgStart);
398 const std::chrono::duration<double> duration{epgStart - m_iTimeshiftProgressStartTime};
399 return MathUtils::round_int(duration.count() / m_iTimeshiftProgressDuration * 100.0);
401 return 0;
404 int CPVRGUITimesInfo::GetTimeshiftProgressEpgEnd() const
406 std::unique_lock<CCriticalSection> lock(m_critSection);
407 if (m_playingEpgTag && m_iTimeshiftProgressDuration)
409 time_t epgEnd = 0;
410 m_playingEpgTag->EndAsUTC().GetAsTime(epgEnd);
411 const std::chrono::duration<double> duration{epgEnd - m_iTimeshiftProgressStartTime};
412 return MathUtils::round_int(duration.count() / m_iTimeshiftProgressDuration * 100.0);
414 return 0;
417 int CPVRGUITimesInfo::GetTimeshiftProgressBufferStart() const
419 std::unique_lock<CCriticalSection> lock(m_critSection);
420 if (m_iTimeshiftProgressDuration)
422 const std::chrono::duration<double> duration{m_iTimeshiftStartTime -
423 m_iTimeshiftProgressStartTime};
424 return MathUtils::round_int(duration.count() / m_iTimeshiftProgressDuration * 100.0);
426 return 0;
429 int CPVRGUITimesInfo::GetTimeshiftProgressBufferEnd() const
431 std::unique_lock<CCriticalSection> lock(m_critSection);
432 if (m_iTimeshiftProgressDuration)
434 const std::chrono::duration<double> duration{m_iTimeshiftEndTime -
435 m_iTimeshiftProgressStartTime};
436 return MathUtils::round_int(duration.count() / m_iTimeshiftProgressDuration * 100.0);
438 return 0;
441 int CPVRGUITimesInfo::GetEpgEventDuration(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
443 std::unique_lock<CCriticalSection> lock(m_critSection);
444 if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
445 return epgTag->GetDuration();
446 else
447 return static_cast<int>(m_iDuration);
450 int CPVRGUITimesInfo::GetEpgEventProgress(const std::shared_ptr<const CPVREpgInfoTag>& epgTag) const
452 std::unique_lock<CCriticalSection> lock(m_critSection);
453 if (epgTag && m_playingEpgTag && *epgTag != *m_playingEpgTag)
454 return MathUtils::round_int(epgTag->ProgressPercentage());
455 else if (m_iDuration)
456 return MathUtils::round_int(static_cast<double>(GetElapsedTime()) / m_iDuration * 100.0);
457 else
458 return 0;
461 bool CPVRGUITimesInfo::IsTimeshifting() const
463 std::unique_lock<CCriticalSection> lock(m_critSection);
464 return (m_iTimeshiftOffset > static_cast<unsigned int>(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_iPVRTimeshiftThreshold));