Skip to content

Commit

Permalink
refactor the XMLTV code to make it more robust. Schedule is now a cla…
Browse files Browse the repository at this point in the history
…ss instead

of a simple typedef. Schedules and programmes are stored as shared pointers
and returned as such (instead of naked pointers). Some functionality that
previously resided in the "vbox" namespace has now been moved here where it
belongs.
  • Loading branch information
Jalle19 committed Jun 12, 2015
1 parent cea1cf4 commit 3e34576
Show file tree
Hide file tree
Showing 9 changed files with 198 additions and 154 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -67,6 +67,7 @@ set(VBOX_SOURCES_XMLTV
src/xmltv/Programme.h
src/xmltv/Programme.cpp
src/xmltv/Schedule.h
src/xmltv/Schedule.cpp
src/xmltv/Utilities.h
src/xmltv/Utilities.cpp)

Expand Down
71 changes: 33 additions & 38 deletions src/client.cpp
Expand Up @@ -539,38 +539,48 @@ extern "C" {

const Channel *channel = it->get();

try {
// See if the programme info is available in the guide
const xmltv::Programme *programme = g_vbox->GetProgramme(timer.iEpgUid, false);

if (programme)
{
g_vbox->AddTimer(channel, programme);
return PVR_ERROR_NO_ERROR;
}
// Find the channel's schedule
const Schedule schedule = g_vbox->GetSchedule(channel);

try {
// Set start time to now if it's missing
time_t startTime = timer.startTime;
time_t endTime = timer.endTime;

// Set start time to now if it's missing
if (startTime == 0)
startTime = time(nullptr);

// See if the programme info is available in the external guide
programme = g_vbox->GetProgramme(timer.iEpgUid, true);
// Add a time-based timer if no programme is available
if (!schedule.schedule)
{
g_vbox->AddTimer(channel, startTime, endTime);
return PVR_ERROR_NO_ERROR;
}

// Add a programme-based timer if the programme exists in the schedule
const xmltv::ProgrammePtr programme = schedule.schedule->GetProgramme(timer.iEpgUid);

if (programme)
{
std::string title = programme->m_title;
std::string description = programme->m_description;
switch (schedule.origin)
{
case Schedule::Origin::INTERNAL_GUIDE:
g_vbox->AddTimer(channel, programme);
break;
case Schedule::Origin::EXTERNAL_GUIDE:
std::string title = programme->m_title;
std::string description = programme->m_description;

g_vbox->AddTimer(channel, startTime, endTime, title, description);
break;
}

g_vbox->AddTimer(channel, startTime, endTime, title, description);
return PVR_ERROR_NO_ERROR;
}

// If no programme info is available we add a time-based timer
g_vbox->AddTimer(channel, startTime, timer.endTime);
return PVR_ERROR_NO_ERROR;
// If the channel has a schedule but not the desired programme, something
// is wrong
return PVR_ERROR_INVALID_PARAMETERS;
}
catch (VBoxException &e)
{
Expand All @@ -594,29 +604,15 @@ extern "C" {
if (!channelPtr)
return PVR_ERROR_INVALID_PARAMETERS;

// Retrieve the schedule and filter out the programmes that don't fit
// within the start and end times
const auto *schedule = g_vbox->GetSchedule(channelPtr);
// Retrieve the schedule
const auto schedule = g_vbox->GetSchedule(channelPtr);

if (!schedule)
if (!schedule.schedule)
return PVR_ERROR_NO_ERROR;

std::string xmltvStartTime = g_vbox->CreateTimestamp(iStart);
std::string xmltvEndTime = g_vbox->CreateTimestamp(iEnd);

auto it = std::find_if(
schedule->cbegin(),
schedule->cend(),
[xmltvStartTime, xmltvEndTime](const xmltv::ProgrammePtr &programme)
{
return programme->m_startTime >= xmltvStartTime &&
programme->m_endTime <= xmltvEndTime;
});

// Transfer the events
while (it != schedule->cend())
// Transfer the programmes between the start and end times
for (const auto &programme : schedule.schedule->GetSegment(iStart, iEnd))
{
const auto &programme = *it;
EPG_TAG event;
memset(&event, 0, sizeof(EPG_TAG));

Expand Down Expand Up @@ -649,7 +645,6 @@ extern "C" {
event.strCast = cast.c_str();

PVR->TransferEpgEntry(handle, &event);
++it;
}

return PVR_ERROR_NO_ERROR;
Expand Down
27 changes: 10 additions & 17 deletions src/vbox/VBox.cpp
Expand Up @@ -423,7 +423,7 @@ bool VBox::DeleteRecordingOrTimer(unsigned int id)
return false;
}

void VBox::AddTimer(const Channel *channel, const ::xmltv::Programme* programme)
void VBox::AddTimer(const Channel *channel, const ::xmltv::ProgrammePtr programme)
{
// Add the timer
request::ApiRequest request("ScheduleProgramRecord");
Expand Down Expand Up @@ -488,18 +488,19 @@ const std::vector<RecordingPtr>& VBox::GetRecordingsAndTimers() const
return m_recordings;
}

const ::xmltv::Schedule* VBox::GetSchedule(const Channel *channel) const
const Schedule VBox::GetSchedule(const Channel *channel) const
{
// Load the schedule
// Load the schedule from the internal guide
m_stateHandler.WaitForState(StartupState::GUIDE_LOADED);
std::unique_lock<std::mutex> lock(m_mutex);

auto *schedule = m_guide.GetSchedule(channel->m_xmltvName);
Schedule schedule;
schedule.schedule = m_guide.GetSchedule(channel->m_xmltvName);

// Try to use the external guide data if a) it's loaded, b) the user prefers
// it or c) if no schedule was found
if (m_stateHandler.GetState() >= StartupState::EXTERNAL_GUIDE_LOADED &&
(m_settings.m_preferExternalXmltv || !schedule))
(m_settings.m_preferExternalXmltv || !schedule.schedule))
{
// Consult the channel mapper to find the corresponding external channel name
std::string mappedName = m_guideChannelMapper->GetExternalChannelName(channel->m_name);
Expand All @@ -508,23 +509,15 @@ const ::xmltv::Schedule* VBox::GetSchedule(const Channel *channel) const
if (!mappedName.empty() && !xmltvName.empty())
{
Log(LOG_DEBUG, "Using external guide data for channel %s", channel->m_name.c_str());
schedule = m_externalGuide.GetSchedule(xmltvName);

schedule.schedule = m_externalGuide.GetSchedule(xmltvName);
schedule.origin = Schedule::Origin::EXTERNAL_GUIDE;
}
}

return schedule;
}

const ::xmltv::Programme* VBox::GetProgramme(int programmeUniqueId, bool useExternalGuide) const
{
std::unique_lock<std::mutex> lock(m_mutex);

if (!useExternalGuide)
return m_guide.GetProgramme(programmeUniqueId);
else
return m_externalGuide.GetProgramme(programmeUniqueId);
}

std::string VBox::GetApiBaseUrl() const
{
std::stringstream ss;
Expand Down Expand Up @@ -705,7 +698,7 @@ void VBox::LogGuideStatistics(const xmltv::Guide &guide) const

for (const auto &schedule : guide.GetSchedules())
{
Log(LOG_INFO, "Fetched %d events for channel %s", schedule.second->size(),
Log(LOG_INFO, "Fetched %d events for channel %s", schedule.second->GetLength(),
schedule.first.c_str());
}
}
Expand Down
21 changes: 18 additions & 3 deletions src/vbox/VBox.h
Expand Up @@ -65,6 +65,22 @@ namespace vbox {
ExternalMediaStatus externalMediaStatus;
};

/**
* Represents a schedule. It contains an actual schedule and an indicator
* which tells if the schedule is from the internal or external guide
*/
struct Schedule
{
enum Origin
{
INTERNAL_GUIDE,
EXTERNAL_GUIDE
};

::xmltv::SchedulePtr schedule = nullptr;
Origin origin = Origin::INTERNAL_GUIDE;
};

/**
* The main class for interfacing with the VBox Gateway
*/
Expand Down Expand Up @@ -121,15 +137,14 @@ namespace vbox {
int GetTimersAmount() const;
request::ApiRequest CreateDeleteRecordingRequest(const RecordingPtr &recording) const;
bool DeleteRecordingOrTimer(unsigned int id);
void AddTimer(const Channel *channel, const ::xmltv::Programme* programme);
void AddTimer(const Channel *channel, const ::xmltv::ProgrammePtr programme);
void AddTimer(const Channel *channel, time_t startTime, time_t endTime,
const std::string title, const std::string description);
void AddTimer(const Channel *channel, time_t startTime, time_t endTime);
const std::vector<RecordingPtr>& GetRecordingsAndTimers() const;

// EPG methods
const ::xmltv::Schedule* GetSchedule(const Channel *channel) const;
const ::xmltv::Programme* GetProgramme(int programmeUniqueId, bool useExternalGuide) const;
const Schedule GetSchedule(const Channel *channel) const;

// Helpers
static void Log(const ADDON::addon_log level, const char *format, ...);
Expand Down
49 changes: 11 additions & 38 deletions src/xmltv/Guide.cpp
Expand Up @@ -20,7 +20,6 @@
*/

#include "Guide.h"
#include <algorithm>
#include "Utilities.h"
#include "tinyxml2.h"
#include "../vbox/ContentIdentifier.h"
Expand All @@ -30,63 +29,37 @@ using namespace tinyxml2;

Guide::Guide(const XMLElement *m_content)
{
// Populate the lookup table which maps XMLTV IDs to display names
for (const XMLElement *element = m_content->FirstChildElement("channel");
element != NULL; element = element->NextSiblingElement("channel"))
{
std::string id = Utilities::UrlDecode(element->Attribute("id"));
// Populate the lookup table which maps XMLTV IDs to display names
std::string channelId = Utilities::UrlDecode(element->Attribute("id"));
std::string displayName = element->FirstChildElement("display-name")->GetText();

AddDisplayNameMapping(displayName, id);
AddDisplayNameMapping(displayName, channelId);

// Create a schedule for the channel
m_schedules[channelId] = SchedulePtr(new Schedule);
}

for (const XMLElement *element = m_content->FirstChildElement("programme");
element != NULL; element = element->NextSiblingElement("programme"))
{
// Extract the channel name and the programme
std::string channelName = Utilities::UrlDecode(element->Attribute("channel"));
std::string channelId = Utilities::UrlDecode(element->Attribute("channel"));
xmltv::ProgrammePtr programme(new Programme(element));

// Add the programme to the guide
AddProgramme(channelName, std::move(programme));
// Add the programme to the channel's schedule
m_schedules[channelId]->AddProgramme(programme);
}
}

const Schedule* Guide::GetSchedule(const std::string &channelId) const
const SchedulePtr Guide::GetSchedule(const std::string &channelId) const
{
auto it = m_schedules.find(channelId);

if (it != m_schedules.cend())
return it->second.get();

return nullptr;
}

const Programme* Guide::GetProgramme(int programmeUniqueId) const
{
for (const auto &entry : m_schedules)
{
const ::xmltv::SchedulePtr &schedule = entry.second;

auto it = std::find_if(
schedule->cbegin(),
schedule->cend(),
[programmeUniqueId](const ProgrammePtr &programme)
{
return programmeUniqueId == vbox::ContentIdentifier::GetUniqueId(programme.get());
});

if (it != schedule->cend())
return it->get();
}
return it->second;

return nullptr;
}

void Guide::AddProgramme(const std::string &channelId, ProgrammePtr programme)
{
if (m_schedules.find(channelId) == m_schedules.end())
AddSchedule(channelId, SchedulePtr(new Schedule);

m_schedules[channelId]->push_back(std::move(programme));
}

0 comments on commit 3e34576

Please sign in to comment.