/* * Copyright (C) 2012-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "PVRChannel.h" #include "ServiceBroker.h" #include "XBDateTime.h" #include "guilib/LocalizeStrings.h" #include "pvr/PVRDatabase.h" #include "pvr/PVRManager.h" #include "pvr/addons/PVRClient.h" #include "pvr/epg/Epg.h" #include "pvr/epg/EpgChannelData.h" #include "pvr/epg/EpgContainer.h" #include "pvr/epg/EpgInfoTag.h" #include "pvr/providers/PVRProviders.h" #include "utils/StringUtils.h" #include "utils/Variant.h" #include "utils/log.h" #include #include #include using namespace PVR; const std::string CPVRChannel::IMAGE_OWNER_PATTERN = "pvrchannel_{}"; bool CPVRChannel::operator==(const CPVRChannel& right) const { return (m_bIsRadio == right.m_bIsRadio && m_iUniqueId == right.m_iUniqueId && m_iClientId == right.m_iClientId); } bool CPVRChannel::operator!=(const CPVRChannel& right) const { return !(*this == right); } CPVRChannel::CPVRChannel(bool bRadio) : m_bIsRadio(bRadio), m_iconPath("", StringUtils::Format(IMAGE_OWNER_PATTERN, bRadio ? "radio" : "tv")) { UpdateEncryptionName(); } CPVRChannel::CPVRChannel(bool bRadio, const std::string& iconPath) : m_bIsRadio(bRadio), m_iconPath(iconPath, StringUtils::Format(IMAGE_OWNER_PATTERN, bRadio ? "radio" : "tv")) { UpdateEncryptionName(); } CPVRChannel::CPVRChannel(const PVR_CHANNEL& channel, unsigned int iClientId) : m_bIsRadio(channel.bIsRadio), m_bIsHidden(channel.bIsHidden), m_iconPath(channel.strIconPath, StringUtils::Format(IMAGE_OWNER_PATTERN, channel.bIsRadio ? "radio" : "tv")), m_strChannelName(channel.strChannelName), m_bHasArchive(channel.bHasArchive), m_bEPGEnabled(!channel.bIsHidden), m_iUniqueId(channel.iUniqueId), m_iClientId(iClientId), m_clientChannelNumber(channel.iChannelNumber, channel.iSubChannelNumber), m_strClientChannelName(channel.strChannelName), m_strMimeType(channel.strMimeType), m_iClientEncryptionSystem(channel.iEncryptionSystem), m_iClientOrder(channel.iOrder), m_iClientProviderUid(channel.iClientProviderUid) { if (m_strChannelName.empty()) m_strChannelName = StringUtils::Format("{} {}", g_localizeStrings.Get(19029), m_iUniqueId); UpdateEncryptionName(); } CPVRChannel::~CPVRChannel() { ResetEPG(); } void CPVRChannel::FillAddonData(PVR_CHANNEL& channel) const { channel = {}; channel.iUniqueId = UniqueID(); channel.iChannelNumber = ClientChannelNumber().GetChannelNumber(); channel.iSubChannelNumber = ClientChannelNumber().GetSubChannelNumber(); strncpy(channel.strChannelName, ClientChannelName().c_str(), sizeof(channel.strChannelName) - 1); strncpy(channel.strIconPath, ClientIconPath().c_str(), sizeof(channel.strIconPath) - 1); channel.iEncryptionSystem = EncryptionSystem(); channel.bIsRadio = IsRadio(); channel.bIsHidden = IsHidden(); strncpy(channel.strMimeType, MimeType().c_str(), sizeof(channel.strMimeType) - 1); channel.iClientProviderUid = ClientProviderUid(); channel.bHasArchive = HasArchive(); } void CPVRChannel::Serialize(CVariant& value) const { value["channelid"] = m_iChannelId; value["channeltype"] = m_bIsRadio ? "radio" : "tv"; value["hidden"] = m_bIsHidden; value["locked"] = m_bIsLocked; value["icon"] = ClientIconPath(); value["channel"] = m_strChannelName; value["uniqueid"] = m_iUniqueId; CDateTime lastPlayed(m_iLastWatched); value["lastplayed"] = lastPlayed.IsValid() ? lastPlayed.GetAsDBDate() : ""; std::shared_ptr epg = GetEPGNow(); if (epg) { // add the properties of the current EPG item to the main object epg->Serialize(value); // and add an extra sub-object with only the current EPG details epg->Serialize(value["broadcastnow"]); } epg = GetEPGNext(); if (epg) epg->Serialize(value["broadcastnext"]); value["hasarchive"] = m_bHasArchive; value["clientid"] = m_iClientId; } bool CPVRChannel::QueueDelete() { bool bReturn = false; const std::shared_ptr database = CServiceBroker::GetPVRManager().GetTVDatabase(); if (!database) return bReturn; const std::shared_ptr epg = GetEPG(); if (epg) ResetEPG(); bReturn = database->QueueDeleteQuery(*this); return bReturn; } std::shared_ptr CPVRChannel::GetEPG() const { const_cast(this)->CreateEPG(); std::unique_lock lock(m_critSection); if (!m_bIsHidden && m_bEPGEnabled) return m_epg; return {}; } bool CPVRChannel::CreateEPG() { std::unique_lock lock(m_critSection); if (!m_epg) { m_epg = CServiceBroker::GetPVRManager().EpgContainer().CreateChannelEpg( m_iEpgId, m_strEPGScraper, std::make_shared(*this)); if (m_epg) { if (m_epg->EpgID() != m_iEpgId) { m_iEpgId = m_epg->EpgID(); m_bChanged = true; } // Subscribe for EPG delete event m_epg->Events().Subscribe(this, &CPVRChannel::Notify); return true; } } return false; } void CPVRChannel::Notify(const PVREvent& event) { if (event == PVREvent::EpgDeleted) { ResetEPG(); } } void CPVRChannel::ResetEPG() { std::shared_ptr epgToUnsubscribe; { std::unique_lock lock(m_critSection); if (m_epg) { epgToUnsubscribe = m_epg; m_epg.reset(); } } if (epgToUnsubscribe) epgToUnsubscribe->Events().Unsubscribe(this); } bool CPVRChannel::UpdateFromClient(const std::shared_ptr& channel) { std::unique_lock lock(m_critSection); SetClientID(channel->ClientID()); SetArchive(channel->HasArchive()); SetClientProviderUid(channel->ClientProviderUid()); m_clientChannelNumber = channel->m_clientChannelNumber; m_strMimeType = channel->MimeType(); m_iClientEncryptionSystem = channel->EncryptionSystem(); m_strClientChannelName = channel->ClientChannelName(); UpdateEncryptionName(); // only update the channel name, icon, and hidden flag if the user hasn't changed them manually if (m_strChannelName.empty() || !IsUserSetName()) SetChannelName(channel->ClientChannelName()); if (IconPath().empty() || !IsUserSetIcon()) SetIconPath(channel->ClientIconPath()); if (!IsUserSetHidden()) SetHidden(channel->IsHidden()); return m_bChanged; } bool CPVRChannel::Persist() { { // not changed std::unique_lock lock(m_critSection); if (!m_bChanged && m_iChannelId > 0) return true; } const std::shared_ptr database = CServiceBroker::GetPVRManager().GetTVDatabase(); if (database) { CLog::LogFC(LOGDEBUG, LOGPVR, "Persisting channel '{}'", m_strChannelName); bool bReturn = database->Persist(*this, true); std::unique_lock lock(m_critSection); m_bChanged = !bReturn; return bReturn; } return false; } bool CPVRChannel::SetChannelID(int iChannelId) { std::unique_lock lock(m_critSection); if (m_iChannelId != iChannelId) { m_iChannelId = iChannelId; const std::shared_ptr epg = GetEPG(); if (epg) epg->GetChannelData()->SetChannelId(m_iChannelId); m_bChanged = true; return true; } return false; } bool CPVRChannel::SetHidden(bool bIsHidden, bool bIsUserSetHidden /*= false*/) { std::unique_lock lock(m_critSection); if (m_bIsHidden != bIsHidden) { m_bIsHidden = bIsHidden; m_bIsUserSetHidden = bIsUserSetHidden; if (m_epg) m_epg->GetChannelData()->SetHidden(m_bIsHidden); m_bChanged = true; return true; } return false; } bool CPVRChannel::SetLocked(bool bIsLocked) { std::unique_lock lock(m_critSection); if (m_bIsLocked != bIsLocked) { m_bIsLocked = bIsLocked; const std::shared_ptr epg = GetEPG(); if (epg) epg->GetChannelData()->SetLocked(m_bIsLocked); m_bChanged = true; return true; } return false; } std::shared_ptr CPVRChannel::GetRadioRDSInfoTag() const { std::unique_lock lock(m_critSection); return m_rdsTag; } void CPVRChannel::SetRadioRDSInfoTag(const std::shared_ptr& tag) { std::unique_lock lock(m_critSection); m_rdsTag = tag; } bool CPVRChannel::HasArchive() const { std::unique_lock lock(m_critSection); return m_bHasArchive; } bool CPVRChannel::SetArchive(bool bHasArchive) { std::unique_lock lock(m_critSection); if (m_bHasArchive != bHasArchive) { m_bHasArchive = bHasArchive; m_bChanged = true; return true; } return false; } bool CPVRChannel::SetIconPath(const std::string& strIconPath, bool bIsUserSetIcon /* = false */) { if (StringUtils::StartsWith(strIconPath, "image://")) { CLog::LogF(LOGERROR, "Not allowed to call this method with an image URL"); return false; } std::unique_lock lock(m_critSection); if (ClientIconPath() == strIconPath) return false; m_iconPath.SetClientImage(strIconPath); const std::shared_ptr epg = GetEPG(); if (epg) epg->GetChannelData()->SetChannelIconPath(strIconPath); m_bChanged = true; m_bIsUserSetIcon = bIsUserSetIcon && !IconPath().empty(); return true; } bool CPVRChannel::SetChannelName(const std::string& strChannelName, bool bIsUserSetName /*= false*/) { std::string strName(strChannelName); if (strName.empty()) strName = StringUtils::Format(g_localizeStrings.Get(19085), m_clientChannelNumber.FormattedChannelNumber()); std::unique_lock lock(m_critSection); if (m_strChannelName != strName) { m_strChannelName = strName; m_bIsUserSetName = bIsUserSetName; /* if the user changes the name manually to an empty string we reset the flag and use the name from the client instead */ if (bIsUserSetName && strChannelName.empty()) { m_bIsUserSetName = false; m_strChannelName = ClientChannelName(); } const std::shared_ptr epg = GetEPG(); if (epg) epg->GetChannelData()->SetChannelName(m_strChannelName); m_bChanged = true; return true; } return false; } bool CPVRChannel::SetLastWatched(time_t iLastWatched) { { std::unique_lock lock(m_critSection); m_iLastWatched = iLastWatched; } const std::shared_ptr database = CServiceBroker::GetPVRManager().GetTVDatabase(); if (database) return database->UpdateLastWatched(*this); return false; } /********** Client related channel methods **********/ bool CPVRChannel::SetClientID(int iClientId) { std::unique_lock lock(m_critSection); if (m_iClientId != iClientId) { m_iClientId = iClientId; m_bChanged = true; return true; } return false; } std::string CPVRChannel::GetEncryptionName(int iCaid) { // http://www.dvb.org/index.php?id=174 // http://en.wikipedia.org/wiki/Conditional_access_system std::string strName(g_localizeStrings.Get(13205)); /* Unknown */ if (iCaid == 0x0000) strName = g_localizeStrings.Get(19013); /* Free To Air */ else if (iCaid >= 0x0001 && iCaid <= 0x009F) strName = g_localizeStrings.Get(19014); /* Fixed */ else if (iCaid >= 0x00A0 && iCaid <= 0x00A1) strName = g_localizeStrings.Get(338); /* Analog */ else if (iCaid >= 0x00A2 && iCaid <= 0x00FF) strName = g_localizeStrings.Get(19014); /* Fixed */ else if (iCaid >= 0x0100 && iCaid <= 0x01FF) strName = "SECA Mediaguard"; else if (iCaid == 0x0464) strName = "EuroDec"; else if (iCaid >= 0x0500 && iCaid <= 0x05FF) strName = "Viaccess"; else if (iCaid >= 0x0600 && iCaid <= 0x06FF) strName = "Irdeto"; else if (iCaid >= 0x0900 && iCaid <= 0x09FF) strName = "NDS Videoguard"; else if (iCaid >= 0x0B00 && iCaid <= 0x0BFF) strName = "Conax"; else if (iCaid >= 0x0D00 && iCaid <= 0x0DFF) strName = "CryptoWorks"; else if (iCaid >= 0x0E00 && iCaid <= 0x0EFF) strName = "PowerVu"; else if (iCaid == 0x1000) strName = "RAS"; else if (iCaid >= 0x1200 && iCaid <= 0x12FF) strName = "NagraVision"; else if (iCaid >= 0x1700 && iCaid <= 0x17FF) strName = "BetaCrypt"; else if (iCaid >= 0x1800 && iCaid <= 0x18FF) strName = "NagraVision"; else if (iCaid == 0x22F0) strName = "Codicrypt"; else if (iCaid == 0x2600) strName = "BISS"; else if (iCaid == 0x4347) strName = "CryptOn"; else if (iCaid == 0x4800) strName = "Accessgate"; else if (iCaid == 0x4900) strName = "China Crypt"; else if (iCaid == 0x4A10) strName = "EasyCas"; else if (iCaid == 0x4A20) strName = "AlphaCrypt"; else if (iCaid == 0x4A70) strName = "DreamCrypt"; else if (iCaid == 0x4A60) strName = "SkyCrypt"; else if (iCaid == 0x4A61) strName = "Neotioncrypt"; else if (iCaid == 0x4A62) strName = "SkyCrypt"; else if (iCaid == 0x4A63) strName = "Neotion SHL"; else if (iCaid >= 0x4A64 && iCaid <= 0x4A6F) strName = "SkyCrypt"; else if (iCaid == 0x4A80) strName = "ThalesCrypt"; else if (iCaid == 0x4AA1) strName = "KeyFly"; else if (iCaid == 0x4ABF) strName = "DG-Crypt"; else if (iCaid >= 0x4AD0 && iCaid <= 0x4AD1) strName = "X-Crypt"; else if (iCaid == 0x4AD4) strName = "OmniCrypt"; else if (iCaid == 0x4AE0) strName = "RossCrypt"; else if (iCaid == 0x5500) strName = "Z-Crypt"; else if (iCaid == 0x5501) strName = "Griffin"; else if (iCaid == 0x5601) strName = "Verimatrix"; if (iCaid >= 0) strName += StringUtils::Format(" ({:04X})", iCaid); return strName; } void CPVRChannel::UpdateEncryptionName() { std::unique_lock lock(m_critSection); m_strClientEncryptionName = GetEncryptionName(m_iClientEncryptionSystem); } bool CPVRChannel::SetClientProviderUid(int iClientProviderUid) { std::unique_lock lock(m_critSection); if (m_iClientProviderUid != iClientProviderUid) { m_iClientProviderUid = iClientProviderUid; m_bChanged = true; return true; } return false; } /********** EPG methods **********/ std::vector> CPVRChannel::GetEpgTags() const { const std::shared_ptr epg = GetEPG(); if (!epg) { CLog::LogFC(LOGDEBUG, LOGPVR, "Cannot get EPG for channel '{}'", m_strChannelName); return {}; } return epg->GetTags(); } std::vector> CPVRChannel::GetEPGTimeline( const CDateTime& timelineStart, const CDateTime& timelineEnd, const CDateTime& minEventEnd, const CDateTime& maxEventStart) const { const std::shared_ptr epg = GetEPG(); if (epg) { return epg->GetTimeline(timelineStart, timelineEnd, minEventEnd, maxEventStart); } else { // return single gap tag spanning whole timeline return std::vector>{ CreateEPGGapTag(timelineStart, timelineEnd)}; } } std::shared_ptr CPVRChannel::CreateEPGGapTag(const CDateTime& start, const CDateTime& end) const { const std::shared_ptr epg = GetEPG(); if (epg) return std::make_shared(epg->GetChannelData(), epg->EpgID(), start, end, true); else return std::make_shared(std::make_shared(*this), -1, start, end, true); } std::shared_ptr CPVRChannel::GetEPGNow() const { std::shared_ptr tag; const std::shared_ptr epg = GetEPG(); if (epg) tag = epg->GetTagNow(); return tag; } std::shared_ptr CPVRChannel::GetEPGNext() const { std::shared_ptr tag; const std::shared_ptr epg = GetEPG(); if (epg) tag = epg->GetTagNext(); return tag; } std::shared_ptr CPVRChannel::GetEPGPrevious() const { std::shared_ptr tag; const std::shared_ptr epg = GetEPG(); if (epg) tag = epg->GetTagPrevious(); return tag; } bool CPVRChannel::SetEPGEnabled(bool bEPGEnabled) { std::unique_lock lock(m_critSection); if (m_bEPGEnabled != bEPGEnabled) { m_bEPGEnabled = bEPGEnabled; if (m_epg) { m_epg->GetChannelData()->SetEPGEnabled(m_bEPGEnabled); if (m_bEPGEnabled) m_epg->ForceUpdate(); else m_epg->Clear(); } m_bChanged = true; return true; } return false; } bool CPVRChannel::SetEPGScraper(const std::string& strScraper) { std::unique_lock lock(m_critSection); if (m_strEPGScraper != strScraper) { bool bCleanEPG = !m_strEPGScraper.empty() || strScraper.empty(); m_strEPGScraper = strScraper; if (bCleanEPG && m_epg) m_epg->Clear(); m_bChanged = true; return true; } return false; } void CPVRChannel::ToSortable(SortItem& sortable, Field field) const { std::unique_lock lock(m_critSection); if (field == FieldChannelName) sortable[FieldChannelName] = m_strChannelName; else if (field == FieldLastPlayed) { const CDateTime lastWatched(m_iLastWatched); sortable[FieldLastPlayed] = lastWatched.IsValid() ? lastWatched.GetAsDBDateTime() : StringUtils::Empty; } else if (field == FieldProvider) sortable[FieldProvider] = StringUtils::Format("{} {}", m_iClientId, m_iClientProviderUid); } int CPVRChannel::ChannelID() const { std::unique_lock lock(m_critSection); return m_iChannelId; } bool CPVRChannel::IsNew() const { std::unique_lock lock(m_critSection); return m_iChannelId <= 0; } bool CPVRChannel::IsHidden() const { std::unique_lock lock(m_critSection); return m_bIsHidden; } bool CPVRChannel::IsLocked() const { std::unique_lock lock(m_critSection); return m_bIsLocked; } std::string CPVRChannel::ClientIconPath() const { std::unique_lock lock(m_critSection); return m_iconPath.GetClientImage(); } std::string CPVRChannel::IconPath() const { std::unique_lock lock(m_critSection); return m_iconPath.GetLocalImage(); } bool CPVRChannel::IsUserSetIcon() const { std::unique_lock lock(m_critSection); return m_bIsUserSetIcon; } bool CPVRChannel::IsUserSetName() const { std::unique_lock lock(m_critSection); return m_bIsUserSetName; } bool CPVRChannel::IsUserSetHidden() const { std::unique_lock lock(m_critSection); return m_bIsUserSetHidden; } std::string CPVRChannel::ChannelName() const { std::unique_lock lock(m_critSection); return m_strChannelName; } time_t CPVRChannel::LastWatched() const { std::unique_lock lock(m_critSection); return m_iLastWatched; } bool CPVRChannel::IsChanged() const { std::unique_lock lock(m_critSection); return m_bChanged; } void CPVRChannel::Persisted() { std::unique_lock lock(m_critSection); m_bChanged = false; } int CPVRChannel::UniqueID() const { return m_iUniqueId; } int CPVRChannel::ClientID() const { std::unique_lock lock(m_critSection); return m_iClientId; } const CPVRChannelNumber& CPVRChannel::ClientChannelNumber() const { std::unique_lock lock(m_critSection); return m_clientChannelNumber; } std::string CPVRChannel::ClientChannelName() const { std::unique_lock lock(m_critSection); return m_strClientChannelName; } std::string CPVRChannel::MimeType() const { std::unique_lock lock(m_critSection); return m_strMimeType; } bool CPVRChannel::IsEncrypted() const { std::unique_lock lock(m_critSection); return m_iClientEncryptionSystem > 0; } int CPVRChannel::EncryptionSystem() const { std::unique_lock lock(m_critSection); return m_iClientEncryptionSystem; } std::string CPVRChannel::EncryptionName() const { std::unique_lock lock(m_critSection); return m_strClientEncryptionName; } int CPVRChannel::EpgID() const { std::unique_lock lock(m_critSection); return m_iEpgId; } bool CPVRChannel::EPGEnabled() const { std::unique_lock lock(m_critSection); return m_bEPGEnabled; } std::string CPVRChannel::EPGScraper() const { std::unique_lock lock(m_critSection); return m_strEPGScraper; } bool CPVRChannel::CanRecord() const { const std::shared_ptr client = CServiceBroker::GetPVRManager().GetClient(m_iClientId); return client && client->GetClientCapabilities().SupportsRecordings() && client->GetClientCapabilities().SupportsTimers(); } std::shared_ptr CPVRChannel::GetDefaultProvider() const { return CServiceBroker::GetPVRManager().Providers()->GetByClient(m_iClientId, PVR_PROVIDER_INVALID_UID); } bool CPVRChannel::HasClientProvider() const { std::unique_lock lock(m_critSection); return m_iClientProviderUid != PVR_PROVIDER_INVALID_UID; } std::shared_ptr CPVRChannel::GetProvider() const { auto provider = CServiceBroker::GetPVRManager().Providers()->GetByClient(m_iClientId, m_iClientProviderUid); if (!provider) provider = GetDefaultProvider(); return provider; }