From 0dfd7c084a7f84ebd03e18f0c703215ed1c01f6a Mon Sep 17 00:00:00 2001 From: Aa! Date: Wed, 12 Dec 2018 13:17:50 +0100 Subject: [PATCH] First commit! --- CMakeLists.txt | 29 + pvr.freebox/addon.xml.in | 17 + pvr.freebox/icon.png | Bin 0 -> 5218 bytes .../resource.language.fr_fr/strings.po | 78 ++ pvr.freebox/resources/settings.xml | 41 ++ src/PVRFreeboxData.cpp | 695 ++++++++++++++++++ src/PVRFreeboxData.h | 203 +++++ src/client.cpp | 314 ++++++++ src/client.h | 28 + 9 files changed, 1405 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 pvr.freebox/addon.xml.in create mode 100644 pvr.freebox/icon.png create mode 100644 pvr.freebox/resources/language/resource.language.fr_fr/strings.po create mode 100644 pvr.freebox/resources/settings.xml create mode 100644 src/PVRFreeboxData.cpp create mode 100644 src/PVRFreeboxData.h create mode 100644 src/client.cpp create mode 100644 src/client.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9dcae15 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +project(pvr.freebox) + +cmake_minimum_required(VERSION 2.6) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}) + +enable_language(CXX) + +find_package(Kodi REQUIRED) +find_package(kodiplatform REQUIRED) +find_package(p8-platform REQUIRED) +find_package(RapidJSON REQUIRED) + +include_directories(${kodiplatform_INCLUDE_DIRS} + ${p8-platform_INCLUDE_DIRS} + ${KODI_INCLUDE_DIR} + ${RAPIDJSON_INCLUDE_DIRS}) + +set(DEPLIBS ${p8-platform_LIBRARIES}) + +set(FREEBOX_SOURCES src/client.cpp + src/PVRFreeboxData.cpp) + +set(FREEBOX_HEADERS src/client.h + src/PVRFreeboxData.h) + +build_addon(pvr.freebox FREEBOX DEPLIBS) + +include(CPack) diff --git a/pvr.freebox/addon.xml.in b/pvr.freebox/addon.xml.in new file mode 100644 index 0000000..c60f770 --- /dev/null +++ b/pvr.freebox/addon.xml.in @@ -0,0 +1,17 @@ + + + @ADDON_DEPENDS@ + + + Extension enregistreur vidéo (PVR) pour Freebox TV. https://github.com/aassif/pvr.freebox + Le client enregistreur vidéo (PVR) gère les chaînes Freebox TV et le guide électronique des programmes. + Logiciel en cours d'élaboration ! + @PLATFORM@ + + diff --git a/pvr.freebox/icon.png b/pvr.freebox/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..98b6795b7a6ba943df7baa8c138d729152ee2c3f GIT binary patch literal 5218 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 1);\n" + +msgctxt "#30000" +msgid "%d channels loaded" +msgstr "%d chaînes chargées" + +msgctxt "#40000" +msgid "Section 1" +msgstr "Section 1" + +msgctxt "#41000" +msgid "Television" +msgstr "Télévision" + +msgctxt "#41100" +msgid "Television 1" +msgstr "Télévision 1" + +msgctxt "#41110" +msgid "Quality" +msgstr "Qualité" + +msgctxt "#41111" +msgid "Auto" +msgstr "Auto" + +msgctxt "#41112" +msgid "High" +msgstr "Haute" + +msgctxt "#41113" +msgid "Standard" +msgstr "Standard" + +msgctxt "#41114" +msgid "Low" +msgstr "Basse" + +msgctxt "#42000" +msgid "EPG" +msgstr "Guide TV" + +msgctxt "#42100" +msgid "EPG 1" +msgstr "EPG 1" + +msgctxt "#42110" +msgid "Extended" +msgstr "Étendu" + +msgctxt "#42120" +msgid "Delay" +msgstr "Délai" + +msgctxt "#142110" +msgid "Extended EPG." +msgstr "Guide TV étendu (résumé)." + +msgctxt "#142120" +msgid "Delay between queries." +msgstr "Délai entre chaque requête." + diff --git a/pvr.freebox/resources/settings.xml b/pvr.freebox/resources/settings.xml new file mode 100644 index 0000000..6e00a67 --- /dev/null +++ b/pvr.freebox/resources/settings.xml @@ -0,0 +1,41 @@ + + +
+ + + + 0 + 1 + + + + + + + + + + + + + + + + 0 + false + + + + 0 + 10 + + 0 + 5 + 30 + + + + + +
+
diff --git a/src/PVRFreeboxData.cpp b/src/PVRFreeboxData.cpp new file mode 100644 index 0000000..1ecc40f --- /dev/null +++ b/src/PVRFreeboxData.cpp @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2018 Aassif Benassarou + * http://github.com/aassif/pvr.freebox/ + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include +#include +#include + +#include "client.h" +#include "PVRFreeboxData.h" +#include "p8-platform/util/StringUtils.h" + +using namespace std; +using namespace rapidjson; +using namespace ADDON; + +#define PVR_FREEBOX_C_STR(s) s.empty () ? NULL : s.c_str () + +class RawChannel +{ + public: + string uuid; + int major, minor; // numéro de chaîne + int position; // position dans le bouquet + + public: + inline RawChannel (const string & id, + int n1, int n2, + int p) : + uuid (id), + major (n1), minor (n2), + position (p) + { + } +}; + +class RawChannelComparator +{ + public: + inline bool operator() (const RawChannel & c1, const RawChannel & c2) + { + return (c1.major < c2.major || c1.major == c2.major && c1.minor < c2.minor); + } +}; + +inline string StrUUIDs (const vector & v) +{ + string text; + if (! v.empty ()) + { + text += v[0].uuid; + for (int i = 1; i < v.size (); ++i) + text += ", " + v[i].uuid; + } + return '[' + text + ']'; +} + +inline string StrNumber (const RawChannel & c, bool minor) +{ + return to_string (c.major) + (minor ? '.' + to_string (c.minor) : ""); +} + +inline string StrNumbers (const vector & v) +{ + string text; + switch (v.size ()) + { + case 0: + break; + + case 1: + text = StrNumber (v[0], false); + break; + + default: + text = StrNumber (v[0], true); + for (int i = 1; i < v.size (); ++i) + text += ", " + StrNumber (v[i], true); + break; + } + return '[' + text + ']'; +} + +enum PVRFreeboxData::Quality PVRFreeboxData::Stream::Parse (const string & q) +{ + if (q == "auto") return AUTO; + if (q == "hd") return HD; + if (q == "sd") return SD; + if (q == "ld") return LD; + return DEFAULT; +} + +int PVRFreeboxData::Stream::Score (enum Quality q, enum Quality q0) +{ + switch (q0) + { + case AUTO: + switch (q) + { + case AUTO: return 1000; + case HD: return 100; + case SD: return 10; + case LD: return 1; + default: return 0; + } + + case HD: + switch (q) + { + case AUTO: return 100; + case HD: return 1000; + case SD: return 10; + case LD: return 1; + default: return 0; + } + + case SD: + switch (q) + { + case AUTO: return 100; + case HD: return 1; + case SD: return 1000; + case LD: return 10; + default: return 0; + } + + case LD: + switch (q) + { + case AUTO: return 100; + case HD: return 1; + case SD: return 10; + case LD: return 1000; + default: return 0; + } + + default: + return 0; + } +} + +PVRFreeboxData::Stream::Stream (enum Quality q, const string & u) : + quality (q), url (u) +{ +} + +PVRFreeboxData::Channel::Channel (const string & uuid, + const string & name, + const string & logo, + int major, int minor, + const Value & item) : + radio (false), + uuid (uuid), + name (name), + logo (logo), + major (major), minor (minor), + streams () +{ + if (item.HasMember("available") && item["available"].GetBool () && item.HasMember("streams")) + { + const Value & streams = item["streams"]; + if (streams.IsArray ()) + { + for (SizeType i = 0; i < streams.Size (); ++i) + { + const Value & s = streams [i]; + const string & q = s["quality"].GetString (); + const string & r = s["rtsp"].GetString (); + this->streams.emplace_back (Stream::Parse (q), r); + } + } + } +} + +int PVRFreeboxData::Channel::Find (enum Quality q) const +{ + if (streams.empty ()) return -1; + + int index = 0; + int score = Stream::Score (streams[0].quality, q); + + for (int i = 1; i < streams.size (); ++i) + { + int s = Stream::Score (streams[i].quality, q); + if (s > score) + { + index = i; + score = s; + } + } + + return index; +} + +void PVRFreeboxData::Channel::GetChannel (ADDON_HANDLE handle, bool radio) const +{ + PVR_CHANNEL channel; + memset (&channel, 0, sizeof (PVR_CHANNEL)); + + channel.iUniqueId = ChannelId (uuid); + channel.bIsRadio = false; + channel.iChannelNumber = major; + channel.iSubChannelNumber = minor; + strncpy (channel.strChannelName, name.c_str (), PVR_ADDON_NAME_STRING_LENGTH - 1); + strncpy (channel.strIconPath, logo.c_str (), PVR_ADDON_URL_STRING_LENGTH - 1); + channel.bIsHidden = streams.empty (); + + PVR->TransferChannelEntry (handle, &channel); +} + +PVR_ERROR PVRFreeboxData::Channel::GetStreamProperties (enum Quality q, PVR_NAMED_VALUE * properties, unsigned int * count) const +{ + int k = Find (q); + if (k != -1) + { + const string & url = streams[k].url; + strncpy (properties[0].strName, PVR_STREAM_PROPERTY_STREAMURL, PVR_ADDON_NAME_STRING_LENGTH - 1); + strncpy (properties[0].strValue, url.c_str (), PVR_ADDON_NAME_STRING_LENGTH - 1); + strncpy (properties[1].strName, PVR_STREAM_PROPERTY_ISREALTIMESTREAM, PVR_ADDON_NAME_STRING_LENGTH - 1); + strncpy (properties[1].strValue, "true", PVR_ADDON_NAME_STRING_LENGTH - 1); + *count = 2; + } + + return PVR_ERROR_NO_ERROR; +} + +bool PVRFreeboxData::ReadJSON (Document * doc, const string & url) +{ + void * file = XBMC->OpenFile (url.c_str (), XFILE::READ_NO_CACHE); + + if (file) + { + string data; + + char buffer [1024]; + while (int bytes = XBMC->ReadFile (file, buffer, 1024)) + data.append (buffer, bytes); + + XBMC->CloseFile (file); + + return ! doc->Parse (data.c_str ()).HasParseError (); + } + + return false; +} + +PVRFreeboxData::Event::Event (const Value & e, unsigned int channel) : + channel (channel), + uuid (JSON (e, "id")), + date (JSON (e, "date")), + duration (JSON (e, "duration")), + title (JSON (e, "title")), + subtitle (JSON (e, "sub_title")), + season (JSON (e, "season_number")), + episode (JSON (e, "episode_number")), + picture (JSON (e, "picture")), + category (JSON (e, "category_name")), + plot (JSON (e, "desc")), + outline (JSON (e, "short_desc")) +{ +} + +bool PVRFreeboxData::ProcessChannels () +{ + m_tv_channels.clear (); + + Document channels; + if (! ReadJSON (&channels, URL ("/api/v5/tv/channels"))) return false; + if (! channels.HasMember ("success") || ! channels["success"].GetBool ()) return false; + if (! channels.HasMember ("result") || ! channels["result"].IsObject ()) return false; + + char * notification = XBMC->GetLocalizedString (30000); // "%d channels loaded" + XBMC->QueueNotification (QUEUE_INFO, notification, channels["result"].MemberCount ()); + XBMC->FreeString (notification); + + //Document bouquets; + //ReadJSON (&m_tv_bouquets, URL ("/api/v5/tv/bouquets")); + + Document bouquet; + if (! ReadJSON (&bouquet, URL ("/api/v5/tv/bouquets/freeboxtv/channels"))) return false; + if (! bouquet.HasMember ("success") || ! bouquet["success"].GetBool ()) return false; + if (! bouquet.HasMember ("result") || ! bouquet["result"].IsArray ()) return false; + + // Channel list. + typedef vector RawChannels; + // Channels by UUID. + map channels_by_uuid; + // Channels by major. + map channels_by_major; + + const Value & r = bouquet ["result"]; + for (SizeType i = 0; i < r.Size (); ++i) + { + string uuid = r[i]["uuid"].GetString (); + int major = r[i]["number"].GetInt (); + int minor = r[i]["sub_number"].GetInt (); + + RawChannel c (uuid, major, minor, i); + + channels_by_uuid [uuid] .push_back (c); + channels_by_major[major].push_back (c); + } + + static const RawChannelComparator comparator; + + for (auto i = channels_by_major.begin (); i != channels_by_major.end (); ++i) + { + RawChannels & q = i->second; + sort (q.begin (), q.end (), comparator); + const RawChannel & c0 = q.front (); + //cout << i->first << " : " << StrUUIDs (q) << endl; + + for (int i = 1; i < q.size (); ++i) + { + RawChannels & discarded = channels_by_uuid [q[i].uuid]; + discarded.erase (remove_if (discarded.begin (), discarded.end (), + [&c0] (const RawChannel & c) {return c.major == c0.major;})); + } + + q.erase (q.begin () + 1, q.end ()); + } + + for (auto i = channels_by_uuid.begin (); i != channels_by_uuid.end (); ++i) + { + RawChannels & q = i->second; + if (! q.empty ()) + { + sort (q.begin (), q.end (), comparator); + const RawChannel & c0 = q.front (); + //cout << i->first << " : " << StrNumbers (q) << endl; + + for (int i = 1; i < q.size (); ++i) + { + RawChannels & discarded = channels_by_major [q[i].major]; + discarded.erase (remove_if (discarded.begin (), discarded.end (), + [&c0] (const RawChannel & c) {return c.uuid == c0.uuid;})); + } + + q.erase (q.begin () + 1, q.end ()); + } + } + +#if 0 + for (auto i = channels_by_major.begin (); i != channels_by_major.end (); ++i) + cout << i->first << " : " << StrUUIDs (i->second) << endl; +#endif + +#if 0 + for (auto i = channels_by_uuid.begin (); i != channels_by_uuid.end (); ++i) + cout << i->first << " : " << StrNumbers (i->second) << endl; +#endif + + for (auto i = channels_by_major.begin (); i != channels_by_major.end (); ++i) + { + const vector & q = i->second; + if (! q.empty ()) + { + const RawChannel & ch = q.front (); + const Value & channel = channels["result"][ch.uuid.c_str ()]; + const string & name = channel["name"].GetString (); + const string & logo = URL (channel["logo_url"].GetString ()); + const Value & item = bouquet["result"][ch.position]; + m_tv_channels.emplace (ChannelId (ch.uuid), Channel (ch.uuid, name, logo, ch.major, ch.minor, item)); + } + } + + return true; +} + +PVRFreeboxData::PVRFreeboxData (const string & path, + int quality, + int days, + bool extended, + int delay) : + m_path (path), + m_server ("mafreebox.freebox.fr"), + m_delay (delay), + m_tv_channels (), + m_tv_quality (Quality (quality)), + m_epg_queries (), + m_epg_cache (), + m_epg_events (), + m_epg_days (0), + m_epg_last (0), + m_epg_extended (extended) +{ + SetDays (days); + ProcessChannels (); + CreateThread (false); +} + +PVRFreeboxData::~PVRFreeboxData () +{ + StopThread (); +} + +string PVRFreeboxData::GetServer () const +{ + P8PLATFORM::CLockObject lock (m_mutex); + return m_server; +} + +// NOT thread-safe ! +string PVRFreeboxData::URL (const string & query) const +{ + return "http://" + m_server + query; +} + +void PVRFreeboxData::SetQuality (int q) +{ + P8PLATFORM::CLockObject lock (m_mutex); + m_tv_quality = Quality (q); +} + +void PVRFreeboxData::SetDays (int d) +{ + P8PLATFORM::CLockObject lock (m_mutex); + m_epg_days = d != EPG_TIMEFRAME_UNLIMITED ? min (d, 7) : 7; +} + +void PVRFreeboxData::SetExtended (bool e) +{ + P8PLATFORM::CLockObject lock (m_mutex); + m_epg_extended = e; +} + +void PVRFreeboxData::SetDelay (int d) +{ + P8PLATFORM::CLockObject lock (m_mutex); + m_delay = d; +} + +void PVRFreeboxData::ProcessEvent (const Event & e, EPG_EVENT_STATE state) +{ +#if 0 + cout << e.uuid << " : " << e.title << ' ' << e.date << '+' << e.duration << " (" << e.channel << ')' << endl; + cout << " " << e.category << ' ' << e.season << 'x' << e.episode << ' ' << '"' << e.subtitle << '"' << ' ' << '[' << e.picture << ']' << endl; + cout << " " << '"' << e.outline << '"' << ' ' << '"' << e.plot << '"' << endl; +#endif + + string picture = e.picture; + { + P8PLATFORM::CLockObject lock (m_mutex); + if (! picture.empty ()) picture = URL (picture); + } + + EPG_TAG tag; + memset (&tag, 0, sizeof (EPG_TAG)); + + tag.iUniqueBroadcastId = BroadcastId (e.uuid); + tag.strTitle = PVR_FREEBOX_C_STR (e.title); + tag.iUniqueChannelId = e.channel; + tag.startTime = e.date; + tag.endTime = e.date + e.duration; + tag.strPlotOutline = PVR_FREEBOX_C_STR (e.outline); + tag.strPlot = PVR_FREEBOX_C_STR (e.plot); + tag.strOriginalTitle = NULL; + tag.strCast = NULL; + tag.strDirector = NULL; + tag.strWriter = NULL; + tag.iYear = 0; + tag.strIMDBNumber = NULL; + tag.strIconPath = PVR_FREEBOX_C_STR (picture); +#if 0 // categories mismatch! lookup table? + tag.iGenreType = category & 0xF0; + tag.iGenreSubType = category & 0x0F; + tag.strGenreDescription = NULL; +#else + tag.iGenreType = EPG_GENRE_USE_STRING; + tag.iGenreSubType = 0; + tag.strGenreDescription = PVR_FREEBOX_C_STR (e.category); +#endif + tag.iParentalRating = 0; + tag.iStarRating = 0; + tag.bNotify = false; + tag.iSeriesNumber = e.season; + tag.iEpisodeNumber = e.episode; + tag.iEpisodePartNumber = 0; + tag.strEpisodeName = PVR_FREEBOX_C_STR (e.subtitle); + tag.iFlags = EPG_TAG_FLAG_UNDEFINED; + + PVR->EpgEventStateChange (&tag, state); + //PVR->TransferEpgEntry (handle, &tag); +} + +void PVRFreeboxData::ProcessEvent (const Value & event, unsigned int channel, EPG_EVENT_STATE state) +{ + switch (state) + { + case EPG_EVENT_CREATED: + { + Event e (event, channel); + { + P8PLATFORM::CLockObject lock (m_mutex); + if (m_epg_extended) + { + string query = "/api/v5/tv/epg/programs/" + e.uuid; + m_epg_events.insert (make_pair (e.uuid, e)); + m_epg_queries.push (Query (EVENT, URL (query), channel)); + //XBMC->Log (LOG_INFO, "Queued: '%s'", query.c_str ()); + } + } + ProcessEvent (e, EPG_EVENT_CREATED); + break; + } + + case EPG_EVENT_UPDATED: + { + Event e (event, channel); + { + P8PLATFORM::CLockObject lock (m_mutex); + auto f = m_epg_events.find (e.uuid); + if (f != m_epg_events.end ()) + { + e.date = f->second.date; // !!! + m_epg_events.erase (f); + } + } + ProcessEvent (e, EPG_EVENT_UPDATED); + break; + } + } +} + +void PVRFreeboxData::ProcessChannel (const Value & epg, unsigned int channel) +{ + for (auto i = epg.MemberBegin (); i != epg.MemberEnd (); ++i) + { + const Value & event = i->value; + string uuid = JSON (event, "id"); + + static const string PREFIX = "pluri_"; + if (uuid.find (PREFIX) != 0) continue; + + string query = "/api/v5/tv/epg/programs/" + uuid; + + { + P8PLATFORM::CLockObject lock (m_mutex); + if (m_epg_cache.count (query) > 0) continue; + } + + ProcessEvent (event, channel, EPG_EVENT_CREATED); + + { + P8PLATFORM::CLockObject lock (m_mutex); + m_epg_cache.insert (query); + } + } +} + +void PVRFreeboxData::ProcessFull (const Value & epg) +{ + for (auto i = epg.MemberBegin (); i != epg.MemberEnd (); ++i) + { + string uuid = i->name.GetString (); + ProcessChannel (i->value, ChannelId (uuid)); + } +} + +void * PVRFreeboxData::Process () +{ + while (! IsStopped ()) + { + int delay = 0; + int days = 1; + time_t last = 0; + time_t now = time (NULL); + time_t end = now + days * 24 * 60 * 60; + + { + P8PLATFORM::CLockObject lock (m_mutex); + delay = m_delay; + days = m_epg_days; + last = max (now, m_epg_last); + } + + for (time_t t = last - (last % 3600); t < end; t += 3600) + { + string epoch = to_string (t); + string query = "/api/v5/tv/epg/by_time/" + epoch; + { + P8PLATFORM::CLockObject lock (m_mutex); + m_epg_queries.push (Query (FULL, URL (query))); + //XBMC->Log (LOG_INFO, "Queued: '%s' %d < %d", query.c_str (), t, end); + m_epg_last = t + 3600; + } + } + + Query q; + { + P8PLATFORM::CLockObject lock (m_mutex); + if (! m_epg_queries.empty ()) + { + q = m_epg_queries.front (); + m_epg_queries.pop (); + } + } + + if (q.type != NONE) + { + //cout << q.query << " [" << delay << ']' << endl; + XBMC->Log (LOG_INFO, "Processing: '%s'", q.query.c_str ()); + + Document json; + if (ReadJSON (&json, q.query) && json["success"].GetBool ()) + { + const Value & r = json["result"]; + if (r.IsObject ()) + { + switch (q.type) + { + case FULL : ProcessFull (r); break; + case CHANNEL : ProcessChannel (r, q.channel); break; + case EVENT : ProcessEvent (r, q.channel, EPG_EVENT_UPDATED); break; + } + } + } + } + + Sleep (delay * 1000); + } + + return NULL; +} + +int PVRFreeboxData::GetChannelsAmount () +{ + P8PLATFORM::CLockObject lock (m_mutex); + return m_tv_channels.size (); +} + +PVR_ERROR PVRFreeboxData::GetChannels (ADDON_HANDLE handle, bool radio) +{ + P8PLATFORM::CLockObject lock (m_mutex); + + for (auto i = m_tv_channels.begin (); i != m_tv_channels.end (); ++i) + i->second.GetChannel (handle, radio); + + return PVR_ERROR_NO_ERROR; +} + +int PVRFreeboxData::GetChannelGroupsAmount () +{ + P8PLATFORM::CLockObject lock (m_mutex); + return 0; +} + +PVR_ERROR PVRFreeboxData::GetChannelGroups (ADDON_HANDLE handle, bool radio) +{ + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR PVRFreeboxData::GetChannelGroupMembers (ADDON_HANDLE handle, const PVR_CHANNEL_GROUP & group) +{ + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR PVRFreeboxData::GetChannelStreamProperties (const PVR_CHANNEL * channel, PVR_NAMED_VALUE * properties, unsigned int * count) +{ + if (!channel || !properties || !count) + return PVR_ERROR_SERVER_ERROR; + + if (*count < 2) + return PVR_ERROR_INVALID_PARAMETERS; + + P8PLATFORM::CLockObject lock (m_mutex); + auto f = m_tv_channels.find (channel->iUniqueId); + if (f != m_tv_channels.end ()) + return f->second.GetStreamProperties (m_tv_quality, properties, count); + + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR PVRFreeboxData::GetEPGForChannel (ADDON_HANDLE handle, const PVR_CHANNEL & channel, time_t start, time_t end) +{ + return PVR_ERROR_NO_ERROR; +} + diff --git a/src/PVRFreeboxData.h b/src/PVRFreeboxData.h new file mode 100644 index 0000000..213dc95 --- /dev/null +++ b/src/PVRFreeboxData.h @@ -0,0 +1,203 @@ +#pragma once +/* + * Copyright (C) 2018 Aassif Benassarou + * http://github.com/aassif/pvr.freebox/ + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include +#include +#include +#include "libXBMC_pvr.h" +#include "p8-platform/os.h" +#include "p8-platform/threads/threads.h" +#include "rapidjson/document.h" + +class PVRFreeboxData : + public P8PLATFORM::CThread +{ + protected: + inline static unsigned int ChannelId (const std::string & uuid) + { + return std::stoi (uuid.substr (11)); // uuid-webtv-* + } + + inline static unsigned int BroadcastId (const std::string & uuid) + { + return std::stoi (uuid.substr (6)); // pluri_* + } + + // Channel quality. + enum Quality {DEFAULT = 0, AUTO = 1, HD = 2, SD = 3, LD = 4}; + + class Stream + { + public: + static enum Quality Parse (const std::string &); + static int Score (enum Quality, enum Quality q0); + + enum Quality quality; + std::string url; + + public: + Stream (enum Quality, const std::string &); + }; + + class Channel + { + public: + typedef std::map Streams; + + protected: + bool radio; + std::string uuid; + std::string name; + std::string logo; + int major; + int minor; + std::vector streams; + + protected: + int Find (enum Quality) const; + + public: + Channel (const std::string & uuid, + const std::string & name, + const std::string & logo, + int major, int minor, + const rapidjson::Value & item); + + void GetChannel (ADDON_HANDLE, bool radio) const; + PVR_ERROR GetStreamProperties (enum Quality, PVR_NAMED_VALUE *, unsigned int * count) const; + }; + + // Query types. + enum QueryType {NONE = 0, FULL = 1, CHANNEL = 2, EVENT = 3}; + + class Query + { + public: + QueryType type; + std::string query; + unsigned int channel; + + public: + Query () : type (NONE) {} + + Query (QueryType t, const std::string & q, unsigned int c = 0) : + type (t), query (q), channel (c) {} + }; + + // EPG events. + class Event + { + public: + unsigned int channel; + std::string uuid; + time_t date; + int duration; + std::string title; + std::string subtitle; + int season; + int episode; + std::string category; + std::string picture; + std::string plot; + std::string outline; + + public: + Event (const rapidjson::Value &, unsigned int channel); + }; + + public: + PVRFreeboxData (const std::string & path, int quality, int days, bool extended, int delay); + virtual ~PVRFreeboxData (); + + // Freebox Server. + std::string GetServer () const; + + // Quality setting. + void SetQuality (int); + // MaxDays setting. + void SetDays (int); + // Extended EPG. + void SetExtended (bool); + // Delay setting. + void SetDelay (int); + + int GetChannelsAmount (); + PVR_ERROR GetChannels (ADDON_HANDLE, bool radio); + int GetChannelGroupsAmount (); + PVR_ERROR GetChannelGroups (ADDON_HANDLE, bool radio); + PVR_ERROR GetChannelGroupMembers (ADDON_HANDLE, const PVR_CHANNEL_GROUP &); + PVR_ERROR GetChannelStreamProperties (const PVR_CHANNEL *, PVR_NAMED_VALUE *, unsigned int * count); + PVR_ERROR GetEPGForChannel (ADDON_HANDLE, const PVR_CHANNEL &, time_t start, time_t end); + + protected: + virtual void * Process (); + + // Process JSON channels. + bool ProcessChannels (); + + // Process JSON EPG. + void ProcessFull (const rapidjson::Value & epg); + void ProcessChannel (const rapidjson::Value & epg, unsigned int channel); + void ProcessEvent (const rapidjson::Value & epg, unsigned int channel, EPG_EVENT_STATE); + + // If /api/v5/tv/epg/programs/* queries had a "date", things would be *way* easier! + void ProcessEvent (const Event &, EPG_EVENT_STATE); + + protected: + static bool ReadJSON (rapidjson::Document *, const std::string & url); + + template + inline static T JSON (const rapidjson::Value &); + + template + inline static T JSON (const rapidjson::Value &, const char * name, const T & value = T ()); + + protected: + // Full URL (protocol + server + query). + std::string URL (const std::string & query) const; + + private: + mutable P8PLATFORM::CMutex m_mutex; + std::string m_path; + std::string m_server; + int m_delay; + std::map m_tv_channels; + enum Quality m_tv_quality; + std::queue m_epg_queries; + std::set m_epg_cache; + std::map m_epg_events; + int m_epg_days; + time_t m_epg_last; + bool m_epg_extended; +}; + +template <> inline bool PVRFreeboxData::JSON (const rapidjson::Value & json) {return json.GetBool ();} +template <> inline int PVRFreeboxData::JSON (const rapidjson::Value & json) {return json.GetInt ();} +template <> inline std::string PVRFreeboxData::JSON (const rapidjson::Value & json) {return json.GetString ();} + +template +T PVRFreeboxData::JSON (const rapidjson::Value & json, const char * name, const T & value) +{ + auto f = json.FindMember (name); + return f != json.MemberEnd () ? JSON (f->value) : value; +} + diff --git a/src/client.cpp b/src/client.cpp new file mode 100644 index 0000000..5daca42 --- /dev/null +++ b/src/client.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2018 Aassif Benassarou + * http://github.com/aassif/pvr.freebox/ + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "client.h" +#include "xbmc_pvr_dll.h" +#include "PVRFreeboxData.h" +#include "p8-platform/util/util.h" + +using namespace ADDON; + +#ifdef TARGET_WINDOWS +#define snprintf _snprintf +#ifdef CreateDirectory +#undef CreateDirectory +#endif +#ifdef DeleteFile +#undef DeleteFile +#endif +#endif + +std::string path; +int quality = 1; +bool extended = false; +int delay = 0; +bool init = false; +ADDON_STATUS status = ADDON_STATUS_UNKNOWN; +PVRFreeboxData * data = nullptr; + +CHelper_libXBMC_addon * XBMC = nullptr; +CHelper_libXBMC_pvr * PVR = nullptr; + +extern "C" { + +void ADDON_ReadSettings () +{ + if (! XBMC->GetSetting ("quality", &quality)) quality = 1; + if (! XBMC->GetSetting ("extended", &extended)) extended = false; + if (! XBMC->GetSetting ("delay", &delay)) delay = 0; +} + +ADDON_STATUS ADDON_Create (void * callbacks, void * properties) +{ + if (! callbacks || ! properties) + { + return ADDON_STATUS_UNKNOWN; + } + + PVR_PROPERTIES * p = (PVR_PROPERTIES *) properties; + + XBMC = new CHelper_libXBMC_addon; + if (! XBMC->RegisterMe (callbacks)) + { + SAFE_DELETE (XBMC); + return ADDON_STATUS_PERMANENT_FAILURE; + } + + PVR = new CHelper_libXBMC_pvr; + if (! PVR->RegisterMe (callbacks)) + { + SAFE_DELETE (PVR); + SAFE_DELETE (XBMC); + return ADDON_STATUS_PERMANENT_FAILURE; + } + + XBMC->Log (LOG_DEBUG, "%s - Creating the Freebox TV add-on", __FUNCTION__); + + status = ADDON_STATUS_UNKNOWN; +// g_strUserPath = p->strUserPath; +// g_strClientPath = p->strClientPath; + +/* + if (! XBMC->DirectoryExists (g_strUserPath.c_str ())) + { + XBMC->CreateDirectory (g_strUserPath.c_str ()); + } +*/ + ADDON_ReadSettings (); + + data = new PVRFreeboxData (p->strClientPath, quality, p->iEpgMaxDays, extended, delay); + status = ADDON_STATUS_OK; + init = true; + + return status; +} + +ADDON_STATUS ADDON_GetStatus () +{ + return status; +} + +void ADDON_Destroy () +{ + delete data; + status = ADDON_STATUS_UNKNOWN; + init = false; +} + +ADDON_STATUS ADDON_SetSetting (const char * name, const void * value) +{ + if (data) + { + if (std::string (name) == "quality") + data->SetQuality (*((int *) value)); + + if (std::string (name) == "delay") + data->SetDelay (*((int *) value)); + } + + return ADDON_STATUS_OK; +} + +/*********************************************************** + * PVR Client AddOn specific public library functions + ***********************************************************/ + +void OnSystemSleep () +{ +} + +void OnSystemWake () +{ +} + +void OnPowerSavingActivated () +{ +} + +void OnPowerSavingDeactivated () +{ +} + +PVR_ERROR GetAddonCapabilities (PVR_ADDON_CAPABILITIES * caps) +{ + caps->bSupportsEPG = true; + caps->bSupportsTV = true; + caps->bSupportsRadio = false; + caps->bSupportsChannelGroups = false; + caps->bSupportsRecordings = false; + caps->bSupportsRecordingsRename = false; + caps->bSupportsRecordingsLifetimeChange = false; + caps->bSupportsDescrambleInfo = false; + caps->bSupportsAsyncEPGTransfer = true; + + return PVR_ERROR_NO_ERROR; +} + +const char * GetBackendName () +{ + return "Freebox TV"; +} + +const char * GetBackendVersion () +{ + return "1.0a"; +} + +const char * GetConnectionString () +{ + return "1.0a"; +} + +const char * GetBackendHostname () +{ + if (data) + { + static std::string server; + server = data->GetServer (); + return server.c_str (); + } + + return NULL; +} + +PVR_ERROR SetEPGTimeFrame (int days) +{ + if (data) + data->SetDays (days); + + return PVR_ERROR_NO_ERROR; +} + +PVR_ERROR GetEPGForChannel (ADDON_HANDLE handle, const PVR_CHANNEL & channel, time_t start, time_t end) +{ + if (data) + return data->GetEPGForChannel (handle, channel, start, end); + + return PVR_ERROR_SERVER_ERROR; +} + +int GetChannelsAmount () +{ + if (data) + return data->GetChannelsAmount (); + + return -1; +} + +PVR_ERROR GetChannels (ADDON_HANDLE handle, bool radio) +{ + if (data) + return data->GetChannels (handle, radio); + + return PVR_ERROR_SERVER_ERROR; +} + +PVR_ERROR GetChannelStreamProperties (const PVR_CHANNEL * channel, PVR_NAMED_VALUE * properties, unsigned int * count) +{ + if (data) + return data->GetChannelStreamProperties (channel, properties, count); + + return PVR_ERROR_SERVER_ERROR; +} + +int GetChannelGroupsAmount () +{ + if (data) + return data->GetChannelGroupsAmount (); + + return -1; +} + +PVR_ERROR GetChannelGroups (ADDON_HANDLE handle, bool radio) +{ + if (data) + return data->GetChannelGroups (handle, radio); + + return PVR_ERROR_SERVER_ERROR; +} + +PVR_ERROR GetChannelGroupMembers (ADDON_HANDLE handle, const PVR_CHANNEL_GROUP & group) +{ + if (data) + return data->GetChannelGroupMembers (handle, group); + + return PVR_ERROR_SERVER_ERROR; +} + +PVR_ERROR GetDriveSpace (long long *, long long *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR SignalStatus (PVR_SIGNAL_STATUS &) {return PVR_ERROR_NOT_IMPLEMENTED;} + +/** UNUSED API FUNCTIONS */ +bool CanPauseStream () {return false;} +int GetRecordingsAmount (bool) {return -1;} +PVR_ERROR GetRecordings (ADDON_HANDLE, bool) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetRecordingStreamProperties (const PVR_RECORDING *, PVR_NAMED_VALUE *, unsigned int *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR OpenDialogChannelScan () {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR CallMenuHook (const PVR_MENUHOOK &, const PVR_MENUHOOK_DATA &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR DeleteChannel (const PVR_CHANNEL &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR RenameChannel (const PVR_CHANNEL &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR OpenDialogChannelSettings (const PVR_CHANNEL &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR OpenDialogChannelAdd (const PVR_CHANNEL &) {return PVR_ERROR_NOT_IMPLEMENTED;} +void CloseLiveStream () {} +bool OpenRecordedStream (const PVR_RECORDING &) {return false;} +bool OpenLiveStream (const PVR_CHANNEL &) {return false;} +void CloseRecordedStream () {} +int ReadRecordedStream (unsigned char *, unsigned int) {return 0;} +long long SeekRecordedStream (long long, int) {return 0;} +long long LengthRecordedStream () {return 0;} +void DemuxReset () {} +void DemuxFlush () {} +int ReadLiveStream (unsigned char *, unsigned int) {return 0;} +long long SeekLiveStream (long long, int) {return -1;} +long long LengthLiveStream () {return -1;} +PVR_ERROR DeleteRecording (const PVR_RECORDING &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR RenameRecording (const PVR_RECORDING &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR SetRecordingPlayCount (const PVR_RECORDING &, int) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR SetRecordingLastPlayedPosition (const PVR_RECORDING &, int) {return PVR_ERROR_NOT_IMPLEMENTED;} +int GetRecordingLastPlayedPosition (const PVR_RECORDING &) {return -1;} +PVR_ERROR GetRecordingEdl (const PVR_RECORDING &, PVR_EDL_ENTRY [], int *) {return PVR_ERROR_NOT_IMPLEMENTED;}; +PVR_ERROR GetTimerTypes (PVR_TIMER_TYPE [], int *) {return PVR_ERROR_NOT_IMPLEMENTED;} +int GetTimersAmount () {return -1;} +PVR_ERROR GetTimers (ADDON_HANDLE) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR AddTimer (const PVR_TIMER &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR DeleteTimer (const PVR_TIMER &, bool) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR UpdateTimer (const PVR_TIMER &) {return PVR_ERROR_NOT_IMPLEMENTED;} +void DemuxAbort () {} +DemuxPacket* DemuxRead () {return NULL;} +bool IsTimeshifting () {return false;} +bool IsRealTimeStream () {return true;} +void PauseStream (bool) {} +bool CanSeekStream () {return false;} +bool SeekTime (double, bool, double *) {return false;} +void SetSpeed (int) {}; +PVR_ERROR UndeleteRecording (const PVR_RECORDING &) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR DeleteAllRecordingsFromTrash () {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetDescrambleInfo (PVR_DESCRAMBLE_INFO *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR SetRecordingLifetime (const PVR_RECORDING *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetStreamTimes (PVR_STREAM_TIMES *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetStreamProperties (PVR_STREAM_PROPERTIES *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR IsEPGTagRecordable (const EPG_TAG *, bool *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR IsEPGTagPlayable (const EPG_TAG *, bool *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetEPGTagStreamProperties (const EPG_TAG *, PVR_NAMED_VALUE *, unsigned int *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetEPGTagEdl (const EPG_TAG *, PVR_EDL_ENTRY [], int *) {return PVR_ERROR_NOT_IMPLEMENTED;} +PVR_ERROR GetStreamReadChunkSize (int *) {return PVR_ERROR_NOT_IMPLEMENTED;} + +} // extern "C" diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..25210ee --- /dev/null +++ b/src/client.h @@ -0,0 +1,28 @@ +#pragma once +/* + * Copyright (C) 2018 Aassif Benassarou + * http://github.com/aassif/pvr.freebox/ + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * http://www.gnu.org/copyleft/gpl.html + * + */ + +#include "libXBMC_addon.h" +#include "libXBMC_pvr.h" + +extern ADDON::CHelper_libXBMC_addon * XBMC; +extern CHelper_libXBMC_pvr * PVR; +