1 /* 2 * Copyright (C) 2016-2018 Team Kodi 3 * This file is part of Kodi - https://kodi.tv 4 * 5 * SPDX-License-Identifier: GPL-2.0-or-later 6 * See LICENSES/README.md for more information. 7 */ 8 9 #include "PVRGUIActions.h" 10 11 #include "Application.h" 12 #include "FileItem.h" 13 #include "ServiceBroker.h" 14 #include "Util.h" 15 #include "cores/DataCacheCore.h" 16 #include "dialogs/GUIDialogBusy.h" 17 #include "dialogs/GUIDialogKaiToast.h" 18 #include "dialogs/GUIDialogNumeric.h" 19 #include "dialogs/GUIDialogProgress.h" 20 #include "dialogs/GUIDialogSelect.h" 21 #include "dialogs/GUIDialogYesNo.h" 22 #include "filesystem/IDirectory.h" 23 #include "guilib/GUIComponent.h" 24 #include "guilib/GUIKeyboardFactory.h" 25 #include "guilib/GUIWindowManager.h" 26 #include "guilib/LocalizeStrings.h" 27 #include "guilib/WindowIDs.h" 28 #include "messaging/ApplicationMessenger.h" 29 #include "messaging/helpers/DialogHelper.h" 30 #include "messaging/helpers/DialogOKHelper.h" 31 #include "network/Network.h" 32 #include "pvr/PVRDatabase.h" 33 #include "pvr/PVREventLogJob.h" 34 #include "pvr/PVRItem.h" 35 #include "pvr/PVRManager.h" 36 #include "pvr/PVRPlaybackState.h" 37 #include "pvr/PVRStreamProperties.h" 38 #include "pvr/addons/PVRClient.h" 39 #include "pvr/addons/PVRClientMenuHooks.h" 40 #include "pvr/addons/PVRClients.h" 41 #include "pvr/channels/PVRChannel.h" 42 #include "pvr/channels/PVRChannelGroup.h" 43 #include "pvr/channels/PVRChannelGroups.h" 44 #include "pvr/channels/PVRChannelGroupsContainer.h" 45 #include "pvr/dialogs/GUIDialogPVRChannelGuide.h" 46 #include "pvr/dialogs/GUIDialogPVRGuideInfo.h" 47 #include "pvr/dialogs/GUIDialogPVRRecordingInfo.h" 48 #include "pvr/dialogs/GUIDialogPVRRecordingSettings.h" 49 #include "pvr/dialogs/GUIDialogPVRTimerSettings.h" 50 #include "pvr/epg/EpgContainer.h" 51 #include "pvr/epg/EpgDatabase.h" 52 #include "pvr/epg/EpgInfoTag.h" 53 #include "pvr/recordings/PVRRecording.h" 54 #include "pvr/recordings/PVRRecordings.h" 55 #include "pvr/recordings/PVRRecordingsPath.h" 56 #include "pvr/timers/PVRTimerInfoTag.h" 57 #include "pvr/timers/PVRTimers.h" 58 #include "pvr/windows/GUIWindowPVRSearch.h" 59 #include "settings/MediaSettings.h" 60 #include "settings/Settings.h" 61 #include "threads/IRunnable.h" 62 #include "threads/SingleLock.h" 63 #include "utils/StringUtils.h" 64 #include "utils/SystemInfo.h" 65 #include "utils/URIUtils.h" 66 #include "utils/Variant.h" 67 #include "utils/log.h" 68 #include "video/VideoDatabase.h" 69 70 #include <chrono> 71 #include <iterator> 72 #include <map> 73 #include <memory> 74 #include <string> 75 #include <thread> 76 #include <utility> 77 #include <vector> 78 79 using namespace KODI::MESSAGING; 80 81 namespace PVR 82 { 83 class AsyncRecordingAction : private IRunnable 84 { 85 public: 86 bool Execute(const CFileItemPtr& item); 87 88 protected: 89 AsyncRecordingAction() = default; 90 91 private: 92 // IRunnable implementation 93 void Run() override; 94 95 // the worker function 96 virtual bool DoRun(const CFileItemPtr& item) = 0; 97 98 CFileItemPtr m_item; 99 bool m_bSuccess = false; 100 }; 101 Execute(const CFileItemPtr & item)102 bool AsyncRecordingAction::Execute(const CFileItemPtr& item) 103 { 104 m_item = item; 105 CGUIDialogBusy::Wait(this, 100, false); 106 return m_bSuccess; 107 } 108 Run()109 void AsyncRecordingAction::Run() 110 { 111 m_bSuccess = DoRun(m_item); 112 113 if (m_bSuccess) 114 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate(); 115 } 116 117 class AsyncRenameRecording : public AsyncRecordingAction 118 { 119 public: AsyncRenameRecording(const std::string & strNewName)120 explicit AsyncRenameRecording(const std::string& strNewName) : m_strNewName(strNewName) {} 121 122 private: DoRun(const std::shared_ptr<CFileItem> & item)123 bool DoRun(const std::shared_ptr<CFileItem>& item) override 124 { 125 if (item->IsUsablePVRRecording()) 126 { 127 return item->GetPVRRecordingInfoTag()->Rename(m_strNewName); 128 } 129 else 130 { 131 CLog::LogF(LOGERROR, "Cannot rename item '{}': no valid recording tag", item->GetPath()); 132 return false; 133 } 134 } 135 std::string m_strNewName; 136 }; 137 138 class AsyncDeleteRecording : public AsyncRecordingAction 139 { 140 public: AsyncDeleteRecording(bool bWatchedOnly=false)141 explicit AsyncDeleteRecording(bool bWatchedOnly = false) : m_bWatchedOnly(bWatchedOnly) {} 142 143 private: DoRun(const std::shared_ptr<CFileItem> & item)144 bool DoRun(const std::shared_ptr<CFileItem>& item) override 145 { 146 CFileItemList items; 147 if (item->m_bIsFolder) 148 { 149 CUtil::GetRecursiveListing(item->GetPath(), items, "", XFILE::DIR_FLAG_NO_FILE_INFO); 150 } 151 else 152 { 153 items.Add(item); 154 } 155 156 bool bReturn = true; 157 for (const auto& itemToDelete : items) 158 { 159 if (itemToDelete->IsPVRRecording() && 160 (!m_bWatchedOnly || itemToDelete->GetPVRRecordingInfoTag()->GetPlayCount() > 0)) 161 bReturn &= itemToDelete->GetPVRRecordingInfoTag()->Delete(); 162 } 163 return bReturn; 164 } 165 bool m_bWatchedOnly = false; 166 }; 167 168 class AsyncEmptyRecordingsTrash : public AsyncRecordingAction 169 { 170 private: DoRun(const std::shared_ptr<CFileItem> & item)171 bool DoRun(const std::shared_ptr<CFileItem>& item) override 172 { 173 return CServiceBroker::GetPVRManager().Clients()->DeleteAllRecordingsFromTrash() == PVR_ERROR_NO_ERROR; 174 } 175 }; 176 177 class AsyncUndeleteRecording : public AsyncRecordingAction 178 { 179 private: DoRun(const std::shared_ptr<CFileItem> & item)180 bool DoRun(const std::shared_ptr<CFileItem>& item) override 181 { 182 if (item->IsDeletedPVRRecording()) 183 { 184 return item->GetPVRRecordingInfoTag()->Undelete(); 185 } 186 else 187 { 188 CLog::LogF(LOGERROR, "Cannot undelete item '{}': no valid recording tag", item->GetPath()); 189 return false; 190 } 191 } 192 }; 193 194 class AsyncSetRecordingPlayCount : public AsyncRecordingAction 195 { 196 private: DoRun(const CFileItemPtr & item)197 bool DoRun(const CFileItemPtr& item) override 198 { 199 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item); 200 if (client) 201 { 202 const std::shared_ptr<CPVRRecording> recording = item->GetPVRRecordingInfoTag(); 203 return client->SetRecordingPlayCount(*recording, recording->GetLocalPlayCount()) == PVR_ERROR_NO_ERROR; 204 } 205 return false; 206 } 207 }; 208 209 class AsyncSetRecordingLifetime : public AsyncRecordingAction 210 { 211 private: DoRun(const CFileItemPtr & item)212 bool DoRun(const CFileItemPtr& item) override 213 { 214 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item); 215 if (client) 216 return client->SetRecordingLifetime(*item->GetPVRRecordingInfoTag()) == PVR_ERROR_NO_ERROR; 217 return false; 218 } 219 }; 220 CPVRGUIActions()221 CPVRGUIActions::CPVRGUIActions() 222 : m_settings({CSettings::SETTING_LOOKANDFEEL_STARTUPACTION, 223 CSettings::SETTING_PVRMANAGER_PRESELECTPLAYINGCHANNEL, 224 CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME, 225 CSettings::SETTING_PVRRECORD_INSTANTRECORDACTION, 226 CSettings::SETTING_PVRPLAYBACK_CONFIRMCHANNELSWITCH, 227 CSettings::SETTING_PVRPLAYBACK_SWITCHTOFULLSCREENCHANNELTYPES, 228 CSettings::SETTING_PVRPARENTAL_PIN, CSettings::SETTING_PVRPARENTAL_ENABLED, 229 CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME, 230 CSettings::SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME, 231 CSettings::SETTING_PVRREMINDERS_AUTOCLOSEDELAY, 232 CSettings::SETTING_PVRREMINDERS_AUTORECORD, 233 CSettings::SETTING_PVRREMINDERS_AUTOSWITCH}) 234 { 235 } 236 ShowEPGInfo(const CFileItemPtr & item) const237 bool CPVRGUIActions::ShowEPGInfo(const CFileItemPtr& item) const 238 { 239 const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel()); 240 if (channel && CheckParentalLock(channel) != ParentalCheckResult::SUCCESS) 241 return false; 242 243 const std::shared_ptr<CPVREpgInfoTag> epgTag(CPVRItem(item).GetEpgInfoTag()); 244 if (!epgTag) 245 { 246 CLog::LogF(LOGERROR, "No epg tag!"); 247 return false; 248 } 249 250 CGUIDialogPVRGuideInfo* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRGuideInfo>(WINDOW_DIALOG_PVR_GUIDE_INFO); 251 if (!pDlgInfo) 252 { 253 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_GUIDE_INFO!"); 254 return false; 255 } 256 257 pDlgInfo->SetProgInfo(epgTag); 258 pDlgInfo->Open(); 259 return true; 260 } 261 262 ShowChannelEPG(const CFileItemPtr & item) const263 bool CPVRGUIActions::ShowChannelEPG(const CFileItemPtr& item) const 264 { 265 const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel()); 266 if (channel && CheckParentalLock(channel) != ParentalCheckResult::SUCCESS) 267 return false; 268 269 CGUIDialogPVRChannelGuide* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRChannelGuide>(WINDOW_DIALOG_PVR_CHANNEL_GUIDE); 270 if (!pDlgInfo) 271 { 272 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_CHANNEL_GUIDE!"); 273 return false; 274 } 275 276 pDlgInfo->Open(channel); 277 return true; 278 } 279 280 ShowRecordingInfo(const CFileItemPtr & item) const281 bool CPVRGUIActions::ShowRecordingInfo(const CFileItemPtr& item) const 282 { 283 if (!item->IsPVRRecording()) 284 { 285 CLog::LogF(LOGERROR, "No recording!"); 286 return false; 287 } 288 289 CGUIDialogPVRRecordingInfo* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRRecordingInfo>(WINDOW_DIALOG_PVR_RECORDING_INFO); 290 if (!pDlgInfo) 291 { 292 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_RECORDING_INFO!"); 293 return false; 294 } 295 296 pDlgInfo->SetRecording(item.get()); 297 pDlgInfo->Open(); 298 return true; 299 } 300 FindSimilar(const std::shared_ptr<CFileItem> & item) const301 bool CPVRGUIActions::FindSimilar(const std::shared_ptr<CFileItem>& item) const 302 { 303 const bool bRadio(CPVRItem(item).IsRadio()); 304 305 int windowSearchId = bRadio ? WINDOW_RADIO_SEARCH : WINDOW_TV_SEARCH; 306 CGUIWindowPVRSearchBase* windowSearch; 307 if (bRadio) 308 windowSearch = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowPVRRadioSearch>(windowSearchId); 309 else 310 windowSearch = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIWindowPVRTVSearch>(windowSearchId); 311 312 if (!windowSearch) 313 { 314 CLog::LogF(LOGERROR, "Unable to get {}!", 315 bRadio ? "WINDOW_RADIO_SEARCH" : "WINDOW_TV_SEARCH"); 316 return false; 317 } 318 319 //! @todo If we want dialogs to spawn program search in a clean way - without having to force-close any 320 // other dialogs - we must introduce a search dialog with functionality similar to the search window. 321 322 for (int iId = CServiceBroker::GetGUI()->GetWindowManager().GetTopmostModalDialog(true /* ignoreClosing */); 323 iId != WINDOW_INVALID; 324 iId = CServiceBroker::GetGUI()->GetWindowManager().GetTopmostModalDialog(true /* ignoreClosing */)) 325 { 326 CLog::LogF(LOGWARNING, 327 "Have to close modal dialog with id {} before search window can be opened.", iId); 328 329 CGUIWindow* window = CServiceBroker::GetGUI()->GetWindowManager().GetWindow(iId); 330 if (window) 331 { 332 window->Close(); 333 } 334 else 335 { 336 CLog::LogF(LOGERROR, "Unable to get window instance {}! Cannot open search window.", iId); 337 return false; // return, otherwise we run into an endless loop 338 } 339 } 340 341 windowSearch->SetItemToSearch(item); 342 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(windowSearchId); 343 return true; 344 }; 345 ShowTimerSettings(const std::shared_ptr<CPVRTimerInfoTag> & timer) const346 bool CPVRGUIActions::ShowTimerSettings(const std::shared_ptr<CPVRTimerInfoTag>& timer) const 347 { 348 CGUIDialogPVRTimerSettings* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRTimerSettings>(WINDOW_DIALOG_PVR_TIMER_SETTING); 349 if (!pDlgInfo) 350 { 351 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_TIMER_SETTING!"); 352 return false; 353 } 354 355 pDlgInfo->SetTimer(timer); 356 pDlgInfo->Open(); 357 358 return pDlgInfo->IsConfirmed(); 359 } 360 AddReminder(const std::shared_ptr<CFileItem> & item) const361 bool CPVRGUIActions::AddReminder(const std::shared_ptr<CFileItem>& item) const 362 { 363 const std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag(); 364 if (!epgTag) 365 { 366 CLog::LogF(LOGERROR, "No epg tag!"); 367 return false; 368 } 369 370 if (CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag)) 371 { 372 HELPERS::ShowOKDialogText(CVariant{19033}, // "Information" 373 CVariant{19034}); // "There is already a timer set for this event" 374 return false; 375 } 376 377 const std::shared_ptr<CPVRTimerInfoTag> newTimer = CPVRTimerInfoTag::CreateReminderFromEpg(epgTag); 378 if (!newTimer) 379 { 380 HELPERS::ShowOKDialogText(CVariant{19033}, // "Information" 381 CVariant{19094}); // Timer creation failed. Unsupported timer type. 382 return false; 383 } 384 385 return AddTimer(newTimer); 386 } 387 AddTimer(bool bRadio) const388 bool CPVRGUIActions::AddTimer(bool bRadio) const 389 { 390 const std::shared_ptr<CPVRTimerInfoTag> newTimer(new CPVRTimerInfoTag(bRadio)); 391 if (ShowTimerSettings(newTimer)) 392 { 393 return AddTimer(newTimer); 394 } 395 return false; 396 } 397 AddTimer(const CFileItemPtr & item,bool bShowTimerSettings) const398 bool CPVRGUIActions::AddTimer(const CFileItemPtr& item, bool bShowTimerSettings) const 399 { 400 return AddTimer(item, false, bShowTimerSettings, false); 401 } 402 AddTimerRule(const std::shared_ptr<CFileItem> & item,bool bShowTimerSettings,bool bFallbackToOneShotTimer) const403 bool CPVRGUIActions::AddTimerRule(const std::shared_ptr<CFileItem>& item, bool bShowTimerSettings, bool bFallbackToOneShotTimer) const 404 { 405 return AddTimer(item, true, bShowTimerSettings, bFallbackToOneShotTimer); 406 } 407 AddTimer(const std::shared_ptr<CFileItem> & item,bool bCreateRule,bool bShowTimerSettings,bool bFallbackToOneShotTimer) const408 bool CPVRGUIActions::AddTimer(const std::shared_ptr<CFileItem>& item, bool bCreateRule, bool bShowTimerSettings, bool bFallbackToOneShotTimer) const 409 { 410 const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel()); 411 if (!channel) 412 { 413 CLog::LogF(LOGERROR, "No channel!"); 414 return false; 415 } 416 417 if (CheckParentalLock(channel) != ParentalCheckResult::SUCCESS) 418 return false; 419 420 std::shared_ptr<CPVREpgInfoTag> epgTag = CPVRItem(item).GetEpgInfoTag(); 421 if (epgTag) 422 { 423 if (epgTag->IsGapTag()) 424 epgTag.reset(); // for gap tags, we can only create instant timers 425 } 426 else if (bCreateRule) 427 { 428 CLog::LogF(LOGERROR, "No epg tag!"); 429 return false; 430 } 431 432 std::shared_ptr<CPVRTimerInfoTag> timer(bCreateRule || !epgTag ? nullptr : CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(epgTag)); 433 std::shared_ptr<CPVRTimerInfoTag> rule (bCreateRule ? CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer) : nullptr); 434 if (timer || rule) 435 { 436 HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19034}); // "Information", "There is already a timer set for this event" 437 return false; 438 } 439 440 std::shared_ptr<CPVRTimerInfoTag> newTimer(epgTag ? CPVRTimerInfoTag::CreateFromEpg(epgTag, bCreateRule) : CPVRTimerInfoTag::CreateInstantTimerTag(channel)); 441 if (!newTimer) 442 { 443 if (bCreateRule && bFallbackToOneShotTimer) 444 newTimer = CPVRTimerInfoTag::CreateFromEpg(epgTag, false); 445 446 if (!newTimer) 447 { 448 HELPERS::ShowOKDialogText(CVariant{19033}, // "Information" 449 bCreateRule 450 ? CVariant{19095} // Timer rule creation failed. Unsupported timer type. 451 : CVariant{19094}); // Timer creation failed. Unsupported timer type. 452 return false; 453 } 454 } 455 456 if (bShowTimerSettings) 457 { 458 if (!ShowTimerSettings(newTimer)) 459 return false; 460 } 461 462 return AddTimer(newTimer); 463 } 464 AddTimer(const std::shared_ptr<CPVRTimerInfoTag> & item) const465 bool CPVRGUIActions::AddTimer(const std::shared_ptr<CPVRTimerInfoTag>& item) const 466 { 467 if (!item->Channel() && item->GetTimerType() && !item->GetTimerType()->IsEpgBasedTimerRule()) 468 { 469 CLog::LogF(LOGERROR, "No channel given"); 470 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19109}); // "Error", "Could not save the timer. Check the log for more information about this message." 471 return false; 472 } 473 474 if (!item->IsTimerRule() && item->GetEpgInfoTag() && !item->GetEpgInfoTag()->IsRecordable()) 475 { 476 HELPERS::ShowOKDialogText(CVariant{19033}, CVariant{19189}); // "Information", "The PVR backend does not allow to record this event." 477 return false; 478 } 479 480 if (CheckParentalLock(item->Channel()) != ParentalCheckResult::SUCCESS) 481 return false; 482 483 if (!CServiceBroker::GetPVRManager().Timers()->AddTimer(item)) 484 { 485 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19109}); // "Error", "Could not save the timer. Check the log for more information about this message." 486 return false; 487 } 488 489 return true; 490 } 491 492 namespace 493 { 494 enum PVRRECORD_INSTANTRECORDACTION 495 { 496 NONE = -1, 497 RECORD_CURRENT_SHOW = 0, 498 RECORD_INSTANTRECORDTIME = 1, 499 ASK = 2, 500 RECORD_30_MINUTES = 3, 501 RECORD_60_MINUTES = 4, 502 RECORD_120_MINUTES = 5, 503 RECORD_NEXT_SHOW = 6 504 }; 505 506 class InstantRecordingActionSelector 507 { 508 public: 509 explicit InstantRecordingActionSelector(int iInstantRecordTime); 510 virtual ~InstantRecordingActionSelector() = default; 511 512 void AddAction(PVRRECORD_INSTANTRECORDACTION eAction, const std::string& title); 513 void PreSelectAction(PVRRECORD_INSTANTRECORDACTION eAction); 514 PVRRECORD_INSTANTRECORDACTION Select(); 515 516 private: 517 int m_iInstantRecordTime; 518 CGUIDialogSelect* m_pDlgSelect; // not owner! 519 std::map<PVRRECORD_INSTANTRECORDACTION, int> m_actions; 520 }; 521 InstantRecordingActionSelector(int iInstantRecordTime)522 InstantRecordingActionSelector::InstantRecordingActionSelector(int iInstantRecordTime) 523 : m_iInstantRecordTime(iInstantRecordTime), 524 m_pDlgSelect(CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT)) 525 { 526 if (m_pDlgSelect) 527 { 528 m_pDlgSelect->SetMultiSelection(false); 529 m_pDlgSelect->SetHeading(CVariant{19086}); // Instant recording action 530 } 531 else 532 { 533 CLog::LogF(LOGERROR, "Unable to obtain WINDOW_DIALOG_SELECT instance"); 534 } 535 } 536 AddAction(PVRRECORD_INSTANTRECORDACTION eAction,const std::string & title)537 void InstantRecordingActionSelector::AddAction(PVRRECORD_INSTANTRECORDACTION eAction, const std::string& title) 538 { 539 if (m_actions.find(eAction) == m_actions.end()) 540 { 541 switch (eAction) 542 { 543 case RECORD_INSTANTRECORDTIME: 544 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19090).c_str(), m_iInstantRecordTime)); // Record next <default duration> minutes 545 break; 546 case RECORD_30_MINUTES: 547 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19090).c_str(), 30)); // Record next 30 minutes 548 break; 549 case RECORD_60_MINUTES: 550 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19090).c_str(), 60)); // Record next 60 minutes 551 break; 552 case RECORD_120_MINUTES: 553 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19090).c_str(), 120)); // Record next 120 minutes 554 break; 555 case RECORD_CURRENT_SHOW: 556 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19091).c_str(), title.c_str())); // Record current show (<title>) 557 break; 558 case RECORD_NEXT_SHOW: 559 m_pDlgSelect->Add(StringUtils::Format(g_localizeStrings.Get(19092).c_str(), title.c_str())); // Record next show (<title>) 560 break; 561 case NONE: 562 case ASK: 563 default: 564 return; 565 } 566 567 m_actions.insert(std::make_pair(eAction, m_actions.size())); 568 } 569 } 570 PreSelectAction(PVRRECORD_INSTANTRECORDACTION eAction)571 void InstantRecordingActionSelector::PreSelectAction(PVRRECORD_INSTANTRECORDACTION eAction) 572 { 573 const auto& it = m_actions.find(eAction); 574 if (it != m_actions.end()) 575 m_pDlgSelect->SetSelected(it->second); 576 } 577 Select()578 PVRRECORD_INSTANTRECORDACTION InstantRecordingActionSelector::Select() 579 { 580 PVRRECORD_INSTANTRECORDACTION eAction = NONE; 581 582 m_pDlgSelect->Open(); 583 584 if (m_pDlgSelect->IsConfirmed()) 585 { 586 int iSelection = m_pDlgSelect->GetSelectedItem(); 587 for (const auto& action : m_actions) 588 { 589 if (action.second == iSelection) 590 { 591 eAction = action.first; 592 break; 593 } 594 } 595 } 596 597 return eAction; 598 } 599 600 } // unnamed namespace 601 ToggleRecordingOnPlayingChannel()602 bool CPVRGUIActions::ToggleRecordingOnPlayingChannel() 603 { 604 const std::shared_ptr<CPVRChannel> channel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); 605 if (channel && channel->CanRecord()) 606 return SetRecordingOnChannel(channel, !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)); 607 608 return false; 609 } 610 SetRecordingOnChannel(const std::shared_ptr<CPVRChannel> & channel,bool bOnOff)611 bool CPVRGUIActions::SetRecordingOnChannel(const std::shared_ptr<CPVRChannel>& channel, bool bOnOff) 612 { 613 bool bReturn = false; 614 615 if (!channel) 616 return bReturn; 617 618 if (CheckParentalLock(channel) != ParentalCheckResult::SUCCESS) 619 return bReturn; 620 621 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(channel->ClientID()); 622 if (client && client->GetClientCapabilities().SupportsTimers()) 623 { 624 /* timers are supported on this channel */ 625 if (bOnOff && !CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)) 626 { 627 std::shared_ptr<CPVREpgInfoTag> epgTag; 628 int iDuration = m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME); 629 630 int iAction = m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDACTION); 631 switch (iAction) 632 { 633 case RECORD_CURRENT_SHOW: 634 epgTag = channel->GetEPGNow(); 635 break; 636 637 case RECORD_INSTANTRECORDTIME: 638 epgTag.reset(); 639 break; 640 641 case ASK: 642 { 643 PVRRECORD_INSTANTRECORDACTION ePreselect = RECORD_INSTANTRECORDTIME; 644 const int iDurationDefault = m_settings.GetIntValue(CSettings::SETTING_PVRRECORD_INSTANTRECORDTIME); 645 InstantRecordingActionSelector selector(iDurationDefault); 646 std::shared_ptr<CPVREpgInfoTag> epgTagNext; 647 648 // fixed length recordings 649 selector.AddAction(RECORD_30_MINUTES, ""); 650 selector.AddAction(RECORD_60_MINUTES, ""); 651 selector.AddAction(RECORD_120_MINUTES, ""); 652 653 if (iDurationDefault != 30 && iDurationDefault != 60 && iDurationDefault != 120) 654 selector.AddAction(RECORD_INSTANTRECORDTIME, ""); 655 656 // epg-based recordings 657 epgTag = channel->GetEPGNow(); 658 if (epgTag) 659 { 660 bool bLocked = CServiceBroker::GetPVRManager().IsParentalLocked(epgTag); 661 662 // "now" 663 const std::string currentTitle = bLocked ? g_localizeStrings.Get(19266) /* Parental locked */ : epgTag->Title(); 664 selector.AddAction(RECORD_CURRENT_SHOW, currentTitle); 665 ePreselect = RECORD_CURRENT_SHOW; 666 667 // "next" 668 epgTagNext = channel->GetEPGNext(); 669 if (epgTagNext) 670 { 671 const std::string nextTitle = bLocked ? g_localizeStrings.Get(19266) /* Parental locked */ : epgTagNext->Title(); 672 selector.AddAction(RECORD_NEXT_SHOW, nextTitle); 673 674 // be smart. if current show is almost over, preselect next show. 675 if (epgTag->ProgressPercentage() > 90.0f) 676 ePreselect = RECORD_NEXT_SHOW; 677 } 678 } 679 680 if (ePreselect == RECORD_INSTANTRECORDTIME) 681 { 682 if (iDurationDefault == 30) 683 ePreselect = RECORD_30_MINUTES; 684 else if (iDurationDefault == 60) 685 ePreselect = RECORD_60_MINUTES; 686 else if (iDurationDefault == 120) 687 ePreselect = RECORD_120_MINUTES; 688 } 689 690 selector.PreSelectAction(ePreselect); 691 692 PVRRECORD_INSTANTRECORDACTION eSelected = selector.Select(); 693 switch (eSelected) 694 { 695 case NONE: 696 return false; // dialog canceled 697 698 case RECORD_30_MINUTES: 699 iDuration = 30; 700 epgTag.reset(); 701 break; 702 703 case RECORD_60_MINUTES: 704 iDuration = 60; 705 epgTag.reset(); 706 break; 707 708 case RECORD_120_MINUTES: 709 iDuration = 120; 710 epgTag.reset(); 711 break; 712 713 case RECORD_INSTANTRECORDTIME: 714 iDuration = iDurationDefault; 715 epgTag.reset(); 716 break; 717 718 case RECORD_CURRENT_SHOW: 719 break; 720 721 case RECORD_NEXT_SHOW: 722 epgTag = epgTagNext; 723 break; 724 725 default: 726 CLog::LogF(LOGERROR, 727 "Unknown instant record action selection ({}), defaulting to fixed " 728 "length recording.", 729 static_cast<int>(eSelected)); 730 epgTag.reset(); 731 break; 732 } 733 break; 734 } 735 736 default: 737 CLog::LogF(LOGERROR, 738 "Unknown instant record action setting value ({}), defaulting to fixed " 739 "length recording.", 740 iAction); 741 break; 742 } 743 744 const std::shared_ptr<CPVRTimerInfoTag> newTimer(epgTag ? CPVRTimerInfoTag::CreateFromEpg(epgTag, false) : CPVRTimerInfoTag::CreateInstantTimerTag(channel, iDuration)); 745 746 if (newTimer) 747 bReturn = CServiceBroker::GetPVRManager().Timers()->AddTimer(newTimer); 748 749 if (!bReturn) 750 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19164}); // "Error", "Could not start recording. Check the log for more information about this message." 751 } 752 else if (!bOnOff && CServiceBroker::GetPVRManager().Timers()->IsRecordingOnChannel(*channel)) 753 { 754 /* delete active timers */ 755 bReturn = CServiceBroker::GetPVRManager().Timers()->DeleteTimersOnChannel(channel, true, true); 756 757 if (!bReturn) 758 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19170}); // "Error", "Could not stop recording. Check the log for more information about this message." 759 } 760 } 761 762 return bReturn; 763 } 764 ToggleTimer(const CFileItemPtr & item) const765 bool CPVRGUIActions::ToggleTimer(const CFileItemPtr& item) const 766 { 767 if (!item->HasEPGInfoTag()) 768 return false; 769 770 const std::shared_ptr<CPVRTimerInfoTag> timer(CPVRItem(item).GetTimerInfoTag()); 771 if (timer) 772 { 773 if (timer->IsRecording()) 774 return StopRecording(item); 775 else 776 return DeleteTimer(item); 777 } 778 else 779 return AddTimer(item, false); 780 } 781 ToggleTimerState(const CFileItemPtr & item) const782 bool CPVRGUIActions::ToggleTimerState(const CFileItemPtr& item) const 783 { 784 if (!item->HasPVRTimerInfoTag()) 785 return false; 786 787 const std::shared_ptr<CPVRTimerInfoTag> timer(item->GetPVRTimerInfoTag()); 788 if (timer->m_state == PVR_TIMER_STATE_DISABLED) 789 timer->m_state = PVR_TIMER_STATE_SCHEDULED; 790 else 791 timer->m_state = PVR_TIMER_STATE_DISABLED; 792 793 if (CServiceBroker::GetPVRManager().Timers()->UpdateTimer(timer)) 794 return true; 795 796 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19263}); // "Error", "Could not update the timer. Check the log for more information about this message." 797 return false; 798 } 799 EditTimer(const CFileItemPtr & item) const800 bool CPVRGUIActions::EditTimer(const CFileItemPtr& item) const 801 { 802 const std::shared_ptr<CPVRTimerInfoTag> timer(CPVRItem(item).GetTimerInfoTag()); 803 if (!timer) 804 { 805 CLog::LogF(LOGERROR, "No timer!"); 806 return false; 807 } 808 809 // clone the timer. 810 const std::shared_ptr<CPVRTimerInfoTag> newTimer(new CPVRTimerInfoTag); 811 newTimer->UpdateEntry(timer); 812 813 if (ShowTimerSettings(newTimer) && (!timer->GetTimerType()->IsReadOnly() || timer->GetTimerType()->SupportsEnableDisable())) 814 { 815 if (newTimer->GetTimerType() == timer->GetTimerType()) 816 { 817 if (CServiceBroker::GetPVRManager().Timers()->UpdateTimer(newTimer)) 818 return true; 819 820 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19263}); // "Error", "Could not update the timer. Check the log for more information about this message." 821 return false; 822 } 823 else 824 { 825 // timer type changed. delete the original timer, then create the new timer. this order is 826 // important. for instance, the new timer might be a rule which schedules the original timer. 827 // deleting the original timer after creating the rule would do literally this and we would 828 // end up with one timer missing wrt to the rule defined by the new timer. 829 if (DeleteTimer(timer, timer->IsRecording(), false)) 830 { 831 if (AddTimer(newTimer)) 832 return true; 833 834 // rollback. 835 return AddTimer(timer); 836 } 837 } 838 } 839 return false; 840 } 841 EditTimerRule(const CFileItemPtr & item) const842 bool CPVRGUIActions::EditTimerRule(const CFileItemPtr& item) const 843 { 844 const std::shared_ptr<CFileItem> parentTimer = GetTimerRule(item); 845 if (parentTimer) 846 return EditTimer(parentTimer); 847 848 return false; 849 } 850 GetTimerRule(const std::shared_ptr<CFileItem> & item) const851 std::shared_ptr<CFileItem> CPVRGUIActions::GetTimerRule(const std::shared_ptr<CFileItem>& item) const 852 { 853 std::shared_ptr<CPVRTimerInfoTag> timer; 854 if (item && item->HasEPGInfoTag()) 855 timer = CServiceBroker::GetPVRManager().Timers()->GetTimerForEpgTag(item->GetEPGInfoTag()); 856 else if (item && item->HasPVRTimerInfoTag()) 857 timer = item->GetPVRTimerInfoTag(); 858 859 if (timer) 860 { 861 timer = CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer); 862 if (timer) 863 return std::make_shared<CFileItem>(timer); 864 } 865 return {}; 866 } 867 DeleteTimer(const CFileItemPtr & item) const868 bool CPVRGUIActions::DeleteTimer(const CFileItemPtr& item) const 869 { 870 return DeleteTimer(item, false, false); 871 } 872 DeleteTimerRule(const CFileItemPtr & item) const873 bool CPVRGUIActions::DeleteTimerRule(const CFileItemPtr& item) const 874 { 875 return DeleteTimer(item, false, true); 876 } 877 DeleteTimer(const CFileItemPtr & item,bool bIsRecording,bool bDeleteRule) const878 bool CPVRGUIActions::DeleteTimer(const CFileItemPtr& item, bool bIsRecording, bool bDeleteRule) const 879 { 880 std::shared_ptr<CPVRTimerInfoTag> timer; 881 const std::shared_ptr<CPVRRecording> recording(CPVRItem(item).GetRecording()); 882 if (recording) 883 timer = recording->GetRecordingTimer(); 884 885 if (!timer) 886 timer = CPVRItem(item).GetTimerInfoTag(); 887 888 if (!timer) 889 { 890 CLog::LogF(LOGERROR, "No timer!"); 891 return false; 892 } 893 894 if (bDeleteRule && !timer->IsTimerRule()) 895 timer = CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer); 896 897 if (!timer) 898 { 899 CLog::LogF(LOGERROR, "No timer rule!"); 900 return false; 901 } 902 903 if (bIsRecording) 904 { 905 if (ConfirmStopRecording(timer)) 906 { 907 if (CServiceBroker::GetPVRManager().Timers()->DeleteTimer(timer, true, false) == TimerOperationResult::OK) 908 return true; 909 910 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19170}); // "Error", "Could not stop recording. Check the log for more information about this message." 911 return false; 912 } 913 } 914 else if (timer->HasTimerType() && !timer->GetTimerType()->AllowsDelete()) 915 { 916 return false; 917 } 918 else 919 { 920 bool bAlsoDeleteRule(false); 921 if (ConfirmDeleteTimer(timer, bAlsoDeleteRule)) 922 return DeleteTimer(timer, false, bAlsoDeleteRule); 923 } 924 return false; 925 } 926 DeleteTimer(const std::shared_ptr<CPVRTimerInfoTag> & timer,bool bIsRecording,bool bDeleteRule) const927 bool CPVRGUIActions::DeleteTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer, bool bIsRecording, bool bDeleteRule) const 928 { 929 TimerOperationResult result = CServiceBroker::GetPVRManager().Timers()->DeleteTimer(timer, bIsRecording, bDeleteRule); 930 switch (result) 931 { 932 case TimerOperationResult::RECORDING: 933 { 934 // recording running. ask the user if it should be deleted anyway 935 if (HELPERS::ShowYesNoDialogText(CVariant{122}, // "Confirm delete" 936 CVariant{19122}) // "This timer is still recording. Are you sure you want to delete this timer?" 937 != HELPERS::DialogResponse::YES) 938 return false; 939 940 return DeleteTimer(timer, true, bDeleteRule); 941 } 942 case TimerOperationResult::OK: 943 { 944 return true; 945 } 946 case TimerOperationResult::FAILED: 947 { 948 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19110}); // "Error", "Could not delete the timer. Check the log for more information about this message." 949 return false; 950 } 951 default: 952 { 953 CLog::LogF(LOGERROR, "Unhandled TimerOperationResult ({})!", static_cast<int>(result)); 954 break; 955 } 956 } 957 return false; 958 } 959 ConfirmDeleteTimer(const std::shared_ptr<CPVRTimerInfoTag> & timer,bool & bDeleteRule) const960 bool CPVRGUIActions::ConfirmDeleteTimer(const std::shared_ptr<CPVRTimerInfoTag>& timer, bool& bDeleteRule) const 961 { 962 bool bConfirmed(false); 963 const std::shared_ptr<CPVRTimerInfoTag> parentTimer(CServiceBroker::GetPVRManager().Timers()->GetTimerRule(timer)); 964 965 if (parentTimer && parentTimer->HasTimerType() && parentTimer->GetTimerType()->AllowsDelete()) 966 { 967 // timer was scheduled by a deletable timer rule. prompt user for confirmation for deleting the timer rule, including scheduled timers. 968 bool bCancel(false); 969 bDeleteRule = CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, // "Confirm delete" 970 CVariant{840}, // "Do you want to delete only this timer or also the timer rule that has scheduled it?" 971 CVariant{""}, 972 CVariant{timer->Title()}, 973 bCancel, 974 CVariant{841}, // "Only this" 975 CVariant{593}, // "All" 976 0); // no autoclose 977 bConfirmed = !bCancel; 978 } 979 else 980 { 981 bDeleteRule = false; 982 983 // prompt user for confirmation for deleting the timer 984 bConfirmed = CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, // "Confirm delete" 985 timer->IsTimerRule() 986 ? CVariant{845} // "Are you sure you want to delete this timer rule and all timers it has scheduled?" 987 : CVariant{846}, // "Are you sure you want to delete this timer?" 988 CVariant{""}, 989 CVariant{timer->Title()}); 990 } 991 992 return bConfirmed; 993 } 994 StopRecording(const CFileItemPtr & item) const995 bool CPVRGUIActions::StopRecording(const CFileItemPtr& item) const 996 { 997 if (!DeleteTimer(item, true, false)) 998 return false; 999 1000 CServiceBroker::GetPVRManager().TriggerRecordingsUpdate(); 1001 return true; 1002 } 1003 ConfirmStopRecording(const std::shared_ptr<CPVRTimerInfoTag> & timer) const1004 bool CPVRGUIActions::ConfirmStopRecording(const std::shared_ptr<CPVRTimerInfoTag>& timer) const 1005 { 1006 return CGUIDialogYesNo::ShowAndGetInput(CVariant{847}, // "Confirm stop recording" 1007 CVariant{848}, // "Are you sure you want to stop this recording?" 1008 CVariant{""}, 1009 CVariant{timer->Title()}); 1010 } 1011 EditRecording(const CFileItemPtr & item) const1012 bool CPVRGUIActions::EditRecording(const CFileItemPtr& item) const 1013 { 1014 const std::shared_ptr<CPVRRecording> recording = CPVRItem(item).GetRecording(); 1015 if (!recording) 1016 { 1017 CLog::LogF(LOGERROR, "No recording!"); 1018 return false; 1019 } 1020 1021 std::shared_ptr<CPVRRecording> origRecording(new CPVRRecording); 1022 origRecording->Update(*recording, 1023 *CServiceBroker::GetPVRManager().GetClient(recording->m_iClientId)); 1024 1025 if (!ShowRecordingSettings(recording)) 1026 return false; 1027 1028 if (origRecording->m_strTitle != recording->m_strTitle) 1029 { 1030 if (!AsyncRenameRecording(recording->m_strTitle).Execute(item)) 1031 CLog::LogF(LOGERROR, "Renaming recording failed!"); 1032 } 1033 1034 if (origRecording->GetLocalPlayCount() != recording->GetLocalPlayCount()) 1035 { 1036 if (!AsyncSetRecordingPlayCount().Execute(item)) 1037 CLog::LogF(LOGERROR, "Setting recording playcount failed!"); 1038 } 1039 1040 if (origRecording->m_iLifetime != recording->m_iLifetime) 1041 { 1042 if (!AsyncSetRecordingLifetime().Execute(item)) 1043 CLog::LogF(LOGERROR, "Setting recording lifetime failed!"); 1044 } 1045 1046 return true; 1047 } 1048 CanEditRecording(const CFileItem & item) const1049 bool CPVRGUIActions::CanEditRecording(const CFileItem& item) const 1050 { 1051 return CGUIDialogPVRRecordingSettings::CanEditRecording(item); 1052 } 1053 DeleteRecording(const CFileItemPtr & item) const1054 bool CPVRGUIActions::DeleteRecording(const CFileItemPtr& item) const 1055 { 1056 if ((!item->IsPVRRecording() && !item->m_bIsFolder) || item->IsParentFolder()) 1057 return false; 1058 1059 if (!ConfirmDeleteRecording(item)) 1060 return false; 1061 1062 if (!AsyncDeleteRecording().Execute(item)) 1063 { 1064 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19111}); // "Error", "PVR backend error. Check the log for more information about this message." 1065 return false; 1066 } 1067 1068 return true; 1069 } 1070 ConfirmDeleteRecording(const CFileItemPtr & item) const1071 bool CPVRGUIActions::ConfirmDeleteRecording(const CFileItemPtr& item) const 1072 { 1073 return CGUIDialogYesNo::ShowAndGetInput(CVariant{122}, // "Confirm delete" 1074 item->m_bIsFolder 1075 ? CVariant{19113} // "Delete all recordings in this folder?" 1076 : item->GetPVRRecordingInfoTag()->IsDeleted() 1077 ? CVariant{19294} // "Remove this deleted recording from trash? This operation cannot be reverted." 1078 : CVariant{19112}, // "Delete this recording?" 1079 CVariant{""}, 1080 CVariant{item->GetLabel()}); 1081 } 1082 DeleteWatchedRecordings(const std::shared_ptr<CFileItem> & item) const1083 bool CPVRGUIActions::DeleteWatchedRecordings(const std::shared_ptr<CFileItem>& item) const 1084 { 1085 if (!item->m_bIsFolder || item->IsParentFolder()) 1086 return false; 1087 1088 if (!ConfirmDeleteWatchedRecordings(item)) 1089 return false; 1090 1091 if (!AsyncDeleteRecording(true).Execute(item)) 1092 { 1093 HELPERS::ShowOKDialogText( 1094 CVariant{257}, 1095 CVariant{ 1096 19111}); // "Error", "PVR backend error. Check the log for more information about this message." 1097 return false; 1098 } 1099 1100 return true; 1101 } 1102 ConfirmDeleteWatchedRecordings(const std::shared_ptr<CFileItem> & item) const1103 bool CPVRGUIActions::ConfirmDeleteWatchedRecordings(const std::shared_ptr<CFileItem>& item) const 1104 { 1105 return CGUIDialogYesNo::ShowAndGetInput( 1106 CVariant{122}, // "Confirm delete" 1107 CVariant{19328}, // "Delete all watched recordings in this folder?" 1108 CVariant{""}, CVariant{item->GetLabel()}); 1109 } 1110 DeleteAllRecordingsFromTrash() const1111 bool CPVRGUIActions::DeleteAllRecordingsFromTrash() const 1112 { 1113 if (!ConfirmDeleteAllRecordingsFromTrash()) 1114 return false; 1115 1116 if (!AsyncEmptyRecordingsTrash().Execute(CFileItemPtr())) 1117 return false; 1118 1119 return true; 1120 } 1121 ConfirmDeleteAllRecordingsFromTrash() const1122 bool CPVRGUIActions::ConfirmDeleteAllRecordingsFromTrash() const 1123 { 1124 return CGUIDialogYesNo::ShowAndGetInput(CVariant{19292}, // "Delete all permanently" 1125 CVariant{19293}); // "Remove all deleted recordings from trash? This operation cannot be reverted." 1126 } 1127 UndeleteRecording(const CFileItemPtr & item) const1128 bool CPVRGUIActions::UndeleteRecording(const CFileItemPtr& item) const 1129 { 1130 if (!item->IsDeletedPVRRecording()) 1131 return false; 1132 1133 if (!AsyncUndeleteRecording().Execute(item)) 1134 { 1135 HELPERS::ShowOKDialogText(CVariant{257}, CVariant{19111}); // "Error", "PVR backend error. Check the log for more information about this message." 1136 return false; 1137 } 1138 1139 return true; 1140 } 1141 ShowRecordingSettings(const std::shared_ptr<CPVRRecording> & recording) const1142 bool CPVRGUIActions::ShowRecordingSettings(const std::shared_ptr<CPVRRecording>& recording) const 1143 { 1144 CGUIDialogPVRRecordingSettings* pDlgInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRRecordingSettings>(WINDOW_DIALOG_PVR_RECORDING_SETTING); 1145 if (!pDlgInfo) 1146 { 1147 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PVR_RECORDING_SETTING!"); 1148 return false; 1149 } 1150 1151 pDlgInfo->SetRecording(recording); 1152 pDlgInfo->Open(); 1153 1154 return pDlgInfo->IsConfirmed(); 1155 } 1156 GetResumeLabel(const CFileItem & item) const1157 std::string CPVRGUIActions::GetResumeLabel(const CFileItem& item) const 1158 { 1159 std::string resumeString; 1160 1161 const std::shared_ptr<CPVRRecording> recording(CPVRItem(CFileItemPtr(new CFileItem(item))).GetRecording()); 1162 if (recording && !recording->IsDeleted()) 1163 { 1164 int positionInSeconds = lrint(recording->GetResumePoint().timeInSeconds); 1165 if (positionInSeconds > 0) 1166 resumeString = StringUtils::Format(g_localizeStrings.Get(12022).c_str(), 1167 StringUtils::SecondsToTimeString(positionInSeconds, TIME_FORMAT_HH_MM_SS).c_str()); 1168 } 1169 return resumeString; 1170 } 1171 CheckResumeRecording(const CFileItemPtr & item) const1172 bool CPVRGUIActions::CheckResumeRecording(const CFileItemPtr& item) const 1173 { 1174 bool bPlayIt(true); 1175 std::string resumeString(GetResumeLabel(*item)); 1176 if (!resumeString.empty()) 1177 { 1178 CContextButtons choices; 1179 choices.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString); 1180 choices.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); // Play from beginning 1181 int choice = CGUIDialogContextMenu::ShowAndGetChoice(choices); 1182 if (choice > 0) 1183 item->m_lStartOffset = choice == CONTEXT_BUTTON_RESUME_ITEM ? STARTOFFSET_RESUME : 0; 1184 else 1185 bPlayIt = false; // context menu cancelled 1186 } 1187 return bPlayIt; 1188 } 1189 ResumePlayRecording(const CFileItemPtr & item,bool bFallbackToPlay) const1190 bool CPVRGUIActions::ResumePlayRecording(const CFileItemPtr& item, bool bFallbackToPlay) const 1191 { 1192 bool bCanResume = !GetResumeLabel(*item).empty(); 1193 if (bCanResume) 1194 { 1195 item->m_lStartOffset = STARTOFFSET_RESUME; 1196 } 1197 else 1198 { 1199 if (bFallbackToPlay) 1200 item->m_lStartOffset = 0; 1201 else 1202 return false; 1203 } 1204 1205 return PlayRecording(item, false); 1206 } 1207 CheckAndSwitchToFullscreen(bool bFullscreen) const1208 void CPVRGUIActions::CheckAndSwitchToFullscreen(bool bFullscreen) const 1209 { 1210 CMediaSettings::GetInstance().SetMediaStartWindowed(!bFullscreen); 1211 1212 if (bFullscreen) 1213 { 1214 CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); 1215 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); 1216 } 1217 } 1218 StartPlayback(CFileItem * item,bool bFullscreen,CPVRStreamProperties * epgProps) const1219 void CPVRGUIActions::StartPlayback(CFileItem* item, 1220 bool bFullscreen, 1221 CPVRStreamProperties* epgProps) const 1222 { 1223 // Obtain dynamic playback url and properties from the respective pvr client 1224 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item); 1225 if (client) 1226 { 1227 CPVRStreamProperties props; 1228 1229 if (item->IsPVRChannel()) 1230 { 1231 // If this was an EPG Tag to be played as live then PlayEpgTag() will create a channel 1232 // fileitem instead and pass the epg tags props so we use those and skip the client call 1233 if (epgProps) 1234 props = *epgProps; 1235 else 1236 client->GetChannelStreamProperties(item->GetPVRChannelInfoTag(), props); 1237 } 1238 else if (item->IsPVRRecording()) 1239 { 1240 client->GetRecordingStreamProperties(item->GetPVRRecordingInfoTag(), props); 1241 } 1242 else if (item->IsEPG()) 1243 { 1244 if (epgProps) // we already have props from PlayEpgTag() 1245 props = *epgProps; 1246 else 1247 client->GetEpgTagStreamProperties(item->GetEPGInfoTag(), props); 1248 } 1249 1250 if (props.size()) 1251 { 1252 const std::string url = props.GetStreamURL(); 1253 if (!url.empty()) 1254 item->SetDynPath(url); 1255 1256 const std::string mime = props.GetStreamMimeType(); 1257 if (!mime.empty()) 1258 { 1259 item->SetMimeType(mime); 1260 item->SetContentLookup(false); 1261 } 1262 1263 for (const auto& prop : props) 1264 item->SetProperty(prop.first, prop.second); 1265 } 1266 } 1267 1268 CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_PLAY, 0, 0, static_cast<void*>(item)); 1269 CheckAndSwitchToFullscreen(bFullscreen); 1270 } 1271 PlayRecording(const CFileItemPtr & item,bool bCheckResume) const1272 bool CPVRGUIActions::PlayRecording(const CFileItemPtr& item, bool bCheckResume) const 1273 { 1274 const std::shared_ptr<CPVRRecording> recording(CPVRItem(item).GetRecording()); 1275 if (!recording) 1276 return false; 1277 1278 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRecording(recording)) 1279 { 1280 CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); 1281 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); 1282 return true; 1283 } 1284 1285 if (!bCheckResume || CheckResumeRecording(item)) 1286 { 1287 CFileItem* itemToPlay = new CFileItem(recording); 1288 itemToPlay->m_lStartOffset = item->m_lStartOffset; 1289 StartPlayback(itemToPlay, true); 1290 } 1291 return true; 1292 } 1293 PlayEpgTag(const CFileItemPtr & item) const1294 bool CPVRGUIActions::PlayEpgTag(const CFileItemPtr& item) const 1295 { 1296 const std::shared_ptr<CPVREpgInfoTag> epgTag(CPVRItem(item).GetEpgInfoTag()); 1297 if (!epgTag) 1298 return false; 1299 1300 const std::shared_ptr<CPVRChannel> channelTag(CPVRItem(item).GetChannel()); 1301 if (!channelTag) 1302 return false; 1303 1304 // Obtain dynamic playback url and properties from the respective pvr client 1305 const std::shared_ptr<CPVRClient> client = 1306 CServiceBroker::GetPVRManager().GetClient(epgTag->ClientID()); 1307 if (!client) 1308 return false; 1309 1310 CPVRStreamProperties props; 1311 client->GetEpgTagStreamProperties(epgTag, props); 1312 1313 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingEpgTag(epgTag)) 1314 { 1315 CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); 1316 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); 1317 return true; 1318 } 1319 1320 StartPlayback(props.EPGPlaybackAsLive() ? new CFileItem(channelTag) : new CFileItem(epgTag), 1321 true, &props); 1322 return true; 1323 } 1324 SwitchToChannel(const CFileItemPtr & item,bool bCheckResume) const1325 bool CPVRGUIActions::SwitchToChannel(const CFileItemPtr& item, bool bCheckResume) const 1326 { 1327 if (item->m_bIsFolder) 1328 return false; 1329 1330 std::shared_ptr<CPVRRecording> recording; 1331 const std::shared_ptr<CPVRChannel> channel(CPVRItem(item).GetChannel()); 1332 if (channel) 1333 { 1334 bool bSwitchToFullscreen = CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingChannel(channel); 1335 1336 if (!bSwitchToFullscreen) 1337 { 1338 recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(channel->GetEPGNow()); 1339 bSwitchToFullscreen = recording && CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRecording(recording); 1340 } 1341 1342 if (bSwitchToFullscreen) 1343 { 1344 CGUIMessage msg(GUI_MSG_FULLSCREEN, 0, CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow()); 1345 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg); 1346 return true; 1347 } 1348 } 1349 1350 ParentalCheckResult result = channel ? CheckParentalLock(channel) : ParentalCheckResult::FAILED; 1351 if (result == ParentalCheckResult::SUCCESS) 1352 { 1353 // switch to channel or if recording present, ask whether to switch or play recording... 1354 if (!recording) 1355 recording = CServiceBroker::GetPVRManager().Recordings()->GetRecordingForEpgTag(channel->GetEPGNow()); 1356 1357 if (recording) 1358 { 1359 bool bCancel(false); 1360 bool bPlayRecording = CGUIDialogYesNo::ShowAndGetInput(CVariant{19687}, // "Play recording" 1361 CVariant{""}, 1362 CVariant{12021}, // "Play from beginning" 1363 CVariant{recording->m_strTitle}, 1364 bCancel, 1365 CVariant{19000}, // "Switch to channel" 1366 CVariant{19687}, // "Play recording" 1367 0); // no autoclose 1368 if (bCancel) 1369 return false; 1370 1371 if (bPlayRecording) 1372 { 1373 const CFileItemPtr recordingItem(new CFileItem(recording)); 1374 return PlayRecording(recordingItem, bCheckResume); 1375 } 1376 } 1377 1378 bool bFullscreen; 1379 switch (m_settings.GetIntValue(CSettings::SETTING_PVRPLAYBACK_SWITCHTOFULLSCREENCHANNELTYPES)) 1380 { 1381 case 0: // never 1382 bFullscreen = false; 1383 break; 1384 case 1: // TV channels 1385 bFullscreen = !channel->IsRadio(); 1386 break; 1387 case 2: // Radio channels 1388 bFullscreen = channel->IsRadio(); 1389 break; 1390 case 3: // TV and radio channels 1391 default: 1392 bFullscreen = true; 1393 break; 1394 } 1395 StartPlayback(new CFileItem(channel), bFullscreen); 1396 return true; 1397 } 1398 else if (result == ParentalCheckResult::FAILED) 1399 { 1400 const std::string channelName = channel ? channel->ChannelName() : g_localizeStrings.Get(19029); // Channel 1401 const std::string msg = StringUtils::Format(g_localizeStrings.Get(19035).c_str(), channelName.c_str()); // CHANNELNAME could not be played. Check the log for details. 1402 1403 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(19166), msg); // PVR information 1404 } 1405 1406 return false; 1407 } 1408 SwitchToChannel(PlaybackType type) const1409 bool CPVRGUIActions::SwitchToChannel(PlaybackType type) const 1410 { 1411 std::shared_ptr<CPVRChannel> channel; 1412 bool bIsRadio(false); 1413 1414 // check if the desired PlaybackType is already playing, 1415 // and if not, try to grab the last played channel of this type 1416 switch (type) 1417 { 1418 case PlaybackTypeRadio: 1419 { 1420 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingRadio()) 1421 return true; 1422 1423 const std::shared_ptr<CPVRChannelGroup> allGroup = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAllRadio(); 1424 if (allGroup) 1425 channel = allGroup->GetLastPlayedChannel(); 1426 1427 bIsRadio = true; 1428 break; 1429 } 1430 case PlaybackTypeTV: 1431 { 1432 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingTV()) 1433 return true; 1434 1435 const std::shared_ptr<CPVRChannelGroup> allGroup = CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAllTV(); 1436 if (allGroup) 1437 channel = allGroup->GetLastPlayedChannel(); 1438 1439 break; 1440 } 1441 default: 1442 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying()) 1443 return true; 1444 1445 channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetLastPlayedChannel(); 1446 break; 1447 } 1448 1449 // if we have a last played channel, start playback 1450 if (channel) 1451 { 1452 return SwitchToChannel(std::make_shared<CFileItem>(channel), true); 1453 } 1454 else 1455 { 1456 // if we don't, find the active channel group of the demanded type and play it's first channel 1457 const std::shared_ptr<CPVRChannelGroup> channelGroup = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingGroup(bIsRadio); 1458 if (channelGroup) 1459 { 1460 // try to start playback of first channel in this group 1461 const std::vector<std::shared_ptr<PVRChannelGroupMember>> groupMembers = channelGroup->GetMembers(); 1462 if (!groupMembers.empty()) 1463 { 1464 return SwitchToChannel(std::make_shared<CFileItem>((*groupMembers.begin())->channel), true); 1465 } 1466 } 1467 } 1468 1469 CLog::LogF(LOGERROR, 1470 "Could not determine {} channel to playback. No last played channel found, and " 1471 "first channel of active group could also not be determined.", 1472 bIsRadio ? "Radio" : "TV"); 1473 1474 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, 1475 g_localizeStrings.Get(19166), // PVR information 1476 StringUtils::Format(g_localizeStrings.Get(19035).c_str(), 1477 g_localizeStrings.Get(bIsRadio ? 19021 : 19020).c_str())); // Radio/TV could not be played. Check the log for details. 1478 return false; 1479 } 1480 PlayChannelOnStartup() const1481 bool CPVRGUIActions::PlayChannelOnStartup() const 1482 { 1483 int iAction = m_settings.GetIntValue(CSettings::SETTING_LOOKANDFEEL_STARTUPACTION); 1484 if (iAction != STARTUP_ACTION_PLAY_TV && 1485 iAction != STARTUP_ACTION_PLAY_RADIO) 1486 return false; 1487 1488 bool playTV = iAction == STARTUP_ACTION_PLAY_TV; 1489 const std::shared_ptr<CPVRChannelGroupsContainer> groups(CServiceBroker::GetPVRManager().ChannelGroups()); 1490 std::shared_ptr<CPVRChannelGroup> group = playTV ? groups->GetGroupAllTV() : groups->GetGroupAllRadio(); 1491 1492 // get the last played channel or fallback to first channel 1493 std::shared_ptr<CPVRChannel> channel = group->GetLastPlayedChannel(); 1494 if (channel) 1495 { 1496 group = groups->GetLastPlayedGroup(channel->ChannelID()); 1497 } 1498 else 1499 { 1500 // fallback to first channel 1501 auto channels(group->GetMembers()); 1502 if (channels.empty()) 1503 return false; 1504 1505 channel = channels.front()->channel; 1506 } 1507 1508 CLog::Log(LOGINFO, "PVR is starting playback of channel '{}'", channel->ChannelName()); 1509 CServiceBroker::GetPVRManager().PlaybackState()->SetPlayingGroup(group); 1510 return SwitchToChannel(std::make_shared<CFileItem>(channel), true); 1511 } 1512 PlayMedia(const CFileItemPtr & item) const1513 bool CPVRGUIActions::PlayMedia(const CFileItemPtr& item) const 1514 { 1515 CFileItemPtr pvrItem(item); 1516 if (URIUtils::IsPVRChannel(item->GetPath()) && !item->HasPVRChannelInfoTag()) 1517 { 1518 const std::shared_ptr<CPVRChannel> pvrChannel = 1519 CServiceBroker::GetPVRManager().ChannelGroups()->GetByPath(item->GetPath()); 1520 if (pvrChannel) 1521 pvrItem = std::make_shared<CFileItem>(pvrChannel); 1522 } 1523 else if (URIUtils::IsPVRRecording(item->GetPath()) && !item->HasPVRRecordingInfoTag()) 1524 pvrItem = std::make_shared<CFileItem>(CServiceBroker::GetPVRManager().Recordings()->GetByPath(item->GetPath())); 1525 1526 bool bCheckResume = true; 1527 if (item->HasProperty("check_resume")) 1528 bCheckResume = item->GetProperty("check_resume").asBoolean(); 1529 1530 if (pvrItem && pvrItem->HasPVRChannelInfoTag()) 1531 { 1532 return SwitchToChannel(pvrItem, bCheckResume); 1533 } 1534 else if (pvrItem && pvrItem->HasPVRRecordingInfoTag()) 1535 { 1536 return PlayRecording(pvrItem, bCheckResume); 1537 } 1538 1539 return false; 1540 } 1541 HideChannel(const CFileItemPtr & item) const1542 bool CPVRGUIActions::HideChannel(const CFileItemPtr& item) const 1543 { 1544 const std::shared_ptr<CPVRChannel> channel(item->GetPVRChannelInfoTag()); 1545 1546 /* check if the channel tag is valid */ 1547 if (!channel || !channel->ChannelNumber().IsValid()) 1548 return false; 1549 1550 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{19054}, // "Hide channel" 1551 CVariant{19039}, // "Are you sure you want to hide this channel?" 1552 CVariant{""}, 1553 CVariant{channel->ChannelName()})) 1554 return false; 1555 1556 if (!CServiceBroker::GetPVRManager().ChannelGroups()->GetGroupAll(channel->IsRadio())->RemoveFromGroup(channel)) 1557 return false; 1558 1559 CGUIWindowPVRBase* pvrWindow = dynamic_cast<CGUIWindowPVRBase*>(CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindow())); 1560 if (pvrWindow) 1561 pvrWindow->DoRefresh(); 1562 else 1563 CLog::LogF(LOGERROR, "Called on non-pvr window. No refresh possible."); 1564 1565 return true; 1566 } 1567 StartChannelScan()1568 bool CPVRGUIActions::StartChannelScan() 1569 { 1570 return StartChannelScan(PVR_INVALID_CLIENT_ID); 1571 } 1572 StartChannelScan(int clientId)1573 bool CPVRGUIActions::StartChannelScan(int clientId) 1574 { 1575 if (!CServiceBroker::GetPVRManager().IsStarted() || IsRunningChannelScan()) 1576 return false; 1577 1578 std::shared_ptr<CPVRClient> scanClient; 1579 std::vector<std::shared_ptr<CPVRClient>> possibleScanClients = CServiceBroker::GetPVRManager().Clients()->GetClientsSupportingChannelScan(); 1580 m_bChannelScanRunning = true; 1581 1582 if (clientId != PVR_INVALID_CLIENT_ID) 1583 { 1584 for (const auto& client : possibleScanClients) 1585 { 1586 if (client->GetID() == clientId) 1587 { 1588 scanClient = client; 1589 break; 1590 } 1591 } 1592 1593 if (!scanClient) 1594 { 1595 CLog::LogF(LOGERROR, 1596 "Provided client id '%d' could not be found in list of possible scan clients!", 1597 clientId); 1598 m_bChannelScanRunning = false; 1599 return false; 1600 } 1601 } 1602 /* multiple clients found */ 1603 else if (possibleScanClients.size() > 1) 1604 { 1605 CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); 1606 if (!pDialog) 1607 { 1608 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT!"); 1609 m_bChannelScanRunning = false; 1610 return false; 1611 } 1612 1613 pDialog->Reset(); 1614 pDialog->SetHeading(CVariant{19119}); // "On which backend do you want to search?" 1615 1616 for (const auto& client : possibleScanClients) 1617 pDialog->Add(client->GetFriendlyName()); 1618 1619 pDialog->Open(); 1620 1621 int selection = pDialog->GetSelectedItem(); 1622 if (selection >= 0) 1623 scanClient = possibleScanClients[selection]; 1624 } 1625 /* one client found */ 1626 else if (possibleScanClients.size() == 1) 1627 { 1628 scanClient = possibleScanClients[0]; 1629 } 1630 /* no clients found */ 1631 else if (!scanClient) 1632 { 1633 HELPERS::ShowOKDialogText(CVariant{19033}, // "Information" 1634 CVariant{19192}); // "None of the connected PVR backends supports scanning for channels." 1635 m_bChannelScanRunning = false; 1636 return false; 1637 } 1638 1639 /* start the channel scan */ 1640 CLog::LogFC(LOGDEBUG, LOGPVR, "Starting to scan for channels on client {}", 1641 scanClient->GetFriendlyName()); 1642 long perfCnt = XbmcThreads::SystemClockMillis(); 1643 1644 /* do the scan */ 1645 if (scanClient->StartChannelScan() != PVR_ERROR_NO_ERROR) 1646 HELPERS::ShowOKDialogText(CVariant{257}, // "Error" 1647 CVariant{19193}); // "The channel scan can't be started. Check the log for more information about this message." 1648 1649 CLog::LogFC(LOGDEBUG, LOGPVR, "Channel scan finished after {}.{} seconds", 1650 (XbmcThreads::SystemClockMillis() - perfCnt) / 1000, 1651 (XbmcThreads::SystemClockMillis() - perfCnt) % 1000); 1652 m_bChannelScanRunning = false; 1653 return true; 1654 } 1655 ProcessSettingsMenuHooks()1656 bool CPVRGUIActions::ProcessSettingsMenuHooks() 1657 { 1658 CPVRClientMap clients; 1659 CServiceBroker::GetPVRManager().Clients()->GetCreatedClients(clients); 1660 1661 std::vector<std::pair<std::shared_ptr<CPVRClient>, CPVRClientMenuHook>> settingsHooks; 1662 for (const auto& client : clients) 1663 { 1664 for (const auto& hook : client.second->GetMenuHooks()->GetSettingsHooks()) 1665 { 1666 settingsHooks.emplace_back(std::make_pair(client.second, hook)); 1667 } 1668 } 1669 1670 if (settingsHooks.empty()) 1671 return true; // no settings hooks, no error 1672 1673 auto selectedHook = settingsHooks.begin(); 1674 1675 // if there is only one settings hook, execute it directly, otherwise let the user select 1676 if (settingsHooks.size() > 1) 1677 { 1678 CGUIDialogSelect* pDialog= CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(WINDOW_DIALOG_SELECT); 1679 if (!pDialog) 1680 { 1681 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_SELECT!"); 1682 return false; 1683 } 1684 1685 pDialog->Reset(); 1686 pDialog->SetHeading(CVariant{19196}); // "PVR client specific actions" 1687 1688 for (const auto& hook : settingsHooks) 1689 { 1690 if (clients.size() == 1) 1691 pDialog->Add(hook.second.GetLabel()); 1692 else 1693 pDialog->Add(hook.first->GetBackendName() + ": " + hook.second.GetLabel()); 1694 } 1695 1696 pDialog->Open(); 1697 1698 int selection = pDialog->GetSelectedItem(); 1699 if (selection < 0) 1700 return true; // cancelled 1701 1702 std::advance(selectedHook, selection); 1703 } 1704 return selectedHook->first->CallSettingsMenuHook(selectedHook->second) == PVR_ERROR_NO_ERROR; 1705 } 1706 ResetPVRDatabase(bool bResetEPGOnly)1707 bool CPVRGUIActions::ResetPVRDatabase(bool bResetEPGOnly) 1708 { 1709 CGUIDialogProgress* pDlgProgress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); 1710 if (!pDlgProgress) 1711 { 1712 CLog::LogF(LOGERROR, "Unable to get WINDOW_DIALOG_PROGRESS!"); 1713 return false; 1714 } 1715 1716 if (bResetEPGOnly) 1717 { 1718 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{19098}, // "Warning!" 1719 CVariant{19188})) // "All your guide data will be cleared. Are you sure?" 1720 return false; 1721 } 1722 else 1723 { 1724 if (CheckParentalPIN() != ParentalCheckResult::SUCCESS || 1725 !CGUIDialogYesNo::ShowAndGetInput(CVariant{19098}, // "Warning!" 1726 CVariant{19186})) // "All your TV related data (channels, groups, guide) will be cleared. Are you sure?" 1727 return false; 1728 } 1729 1730 CDateTime::ResetTimezoneBias(); 1731 1732 CLog::LogFC(LOGDEBUG, LOGPVR, "PVR clearing {} database", 1733 bResetEPGOnly ? "EPG" : "PVR and EPG"); 1734 1735 pDlgProgress->SetHeading(CVariant{313}); // "Cleaning database" 1736 pDlgProgress->SetLine(0, CVariant{g_localizeStrings.Get(19187)}); // "Clearing all related data." 1737 pDlgProgress->SetLine(1, CVariant{""}); 1738 pDlgProgress->SetLine(2, CVariant{""}); 1739 1740 pDlgProgress->Open(); 1741 pDlgProgress->Progress(); 1742 1743 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying()) 1744 { 1745 CLog::Log(LOGINFO, "PVR is stopping playback for {} database reset", 1746 bResetEPGOnly ? "EPG" : "PVR and EPG"); 1747 CApplicationMessenger::GetInstance().SendMsg(TMSG_MEDIA_STOP); 1748 } 1749 1750 pDlgProgress->SetPercentage(10); 1751 pDlgProgress->Progress(); 1752 1753 const std::shared_ptr<CPVRDatabase> pvrDatabase(CServiceBroker::GetPVRManager().GetTVDatabase()); 1754 const std::shared_ptr<CPVREpgDatabase> epgDatabase(CServiceBroker::GetPVRManager().EpgContainer().GetEpgDatabase()); 1755 1756 // increase db open refcounts, so they don't get closed during following pvr manager shutdown 1757 pvrDatabase->Open(); 1758 epgDatabase->Open(); 1759 1760 // stop pvr manager; close both pvr and epg databases 1761 CServiceBroker::GetPVRManager().Stop(); 1762 1763 /* reset the EPG pointers */ 1764 pvrDatabase->ResetEPG(); 1765 pDlgProgress->SetPercentage(bResetEPGOnly ? 40 : 20); 1766 pDlgProgress->Progress(); 1767 1768 /* clean the EPG database */ 1769 epgDatabase->DeleteEpg(); 1770 pDlgProgress->SetPercentage(bResetEPGOnly ? 70 : 40); 1771 pDlgProgress->Progress(); 1772 1773 if (!bResetEPGOnly) 1774 { 1775 pvrDatabase->DeleteChannelGroups(); 1776 pDlgProgress->SetPercentage(60); 1777 pDlgProgress->Progress(); 1778 1779 /* delete all channels */ 1780 pvrDatabase->DeleteChannels(); 1781 pDlgProgress->SetPercentage(70); 1782 pDlgProgress->Progress(); 1783 1784 /* delete all timers */ 1785 pvrDatabase->DeleteTimers(); 1786 pDlgProgress->SetPercentage(80); 1787 pDlgProgress->Progress(); 1788 1789 /* delete all clients */ 1790 pvrDatabase->DeleteClients(); 1791 pDlgProgress->SetPercentage(90); 1792 pDlgProgress->Progress(); 1793 1794 /* delete all channel and recording settings */ 1795 CVideoDatabase videoDatabase; 1796 1797 if (videoDatabase.Open()) 1798 { 1799 videoDatabase.EraseAllVideoSettings("pvr://channels/"); 1800 videoDatabase.EraseAllVideoSettings(CPVRRecordingsPath::PATH_RECORDINGS); 1801 videoDatabase.Close(); 1802 } 1803 } 1804 1805 // decrease db open refcounts; this actually closes dbs because refcounts drops to zero 1806 pvrDatabase->Close(); 1807 epgDatabase->Close(); 1808 1809 CLog::LogFC(LOGDEBUG, LOGPVR, "{} database cleared", bResetEPGOnly ? "EPG" : "PVR and EPG"); 1810 1811 CLog::Log(LOGINFO, "Restarting the PVR Manager after {} database reset", 1812 bResetEPGOnly ? "EPG" : "PVR and EPG"); 1813 CServiceBroker::GetPVRManager().Start(); 1814 1815 pDlgProgress->SetPercentage(100); 1816 pDlgProgress->Close(); 1817 return true; 1818 } 1819 CheckParentalLock(const std::shared_ptr<CPVRChannel> & channel) const1820 ParentalCheckResult CPVRGUIActions::CheckParentalLock(const std::shared_ptr<CPVRChannel>& channel) const 1821 { 1822 if (!CServiceBroker::GetPVRManager().IsParentalLocked(channel)) 1823 return ParentalCheckResult::SUCCESS; 1824 1825 ParentalCheckResult ret = CheckParentalPIN(); 1826 1827 if (ret == ParentalCheckResult::FAILED) 1828 CLog::LogF(LOGERROR, "Parental lock verification failed for channel '{}': wrong PIN entered.", 1829 channel->ChannelName()); 1830 1831 return ret; 1832 } 1833 CheckParentalPIN() const1834 ParentalCheckResult CPVRGUIActions::CheckParentalPIN() const 1835 { 1836 if (!m_settings.GetBoolValue(CSettings::SETTING_PVRPARENTAL_ENABLED)) 1837 return ParentalCheckResult::SUCCESS; 1838 1839 std::string pinCode = m_settings.GetStringValue(CSettings::SETTING_PVRPARENTAL_PIN); 1840 if (pinCode.empty()) 1841 return ParentalCheckResult::SUCCESS; 1842 1843 InputVerificationResult ret = CGUIDialogNumeric::ShowAndVerifyInput(pinCode, g_localizeStrings.Get(19262), true); // "Parental control. Enter PIN:" 1844 1845 if (ret == InputVerificationResult::SUCCESS) 1846 { 1847 CServiceBroker::GetPVRManager().RestartParentalTimer(); 1848 return ParentalCheckResult::SUCCESS; 1849 } 1850 else if (ret == InputVerificationResult::FAILED) 1851 { 1852 HELPERS::ShowOKDialogText(CVariant{19264}, CVariant{19265}); // "Incorrect PIN", "The entered PIN was incorrect." 1853 return ParentalCheckResult::FAILED; 1854 } 1855 else 1856 { 1857 return ParentalCheckResult::CANCELED; 1858 } 1859 } 1860 CanSystemPowerdown(bool bAskUser) const1861 bool CPVRGUIActions::CanSystemPowerdown(bool bAskUser /*= true*/) const 1862 { 1863 bool bReturn(true); 1864 if (CServiceBroker::GetPVRManager().IsStarted()) 1865 { 1866 std::shared_ptr<CPVRTimerInfoTag> cause; 1867 if (!AllLocalBackendsIdle(cause)) 1868 { 1869 if (bAskUser) 1870 { 1871 std::string text; 1872 1873 if (cause) 1874 { 1875 if (cause->IsRecording()) 1876 { 1877 text = StringUtils::Format(g_localizeStrings.Get(19691).c_str(), // "PVR is currently recording...." 1878 cause->Title().c_str(), 1879 cause->ChannelName().c_str()); 1880 } 1881 else 1882 { 1883 // Next event is due to a local recording or reminder. 1884 const CDateTime now(CDateTime::GetUTCDateTime()); 1885 const CDateTime start(cause->StartAsUTC()); 1886 const CDateTimeSpan prestart(0, 0, cause->MarginStart(), 0); 1887 1888 CDateTimeSpan diff(start - now); 1889 diff -= prestart; 1890 int mins = diff.GetSecondsTotal() / 60; 1891 1892 std::string dueStr; 1893 if (mins > 1) 1894 { 1895 // "%d minutes" 1896 dueStr = StringUtils::Format(g_localizeStrings.Get(19694).c_str(), mins); 1897 } 1898 else 1899 { 1900 // "about a minute" 1901 dueStr = g_localizeStrings.Get(19695); 1902 } 1903 1904 text = StringUtils::Format(cause->IsReminder() 1905 ? g_localizeStrings.Get(19690).c_str() // "PVR has scheduled a reminder...." 1906 : g_localizeStrings.Get(19692).c_str(), // "PVR will start recording...." 1907 cause->Title().c_str(), 1908 cause->ChannelName().c_str(), 1909 dueStr.c_str()); 1910 } 1911 } 1912 else 1913 { 1914 // Next event is due to automatic daily wakeup of PVR. 1915 const CDateTime now(CDateTime::GetUTCDateTime()); 1916 1917 CDateTime dailywakeuptime; 1918 dailywakeuptime.SetFromDBTime(m_settings.GetStringValue(CSettings::SETTING_PVRPOWERMANAGEMENT_DAILYWAKEUPTIME)); 1919 dailywakeuptime = dailywakeuptime.GetAsUTCDateTime(); 1920 1921 const CDateTimeSpan diff(dailywakeuptime - now); 1922 int mins = diff.GetSecondsTotal() / 60; 1923 1924 std::string dueStr; 1925 if (mins > 1) 1926 { 1927 // "%d minutes" 1928 dueStr = StringUtils::Format(g_localizeStrings.Get(19694).c_str(), mins); 1929 } 1930 else 1931 { 1932 // "about a minute" 1933 dueStr = g_localizeStrings.Get(19695); 1934 } 1935 1936 text = StringUtils::Format(g_localizeStrings.Get(19693).c_str(), // "Daily wakeup is due in...." 1937 dueStr.c_str()); 1938 } 1939 1940 // Inform user about PVR being busy. Ask if user wants to powerdown anyway. 1941 bReturn = HELPERS::ShowYesNoDialogText(CVariant{19685}, // "Confirm shutdown" 1942 CVariant{text}, 1943 CVariant{222}, // "Shutdown anyway", 1944 CVariant{19696}, // "Cancel" 1945 10000) // timeout value before closing 1946 == HELPERS::DialogResponse::YES; 1947 } 1948 else 1949 bReturn = false; // do not powerdown (busy, but no user interaction requested). 1950 } 1951 } 1952 return bReturn; 1953 } 1954 AllLocalBackendsIdle(std::shared_ptr<CPVRTimerInfoTag> & causingEvent) const1955 bool CPVRGUIActions::AllLocalBackendsIdle(std::shared_ptr<CPVRTimerInfoTag>& causingEvent) const 1956 { 1957 // active recording on local backend? 1958 const std::vector<std::shared_ptr<CPVRTimerInfoTag>> activeRecordings = CServiceBroker::GetPVRManager().Timers()->GetActiveRecordings(); 1959 for (const auto& timer : activeRecordings) 1960 { 1961 if (EventOccursOnLocalBackend(std::make_shared<CFileItem>(timer))) 1962 { 1963 causingEvent = timer; 1964 return false; 1965 } 1966 } 1967 1968 // soon recording on local backend? 1969 if (IsNextEventWithinBackendIdleTime()) 1970 { 1971 const std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetNextActiveTimer(false); 1972 if (!timer) 1973 { 1974 // Next event is due to automatic daily wakeup of PVR! 1975 causingEvent.reset(); 1976 return false; 1977 } 1978 1979 if (EventOccursOnLocalBackend(std::make_shared<CFileItem>(timer))) 1980 { 1981 causingEvent = timer; 1982 return false; 1983 } 1984 } 1985 return true; 1986 } 1987 EventOccursOnLocalBackend(const CFileItemPtr & item) const1988 bool CPVRGUIActions::EventOccursOnLocalBackend(const CFileItemPtr& item) const 1989 { 1990 if (item && item->HasPVRTimerInfoTag()) 1991 { 1992 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(*item); 1993 if (client) 1994 { 1995 const std::string hostname = client->GetBackendHostname(); 1996 if (!hostname.empty() && CServiceBroker::GetNetwork().IsLocalHost(hostname)) 1997 return true; 1998 } 1999 } 2000 return false; 2001 } 2002 2003 namespace 2004 { GetAnnouncerText(const std::shared_ptr<CPVRTimerInfoTag> & timer,int idEpg,int idNoEpg)2005 std::string GetAnnouncerText(const std::shared_ptr<CPVRTimerInfoTag>& timer, int idEpg, int idNoEpg) 2006 { 2007 std::string text; 2008 if (timer->IsEpgBased()) 2009 { 2010 text = StringUtils::Format(g_localizeStrings.Get(idEpg), 2011 timer->Title(), // tv show title 2012 timer->ChannelName(), 2013 timer->StartAsLocalTime().GetAsLocalizedDateTime(false, false)); 2014 } 2015 else 2016 { 2017 text = StringUtils::Format(g_localizeStrings.Get(idNoEpg), 2018 timer->ChannelName(), 2019 timer->StartAsLocalTime().GetAsLocalizedDateTime(false, false)); 2020 } 2021 return text; 2022 } 2023 AddEventLogEntry(const std::shared_ptr<CPVRTimerInfoTag> & timer,int idEpg,int idNoEpg)2024 void AddEventLogEntry(const std::shared_ptr<CPVRTimerInfoTag>& timer, int idEpg, int idNoEpg) 2025 { 2026 std::string name; 2027 std::string icon; 2028 2029 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(timer->GetTimerType()->GetClientId()); 2030 if (client) 2031 { 2032 name = client->Name(); 2033 icon = client->Icon(); 2034 } 2035 else 2036 { 2037 name = g_sysinfo.GetAppName(); 2038 icon = "special://xbmc/media/icon256x256.png"; 2039 } 2040 2041 CPVREventLogJob* job = new CPVREventLogJob; 2042 job->AddEvent(false, // do not display a toast, only log event 2043 false, // info, no error 2044 name, 2045 GetAnnouncerText(timer, idEpg, idNoEpg), 2046 icon); 2047 CJobManager::GetInstance().AddJob(job, nullptr); 2048 } 2049 } // unnamed namespace 2050 IsNextEventWithinBackendIdleTime() const2051 bool CPVRGUIActions::IsNextEventWithinBackendIdleTime() const 2052 { 2053 // timers going off soon? 2054 const CDateTime now(CDateTime::GetUTCDateTime()); 2055 const CDateTimeSpan idle(0, 0, m_settings.GetIntValue(CSettings::SETTING_PVRPOWERMANAGEMENT_BACKENDIDLETIME), 0); 2056 const CDateTime next(CServiceBroker::GetPVRManager().Timers()->GetNextEventTime()); 2057 const CDateTimeSpan delta(next - now); 2058 2059 return (delta <= idle); 2060 } 2061 AnnounceReminder(const std::shared_ptr<CPVRTimerInfoTag> & timer) const2062 void CPVRGUIActions::AnnounceReminder(const std::shared_ptr<CPVRTimerInfoTag>& timer) const 2063 { 2064 if (!timer->IsReminder()) 2065 { 2066 CLog::LogF(LOGERROR, "No reminder timer!"); 2067 return; 2068 } 2069 2070 if (timer->EndAsUTC() < CDateTime::GetUTCDateTime()) 2071 { 2072 // expired. timer end is in the past. write event log entry. 2073 AddEventLogEntry(timer, 19305, 19306); // Deleted missed PVR reminder ... 2074 return; 2075 } 2076 2077 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlayingChannel(timer->Channel())) 2078 { 2079 // no need for an announcement. channel in question is already playing. 2080 return; 2081 } 2082 2083 // show the reminder dialog 2084 CGUIDialogProgress* dialog = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS); 2085 if (!dialog) 2086 return; 2087 2088 dialog->Reset(); 2089 2090 dialog->SetHeading(CVariant{19312}); // "PVR reminder" 2091 dialog->ShowChoice(0, CVariant{19165}); // "Switch" 2092 2093 std::string text = GetAnnouncerText(timer, 19307, 19308); // Reminder for ... 2094 2095 bool bCanRecord = false; 2096 const std::shared_ptr<CPVRClient> client = CServiceBroker::GetPVRManager().GetClient(timer->m_iClientId); 2097 if (client && client->GetClientCapabilities().SupportsTimers()) 2098 { 2099 bCanRecord = true; 2100 dialog->ShowChoice(1, CVariant{264}); // "Record" 2101 dialog->ShowChoice(2, CVariant{222}); // "Cancel" 2102 2103 if (m_settings.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTORECORD)) 2104 text += "\n\n" + g_localizeStrings.Get( 2105 19309); // (Auto-close of this reminder will schedule a recording...) 2106 else if (m_settings.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTOSWITCH)) 2107 text += "\n\n" + g_localizeStrings.Get( 2108 19331); // (Auto-close of this reminder will swicth to channel...) 2109 } 2110 else 2111 { 2112 dialog->ShowChoice(1, CVariant{222}); // "Cancel" 2113 } 2114 2115 dialog->SetText(text); 2116 dialog->SetPercentage(100); 2117 2118 dialog->Open(); 2119 2120 int result = CGUIDialogProgress::CHOICE_NONE; 2121 2122 static constexpr int PROGRESS_TIMESLICE_MILLISECS = 50; 2123 2124 const int iWait = m_settings.GetIntValue(CSettings::SETTING_PVRREMINDERS_AUTOCLOSEDELAY) * 1000; 2125 int iRemaining = iWait; 2126 while (iRemaining > 0) 2127 { 2128 result = dialog->GetChoice(); 2129 if (result != CGUIDialogProgress::CHOICE_NONE) 2130 break; 2131 2132 std::this_thread::sleep_for(std::chrono::milliseconds(PROGRESS_TIMESLICE_MILLISECS)); 2133 2134 iRemaining -= PROGRESS_TIMESLICE_MILLISECS; 2135 dialog->SetPercentage(iRemaining * 100 / iWait); 2136 dialog->Progress(); 2137 } 2138 2139 dialog->Close(); 2140 2141 bool bAutoClosed = (iRemaining <= 0); 2142 bool bSwitch = (result == 0); 2143 bool bRecord = (result == 1); 2144 2145 if (bAutoClosed) 2146 { 2147 bRecord = (bCanRecord && m_settings.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTORECORD)); 2148 bSwitch = m_settings.GetBoolValue(CSettings::SETTING_PVRREMINDERS_AUTOSWITCH); 2149 } 2150 2151 if (bRecord) 2152 { 2153 std::shared_ptr<CPVRTimerInfoTag> newTimer; 2154 2155 std::shared_ptr<CPVREpgInfoTag> epgTag = timer->GetEpgInfoTag(); 2156 if (epgTag) 2157 { 2158 newTimer = CPVRTimerInfoTag::CreateFromEpg(epgTag, false); 2159 if (newTimer) 2160 { 2161 // an epgtag can only have max one timer - we need to clear the reminder to be able to 2162 // attach the recording timer 2163 DeleteTimer(timer, false, false); 2164 } 2165 } 2166 else 2167 { 2168 int iDuration = (timer->EndAsUTC() - timer->StartAsUTC()).GetSecondsTotal() / 60; 2169 newTimer = 2170 CPVRTimerInfoTag::CreateTimerTag(timer->Channel(), timer->StartAsUTC(), iDuration); 2171 } 2172 2173 if (newTimer) 2174 { 2175 // schedule recording 2176 AddTimer(std::make_shared<CFileItem>(newTimer), false); 2177 } 2178 2179 if (bAutoClosed) 2180 { 2181 AddEventLogEntry(timer, 19310, 2182 19311); // Scheduled recording for auto-closed PVR reminder ... 2183 } 2184 } 2185 2186 if (bSwitch) 2187 { 2188 SwitchToChannel(std::make_shared<CFileItem>(timer->Channel()), false); 2189 2190 if (bAutoClosed) 2191 { 2192 AddEventLogEntry(timer, 19332, 19333); // Switched channel for auto-closed PVR reminder ... 2193 } 2194 } 2195 } 2196 AnnounceReminders() const2197 void CPVRGUIActions::AnnounceReminders() const 2198 { 2199 // Prevent multiple yesno dialogs, all on same call stack, due to gui message processing while dialog is open. 2200 if (m_bReminderAnnouncementRunning) 2201 return; 2202 2203 m_bReminderAnnouncementRunning = true; 2204 std::shared_ptr<CPVRTimerInfoTag> timer = CServiceBroker::GetPVRManager().Timers()->GetNextReminderToAnnnounce(); 2205 while (timer) 2206 { 2207 AnnounceReminder(timer); 2208 timer = CServiceBroker::GetPVRManager().Timers()->GetNextReminderToAnnnounce(); 2209 } 2210 m_bReminderAnnouncementRunning = false; 2211 } 2212 SetSelectedItemPath(bool bRadio,const std::string & path)2213 void CPVRGUIActions::SetSelectedItemPath(bool bRadio, const std::string& path) 2214 { 2215 CSingleLock lock(m_critSection); 2216 if (bRadio) 2217 m_selectedItemPathRadio = path; 2218 else 2219 m_selectedItemPathTV = path; 2220 } 2221 GetSelectedItemPath(bool bRadio) const2222 std::string CPVRGUIActions::GetSelectedItemPath(bool bRadio) const 2223 { 2224 if (m_settings.GetBoolValue(CSettings::SETTING_PVRMANAGER_PRESELECTPLAYINGCHANNEL)) 2225 { 2226 CPVRManager& mgr = CServiceBroker::GetPVRManager(); 2227 2228 // if preselect playing channel is activated, return the path of the playing channel, if any. 2229 const std::shared_ptr<CPVRChannel> playingChannel = mgr.PlaybackState()->GetPlayingChannel(); 2230 if (playingChannel && playingChannel->IsRadio() == bRadio) 2231 return playingChannel->Path(); 2232 2233 const std::shared_ptr<CPVREpgInfoTag> playingTag = mgr.PlaybackState()->GetPlayingEpgTag(); 2234 if (playingTag && playingTag->IsRadio() == bRadio) 2235 { 2236 const std::shared_ptr<CPVRChannel> channel = 2237 mgr.ChannelGroups()->GetChannelForEpgTag(playingTag); 2238 if (channel) 2239 return channel->Path(); 2240 } 2241 } 2242 2243 CSingleLock lock(m_critSection); 2244 return bRadio ? m_selectedItemPathRadio : m_selectedItemPathTV; 2245 } 2246 SeekForward()2247 void CPVRGUIActions::SeekForward() 2248 { 2249 time_t playbackStartTime = CServiceBroker::GetDataCacheCore().GetStartTime(); 2250 if (playbackStartTime > 0) 2251 { 2252 const std::shared_ptr<CPVRChannel> playingChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); 2253 if (playingChannel) 2254 { 2255 time_t nextTime = 0; 2256 std::shared_ptr<CPVREpgInfoTag> next = playingChannel->GetEPGNext(); 2257 if (next) 2258 { 2259 next->StartAsUTC().GetAsTime(nextTime); 2260 } 2261 else 2262 { 2263 // if there is no next event, jump to end of currently playing event 2264 next = playingChannel->GetEPGNow(); 2265 if (next) 2266 next->EndAsUTC().GetAsTime(nextTime); 2267 } 2268 2269 int64_t seekTime = 0; 2270 if (nextTime != 0) 2271 { 2272 seekTime = (nextTime - playbackStartTime) * 1000; 2273 } 2274 else 2275 { 2276 // no epg; jump to end of buffer 2277 seekTime = CServiceBroker::GetDataCacheCore().GetMaxTime(); 2278 } 2279 CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_SEEK_TIME, seekTime); 2280 } 2281 } 2282 } 2283 SeekBackward(unsigned int iThreshold)2284 void CPVRGUIActions::SeekBackward(unsigned int iThreshold) 2285 { 2286 time_t playbackStartTime = CServiceBroker::GetDataCacheCore().GetStartTime(); 2287 if (playbackStartTime > 0) 2288 { 2289 const std::shared_ptr<CPVRChannel> playingChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); 2290 if (playingChannel) 2291 { 2292 time_t prevTime = 0; 2293 std::shared_ptr<CPVREpgInfoTag> prev = playingChannel->GetEPGNow(); 2294 if (prev) 2295 { 2296 prev->StartAsUTC().GetAsTime(prevTime); 2297 2298 // if playback time of current event is above threshold jump to start of current event 2299 int64_t playTime = CServiceBroker::GetDataCacheCore().GetPlayTime() / 1000; 2300 if ((playbackStartTime + playTime - prevTime) <= iThreshold) 2301 { 2302 // jump to start of previous event 2303 prevTime = 0; 2304 prev = playingChannel->GetEPGPrevious(); 2305 if (prev) 2306 prev->StartAsUTC().GetAsTime(prevTime); 2307 } 2308 } 2309 2310 int64_t seekTime = 0; 2311 if (prevTime != 0) 2312 { 2313 seekTime = (prevTime - playbackStartTime) * 1000; 2314 } 2315 else 2316 { 2317 // no epg; jump to begin of buffer 2318 seekTime = CServiceBroker::GetDataCacheCore().GetMinTime(); 2319 } 2320 CApplicationMessenger::GetInstance().PostMsg(TMSG_MEDIA_SEEK_TIME, seekTime); 2321 } 2322 } 2323 } 2324 GetChannelNumberInputHandler()2325 CPVRChannelNumberInputHandler& CPVRGUIActions::GetChannelNumberInputHandler() 2326 { 2327 // window/dialog specific input handler 2328 CPVRChannelNumberInputHandler* windowInputHandler = dynamic_cast<CPVRChannelNumberInputHandler*>(CServiceBroker::GetGUI()->GetWindowManager().GetWindow(CServiceBroker::GetGUI()->GetWindowManager().GetActiveWindowOrDialog())); 2329 if (windowInputHandler) 2330 return *windowInputHandler; 2331 2332 // default 2333 return m_channelNumberInputHandler; 2334 } 2335 GetChannelNavigator()2336 CPVRGUIChannelNavigator& CPVRGUIActions::GetChannelNavigator() 2337 { 2338 return m_channelNavigator; 2339 } 2340 OnPlaybackStarted(const CFileItemPtr & item)2341 void CPVRGUIActions::OnPlaybackStarted(const CFileItemPtr& item) 2342 { 2343 std::shared_ptr<CPVRChannel> channel = item->GetPVRChannelInfoTag(); 2344 if (!channel && item->HasEPGInfoTag()) 2345 { 2346 channel = CServiceBroker::GetPVRManager().ChannelGroups()->GetChannelForEpgTag( 2347 item->GetEPGInfoTag()); 2348 } 2349 2350 if (channel) 2351 { 2352 m_channelNavigator.SetPlayingChannel(channel); 2353 SetSelectedItemPath(channel->IsRadio(), channel->Path()); 2354 } 2355 } 2356 OnPlaybackStopped(const CFileItemPtr & item)2357 void CPVRGUIActions::OnPlaybackStopped(const CFileItemPtr& item) 2358 { 2359 if (item->HasPVRChannelInfoTag() || item->HasEPGInfoTag()) 2360 { 2361 m_channelNavigator.ClearPlayingChannel(); 2362 } 2363 } 2364 AppendChannelNumberCharacter(char cCharacter)2365 void CPVRChannelSwitchingInputHandler::AppendChannelNumberCharacter(char cCharacter) 2366 { 2367 // special case. if only a single zero was typed in, switch to previously played channel. 2368 if (GetCurrentDigitCount() == 0 && cCharacter == '0') 2369 { 2370 SwitchToPreviousChannel(); 2371 return; 2372 } 2373 2374 CPVRChannelNumberInputHandler::AppendChannelNumberCharacter(cCharacter); 2375 } 2376 GetChannelNumbers(std::vector<std::string> & channelNumbers)2377 void CPVRChannelSwitchingInputHandler::GetChannelNumbers(std::vector<std::string>& channelNumbers) 2378 { 2379 CPVRManager& pvrMgr = CServiceBroker::GetPVRManager(); 2380 const std::shared_ptr<CPVRChannel> playingChannel = pvrMgr.PlaybackState()->GetPlayingChannel(); 2381 if (playingChannel) 2382 { 2383 const std::shared_ptr<CPVRChannelGroup> group = pvrMgr.ChannelGroups()->GetGroupAll(playingChannel->IsRadio()); 2384 if (group) 2385 group->GetChannelNumbers(channelNumbers); 2386 } 2387 } 2388 OnInputDone()2389 void CPVRChannelSwitchingInputHandler::OnInputDone() 2390 { 2391 CPVRChannelNumber channelNumber = GetChannelNumber(); 2392 if (channelNumber.GetChannelNumber()) 2393 SwitchToChannel(channelNumber); 2394 } 2395 SwitchToChannel(const CPVRChannelNumber & channelNumber)2396 void CPVRChannelSwitchingInputHandler::SwitchToChannel(const CPVRChannelNumber& channelNumber) 2397 { 2398 if (channelNumber.IsValid() && CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying()) 2399 { 2400 const std::shared_ptr<CPVRChannel> playingChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); 2401 if (playingChannel) 2402 { 2403 if (channelNumber != playingChannel->ChannelNumber()) 2404 { 2405 // channel number present in playing group? 2406 bool bRadio = playingChannel->IsRadio(); 2407 const std::shared_ptr<CPVRChannelGroup> group = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingGroup(bRadio); 2408 std::shared_ptr<CPVRChannel> channel = group->GetByChannelNumber(channelNumber); 2409 2410 if (!channel) 2411 { 2412 // channel number present in any group? 2413 const CPVRChannelGroups* groupAccess = CServiceBroker::GetPVRManager().ChannelGroups()->Get(bRadio); 2414 const std::vector<std::shared_ptr<CPVRChannelGroup>> groups = groupAccess->GetMembers(true); 2415 for (const auto& currentGroup : groups) 2416 { 2417 channel = currentGroup->GetByChannelNumber(channelNumber); 2418 if (channel) 2419 { 2420 // switch channel group 2421 CServiceBroker::GetPVRManager().PlaybackState()->SetPlayingGroup(currentGroup); 2422 break; 2423 } 2424 } 2425 } 2426 2427 if (channel) 2428 { 2429 CApplicationMessenger::GetInstance().PostMsg( 2430 TMSG_GUI_ACTION, WINDOW_INVALID, -1, 2431 static_cast<void*>(new CAction(ACTION_CHANNEL_SWITCH, 2432 static_cast<float>(channelNumber.GetChannelNumber()), 2433 static_cast<float>(channelNumber.GetSubChannelNumber())))); 2434 } 2435 } 2436 } 2437 } 2438 } 2439 SwitchToPreviousChannel()2440 void CPVRChannelSwitchingInputHandler::SwitchToPreviousChannel() 2441 { 2442 if (CServiceBroker::GetPVRManager().PlaybackState()->IsPlaying()) 2443 { 2444 const std::shared_ptr<CPVRChannel> playingChannel = CServiceBroker::GetPVRManager().PlaybackState()->GetPlayingChannel(); 2445 if (playingChannel) 2446 { 2447 const std::shared_ptr<CPVRChannelGroup> group = CServiceBroker::GetPVRManager().ChannelGroups()->GetPreviousPlayedGroup(); 2448 if (group) 2449 { 2450 CServiceBroker::GetPVRManager().PlaybackState()->SetPlayingGroup(group); 2451 const std::shared_ptr<CPVRChannel> channel = group->GetLastPlayedChannel(playingChannel->ChannelID()); 2452 if (channel) 2453 { 2454 const CPVRChannelNumber channelNumber = channel->ChannelNumber(); 2455 CApplicationMessenger::GetInstance().SendMsg( 2456 TMSG_GUI_ACTION, WINDOW_INVALID, -1, 2457 static_cast<void*>(new CAction(ACTION_CHANNEL_SWITCH, 2458 static_cast<float>(channelNumber.GetChannelNumber()), 2459 static_cast<float>(channelNumber.GetSubChannelNumber())))); 2460 } 2461 } 2462 } 2463 } 2464 } 2465 2466 } // namespace PVR 2467