From 4eb898a5d52567ba087d570793a9c108eb41af4d Mon Sep 17 00:00:00 2001 From: ksooo <3226626+ksooo@users.noreply.github.com> Date: Sat, 31 Aug 2024 14:15:18 +0200 Subject: [PATCH 1/3] Refactor recordings class hierarchy (eliminate duplicate data etc.). --- CMakeLists.txt | 2 + src/tvheadend/entity/AutoRecording.cpp | 11 +- src/tvheadend/entity/AutoRecording.h | 19 +-- src/tvheadend/entity/Entity.h | 21 +-- src/tvheadend/entity/Recording.h | 128 ++++++----------- src/tvheadend/entity/RecordingBase.cpp | 140 ------------------- src/tvheadend/entity/RecordingBase.h | 73 ++++------ src/tvheadend/entity/SeriesRecordingBase.cpp | 40 ++++++ src/tvheadend/entity/SeriesRecordingBase.h | 66 +++++++++ src/tvheadend/entity/TimeRecording.cpp | 6 +- src/tvheadend/entity/TimeRecording.h | 18 +-- 11 files changed, 210 insertions(+), 314 deletions(-) create mode 100644 src/tvheadend/entity/SeriesRecordingBase.cpp create mode 100644 src/tvheadend/entity/SeriesRecordingBase.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 97e3b903..ef9a3818 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,6 +83,8 @@ set(HTS_SOURCES_TVHEADEND_ENTITY src/tvheadend/entity/Recording.h src/tvheadend/entity/RecordingBase.h src/tvheadend/entity/RecordingBase.cpp + src/tvheadend/entity/SeriesRecordingBase.h + src/tvheadend/entity/SeriesRecordingBase.cpp src/tvheadend/entity/Schedule.h src/tvheadend/entity/Schedule.cpp src/tvheadend/entity/Tag.h diff --git a/src/tvheadend/entity/AutoRecording.cpp b/src/tvheadend/entity/AutoRecording.cpp index c0e7c815..5e8e41ef 100644 --- a/src/tvheadend/entity/AutoRecording.cpp +++ b/src/tvheadend/entity/AutoRecording.cpp @@ -12,20 +12,13 @@ using namespace tvheadend; using namespace tvheadend::entity; -AutoRecording::AutoRecording(const std::string& id /*= ""*/) - : RecordingBase(id), - m_startWindowBegin(0), - m_startWindowEnd(0), - m_startExtra(0), - m_stopExtra(0), - m_dupDetect(0), - m_fulltext(0) +AutoRecording::AutoRecording(const std::string& id /*= ""*/) : SeriesRecordingBase(id) { } bool AutoRecording::operator==(const AutoRecording& right) { - return RecordingBase::operator==(right) && m_startWindowBegin == right.m_startWindowBegin && + return SeriesRecordingBase::operator==(right) && m_startWindowBegin == right.m_startWindowBegin && m_startWindowEnd == right.m_startWindowEnd && m_startExtra == right.m_startExtra && m_stopExtra == right.m_stopExtra && m_dupDetect == right.m_dupDetect && m_fulltext == right.m_fulltext && m_seriesLink == right.m_seriesLink; diff --git a/src/tvheadend/entity/AutoRecording.h b/src/tvheadend/entity/AutoRecording.h index dcfd8373..dbdde4f0 100644 --- a/src/tvheadend/entity/AutoRecording.h +++ b/src/tvheadend/entity/AutoRecording.h @@ -7,10 +7,13 @@ #pragma once -#include "RecordingBase.h" +#include "SeriesRecordingBase.h" +#include #include #include +#include +#include namespace tvheadend { @@ -19,7 +22,7 @@ class InstanceSettings; namespace entity { -class AutoRecording : public RecordingBase +class AutoRecording : public SeriesRecordingBase { public: AutoRecording(const std::string& id = ""); @@ -53,12 +56,12 @@ class AutoRecording : public RecordingBase private: std::shared_ptr m_settings; - int32_t m_startWindowBegin; // Begin of the starting window (minutes from midnight). - int32_t m_startWindowEnd; // End of the starting window (minutes from midnight). - int64_t m_startExtra; // Extra start minutes (pre-time). - int64_t m_stopExtra; // Extra stop minutes (post-time). - uint32_t m_dupDetect; // duplicate episode detect (numeric values: see dvr_autorec_dedup_t). - uint32_t m_fulltext; // Fulltext epg search. + int32_t m_startWindowBegin{0}; // Begin of the starting window (minutes from midnight). + int32_t m_startWindowEnd{0}; // End of the starting window (minutes from midnight). + int64_t m_startExtra{0}; // Extra start minutes (pre-time). + int64_t m_stopExtra{0}; // Extra stop minutes (post-time). + uint32_t m_dupDetect{0}; // duplicate episode detect (numeric values: see dvr_autorec_dedup_t). + uint32_t m_fulltext{0}; // Fulltext epg search. std::string m_seriesLink; // Series link. }; diff --git a/src/tvheadend/entity/Entity.h b/src/tvheadend/entity/Entity.h index ba1d09c1..d0f9fa5f 100644 --- a/src/tvheadend/entity/Entity.h +++ b/src/tvheadend/entity/Entity.h @@ -9,9 +9,7 @@ #include -namespace tvheadend -{ -namespace entity +namespace tvheadend::entity { /** @@ -20,9 +18,13 @@ namespace entity class Entity { public: - Entity() : m_id(0), m_dirty(false) {} + Entity() = default; virtual ~Entity() = default; + bool operator==(const Entity& right) { return m_id == right.m_id; } + + bool operator!=(const Entity& right) { return !(*this == right); } + /** * @return if the entity is dirty */ @@ -30,7 +32,7 @@ class Entity /** * Marks the entity as dirty or not - * @param dirty + * @param dirty The new dirty state */ virtual void SetDirty(bool dirty) { m_dirty = dirty; } @@ -41,16 +43,15 @@ class Entity /** * Sets the entity ID - * @param id + * @param id The entity id */ void SetId(uint32_t id) { m_id = id; } protected: - uint32_t m_id; + uint32_t m_id{0}; private: - bool m_dirty; + bool m_dirty{false}; }; -} // namespace entity -} // namespace tvheadend +} // namespace tvheadend::entity diff --git a/src/tvheadend/entity/Recording.h b/src/tvheadend/entity/Recording.h index 7d011a2c..57eae314 100644 --- a/src/tvheadend/entity/Recording.h +++ b/src/tvheadend/entity/Recording.h @@ -7,14 +7,14 @@ #pragma once -#include "../utilities/LifetimeMapper.h" -#include "Entity.h" +#include "RecordingBase.h" #include "kodi/addon-instance/pvr/Timers.h" -#include +#include #include #include +#include // Timer types #define TIMER_ONCE_MANUAL (PVR_TIMER_TYPE_NONE + 1) @@ -25,9 +25,7 @@ #define TIMER_REPEATING_EPG (PVR_TIMER_TYPE_NONE + 6) #define TIMER_REPEATING_SERIESLINK (PVR_TIMER_TYPE_NONE + 7) -namespace tvheadend -{ -namespace entity +namespace tvheadend::entity { class Recording; @@ -39,54 +37,31 @@ typedef std::map Recordings; * TODO: Create separate classes for recordings and timers since a * recording obviously can't have a "timer type" */ -class Recording : public Entity +class Recording : public RecordingBase { public: - Recording() - : m_enabled(0), - m_channel(0), - m_channelType(0), - m_eventId(0), - m_start(0), - m_stop(0), - m_startExtra(0), - m_stopExtra(0), - m_filesStart(0), - m_filesStop(0), - m_filesSize(0), - m_state(PVR_TIMER_STATE_ERROR), - m_lifetime(0), - m_priority(50), // Kodi default - "normal" - m_playCount(0), - m_playPosition(0), - m_contentType(0), - m_season(-1), - m_episode(-1), - m_part(0), - m_ageRating(0) - { - } + Recording() = default; - bool operator==(const Recording& other) const + bool operator==(const Recording& other) { - return m_id == other.m_id && m_enabled == other.m_enabled && m_channel == other.m_channel && - m_channelType == other.m_channelType && m_channelName == other.m_channelName && - m_eventId == other.m_eventId && m_start == other.m_start && m_stop == other.m_stop && + return RecordingBase::operator==(other) && m_channelType == other.m_channelType && + m_channelName == other.m_channelName && m_eventId == other.m_eventId && + m_start == other.m_start && m_stop == other.m_stop && m_startExtra == other.m_startExtra && m_stopExtra == other.m_stopExtra && m_filesStart == other.m_filesStart && m_filesStop == other.m_filesStop && - m_filesSize == other.m_filesSize && m_title == other.m_title && m_path == other.m_path && - m_description == other.m_description && m_image == other.m_image && - m_fanartImage == other.m_fanartImage && m_timerecId == other.m_timerecId && - m_autorecId == other.m_autorecId && m_state == other.m_state && - m_error == other.m_error && m_lifetime == other.m_lifetime && - m_priority == other.m_priority && m_playCount == other.m_playCount && - m_playPosition == other.m_playPosition && m_contentType == other.m_contentType && - m_season == other.m_season && m_episode == other.m_episode && m_part == other.m_part && + m_filesSize == other.m_filesSize && m_subtitle == other.m_subtitle && + m_path == other.m_path && m_description == other.m_description && + m_image == other.m_image && m_fanartImage == other.m_fanartImage && + m_timerecId == other.m_timerecId && m_autorecId == other.m_autorecId && + m_state == other.m_state && m_error == other.m_error && + m_playCount == other.m_playCount && m_playPosition == other.m_playPosition && + m_contentType == other.m_contentType && m_season == other.m_season && + m_episode == other.m_episode && m_part == other.m_part && m_ageRating == other.m_ageRating && m_ratingLabel == other.m_ratingLabel && m_ratingIcon == other.m_ratingIcon && m_ratingSource == other.m_ratingSource; } - bool operator!=(const Recording& other) const { return !(*this == other); } + bool operator!=(const Recording& other) { return !(*this == other); } bool IsRecording() const { @@ -115,12 +90,6 @@ class Recording : public Entity return TIMER_ONCE_MANUAL; } - bool IsEnabled() const { return m_enabled > 0; } - void SetEnabled(uint32_t enabled) { m_enabled = enabled; } - - uint32_t GetChannel() const { return m_channel; } - void SetChannel(uint32_t channel) { m_channel = channel; } - uint32_t GetChannelType() const { return m_channelType; } void SetChannelType(uint32_t channelType) { m_channelType = channelType; } @@ -130,19 +99,19 @@ class Recording : public Entity uint32_t GetEventId() const { return m_eventId; } void SetEventId(uint32_t eventId) { m_eventId = eventId; } - // TODO: Change to time_t + //! @todo Change to time_t int64_t GetStart() const { return m_start; } void SetStart(int64_t start) { m_start = start; } - // TODO: Change to time_t + //! @todo Change to time_t int64_t GetStop() const { return m_stop; } void SetStop(int64_t stop) { m_stop = stop; } - // TODO: Change to time_t + //! @todo Change to time_t int64_t GetStartExtra() const { return m_startExtra; } void SetStartExtra(int64_t startExtra) { m_startExtra = startExtra; } - // TODO: Change to time_t + //! @todo Change to time_t int64_t GetStopExtra() const { return m_stopExtra; } void SetStopExtra(int64_t stopExtra) { m_stopExtra = stopExtra; } @@ -155,9 +124,6 @@ class Recording : public Entity int64_t GetFilesSize() const { return m_filesSize; } void SetFilesSize(int64_t size) { m_filesSize = size; } - const std::string& GetTitle() const { return m_title; } - void SetTitle(const std::string& title) { m_title = title; } - const std::string& GetSubtitle() const { return m_subtitle; } void SetSubtitle(const std::string& subtitle) { m_subtitle = subtitle; } @@ -185,12 +151,6 @@ class Recording : public Entity const std::string& GetError() const { return m_error; } void SetError(const std::string& error) { m_error = error; } - int GetLifetime() const { return utilities::LifetimeMapper::TvhToKodi(m_lifetime); } - void SetLifetime(uint32_t lifetime) { m_lifetime = lifetime; } - - uint32_t GetPriority() const { return m_priority; } - void SetPriority(uint32_t priority) { m_priority = priority; } - uint32_t GetPlayCount() const { return m_playCount; } void SetPlayCount(uint32_t playCount) { m_playCount = playCount; } @@ -226,19 +186,16 @@ class Recording : public Entity void SetRatingSource(const std::string& ratingSource) { m_ratingSource = ratingSource; } private: - uint32_t m_enabled; - uint32_t m_channel; - uint32_t m_channelType; + uint32_t m_channelType{0}; std::string m_channelName; - uint32_t m_eventId; - int64_t m_start; - int64_t m_stop; - int64_t m_startExtra; - int64_t m_stopExtra; - int64_t m_filesStart; - int64_t m_filesStop; - int64_t m_filesSize; - std::string m_title; + uint32_t m_eventId{0}; + int64_t m_start{0}; + int64_t m_stop{0}; + int64_t m_startExtra{0}; + int64_t m_stopExtra{0}; + int64_t m_filesStart{0}; + int64_t m_filesStop{0}; + int64_t m_filesSize{0}; std::string m_subtitle; std::string m_path; std::string m_description; @@ -246,21 +203,18 @@ class Recording : public Entity std::string m_fanartImage; std::string m_timerecId; std::string m_autorecId; - PVR_TIMER_STATE m_state; + PVR_TIMER_STATE m_state{PVR_TIMER_STATE_ERROR}; std::string m_error; - uint32_t m_lifetime; - uint32_t m_priority; - uint32_t m_playCount; - uint32_t m_playPosition; - uint32_t m_contentType; - int32_t m_season; - int32_t m_episode; - uint32_t m_part; - uint32_t m_ageRating; + uint32_t m_playCount{0}; + uint32_t m_playPosition{0}; + uint32_t m_contentType{0}; + int32_t m_season{-1}; + int32_t m_episode{-1}; + uint32_t m_part{0}; + uint32_t m_ageRating{0}; std::string m_ratingLabel; std::string m_ratingIcon; std::string m_ratingSource; }; -} // namespace entity -} // namespace tvheadend +} // namespace tvheadend::entity diff --git a/src/tvheadend/entity/RecordingBase.cpp b/src/tvheadend/entity/RecordingBase.cpp index 4d888e01..6beac776 100644 --- a/src/tvheadend/entity/RecordingBase.cpp +++ b/src/tvheadend/entity/RecordingBase.cpp @@ -9,149 +9,9 @@ #include "../utilities/LifetimeMapper.h" -#include -#include - using namespace tvheadend::entity; -RecordingBase::RecordingBase(const std::string& id /*= ""*/) - : m_sid(id), m_enabled(0), m_daysOfWeek(0), m_lifetime(0), m_priority(0), m_channel(0) -{ - m_id = GetNextIntId(); -} - -bool RecordingBase::operator==(const RecordingBase& right) -{ - return m_id == right.m_id && m_enabled == right.m_enabled && m_daysOfWeek == right.m_daysOfWeek && - m_lifetime == right.m_lifetime && m_priority == right.m_priority && - m_title == right.m_title && m_name == right.m_name && m_directory == right.m_directory && - m_owner == right.m_owner && m_creator == right.m_creator && m_channel == right.m_channel; -} - -bool RecordingBase::operator!=(const RecordingBase& right) -{ - return !(*this == right); -} - -std::string RecordingBase::GetStringId() const -{ - return m_sid; -} - -void RecordingBase::SetStringId(const std::string& id) -{ - m_sid = id; -} - -bool RecordingBase::IsEnabled() const -{ - return m_enabled != 0; -} - -void RecordingBase::SetEnabled(uint32_t enabled) -{ - m_enabled = enabled; -} - -int RecordingBase::GetDaysOfWeek() const -{ - return m_daysOfWeek; -} - -void RecordingBase::SetDaysOfWeek(uint32_t daysOfWeek) -{ - m_daysOfWeek = daysOfWeek; -} - int RecordingBase::GetLifetime() const { return utilities::LifetimeMapper::TvhToKodi(m_lifetime); } - -void RecordingBase::SetLifetime(uint32_t lifetime) -{ - m_lifetime = lifetime; -} - -uint32_t RecordingBase::GetPriority() const -{ - return m_priority; -} - -void RecordingBase::SetPriority(uint32_t priority) -{ - m_priority = priority; -} - -const std::string& RecordingBase::GetTitle() const -{ - return m_title; -} - -void RecordingBase::SetTitle(const std::string& title) -{ - m_title = title; -} - -const std::string& RecordingBase::GetName() const -{ - return m_name; -} - -void RecordingBase::SetName(const std::string& name) -{ - m_name = name; -} - -const std::string& RecordingBase::GetDirectory() const -{ - return m_directory; -} - -void RecordingBase::SetDirectory(const std::string& directory) -{ - m_directory = directory; -} - -void RecordingBase::SetOwner(const std::string& owner) -{ - m_owner = owner; -} - -void RecordingBase::SetCreator(const std::string& creator) -{ - m_creator = creator; -} - -uint32_t RecordingBase::GetChannel() const -{ - return m_channel; -} - -void RecordingBase::SetChannel(uint32_t channel) -{ - m_channel = channel; -} - -// static -time_t RecordingBase::LocaltimeToUTC(int32_t lctime) -{ - /* Note: lctime contains minutes from midnight (up to 24*60) as local time. */ - - /* complete lctime with current year, month, day, ... */ - time_t t = std::time(nullptr); - struct tm* tm_time = std::localtime(&t); - - tm_time->tm_hour = lctime / 60; - tm_time->tm_min = lctime % 60; - tm_time->tm_sec = 0; - - return std::mktime(tm_time); -} - -// static -unsigned int RecordingBase::GetNextIntId() -{ - static unsigned int intId = 0; - return ++intId; -} diff --git a/src/tvheadend/entity/RecordingBase.h b/src/tvheadend/entity/RecordingBase.h index d9751015..0bfd07fb 100644 --- a/src/tvheadend/entity/RecordingBase.h +++ b/src/tvheadend/entity/RecordingBase.h @@ -7,74 +7,51 @@ #pragma once +#include "../HTSPTypes.h" #include "Entity.h" #include -#include #include -namespace tvheadend -{ -namespace entity +namespace tvheadend::entity { class RecordingBase : public Entity { protected: - RecordingBase(const std::string& id = ""); - bool operator==(const RecordingBase& right); - bool operator!=(const RecordingBase& right); + RecordingBase() = default; -public: - std::string GetStringId() const; - void SetStringId(const std::string& id); + bool operator==(const RecordingBase& right) + { + return Entity::operator==(right) && m_enabled == right.m_enabled && + m_lifetime == right.m_lifetime && m_priority == right.m_priority && + m_title == right.m_title && m_channel == right.m_channel; + } - bool IsEnabled() const; - void SetEnabled(uint32_t enabled); + bool operator!=(const RecordingBase& right) { return !(*this == right); } - int GetDaysOfWeek() const; - void SetDaysOfWeek(uint32_t daysOfWeek); +public: + bool IsEnabled() const { return m_enabled != 0; } + void SetEnabled(uint32_t enabled) { m_enabled = enabled; } int GetLifetime() const; - void SetLifetime(uint32_t retention); - - uint32_t GetPriority() const; - void SetPriority(uint32_t priority); - - const std::string& GetTitle() const; - void SetTitle(const std::string& title); + void SetLifetime(uint32_t lifetime) { m_lifetime = lifetime; } - const std::string& GetName() const; - void SetName(const std::string& name); + uint32_t GetPriority() const { return m_priority; } + void SetPriority(uint32_t priority) { m_priority = priority; } - const std::string& GetDirectory() const; - void SetDirectory(const std::string& directory); + const std::string& GetTitle() const { return m_title; } + void SetTitle(const std::string& title) { m_title = title; } - void SetOwner(const std::string& owner); - void SetCreator(const std::string& creator); - - uint32_t GetChannel() const; - void SetChannel(uint32_t channel); - -protected: - static time_t LocaltimeToUTC(int32_t lctime); + uint32_t GetChannel() const { return m_channel; } + void SetChannel(uint32_t channel) { m_channel = channel; } private: - static unsigned int GetNextIntId(); - - std::string m_sid; // ID (string!) of dvr[Time|Auto]recEntry. - uint32_t m_enabled; // If [time|auto]rec entry is enabled (activated). - uint32_t - m_daysOfWeek; // Bitmask - Days of week (0x01 = Monday, 0x40 = Sunday, 0x7f = Whole Week, 0 = Not set). - uint32_t m_lifetime; // Lifetime (in days). - uint32_t m_priority; // Priority (0 = Important, 1 = High, 2 = Normal, 3 = Low, 4 = Unimportant). + uint32_t m_enabled{0}; // If [time|auto]rec entry is enabled (activated). + uint32_t m_lifetime{0}; // Lifetime (in days). + uint32_t m_priority{DVR_PRIO_DEFAULT}; // Priority. std::string m_title; // Title (pattern) for the recording files. - std::string m_name; // Name. - std::string m_directory; // Directory for the recording files. - std::string m_owner; // Owner. - std::string m_creator; // Creator. - uint32_t m_channel; // Channel ID. + uint32_t m_channel{0}; // Channel ID. }; -} // namespace entity -} // namespace tvheadend +} // namespace tvheadend::entity diff --git a/src/tvheadend/entity/SeriesRecordingBase.cpp b/src/tvheadend/entity/SeriesRecordingBase.cpp new file mode 100644 index 00000000..c0451f14 --- /dev/null +++ b/src/tvheadend/entity/SeriesRecordingBase.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "SeriesRecordingBase.h" + +#include + +using namespace tvheadend::entity; + +SeriesRecordingBase::SeriesRecordingBase(const std::string& id /*= ""*/) : m_sid(id) +{ + m_id = GetNextIntId(); +} + +// static +time_t SeriesRecordingBase::LocaltimeToUTC(int32_t lctime) +{ + /* Note: lctime contains minutes from midnight (up to 24*60) as local time. */ + + /* complete lctime with current year, month, day, ... */ + time_t t = std::time(nullptr); + struct tm* tm_time = std::localtime(&t); + + tm_time->tm_hour = lctime / 60; + tm_time->tm_min = lctime % 60; + tm_time->tm_sec = 0; + + return std::mktime(tm_time); +} + +// static +unsigned int SeriesRecordingBase::GetNextIntId() +{ + static unsigned int intId = 0; + return ++intId; +} diff --git a/src/tvheadend/entity/SeriesRecordingBase.h b/src/tvheadend/entity/SeriesRecordingBase.h new file mode 100644 index 00000000..b5d9658d --- /dev/null +++ b/src/tvheadend/entity/SeriesRecordingBase.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include "RecordingBase.h" + +#include +#include +#include + +namespace tvheadend::entity +{ + +class SeriesRecordingBase : public RecordingBase +{ +protected: + SeriesRecordingBase(const std::string& id = ""); + + bool operator==(const SeriesRecordingBase& right) + { + return RecordingBase::operator==(right) && m_sid == right.m_sid && + m_daysOfWeek == right.m_daysOfWeek && m_name == right.m_name && + m_directory == right.m_directory && m_owner == right.m_owner && + m_creator == right.m_creator; + } + + bool operator!=(const SeriesRecordingBase& right) { return !(*this == right); } + +public: + std::string GetStringId() const { return m_sid; } + void SetStringId(const std::string& sid) { m_sid = sid; } + + int GetDaysOfWeek() const { return m_daysOfWeek; } + void SetDaysOfWeek(uint32_t daysOfWeek) { m_daysOfWeek = daysOfWeek; } + + const std::string& GetName() const { return m_name; } + void SetName(const std::string& name) { m_name = name; } + + const std::string& GetDirectory() const { return m_directory; } + void SetDirectory(const std::string& directory) { m_directory = directory; } + + void SetOwner(const std::string& owner) { m_owner = owner; } + + void SetCreator(const std::string& creator) { m_creator = creator; } + +protected: + static time_t LocaltimeToUTC(int32_t lctime); + +private: + static unsigned int GetNextIntId(); + + std::string m_sid; // ID (string!) of dvr[Time|Auto]recEntry. + uint32_t m_daysOfWeek{ + 0}; // Bitmask - Days of week (0x01 = Monday, 0x40 = Sunday, 0x7f = Whole Week, 0 = Not set). + std::string m_name; // Name. + std::string m_directory; // Directory for the recording files. + std::string m_owner; // Owner. + std::string m_creator; // Creator. +}; + +} // namespace tvheadend::entity diff --git a/src/tvheadend/entity/TimeRecording.cpp b/src/tvheadend/entity/TimeRecording.cpp index dd3020e6..71131d8b 100644 --- a/src/tvheadend/entity/TimeRecording.cpp +++ b/src/tvheadend/entity/TimeRecording.cpp @@ -9,14 +9,14 @@ using namespace tvheadend::entity; -TimeRecording::TimeRecording(const std::string& id /*= ""*/) - : RecordingBase(id), m_start(0), m_stop(0) +TimeRecording::TimeRecording(const std::string& id /*= ""*/) : SeriesRecordingBase(id) { } bool TimeRecording::operator==(const TimeRecording& right) { - return RecordingBase::operator==(right) && m_start == right.m_start && m_stop == right.m_stop; + return SeriesRecordingBase::operator==(right) && m_start == right.m_start && + m_stop == right.m_stop; } bool TimeRecording::operator!=(const TimeRecording& right) diff --git a/src/tvheadend/entity/TimeRecording.h b/src/tvheadend/entity/TimeRecording.h index 900f02fd..97989d3c 100644 --- a/src/tvheadend/entity/TimeRecording.h +++ b/src/tvheadend/entity/TimeRecording.h @@ -7,16 +7,17 @@ #pragma once -#include "RecordingBase.h" +#include "SeriesRecordingBase.h" +#include +#include #include +#include -namespace tvheadend -{ -namespace entity +namespace tvheadend::entity { -class TimeRecording : public RecordingBase +class TimeRecording : public SeriesRecordingBase { public: TimeRecording(const std::string& id = ""); @@ -31,12 +32,11 @@ class TimeRecording : public RecordingBase void SetStop(int32_t stop); private: - int32_t m_start; // Start time in minutes from midnight (up to 24*60). - int32_t m_stop; // Stop time in minutes from midnight (up to 24*60). + int32_t m_start{0}; // Start time in minutes from midnight (up to 24*60). + int32_t m_stop{0}; // Stop time in minutes from midnight (up to 24*60). }; typedef std::map TimeRecordingsMap; typedef std::pair TimeRecordingMapEntry; -} // namespace entity -} // namespace tvheadend +} // namespace tvheadend::entity From 7b238c0b0babfe236dc6503b8c5c37edbbd3d41a Mon Sep 17 00:00:00 2001 From: ksooo <3226626+ksooo@users.noreply.github.com> Date: Wed, 28 Aug 2024 17:18:08 +0200 Subject: [PATCH 2/3] Add support for Autorec property 'Broadcast type' (HTSPv39+), DVR entry property 'DVR configuration' (HTSPv40+), DVR entry property 'comment' (HTSPv42+). --- CMakeLists.txt | 2 + pvr.hts/addon.xml.in | 2 +- pvr.hts/changelog.txt | 6 + .../resource.language.en_gb/strings.po | 46 ++- src/Tvheadend.cpp | 113 +++++- src/Tvheadend.h | 16 +- src/tvheadend/AutoRecordings.cpp | 38 +- src/tvheadend/AutoRecordings.h | 8 +- src/tvheadend/CustomTimerProperties.cpp | 333 ++++++++++++++++++ src/tvheadend/CustomTimerProperties.h | 92 +++++ src/tvheadend/HTSPTypes.h | 20 +- src/tvheadend/Profile.h | 26 +- src/tvheadend/TimeRecordings.cpp | 26 +- src/tvheadend/TimeRecordings.h | 6 +- src/tvheadend/entity/AutoRecording.cpp | 12 +- src/tvheadend/entity/AutoRecording.h | 4 + src/tvheadend/entity/Recording.h | 9 +- src/tvheadend/entity/RecordingBase.h | 11 +- 18 files changed, 726 insertions(+), 44 deletions(-) create mode 100644 src/tvheadend/CustomTimerProperties.cpp create mode 100644 src/tvheadend/CustomTimerProperties.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef9a3818..43228997 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,6 +54,8 @@ set(HTS_SOURCES_TVHEADEND src/tvheadend/AutoRecordings.h src/tvheadend/ChannelTuningPredictor.h src/tvheadend/ChannelTuningPredictor.cpp + src/tvheadend/CustomTimerProperties.h + src/tvheadend/CustomTimerProperties.cpp src/tvheadend/HTSPConnection.h src/tvheadend/HTSPConnection.cpp src/tvheadend/HTSPDemuxer.h diff --git a/pvr.hts/addon.xml.in b/pvr.hts/addon.xml.in index 81fb54ee..6949f098 100644 --- a/pvr.hts/addon.xml.in +++ b/pvr.hts/addon.xml.in @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index 081821cd..273b1d14 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -1,3 +1,9 @@ +v22.4.0 +- PVR Add-on API v9.1.0 +- Add support for Autorec property "Broadcast type" (HTSPv39+) +- Add support for DVR entry property "DVR configuration" (HTSPv40+) +- Add support for DVR entry property "Comment" (HTSPv42+) + v22.3.0 - Add support for PVR Providers (HTSPv38+) diff --git a/pvr.hts/resources/language/resource.language.en_gb/strings.po b/pvr.hts/resources/language/resource.language.en_gb/strings.po index f511615c..a2face4a 100644 --- a/pvr.hts/resources/language/resource.language.en_gb/strings.po +++ b/pvr.hts/resources/language/resource.language.en_gb/strings.po @@ -298,7 +298,13 @@ msgctxt "#30372" msgid "Record if unique episode according EPG/XMLTV" msgstr "" -#empty strings from id 30373 to 30374 +#. Prevent duplicate episodes representation +#: src/Tvheadend.cpp +msgctxt "#30373" +msgid "Use DVR configuration" +msgstr "" + +#empty string with id 30374 #. Recording lifetime representation #: src/Tvheadend.cpp @@ -441,7 +447,15 @@ msgctxt "#30456" msgid "Subscription error" msgstr "" -#empty strings from id 30457 to 30499 +msgctxt "#30457" +msgid "DVR configuration" +msgstr "" + +msgctxt "#30458" +msgid "Comment" +msgstr "" + +#empty strings from id 30459 to 30499 msgctxt "#30500" msgid "Streaming profile" @@ -451,8 +465,6 @@ msgctxt "#30501" msgid "Profile to use (empty = default)" msgstr "" -#. Check streaming profile validity during startup -#: src/client.cpp msgctxt "#30502" msgid "Streaming profile %s is not available" msgstr "" @@ -478,3 +490,29 @@ msgstr "" msgctxt "#30511" msgid "Server based play status" msgstr "" + +#empty strings from id 30512 to 30599 + +msgctxt "#30600" +msgid "Broadcast type" +msgstr "" + +msgctxt "#30601" +msgid "Any" +msgstr "" + +msgctxt "#30602" +msgid "New / premiere / unknown" +msgstr "" + +msgctxt "#30603" +msgid "Repeated" +msgstr "" + +msgctxt "#30604" +msgid "New / premiere" +msgstr "" + +msgctxt "#30605" +msgid "(Default profile)" +msgstr "" diff --git a/src/Tvheadend.cpp b/src/Tvheadend.cpp index b71f7191..9ecbd278 100644 --- a/src/Tvheadend.cpp +++ b/src/Tvheadend.cpp @@ -7,6 +7,7 @@ #include "Tvheadend.h" +#include "tvheadend/CustomTimerProperties.h" #include "tvheadend/HTSPConnection.h" #include "tvheadend/HTSPDemuxer.h" #include "tvheadend/HTSPMessage.h" @@ -32,12 +33,14 @@ CTvheadend::CTvheadend(const kodi::addon::IInstanceInfo& instance) : kodi::addon::CInstancePVRClient(instance), m_settings(new InstanceSettings(*this)), m_conn(new HTSPConnection(m_settings, *this)), + m_customTimerProps( + {CUSTOM_PROP_ID_DVR_CONFIGURATION, CUSTOM_PROP_ID_DVR_COMMENT}, *m_conn, m_dvrConfigs), m_streamchange(false), m_vfs(new HTSPVFS(m_settings, *m_conn)), m_queue(static_cast(-1)), m_asyncState(m_settings->GetResponseTimeout()), - m_timeRecordings(*m_conn), - m_autoRecordings(m_settings, *m_conn), + m_timeRecordings(*m_conn, m_dvrConfigs), + m_autoRecordings(m_settings, *m_conn, m_dvrConfigs), m_epgMaxDays(EpgMaxFutureDays()), m_playingLiveStream(false), m_playingRecording(nullptr) @@ -167,6 +170,56 @@ std::string CTvheadend::GetImageURL(const char* str) } } +void CTvheadend::QueryAvailableDvrConfigurations(std::unique_lock& lock) +{ + /* Build message */ + htsmsg_t* m = htsmsg_create_map(); + + /* Send */ + m = m_conn->SendAndWait0(lock, "getDvrConfigs", m); + + /* Validate */ + if (!m) + return; + + htsmsg_t* l = htsmsg_get_list(m, "dvrconfigs"); + + if (!l) + { + Logger::Log(LogLevel::LEVEL_ERROR, "malformed getDvrConfigs: 'dvrconfigs' missing"); + htsmsg_destroy(m); + return; + } + + /* Process */ + Logger::Log(LogLevel::LEVEL_INFO, " Available DVR configurations:"); + + htsmsg_field_t* f = nullptr; + HTSMSG_FOREACH(f, l) + { + Profile profile; + + const char* str = htsmsg_get_str(&f->hmf_msg, "uuid"); + if (str) + profile.SetUuid(str); + + str = htsmsg_get_str(&f->hmf_msg, "name"); + if (str) + profile.SetName(str); + + str = htsmsg_get_str(&f->hmf_msg, "comment"); + if (str) + profile.SetComment(str); + + Logger::Log(LogLevel::LEVEL_INFO, " Name: %s, Comment: %s", profile.GetName().c_str(), + profile.GetComment().c_str()); + + m_dvrConfigs.emplace_back(std::move(profile)); + } + + htsmsg_destroy(m); +} + void CTvheadend::QueryAvailableProfiles(std::unique_lock& lock) { /* Build message */ @@ -840,16 +893,16 @@ struct TimerType : kodi::addon::PVRTimerType unsigned int id, unsigned int attributes, const std::string& description, - const std::vector& priorityValues = - std::vector(), - const std::vector& lifetimeValues = - std::vector(), + const std::vector& customSettingDefs, + const std::vector& priorityValues, + const std::vector& lifetimeValues, const std::vector& dupEpisodesValues = std::vector()) { SetId(id); SetAttributes(attributes); SetDescription(description); + SetCustomSettingDefinitions(customSettingDefs); SetPriorities(priorityValues, settings->GetDvrPriority()); SetLifetimes(lifetimeValues, LifetimeMapper::TvhToKodi(settings->GetDvrLifetime())); SetPreventDuplicateEpisodes(dupEpisodesValues, settings->GetDvrDupdetect()); @@ -898,6 +951,7 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type /* PVR_Timer.iPreventDuplicateEpisodes values and presentation.*/ std::vector deDupValues = { + {DVR_AUTOREC_RECORD_DVR_PROFILE, kodi::addon::GetLocalizedString(30373)}, {DVR_AUTOREC_RECORD_ALL, kodi::addon::GetLocalizedString(30356)}, {DVR_AUTOREC_RECORD_DIFFERENT_EPISODE_NUMBER, kodi::addon::GetLocalizedString(30357)}, {DVR_AUTOREC_RECORD_DIFFERENT_SUBTITLE, kodi::addon::GetLocalizedString(30358)}, @@ -951,6 +1005,10 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type /* Timer types definition. */ + /* Custom setting definitions */ + const std::vector customSettingDefs{ + m_customTimerProps.GetSettingDefinitions()}; + /* One-shot manual (time and channel based) */ types.emplace_back(TimerType( /* Settings */ @@ -961,6 +1019,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_ONCE_MANUAL_ATTRIBS, /* Let Kodi generate the description. */ "", + /* Custom settings definitions. */ + customSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ @@ -976,6 +1036,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_ONCE_EPG_ATTRIBS, /* Let Kodi generate the description. */ "", + /* Custom settings definitions. */ + customSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ @@ -991,6 +1053,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_ONCE_MANUAL_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, /* Description. */ kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)" + /* Custom settings definitions. */ + customSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ @@ -1006,11 +1070,17 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_ONCE_EPG_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, /* Description. */ kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)" + /* Custom settings definitions. */ + customSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ lifetimeValues)); + /* Custom Timerec setting definitions */ + const std::vector customTimeRecSettingDefs{ + m_timeRecordings.GetCustomSettingDefinitions()}; + /* Repeating manual (time and channel based) - timerec */ types.emplace_back(TimerType( /* Settings */ @@ -1025,11 +1095,17 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type PVR_TIMER_TYPE_SUPPORTS_LIFETIME | PVR_TIMER_TYPE_SUPPORTS_RECORDING_FOLDERS, /* Let Kodi generate the description. */ "", + /* Custom settings definitions. */ + customTimeRecSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ lifetimeValues)); + /* Custom Autorec setting definitions */ + const std::vector customAutoRecSettingDefs{ + m_autoRecordings.GetCustomSettingDefinitions()}; + if (m_conn->GetProtocol() >= 29) { unsigned int TIMER_REPEATING_SERIESLINK_ATTRIBS = @@ -1057,6 +1133,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_REPEATING_SERIESLINK_ATTRIBS, /* Description. */ kodi::addon::GetLocalizedString(30369), // "Timer rule (series link)" + /* Custom settings definitions. */ + customAutoRecSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ @@ -1089,6 +1167,8 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type TIMER_REPEATING_EPG_ATTRIBS, /* Let Kodi generate the description. */ "", + /* Custom settings definitions. */ + customAutoRecSettingDefs, /* Values definitions for priorities. */ priorityValues, /* Values definitions for lifetime. */ @@ -1148,6 +1228,10 @@ bool CTvheadend::CreateTimer(const Recording& tvhTmr, kodi::addon::PVRTimer& tmr : tmr.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC ? m_autoRecordings.GetTimerIntIdFromStringId(tvhTmr.GetAutorecId()) : 0); + + /* Custom props */ + tmr.SetCustomProperties(m_customTimerProps.GetProperties(tvhTmr)); + return true; } @@ -1231,6 +1315,9 @@ PVR_ERROR CTvheadend::AddTimer(const kodi::addon::PVRTimer& timer) LifetimeMapper::KodiToTvh(timer.GetLifetime())); // remove from disk htsmsg_add_u32(m, "priority", timer.GetPriority()); + /* Custom props. */ + m_customTimerProps.AppendPropertiesToHTSPMessage(timer.GetCustomProperties(), m); + /* Send and Wait */ { std::unique_lock lock(m_conn->Mutex()); @@ -1346,6 +1433,9 @@ PVR_ERROR CTvheadend::UpdateTimer(const kodi::addon::PVRTimer& timer) LifetimeMapper::KodiToTvh(timer.GetLifetime())); // remove from disk htsmsg_add_u32(m, "priority", timer.GetPriority()); + /* Custom props. */ + m_customTimerProps.AppendPropertiesToHTSPMessage(timer.GetCustomProperties(), m); + return SendDvrUpdate(m); } else if (timer.GetTimerType() == TIMER_REPEATING_MANUAL) @@ -1539,6 +1629,9 @@ bool CTvheadend::Connected(std::unique_lock& lock) /* Query the server for available streaming profiles */ QueryAvailableProfiles(lock); + /* Query the server for available DVR configurations */ + QueryAvailableDvrConfigurations(lock); + /* Show a notification if the profile is not available */ const std::string streamingProfile = m_settings->GetStreamingProfile(); @@ -2686,6 +2779,14 @@ void CTvheadend::ParseRecordingAddOrUpdate(htsmsg_t* msg, bool bAdd) rec.SetRatingSource(str); } + str = htsmsg_get_str(msg, "configId"); + if (str) + rec.SetConfigUuid(str); + + str = htsmsg_get_str(msg, "comment"); + if (str) + rec.SetComment(str); + if (m_conn->GetProtocol() >= 32) { if (rec.GetDescription().empty() && !rec.GetSubtitle().empty()) diff --git a/src/Tvheadend.h b/src/Tvheadend.h index db8224c3..e77a188a 100644 --- a/src/Tvheadend.h +++ b/src/Tvheadend.h @@ -16,6 +16,7 @@ extern "C" #include "tvheadend/AutoRecordings.h" #include "tvheadend/ChannelTuningPredictor.h" +#include "tvheadend/CustomTimerProperties.h" #include "tvheadend/HTSPMessage.h" #include "tvheadend/IHTSPConnectionListener.h" #include "tvheadend/IHTSPDemuxPacketHandler.h" @@ -140,8 +141,12 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient, std::string GetImageURL(const char* str); /** - * Queries the server for available streaming profiles and populates - * m_profiles + * Queries the server for available DVR configurations and populates m_dvrConfigs + */ + void QueryAvailableDvrConfigurations(std::unique_lock& lock); + + /** + * Queries the server for available streaming profiles and populates m_profiles */ void QueryAvailableProfiles(std::unique_lock& lock); @@ -262,6 +267,11 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient, */ PVR_ERROR GetStreamTimes(kodi::addon::PVRStreamTimes& times) override; + /** + * The DVR configurations available on the server + */ + tvheadend::Profiles m_dvrConfigs; + /** * The streaming profiles available on the server */ @@ -273,6 +283,8 @@ class ATTR_DLL_LOCAL CTvheadend : public kodi::addon::CInstancePVRClient, tvheadend::HTSPConnection* m_conn; + const tvheadend::CustomTimerProperties m_customTimerProps; + std::vector m_dmx; tvheadend::HTSPDemuxer* m_dmx_active; bool m_streamchange; diff --git a/src/tvheadend/AutoRecordings.cpp b/src/tvheadend/AutoRecordings.cpp index dfb9990a..3af42147 100644 --- a/src/tvheadend/AutoRecordings.cpp +++ b/src/tvheadend/AutoRecordings.cpp @@ -7,6 +7,7 @@ #include "AutoRecordings.h" +#include "CustomTimerProperties.h" #include "HTSPConnection.h" #include "InstanceSettings.h" #include "entity/Recording.h" @@ -23,8 +24,14 @@ using namespace tvheadend::entity; using namespace tvheadend::utilities; AutoRecordings::AutoRecordings(const std::shared_ptr& settings, - HTSPConnection& conn) - : m_settings(settings), m_conn(conn) + HTSPConnection& conn, + Profiles& dvrConfigs) + : m_settings(settings), + m_conn(conn), + m_customTimerProps({CUSTOM_PROP_ID_DVR_CONFIGURATION, CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE, + CUSTOM_PROP_ID_DVR_COMMENT}, + conn, + dvrConfigs) { } @@ -105,6 +112,9 @@ void AutoRecordings::GetAutorecTimers(std::vector& timers tmr.SetFullTextEpgSearch(rec.second.GetFulltext()); tmr.SetParentClientIndex(0); + /* Custom props. */ + tmr.SetCustomProperties(m_customTimerProps.GetProperties(rec.second)); + timers.emplace_back(std::move(tmr)); } } @@ -133,6 +143,12 @@ const std::string AutoRecordings::GetTimerStringIdFromIntId(unsigned int intId) return ""; } +const std::vector AutoRecordings::GetCustomSettingDefinitions() + const +{ + return m_customTimerProps.GetSettingDefinitions(); +} + PVR_ERROR AutoRecordings::SendAutorecAdd(const kodi::addon::PVRTimer& timer) { return SendAutorecAddOrUpdate(timer, false); @@ -259,6 +275,9 @@ PVR_ERROR AutoRecordings::SendAutorecAddOrUpdate(const kodi::addon::PVRTimer& ti if (timer.GetTimerType() == TIMER_REPEATING_SERIESLINK) htsmsg_add_str(m, "serieslinkUri", timer.GetSeriesLink().c_str()); + /* Custom props. */ + m_customTimerProps.AppendPropertiesToHTSPMessage(timer.GetCustomProperties(), m); + /* Send and Wait */ { std::unique_lock lock(m_conn.Mutex()); @@ -443,21 +462,28 @@ bool AutoRecordings::ParseAutorecAddOrUpdate(htsmsg_t* msg, bool bAdd) rec.SetCreator(str); if (!htsmsg_get_u32(msg, "channel", &u32)) - { rec.SetChannel(u32); - } else rec.SetChannel(PVR_TIMER_ANY_CHANNEL); // an empty channel field = any channel if (!htsmsg_get_u32(msg, "fulltext", &u32)) - { rec.SetFulltext(u32); - } str = htsmsg_get_str(msg, "serieslinkUri"); if (str) rec.SetSeriesLink(str); + if (!htsmsg_get_u32(msg, "broadcastType", &u32)) + rec.SetBroadcastType(u32); + + str = htsmsg_get_str(msg, "configId"); + if (str) + rec.SetConfigUuid(str); + + str = htsmsg_get_str(msg, "comment"); + if (str) + rec.SetComment(str); + return true; } diff --git a/src/tvheadend/AutoRecordings.h b/src/tvheadend/AutoRecordings.h index ccd66725..88a62abe 100644 --- a/src/tvheadend/AutoRecordings.h +++ b/src/tvheadend/AutoRecordings.h @@ -17,6 +17,8 @@ extern "C" #include } +#include "CustomTimerProperties.h" +#include "Profile.h" #include "entity/AutoRecording.h" #include "kodi/addon-instance/pvr/Timers.h" @@ -30,7 +32,9 @@ class InstanceSettings; class AutoRecordings { public: - AutoRecordings(const std::shared_ptr& settings, HTSPConnection& conn); + AutoRecordings(const std::shared_ptr& settings, + HTSPConnection& conn, + Profiles& dvrConfigs); ~AutoRecordings(); /* state updates */ @@ -41,6 +45,7 @@ class AutoRecordings int GetAutorecTimerCount() const; void GetAutorecTimers(std::vector& timers); const unsigned int GetTimerIntIdFromStringId(const std::string& strId) const; + const std::vector GetCustomSettingDefinitions() const; /* client to server messages */ PVR_ERROR SendAutorecAdd(const kodi::addon::PVRTimer& timer); @@ -56,6 +61,7 @@ class AutoRecordings PVR_ERROR SendAutorecAddOrUpdate(const kodi::addon::PVRTimer& timer, bool update); HTSPConnection& m_conn; + const tvheadend::CustomTimerProperties m_customTimerProps; tvheadend::entity::AutoRecordingsMap m_autoRecordings; std::shared_ptr m_settings; }; diff --git a/src/tvheadend/CustomTimerProperties.cpp b/src/tvheadend/CustomTimerProperties.cpp new file mode 100644 index 00000000..00454426 --- /dev/null +++ b/src/tvheadend/CustomTimerProperties.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2005-2024 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#include "CustomTimerProperties.h" + +#include "HTSPConnection.h" +#include "entity/AutoRecording.h" +#include "entity/Recording.h" +#include "entity/TimeRecording.h" +#include "utilities/Logger.h" + +#include "kodi/addon-instance/pvr/General.h" + +#include + +using namespace tvheadend; +using namespace tvheadend::entity; +using namespace tvheadend::utilities; + +CustomTimerProperties::CustomTimerProperties(const std::vector& propIds, + HTSPConnection& conn, + const Profiles& dvrConfigs) + : m_propIds(propIds), m_conn(conn), m_dvrConfigs(dvrConfigs) +{ +} + +std::vector CustomTimerProperties::GetProperties( + const tvheadend::entity::RecordingBase& rec) const +{ + std::vector customProps; + GetCommonProperties(customProps, rec); + return customProps; +} + +std::vector CustomTimerProperties::GetProperties( + const tvheadend::entity::AutoRecording& autorec) const +{ + std::vector customProps; + GetCommonProperties(customProps, autorec); + + for (unsigned int propId : m_propIds) + { + switch (propId) + { + case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE: + { + // Broadcast type + if (m_conn.GetProtocol() >= 39) + customProps.emplace_back(CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE, + autorec.GetBroadcastType()); + break; + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", propId); + break; + } + } + return customProps; +} + +void CustomTimerProperties::GetCommonProperties( + std::vector& props, + const tvheadend::entity::RecordingBase& rec) const +{ + for (unsigned int propId : m_propIds) + { + switch (propId) + { + case CUSTOM_PROP_ID_DVR_CONFIGURATION: + { + // DVR configuration + if (m_conn.GetProtocol() >= 40) + { + const int configId{GetDvrConfigurationId(rec.GetConfigUuid())}; + if (configId != -1) + props.emplace_back(CUSTOM_PROP_ID_DVR_CONFIGURATION, configId); + } + break; + } + case CUSTOM_PROP_ID_DVR_COMMENT: + { + // User comment + if (m_conn.GetProtocol() >= 42) + { + /* user comment */ + props.emplace_back(CUSTOM_PROP_ID_DVR_COMMENT, rec.GetComment()); + break; + } + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", propId); + break; + } + } +} + +const std::vector CustomTimerProperties::GetSettingDefinitions() + const +{ + std::vector ret; + + for (unsigned int propId : m_propIds) + { + switch (propId) + { + case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE: + { + // Broadcast type + if (m_conn.GetProtocol() >= 39) + { + int defaultValue{0}; + const std::vector broadcastTypeValues{ + GetPossibleValues(CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE, defaultValue)}; + ret.emplace_back(CreateSettingDefinition(CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE, + 30600, // Broadcast type + broadcastTypeValues, defaultValue, + PVR_SETTING_READONLY_CONDITION_NONE)); + } + break; + } + case CUSTOM_PROP_ID_DVR_CONFIGURATION: + { + // DVR configuration + if (m_conn.GetProtocol() >= 40) + { + int defaultValue{0}; + const std::vector dvrConfigValues{ + GetPossibleValues(CUSTOM_PROP_ID_DVR_CONFIGURATION, defaultValue)}; + if (dvrConfigValues.size() > 1) + { + ret.emplace_back(CreateSettingDefinition( + CUSTOM_PROP_ID_DVR_CONFIGURATION, + 30457, // DVR configuration + dvrConfigValues, defaultValue, PVR_SETTING_READONLY_CONDITION_TIMER_RECORDING)); + } + } + break; + } + case CUSTOM_PROP_ID_DVR_COMMENT: + { + // User comment + if (m_conn.GetProtocol() >= 42) + { + std::string defaultValue; + const std::vector values{ + GetPossibleValues(CUSTOM_PROP_ID_DVR_COMMENT, defaultValue)}; + ret.emplace_back(CreateSettingDefinition(CUSTOM_PROP_ID_DVR_COMMENT, + 30458, // Comment + values, defaultValue, + PVR_SETTING_READONLY_CONDITION_NONE)); + } + break; + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", propId); + break; + } + } + return ret; +} + +const std::vector CustomTimerProperties::GetPossibleValues( + unsigned int propId, int& defaultValue) const +{ + switch (propId) + { + case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE: + { + // Broadcast type + if (m_conn.GetProtocol() >= 39) + { + static std::vector broadcastTypeValues = { + {DVR_AUTOREC_BTYPE_ALL, kodi::addon::GetLocalizedString(30601)}, + {DVR_AUTOREC_BTYPE_NEW_OR_UNKNOWN, kodi::addon::GetLocalizedString(30602)}, + {DVR_AUTOREC_BTYPE_REPEAT, kodi::addon::GetLocalizedString(30603)}, + {DVR_AUTOREC_BTYPE_NEW, kodi::addon::GetLocalizedString(30604)}}; + + defaultValue = DVR_AUTOREC_BTYPE_ALL; + return broadcastTypeValues; + } + break; + } + case CUSTOM_PROP_ID_DVR_CONFIGURATION: + { + // DVR configuration + if (m_conn.GetProtocol() >= 40) + { + // DVR configuration + std::vector dvrConfigValues; + for (const auto& entry : m_dvrConfigs) + { + std::string name{entry.GetName()}; + if (name.empty()) + { + name = kodi::addon::GetLocalizedString(30605); // (default profile) + defaultValue = entry.GetId(); + } + + dvrConfigValues.emplace_back(entry.GetId(), name); + } + return dvrConfigValues; + } + break; + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", propId); + break; + } + return {}; +} + +const std::vector CustomTimerProperties::GetPossibleValues( + unsigned int propId, std::string& defaultValue) const +{ + switch (propId) + { + case CUSTOM_PROP_ID_DVR_COMMENT: + { + // User comment + if (m_conn.GetProtocol() >= 42) + { + // Simple string prop, no pre-defined values; default is empty string. + static const std::vector values{}; + defaultValue = ""; + return values; + } + break; + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", propId); + break; + } + return {}; +} + +void CustomTimerProperties::AppendPropertiesToHTSPMessage( + const std::vector& props, htsmsg_t* msg) const +{ + for (const auto& prop : props) + { + switch (prop.GetKey()) + { + case CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE: + { + // Broadcast type + htsmsg_add_u32(msg, "broadcastType", prop.GetIntValue()); + break; + } + case CUSTOM_PROP_ID_DVR_CONFIGURATION: + { + // DVR configuration + for (const auto& config : m_dvrConfigs) + { + if (config.GetId() == prop.GetIntValue()) + { + htsmsg_add_str(msg, "configName", config.GetUuid().c_str()); + break; + } + } + break; + } + case CUSTOM_PROP_ID_DVR_COMMENT: + { + // User comment + htsmsg_add_str(msg, "comment", prop.GetStringValue().c_str()); + break; + } + default: + Logger::Log(LogLevel::LEVEL_ERROR, "Unknown property %u", prop.GetKey()); + break; + } + } +} + +int CustomTimerProperties::GetDvrConfigurationId(const std::string& uuid) const +{ + if (m_conn.GetProtocol() >= 40 && m_dvrConfigs.size() > 1) + { + for (const auto& cfg : m_dvrConfigs) + { + if (cfg.GetUuid() == uuid) + return cfg.GetId(); + } + } + return -1; +} + +kodi::addon::PVRSettingDefinition CustomTimerProperties::CreateSettingDefinition( + unsigned int settingId, + int resourceId, + const std::vector& values, + int defaultValue, + uint64_t readonlyConditions) const +{ + kodi::addon::PVRSettingDefinition settingDef; + settingDef.SetId(settingId); + settingDef.SetName(kodi::addon::GetLocalizedString(resourceId)); + settingDef.SetType(PVR_SETTING_TYPE::INTEGER); + settingDef.SetReadonlyConditions(readonlyConditions); + + kodi::addon::PVRIntSettingDefinition intSettingDef; + intSettingDef.SetValues(std::move(values)); + intSettingDef.SetDefaultValue(defaultValue); + + settingDef.SetIntDefinition(intSettingDef); + return settingDef; +} + +kodi::addon::PVRSettingDefinition CustomTimerProperties::CreateSettingDefinition( + unsigned int settingId, + int resourceId, + const std::vector& values, + const std::string& defaultValue, + uint64_t readonlyConditions) const +{ + kodi::addon::PVRSettingDefinition settingDef; + settingDef.SetId(settingId); + settingDef.SetName(kodi::addon::GetLocalizedString(resourceId)); + settingDef.SetType(PVR_SETTING_TYPE::STRING); + settingDef.SetReadonlyConditions(readonlyConditions); + + kodi::addon::PVRStringSettingDefinition stringSettingDef; + stringSettingDef.SetValues(std::move(values)); + stringSettingDef.SetDefaultValue(defaultValue); + stringSettingDef.SetAllowEmptyValue(true); + + settingDef.SetStringDefinition(stringSettingDef); + return settingDef; +} diff --git a/src/tvheadend/CustomTimerProperties.h b/src/tvheadend/CustomTimerProperties.h new file mode 100644 index 00000000..ef6bbcc9 --- /dev/null +++ b/src/tvheadend/CustomTimerProperties.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2024 Team Kodi (https://kodi.tv) + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSE.md for more information. + */ + +#pragma once + +#include "Profile.h" + +#include + +extern "C" +{ +#include "libhts/htsmsg.h" +} + +namespace kodi::addon +{ +class PVRSettingDefinition; +class PVRSettingKeyValuePair; +class PVRTypeIntValue; +class PVRTypeStringValue; +} // namespace kodi::addon + +namespace tvheadend +{ +class HTSPConnection; + +namespace entity +{ +class AutoRecording; +class RecordingBase; +} // namespace entity + +// custom property ids +constexpr unsigned int CUSTOM_PROP_ID_AUTOREC_BROADCASTTYPE{1}; +constexpr unsigned int CUSTOM_PROP_ID_DVR_CONFIGURATION{2}; +constexpr unsigned int CUSTOM_PROP_ID_DVR_COMMENT{3}; + +class CustomTimerProperties +{ +public: + CustomTimerProperties(const std::vector& propIds, + HTSPConnection& conn, + const tvheadend::Profiles& dvrConfigs); + virtual ~CustomTimerProperties() = default; + + // Get custom props for all timers + std::vector GetProperties( + const tvheadend::entity::RecordingBase& rec) const; + + // Get custom props for Autorecs, inlcuding props for all timers + std::vector GetProperties( + const tvheadend::entity::AutoRecording& autorec) const; + + // Get setting definitions + const std::vector GetSettingDefinitions() const; + + // Append given props to given HTSP message + void AppendPropertiesToHTSPMessage(const std::vector& props, + htsmsg_t* msg) const; + +private: + const std::vector GetPossibleValues(unsigned int propId, + int& defaultValue) const; + const std::vector GetPossibleValues( + unsigned int propId, std::string& defaultValue) const; + kodi::addon::PVRSettingDefinition CreateSettingDefinition( + unsigned int settingId, + int resourceId, + const std::vector& values, + int defaultValue, + uint64_t readonlyConditions) const; + kodi::addon::PVRSettingDefinition CreateSettingDefinition( + unsigned int settingId, + int resourceId, + const std::vector& values, + const std::string& defaultValue, + uint64_t readonlyConditions) const; + + int GetDvrConfigurationId(const std::string& uuid) const; + void GetCommonProperties(std::vector& props, + const tvheadend::entity::RecordingBase& rec) const; + + const std::vector m_propIds; + const HTSPConnection& m_conn; + const tvheadend::Profiles& m_dvrConfigs; +}; + +} // namespace tvheadend diff --git a/src/tvheadend/HTSPTypes.h b/src/tvheadend/HTSPTypes.h index de04e7cd..01669e8a 100644 --- a/src/tvheadend/HTSPTypes.h +++ b/src/tvheadend/HTSPTypes.h @@ -37,7 +37,6 @@ typedef enum DVR_ACTION_TYPE_MUTE, DVR_ACTION_TYPE_SCENE, DVR_ACTION_TYPE_COMBREAK, - } dvr_action_type_t; typedef enum @@ -50,6 +49,7 @@ typedef enum DVR_AUTOREC_RECORD_ONCE_PER_MONTH = 12, DVR_AUTOREC_RECORD_ONCE_PER_WEEK = 4, DVR_AUTOREC_RECORD_ONCE_PER_DAY = 5, + DVR_AUTOREC_RECORD_DVR_PROFILE = 15, // Use DVR configuration DVR_AUTOREC_LRECORD_DIFFERENT_EPISODE_NUMBER = 6, DVR_AUTOREC_LRECORD_DIFFERENT_TITLE = 7, DVR_AUTOREC_LRECORD_DIFFERENT_SUBTITLE = 8, @@ -80,11 +80,10 @@ typedef enum DVR_RET_1YEAR = 366, DVR_RET_2YEARS = 731, DVR_RET_3YEARS = 1096, - DVR_RET_SPACE = - INT32_MAX - - 1, // the server may delete this recording if space for a new recording is needed (removal only) - DVR_RET_FOREVER = - INT32_MAX // the server should never delete this recording or database entry, only the user can do this + // the server may delete this recording if space for a new recording is needed (removal only) + DVR_RET_SPACE = (INT32_MAX - 1), + // the server should never delete this recording or database entry, only the user can do this + DVR_RET_FOREVER = (INT32_MAX) } dvr_retention_t; typedef enum @@ -94,6 +93,15 @@ typedef enum CHANNEL_TYPE_RADIO = 2 } channel_type_t; +// Autorecs: Broadcast type +typedef enum +{ + DVR_AUTOREC_BTYPE_ALL = 0, // "Any" + DVR_AUTOREC_BTYPE_NEW_OR_UNKNOWN = 1, // "New / premiere / unknown" + DVR_AUTOREC_BTYPE_REPEAT = 2, // "Repeated" + DVR_AUTOREC_BTYPE_NEW = 3, // "New / premiere" +} dvr_autorec_btype_t; + typedef enum { HTSP_DVR_PLAYCOUNT_RESET = 0, diff --git a/src/tvheadend/Profile.h b/src/tvheadend/Profile.h index 1846d7b4..9066aa21 100644 --- a/src/tvheadend/Profile.h +++ b/src/tvheadend/Profile.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -17,11 +18,16 @@ class Profile; typedef std::vector Profiles; /** - * Represents a single streaming profile + * Represents a single streaming profile or a single DVR configuration */ class Profile { public: + Profile() : m_id(GetNextIntId()) {} + + uint32_t GetId() const { return m_id; } + void SetId(uint32_t profileId) { m_id = profileId; } + std::string GetUuid() const { return m_uuid; } void SetUuid(const std::string& uuid) { m_uuid = uuid; } @@ -32,19 +38,15 @@ class Profile void SetComment(const std::string& comment) { m_comment = comment; } private: - /** - * The profile UUID - */ - std::string m_uuid; + static unsigned int GetNextIntId() + { + static unsigned int intId = 0; + return ++intId; + } - /** - * The profile name - */ + uint32_t m_id{0}; + std::string m_uuid; std::string m_name; - - /** - * The profile comment - */ std::string m_comment; }; diff --git a/src/tvheadend/TimeRecordings.cpp b/src/tvheadend/TimeRecordings.cpp index fbf52e32..719a026e 100644 --- a/src/tvheadend/TimeRecordings.cpp +++ b/src/tvheadend/TimeRecordings.cpp @@ -7,6 +7,7 @@ #include "TimeRecordings.h" +#include "CustomTimerProperties.h" #include "HTSPConnection.h" #include "entity/Recording.h" #include "utilities/LifetimeMapper.h" @@ -20,7 +21,10 @@ using namespace tvheadend; using namespace tvheadend::entity; using namespace tvheadend::utilities; -TimeRecordings::TimeRecordings(HTSPConnection& conn) : m_conn(conn) +TimeRecordings::TimeRecordings(HTSPConnection& conn, Profiles& dvrConfigs) + : m_conn(conn), + m_customTimerProps( + {CUSTOM_PROP_ID_DVR_CONFIGURATION, CUSTOM_PROP_ID_DVR_COMMENT}, conn, dvrConfigs) { } @@ -81,6 +85,9 @@ void TimeRecordings::GetTimerecTimers(std::vector& timers tmr.SetFullTextEpgSearch(false); // n/a for manual timers tmr.SetParentClientIndex(0); + /* Custom props. */ + tmr.SetCustomProperties(m_customTimerProps.GetProperties(rec.second)); + timers.emplace_back(std::move(tmr)); } } @@ -109,6 +116,12 @@ const std::string TimeRecordings::GetTimerStringIdFromIntId(unsigned int intId) return ""; } +const std::vector TimeRecordings::GetCustomSettingDefinitions() + const +{ + return m_customTimerProps.GetSettingDefinitions(); +} + PVR_ERROR TimeRecordings::SendTimerecAdd(const kodi::addon::PVRTimer& timer) { return SendTimerecAddOrUpdate(timer, false); @@ -164,6 +177,9 @@ PVR_ERROR TimeRecordings::SendTimerecAddOrUpdate(const kodi::addon::PVRTimer& ti if (timer.GetDirectory() != "/") htsmsg_add_str(m, "directory", timer.GetDirectory().c_str()); + /* Custom props. */ + m_customTimerProps.AppendPropertiesToHTSPMessage(timer.GetCustomProperties(), m); + /* Send and Wait */ { std::unique_lock lock(m_conn.Mutex()); @@ -328,6 +344,14 @@ bool TimeRecordings::ParseTimerecAddOrUpdate(htsmsg_t* msg, bool bAdd) rec.SetChannel(PVR_TIMER_ANY_CHANNEL); } + str = htsmsg_get_str(msg, "configId"); + if (str) + rec.SetConfigUuid(str); + + str = htsmsg_get_str(msg, "comment"); + if (str) + rec.SetComment(str); + return true; } diff --git a/src/tvheadend/TimeRecordings.h b/src/tvheadend/TimeRecordings.h index 353b5fac..8248385c 100644 --- a/src/tvheadend/TimeRecordings.h +++ b/src/tvheadend/TimeRecordings.h @@ -16,6 +16,8 @@ extern "C" #include } +#include "CustomTimerProperties.h" +#include "Profile.h" #include "entity/TimeRecording.h" #include "kodi/addon-instance/pvr/Timers.h" @@ -28,7 +30,7 @@ class HTSPConnection; class TimeRecordings { public: - TimeRecordings(HTSPConnection& conn); + TimeRecordings(HTSPConnection& conn, Profiles& dvrConfigs); ~TimeRecordings(); /* state updates */ @@ -39,6 +41,7 @@ class TimeRecordings int GetTimerecTimerCount() const; void GetTimerecTimers(std::vector& timers); const unsigned int GetTimerIntIdFromStringId(const std::string& strId) const; + const std::vector GetCustomSettingDefinitions() const; /* client to server messages */ PVR_ERROR SendTimerecAdd(const kodi::addon::PVRTimer& timer); @@ -54,6 +57,7 @@ class TimeRecordings PVR_ERROR SendTimerecAddOrUpdate(const kodi::addon::PVRTimer& timer, bool update); HTSPConnection& m_conn; + const tvheadend::CustomTimerProperties m_customTimerProps; tvheadend::entity::TimeRecordingsMap m_timeRecordings; }; diff --git a/src/tvheadend/entity/AutoRecording.cpp b/src/tvheadend/entity/AutoRecording.cpp index 5e8e41ef..76530288 100644 --- a/src/tvheadend/entity/AutoRecording.cpp +++ b/src/tvheadend/entity/AutoRecording.cpp @@ -21,7 +21,8 @@ bool AutoRecording::operator==(const AutoRecording& right) return SeriesRecordingBase::operator==(right) && m_startWindowBegin == right.m_startWindowBegin && m_startWindowEnd == right.m_startWindowEnd && m_startExtra == right.m_startExtra && m_stopExtra == right.m_stopExtra && m_dupDetect == right.m_dupDetect && - m_fulltext == right.m_fulltext && m_seriesLink == right.m_seriesLink; + m_fulltext == right.m_fulltext && m_broadcastType == right.m_broadcastType && + m_seriesLink == right.m_seriesLink; } bool AutoRecording::operator!=(const AutoRecording& right) @@ -128,6 +129,15 @@ void AutoRecording::SetFulltext(uint32_t fulltext) m_fulltext = fulltext; } +uint32_t AutoRecording::GetBroadcastType() const +{ + return m_broadcastType; +} +void AutoRecording::SetBroadcastType(uint32_t broadcastType) +{ + m_broadcastType = broadcastType; +} + const std::string& AutoRecording::GetSeriesLink() const { return m_seriesLink; diff --git a/src/tvheadend/entity/AutoRecording.h b/src/tvheadend/entity/AutoRecording.h index dbdde4f0..f56ccd9a 100644 --- a/src/tvheadend/entity/AutoRecording.h +++ b/src/tvheadend/entity/AutoRecording.h @@ -50,6 +50,9 @@ class AutoRecording : public SeriesRecordingBase bool GetFulltext() const; void SetFulltext(uint32_t fulltext); + uint32_t GetBroadcastType() const; + void SetBroadcastType(uint32_t broadcastType); + const std::string& GetSeriesLink() const; void SetSeriesLink(const std::string& seriesLink); @@ -62,6 +65,7 @@ class AutoRecording : public SeriesRecordingBase int64_t m_stopExtra{0}; // Extra stop minutes (post-time). uint32_t m_dupDetect{0}; // duplicate episode detect (numeric values: see dvr_autorec_dedup_t). uint32_t m_fulltext{0}; // Fulltext epg search. + uint32_t m_broadcastType{0}; // Broadcast type (numeric values: see dvr_autorec_btype_t). std::string m_seriesLink; // Series link. }; diff --git a/src/tvheadend/entity/Recording.h b/src/tvheadend/entity/Recording.h index 57eae314..1b391cc4 100644 --- a/src/tvheadend/entity/Recording.h +++ b/src/tvheadend/entity/Recording.h @@ -58,7 +58,8 @@ class Recording : public RecordingBase m_contentType == other.m_contentType && m_season == other.m_season && m_episode == other.m_episode && m_part == other.m_part && m_ageRating == other.m_ageRating && m_ratingLabel == other.m_ratingLabel && - m_ratingIcon == other.m_ratingIcon && m_ratingSource == other.m_ratingSource; + m_ratingIcon == other.m_ratingIcon && m_ratingSource == other.m_ratingSource && + m_configUuid == other.m_configUuid; } bool operator!=(const Recording& other) { return !(*this == other); } @@ -72,7 +73,7 @@ class Recording : public RecordingBase bool IsTimer() const { return m_state == PVR_TIMER_STATE_SCHEDULED || m_state == PVR_TIMER_STATE_RECORDING || - m_state == PVR_TIMER_STATE_CONFLICT_NOK; + m_state == PVR_TIMER_STATE_CONFLICT_OK; } /** @@ -185,6 +186,9 @@ class Recording : public RecordingBase const std::string& GetRatingSource() const { return m_ratingSource; } void SetRatingSource(const std::string& ratingSource) { m_ratingSource = ratingSource; } + const std::string& GetConfigUuid() const { return m_configUuid; } + void SetConfigUuid(const std::string& uuid) { m_configUuid = uuid; } + private: uint32_t m_channelType{0}; std::string m_channelName; @@ -215,6 +219,7 @@ class Recording : public RecordingBase std::string m_ratingLabel; std::string m_ratingIcon; std::string m_ratingSource; + std::string m_configUuid; // DVR configuration UUID. }; } // namespace tvheadend::entity diff --git a/src/tvheadend/entity/RecordingBase.h b/src/tvheadend/entity/RecordingBase.h index 0bfd07fb..00e13b6f 100644 --- a/src/tvheadend/entity/RecordingBase.h +++ b/src/tvheadend/entity/RecordingBase.h @@ -25,7 +25,8 @@ class RecordingBase : public Entity { return Entity::operator==(right) && m_enabled == right.m_enabled && m_lifetime == right.m_lifetime && m_priority == right.m_priority && - m_title == right.m_title && m_channel == right.m_channel; + m_title == right.m_title && m_channel == right.m_channel && + m_configUuid == right.m_configUuid && m_comment == right.m_comment; } bool operator!=(const RecordingBase& right) { return !(*this == right); } @@ -46,12 +47,20 @@ class RecordingBase : public Entity uint32_t GetChannel() const { return m_channel; } void SetChannel(uint32_t channel) { m_channel = channel; } + const std::string& GetConfigUuid() const { return m_configUuid; } + void SetConfigUuid(const std::string& uuid) { m_configUuid = uuid; } + + const std::string& GetComment() const { return m_comment; } + void SetComment(const std::string& comment) { m_comment = comment; } + private: uint32_t m_enabled{0}; // If [time|auto]rec entry is enabled (activated). uint32_t m_lifetime{0}; // Lifetime (in days). uint32_t m_priority{DVR_PRIO_DEFAULT}; // Priority. std::string m_title; // Title (pattern) for the recording files. uint32_t m_channel{0}; // Channel ID. + std::string m_configUuid; // DVR configuration UUID. + std::string m_comment; // user supplied comment }; } // namespace tvheadend::entity From 5a028e37dcbca795f770a65810eda92f723213f9 Mon Sep 17 00:00:00 2001 From: ksooo <3226626+ksooo@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:10:38 +0200 Subject: [PATCH 3/3] Make timers created by autorec and timerec editable. --- pvr.hts/changelog.txt | 1 + src/Tvheadend.cpp | 67 ++++++++++++++++++++----------------------- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index 273b1d14..b71e3bf2 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -3,6 +3,7 @@ v22.4.0 - Add support for Autorec property "Broadcast type" (HTSPv39+) - Add support for DVR entry property "DVR configuration" (HTSPv40+) - Add support for DVR entry property "Comment" (HTSPv42+) +- Make timers created by autorec and timerec editable v22.3.0 - Add support for PVR Providers (HTSPv38+) diff --git a/src/Tvheadend.cpp b/src/Tvheadend.cpp index 9ecbd278..1af01fda 100644 --- a/src/Tvheadend.cpp +++ b/src/Tvheadend.cpp @@ -1043,14 +1043,14 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type /* Values definitions for lifetime. */ lifetimeValues)); - /* Read-only one-shot for timers generated by timerec */ + /* One-shot for timers generated by timerec */ types.emplace_back(TimerType( /* Settings */ m_settings, /* Type id. */ TIMER_ONCE_CREATED_BY_TIMEREC, /* Attributes. */ - TIMER_ONCE_MANUAL_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, + TIMER_ONCE_MANUAL_ATTRIBS | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, /* Description. */ kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)" /* Custom settings definitions. */ @@ -1060,14 +1060,14 @@ PVR_ERROR CTvheadend::GetTimerTypes(std::vector& type /* Values definitions for lifetime. */ lifetimeValues)); - /* Read-only one-shot for timers generated by autorec */ + /* One-shot for timers generated by autorec */ types.emplace_back(TimerType( /* Settings */ m_settings, /* Type id. */ TIMER_ONCE_CREATED_BY_AUTOREC, /* Attributes. */ - TIMER_ONCE_EPG_ATTRIBS | PVR_TIMER_TYPE_IS_READONLY | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, + TIMER_ONCE_EPG_ATTRIBS | PVR_TIMER_TYPE_FORBIDS_NEW_INSTANCES, /* Description. */ kodi::addon::GetLocalizedString(30350), // "One Time (Scheduled by timer rule)" /* Custom settings definitions. */ @@ -1373,7 +1373,9 @@ PVR_ERROR CTvheadend::DeleteTimer(const kodi::addon::PVRTimer& timer, bool) } } - if ((timer.GetTimerType() == TIMER_ONCE_MANUAL) || (timer.GetTimerType() == TIMER_ONCE_EPG)) + if ((timer.GetTimerType() == TIMER_ONCE_MANUAL) || (timer.GetTimerType() == TIMER_ONCE_EPG) || + (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_TIMEREC) || + (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC)) { /* one shot timer */ return SendDvrDelete(timer.GetClientIndex(), "cancelDvrEntry"); @@ -1389,13 +1391,6 @@ PVR_ERROR CTvheadend::DeleteTimer(const kodi::addon::PVRTimer& timer, bool) /* EPG-query-based repeating timer */ return m_autoRecordings.SendAutorecDelete(timer); } - else if ((timer.GetTimerType() == TIMER_ONCE_CREATED_BY_TIMEREC) || - (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC)) - { - /* Read-only timer created by autorec or timerec */ - Logger::Log(LogLevel::LEVEL_ERROR, "timer is read-only"); - return PVR_ERROR_INVALID_PARAMETERS; - } else { /* unknown timer */ @@ -1406,7 +1401,30 @@ PVR_ERROR CTvheadend::DeleteTimer(const kodi::addon::PVRTimer& timer, bool) PVR_ERROR CTvheadend::UpdateTimer(const kodi::addon::PVRTimer& timer) { - if ((timer.GetTimerType() == TIMER_ONCE_MANUAL) || (timer.GetTimerType() == TIMER_ONCE_EPG)) + if ((timer.GetTimerType() == TIMER_ONCE_CREATED_BY_TIMEREC) || + (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC)) + { + if (!m_asyncState.WaitForState(ASYNC_EPG)) + return PVR_ERROR_FAILED; + + /* Timer created by autorec or timerec */ + std::lock_guard lock(m_mutex); + + const auto& it = m_recordings.find(timer.GetClientIndex()); + if (it != m_recordings.end() && + (it->second.IsEnabled() == (timer.GetState() == PVR_TIMER_STATE_DISABLED))) + { + /* This is actually a request to enable/disable a timer. */ + htsmsg_t* m = htsmsg_create_map(); + htsmsg_add_u32(m, "id", timer.GetClientIndex()); + htsmsg_add_u32(m, "enabled", timer.GetState() == PVR_TIMER_STATE_DISABLED ? 0 : 1); + return SendDvrUpdate(m); + } + } + + if ((timer.GetTimerType() == TIMER_ONCE_MANUAL) || (timer.GetTimerType() == TIMER_ONCE_EPG) || + (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_TIMEREC) || + (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC)) { /* one shot timer */ @@ -1449,29 +1467,6 @@ PVR_ERROR CTvheadend::UpdateTimer(const kodi::addon::PVRTimer& timer) /* EPG-query-based repeating timers */ return m_autoRecordings.SendAutorecUpdate(timer); } - else if ((timer.GetTimerType() == TIMER_ONCE_CREATED_BY_TIMEREC) || - (timer.GetTimerType() == TIMER_ONCE_CREATED_BY_AUTOREC)) - { - if (!m_asyncState.WaitForState(ASYNC_EPG)) - return PVR_ERROR_FAILED; - - /* Read-only timer created by autorec or timerec */ - std::lock_guard lock(m_mutex); - - const auto& it = m_recordings.find(timer.GetClientIndex()); - if (it != m_recordings.end() && - (it->second.IsEnabled() == (timer.GetState() == PVR_TIMER_STATE_DISABLED))) - { - /* This is actually a request to enable/disable a timer. */ - htsmsg_t* m = htsmsg_create_map(); - htsmsg_add_u32(m, "id", timer.GetClientIndex()); - htsmsg_add_u32(m, "enabled", timer.GetState() == PVR_TIMER_STATE_DISABLED ? 0 : 1); - return SendDvrUpdate(m); - } - - Logger::Log(LogLevel::LEVEL_ERROR, "timer is read-only"); - return PVR_ERROR_INVALID_PARAMETERS; - } else { /* unknown timer */