summaryrefslogtreecommitdiff
path: root/libtmdb
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2014-09-19 23:52:30 +0000
committerrandomdan <randomdan@localhost>2014-09-19 23:52:30 +0000
commit28691c29e93fbc4fdb5ae4866287282840fba021 (patch)
tree8b83ca80afc3f3a5d11b04a56c440d2aa38f4e16 /libtmdb
parentSupport muxing directly in storage (diff)
downloadp2pvr-28691c29e93fbc4fdb5ae4866287282840fba021.tar.bz2
p2pvr-28691c29e93fbc4fdb5ae4866287282840fba021.tar.xz
p2pvr-28691c29e93fbc4fdb5ae4866287282840fba021.zip
First bash at a slicer ice proxy for TMDb and an untested instantiation in p2pvr's core adapter
Diffstat (limited to 'libtmdb')
-rw-r--r--libtmdb/.ycm_extra_conf.py114
-rw-r--r--libtmdb/Jamfile.jam67
-rw-r--r--libtmdb/conversions.cpp35
-rw-r--r--libtmdb/httpClient.cpp77
-rw-r--r--libtmdb/httpClient.h73
-rw-r--r--libtmdb/samples/movie_550.json57
-rw-r--r--libtmdb/samples/searchMulti_breakingBad.json39
-rw-r--r--libtmdb/samples/tv_1396.json89
-rw-r--r--libtmdb/testCallMockApi.cpp26
-rw-r--r--libtmdb/testFormatUrls.cpp58
-rw-r--r--libtmdb/testModels.cpp117
-rw-r--r--libtmdb/tmdb-api.ice21
-rw-r--r--libtmdb/tmdb-common.ice90
-rw-r--r--libtmdb/tmdb-models.ice269
-rw-r--r--libtmdb/tmdb-proxy.cpp46
-rw-r--r--libtmdb/tmdb-proxy.h17
16 files changed, 1195 insertions, 0 deletions
diff --git a/libtmdb/.ycm_extra_conf.py b/libtmdb/.ycm_extra_conf.py
new file mode 100644
index 0000000..2f379fc
--- /dev/null
+++ b/libtmdb/.ycm_extra_conf.py
@@ -0,0 +1,114 @@
+import os
+import ycm_core
+
+flags = [
+'-Wall',
+'-Wextra',
+'-Werror',
+'-Wc++98-compat',
+'-Wno-long-long',
+'-Wno-variadic-macros',
+'-fexceptions',
+'-DNDEBUG',
+'-std=c++11',
+'-x',
+'c++',
+'-I',
+'.',
+'-isystem',
+'/usr/include/Ice',
+'-isystem',
+'/usr/include/slicer',
+'-isystem',
+'bin/gcc-4.8.3/debug/slicer-yes',
+'-isystem',
+'/usr/include',
+'-isystem',
+'/usr/include/glibmm-2.4',
+]
+
+compilation_database_folder = ''
+
+if os.path.exists( compilation_database_folder ):
+ database = ycm_core.CompilationDatabase( compilation_database_folder )
+else:
+ database = None
+
+SOURCE_EXTENSIONS = [ '.cpp', '.cxx', '.cc', '.c', '.m', '.mm' ]
+
+def DirectoryOfThisScript():
+ return os.path.dirname( os.path.abspath( __file__ ) )
+
+
+def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
+ if not working_directory:
+ return list( flags )
+ new_flags = []
+ make_next_absolute = False
+ path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
+ for flag in flags:
+ new_flag = flag
+
+ if make_next_absolute:
+ make_next_absolute = False
+ if not flag.startswith( '/' ):
+ new_flag = os.path.join( working_directory, flag )
+
+ for path_flag in path_flags:
+ if flag == path_flag:
+ make_next_absolute = True
+ break
+
+ if flag.startswith( path_flag ):
+ path = flag[ len( path_flag ): ]
+ new_flag = path_flag + os.path.join( working_directory, path )
+ break
+
+ if new_flag:
+ new_flags.append( new_flag )
+ return new_flags
+
+
+def IsHeaderFile( filename ):
+ extension = os.path.splitext( filename )[ 1 ]
+ return extension in [ '.h', '.hxx', '.hpp', '.hh' ]
+
+
+def GetCompilationInfoForFile( filename ):
+ # The compilation_commands.json file generated by CMake does not have entries
+ # for header files. So we do our best by asking the db for flags for a
+ # corresponding source file, if any. If one exists, the flags for that file
+ # should be good enough.
+ if IsHeaderFile( filename ):
+ basename = os.path.splitext( filename )[ 0 ]
+ for extension in SOURCE_EXTENSIONS:
+ replacement_file = basename + extension
+ if os.path.exists( replacement_file ):
+ compilation_info = database.GetCompilationInfoForFile(
+ replacement_file )
+ if compilation_info.compiler_flags_:
+ return compilation_info
+ return None
+ return database.GetCompilationInfoForFile( filename )
+
+
+def FlagsForFile( filename, **kwargs ):
+ if database:
+ # Bear in mind that compilation_info.compiler_flags_ does NOT return a
+ # python list, but a "list-like" StringVec object
+ compilation_info = GetCompilationInfoForFile( filename )
+ if not compilation_info:
+ return None
+
+ final_flags = MakeRelativePathsInFlagsAbsolute(
+ compilation_info.compiler_flags_,
+ compilation_info.compiler_working_dir_ )
+ else:
+ relative_to = DirectoryOfThisScript()
+ final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
+
+ return {
+ 'flags': final_flags,
+ 'do_cache': True
+ }
+
diff --git a/libtmdb/Jamfile.jam b/libtmdb/Jamfile.jam
new file mode 100644
index 0000000..2e51377
--- /dev/null
+++ b/libtmdb/Jamfile.jam
@@ -0,0 +1,67 @@
+import testing ;
+
+alias glibmm : : : :
+ <cflags>"`pkg-config --cflags glibmm-2.4`"
+ <linkflags>"`pkg-config --libs glibmm-2.4`"
+ ;
+lib slicer : : : : <include>/usr/include/slicer ;
+lib slicer-json : : : : <include>/usr/include/slicer ;
+lib Ice ;
+lib IceUtil ;
+lib jsonpp ;
+lib pthread ;
+lib boost_system ;
+lib boost_filesystem ;
+lib curl ;
+
+lib tmdb :
+ [ glob *.cpp *.ice : test*.cpp ] :
+ <library>Ice
+ <library>IceUtil
+ <library>jsonpp
+ <library>pthread
+ <library>slicer
+ <library>slicer-json
+ <library>boost_system
+ <library>glibmm
+ <library>curl
+ <slicer>yes
+ : :
+ <include>.
+ <library>Ice
+ <library>IceUtil
+ <library>pthread
+ ;
+
+unit-test testModels :
+ [ glob testModels.cpp ]
+ :
+ <library>tmdb
+ <library>slicer
+ <library>slicer-json
+ <library>boost_filesystem
+ <library>boost_system
+ <implicit-dependency>tmdb
+ ;
+
+unit-test testFormatUrls :
+ [ glob testFormatUrls.cpp ]
+ :
+ <library>tmdb
+ <library>slicer
+ <library>slicer-json
+ <library>boost_filesystem
+ <library>boost_system
+ <implicit-dependency>tmdb
+ ;
+
+unit-test testCallMockApi :
+ [ glob testCallMockApi.cpp ]
+ :
+ <library>tmdb
+ <library>slicer
+ <library>slicer-json
+ <library>boost_filesystem
+ <library>boost_system
+ <implicit-dependency>tmdb
+ ;
diff --git a/libtmdb/conversions.cpp b/libtmdb/conversions.cpp
new file mode 100644
index 0000000..488419f
--- /dev/null
+++ b/libtmdb/conversions.cpp
@@ -0,0 +1,35 @@
+#include <tmdb-common.h>
+#include <boost/numeric/conversion/cast.hpp>
+#include <stdexcept>
+
+#define SHORT(x) boost::numeric_cast< ::Ice::Short >(x)
+
+namespace Slicer {
+ std::string
+ dateToString(const ::TMDb::Date & in)
+ {
+ char buf[BUFSIZ];
+ struct tm tm({ 0, 0, 0, in.Day, in.Month, in.Year, 0, 0, 0
+#ifdef _BSD_SOURCE
+ , 0, 0
+#endif
+ });
+ mktime(&tm);
+ auto len = strftime(buf, BUFSIZ, "%Y-%m-%d", &tm);
+ return std::string(buf, len);
+ }
+
+ ::TMDb::Date
+ stringToDate(const std::string & in)
+ {
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ auto end = strptime(in.c_str(), "%Y-%m-%d", &tm);
+ mktime(&tm);
+ if (!end || *end) {
+ throw std::runtime_error("Invalid date string: " + in);
+ }
+ return { SHORT(1900 + tm.tm_year), SHORT(1 + tm.tm_mon), SHORT(tm.tm_mday) };
+ }
+
+}
diff --git a/libtmdb/httpClient.cpp b/libtmdb/httpClient.cpp
new file mode 100644
index 0000000..93fdd76
--- /dev/null
+++ b/libtmdb/httpClient.cpp
@@ -0,0 +1,77 @@
+#include "httpClient.h"
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <curl/curl.h>
+#include <tmdb-api.h>
+
+namespace TMDb {
+ HttpClient::HttpClient(const std::string & bu, const std::string & k) :
+ BaseURL(bu),
+ ApiKey(k)
+ {
+ }
+
+ void
+ HttpClient::packParams(boost::format &)
+ {
+ }
+
+ void
+ HttpClient::appendQueryParameters(std::string & path, const Parameters::value_type & nvp) const
+ {
+ path += nvp.first;
+ path += "=";
+ auto ev = curl_easy_escape(NULL, nvp.second.value->c_str(), nvp.second.value->size());
+ path += ev;
+ curl_free(ev);
+ }
+
+ void
+ HttpClient::appendQueryParameters(std::string & path, const Parameters & parameters) const
+ {
+ path += "?";
+ appendQueryParameters(path, { "apikey", ApiKey });
+ BOOST_FOREACH(const auto & nvp, parameters) {
+ if (nvp.second.value) {
+ path += "&";
+ appendQueryParameters(path, nvp);
+ }
+ }
+ }
+
+ static size_t
+ appendString(void * contents, size_t size, size_t nmemb, void * userp)
+ {
+ auto data = static_cast<Glib::ustring *>(userp);
+ data->append(static_cast<const char *>(contents), size * nmemb);
+ return size * nmemb;
+ }
+
+ json::Value
+ HttpClient::FetchJson(const std::string & path) const
+ {
+ Glib::ustring jsonData;
+
+ struct curl_slist *headers = NULL;
+ curl_slist_append(headers, "Accept: application/json");
+
+ CURL * curl_handle = curl_easy_init();
+ curl_easy_setopt(curl_handle, CURLOPT_URL, path.c_str());
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, appendString);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&jsonData);
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers);
+ CURLcode res = curl_easy_perform(curl_handle);
+
+ long http_code = 0;
+ curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &http_code);
+ curl_easy_cleanup(curl_handle);
+ if (res != CURLE_OK) {
+ throw TMDb::HttpException(http_code);
+ }
+
+ Glib::ustring::const_iterator itr = jsonData.begin();
+ return json::parseValue(itr);
+ }
+}
+
diff --git a/libtmdb/httpClient.h b/libtmdb/httpClient.h
new file mode 100644
index 0000000..1b50b7e
--- /dev/null
+++ b/libtmdb/httpClient.h
@@ -0,0 +1,73 @@
+#include <boost/variant.hpp>
+#include <jsonpp.h>
+#include <slicer/slicer.h>
+#include <json/serializer.h>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+
+namespace TMDb {
+ class HttpClient {
+ public:
+ class ParameterValue {
+ public:
+ ParameterValue(const std::string & v) :
+ value(v)
+ {
+ }
+
+ template <typename V>
+ ParameterValue(const V & v) :
+ value(boost::lexical_cast<std::string>(v))
+ {
+ }
+
+ template <typename V>
+ ParameterValue(const IceUtil::Optional<V> & v) :
+ value(v ? boost::lexical_cast<std::string>(*v) : IceUtil::Optional<std::string>())
+ {
+ }
+
+ IceUtil::Optional<std::string> value;
+ };
+ typedef std::map<std::string, ParameterValue> Parameters;
+
+ protected:
+ HttpClient(const std::string & baseUrl, const std::string & apikey);
+
+ template <typename ... Params>
+ std::string
+ GetUrl(const std::string & pathFormat, const Params & ... params, const Parameters & parameters) const
+ {
+ boost::format path(pathFormat);
+ packParams(path, params...);
+ std::string pathStr(path.str());
+ appendQueryParameters(pathStr, parameters);
+ return BaseURL + pathStr;
+ }
+
+ template <typename ReturnModel, typename ... Params>
+ IceInternal::Handle<ReturnModel>
+ GetData(const std::string & pathFormat, const Params & ... params, const Parameters & parameters) const
+ {
+ json::Value data = FetchJson(GetUrl<Params...>(pathFormat, params..., parameters));
+ return Slicer::Deserialize<Slicer::JsonValue, ReturnModel>(data);
+ }
+
+ static void packParams(boost::format &);
+
+ template <typename T, typename ... Ts>
+ static void packParams(boost::format & fmt, const T & param, const Ts & ... params)
+ {
+ fmt % param;
+ packParams(fmt, params...);
+ }
+
+ void appendQueryParameters(std::string & path, const Parameters & parameters) const;
+ void appendQueryParameters(std::string & path, const Parameters::value_type & nvp) const;
+ json::Value FetchJson(const std::string & path) const;
+
+ const std::string BaseURL;
+ const std::string ApiKey;
+ };
+};
+
diff --git a/libtmdb/samples/movie_550.json b/libtmdb/samples/movie_550.json
new file mode 100644
index 0000000..fe0b4a8
--- /dev/null
+++ b/libtmdb/samples/movie_550.json
@@ -0,0 +1,57 @@
+{
+ "adult": false,
+ "backdrop_path": "/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg",
+ "belongs_to_collection": null,
+ "budget": 63000000,
+ "genres": [
+ {
+ "id": 28,
+ "name": "Action"
+ },
+ {
+ "id": 18,
+ "name": "Drama"
+ },
+ {
+ "id": 53,
+ "name": "Thriller"
+ }
+ ],
+ "homepage": "http://www.fightclub.com/",
+ "id": 550,
+ "imdb_id": "tt0137523",
+ "original_title": "Fight Club",
+ "overview": "A ticking-time-bomb insomniac and a slippery soap salesman channel primal male aggression into a shocking new form of therapy. Their concept catches on, with underground \"fight clubs\" forming in every town, until an eccentric gets in the way and ignites an out-of-control spiral toward oblivion.",
+ "popularity": 61151.745000000003,
+ "poster_path": "/2lECpi35Hnbpa4y46JX0aY3AWTy.jpg",
+ "production_companies": [
+ {
+ "name": "20th Century Fox",
+ "id": 25
+ }
+ ],
+ "production_countries": [
+ {
+ "iso_3166_1": "DE",
+ "name": "Germany"
+ },
+ {
+ "iso_3166_1": "US",
+ "name": "United States of America"
+ }
+ ],
+ "release_date": "1999-10-15",
+ "revenue": 100853753,
+ "runtime": 139,
+ "spoken_languages": [
+ {
+ "iso_639_1": "en",
+ "name": "English"
+ }
+ ],
+ "status": "Released",
+ "tagline": "How much can you know about yourself if you've never been in a fight?",
+ "title": "Fight Club",
+ "vote_average": 9.0999999999999996,
+ "vote_count": 174
+}
diff --git a/libtmdb/samples/searchMulti_breakingBad.json b/libtmdb/samples/searchMulti_breakingBad.json
new file mode 100644
index 0000000..fb0ec82
--- /dev/null
+++ b/libtmdb/samples/searchMulti_breakingBad.json
@@ -0,0 +1,39 @@
+{
+ "page": 1,
+ "results": [
+ {
+ "backdrop_path": "/sIJyCJedGlZf1TId41gCtkblBGo.jpg",
+ "id": 1396,
+ "original_name": "Breaking Bad",
+ "first_air_date": "2008-01-19",
+ "poster_path": "/4yMXf3DW6oCL0lVPZaZM2GypgwE.jpg",
+ "popularity": 12.2665228348243,
+ "name": "Breaking Bad",
+ "vote_average": 9,
+ "vote_count": 68,
+ "media_type": "tv"
+ },
+ {
+ "adult": false,
+ "backdrop_path": "/mMKahLSpwb9Yj2B0tB6vku3tkGy.jpg",
+ "id": 239459,
+ "original_title": "No Half Measures: Creating the Final Season of Breaking Bad",
+ "release_date": "2013-11-26",
+ "poster_path": "/8OixSR45U5dbqv8F0tlspmTbXxN.jpg",
+ "popularity": 2.8761099924108,
+ "title": "No Half Measures: Creating the Final Season of Breaking Bad",
+ "vote_average": 8.5,
+ "vote_count": 5,
+ "media_type": "movie"
+ },
+ {
+ "adult": false,
+ "id": 287,
+ "name": "Brad Pitt",
+ "profile_path": "/w8zJQuN7tzlm6FY9mfGKihxp3Cb.jpg",
+ "media_type": "person"
+ }
+ ],
+ "total_pages": 1,
+ "total_results": 3
+}
diff --git a/libtmdb/samples/tv_1396.json b/libtmdb/samples/tv_1396.json
new file mode 100644
index 0000000..6043750
--- /dev/null
+++ b/libtmdb/samples/tv_1396.json
@@ -0,0 +1,89 @@
+{
+ "backdrop_path": "/sIJyCJedGlZf1TId41gCtkblBGo.jpg",
+ "created_by": [
+ {
+ "id": 66633,
+ "name": "Vince Gilligan",
+ "profile_path": "/rLSUjr725ez1cK7SKVxC9udO03Y.jpg"
+ }
+ ],
+ "episode_run_time": [
+ 45,
+ 47
+ ],
+ "first_air_date": "2008-01-19",
+ "genres": [
+ {
+ "id": 18,
+ "name": "Drama"
+ }
+ ],
+ "homepage": "http://www.amctv.com/shows/breaking-bad",
+ "id": 1396,
+ "in_production": false,
+ "languages": [
+ "en",
+ "de",
+ "ro",
+ "es",
+ "fa"
+ ],
+ "last_air_date": "2013-09-29",
+ "name": "Breaking Bad",
+ "networks": [
+ {
+ "id": 174,
+ "name": "AMC"
+ }
+ ],
+ "number_of_episodes": 62,
+ "number_of_seasons": 5,
+ "original_name": "Breaking Bad",
+ "origin_country": [
+ "US"
+ ],
+ "overview": "Breaking Bad is an American crime drama television series created and produced by Vince Gilligan. Set and produced in Albuquerque, New Mexico, Breaking Bad is the story of Walter White, a struggling high school chemistry teacher who is diagnosed with inoperable lung cancer at the beginning of the series. He turns to a life of crime, producing and selling methamphetamine, in order to secure his family's financial future before he dies, teaming with his former student, Jesse Pinkman. Heavily serialized, the series is known for positioning its characters in seemingly inextricable corners and has been labeled a contemporary western by its creator.",
+ "popularity": 7.68402647476576,
+ "poster_path": "/4yMXf3DW6oCL0lVPZaZM2GypgwE.jpg",
+ "seasons": [
+ {
+ "air_date": "2009-02-17",
+ "id": 3577,
+ "poster_path": "/spPmYZAq2xLKQOEIdBPkhiRxrb9.jpg",
+ "season_number": 0
+ },
+ {
+ "air_date": "2008-01-19",
+ "id": 3572,
+ "poster_path": "/dHCYpEoHEjAV6Xt3eyNthkdLRl3.jpg",
+ "season_number": 1
+ },
+ {
+ "air_date": "2009-03-08",
+ "id": 3573,
+ "poster_path": "/ww6cDy0dhrVEdMqielNEsYz96mg.jpg",
+ "season_number": 2
+ },
+ {
+ "air_date": "2010-03-21",
+ "id": 3575,
+ "poster_path": "/rINvcsYHUprsx9L8zNr5JltALda.jpg",
+ "season_number": 3
+ },
+ {
+ "air_date": "2011-07-17",
+ "id": 3576,
+ "poster_path": "/ngnE7FFQqrrLgK3yVsv3kjwtQMZ.jpg",
+ "season_number": 4
+ },
+ {
+ "air_date": "2012-07-15",
+ "id": 3578,
+ "poster_path": "/ih1JKNxEzW56azeFpEQmdu4poA4.jpg",
+ "season_number": 5
+ }
+ ],
+ "status": "Ended",
+ "vote_average": 9,
+ "vote_count": 72
+}
diff --git a/libtmdb/testCallMockApi.cpp b/libtmdb/testCallMockApi.cpp
new file mode 100644
index 0000000..08719ca
--- /dev/null
+++ b/libtmdb/testCallMockApi.cpp
@@ -0,0 +1,26 @@
+#define BOOST_TEST_MODULE CallMockApi
+#include <boost/test/included/unit_test.hpp>
+
+#include "tmdb-proxy.h"
+
+const std::string MockBase = "http://private-5513-themoviedb.apiary-mock.com/3";
+const std::string ApiKey = "48b32823d2b60c5c1085af36daed03fa";
+
+BOOST_AUTO_TEST_CASE( search_tmdb )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto resp = test.SearchMulti("breaking bad", 0, Ice::Current());
+ BOOST_REQUIRE_EQUAL(3, resp->Results.size());
+ BOOST_REQUIRE_EQUAL(3, resp->TotalResults);
+ BOOST_REQUIRE_EQUAL(1396, resp->Results[0]->Id);
+ BOOST_REQUIRE_EQUAL(239459, resp->Results[1]->Id);
+ BOOST_REQUIRE_EQUAL(19050, resp->Results[2]->Id);
+}
+
+BOOST_AUTO_TEST_CASE( get_movie )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto resp = test.GetMovie(1396, Ice::Current());
+ BOOST_REQUIRE_EQUAL("Fight Club", resp->Title);
+}
+
diff --git a/libtmdb/testFormatUrls.cpp b/libtmdb/testFormatUrls.cpp
new file mode 100644
index 0000000..f3aa2a8
--- /dev/null
+++ b/libtmdb/testFormatUrls.cpp
@@ -0,0 +1,58 @@
+#define BOOST_TEST_MODULE FormatUrls
+#include <boost/test/included/unit_test.hpp>
+
+#define private public
+#define protected public
+
+#include "tmdb-proxy.h"
+
+const std::string MockBase = "http://private-5513-themoviedb.apiary-mock.com/3";
+const std::string ApiKey = "48b32823d2b60c5c1085af36daed03fa";
+
+BOOST_AUTO_TEST_CASE( initialize_simple_proxy )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_simple )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl<int>("/something/%d", 23, { });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something/23?apikey=48b32823d2b60c5c1085af36daed03fa", url);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_query )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl<int>("/something/%d", 23, { { "page", 5 } });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something/23?apikey=48b32823d2b60c5c1085af36daed03fa&page=5", url);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_multiple )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl("/something", { { "page", 5}, {"query", "string" } });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something?apikey=48b32823d2b60c5c1085af36daed03fa&page=5&query=string", url);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_escaped )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl("/something", { { "query", "sample string" } });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something?apikey=48b32823d2b60c5c1085af36daed03fa&query=sample%20string", url);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_optionalvalue )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl("/something", { { "query", IceUtil::Optional<Ice::Int>(10) } });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something?apikey=48b32823d2b60c5c1085af36daed03fa&query=10", url);
+}
+
+BOOST_AUTO_TEST_CASE( format_url_optionalnovalue )
+{
+ TMDb::Proxy test(MockBase, ApiKey);
+ auto url = test.GetUrl("/something", { { "query", IceUtil::Optional<Ice::Int>() } });
+ BOOST_REQUIRE_EQUAL("http://private-5513-themoviedb.apiary-mock.com/3/something?apikey=48b32823d2b60c5c1085af36daed03fa", url);
+}
+
diff --git a/libtmdb/testModels.cpp b/libtmdb/testModels.cpp
new file mode 100644
index 0000000..19e1df7
--- /dev/null
+++ b/libtmdb/testModels.cpp
@@ -0,0 +1,117 @@
+#define BOOST_TEST_MODULE Deserialize
+#include <boost/test/included/unit_test.hpp>
+
+#include <tmdb-models.h>
+#include <slicer/slicer.h>
+#include <slicer/json/serializer.h>
+
+namespace std {
+ std::ostream &
+ operator<<(std::ostream & o, const TMDb::Date & d)
+ {
+ o << d.Year << '-' << d.Month << '-' << d.Day;
+ return o;
+ }
+};
+
+BOOST_AUTO_TEST_CASE( deserialize_searchmulti_json )
+{
+ auto results = Slicer::Deserialize<Slicer::JsonFile, TMDb::SearchMultiResults>("samples/searchMulti_breakingBad.json");
+ BOOST_REQUIRE_EQUAL(1, results->Page);
+ BOOST_REQUIRE_EQUAL(1, results->TotalPages);
+ BOOST_REQUIRE_EQUAL(3, results->TotalResults);
+
+ BOOST_REQUIRE_EQUAL("::TMDb::SearchMatchTv", results->Results[0]->ice_id());
+ auto tv = TMDb::SearchMatchTvPtr::dynamicCast(results->Results[0]);
+ BOOST_REQUIRE_EQUAL("Breaking Bad", tv->Name);
+ BOOST_REQUIRE_EQUAL("/4yMXf3DW6oCL0lVPZaZM2GypgwE.jpg", tv->PosterPath);
+ BOOST_REQUIRE_EQUAL(1396, tv->Id);
+
+ BOOST_REQUIRE_EQUAL("::TMDb::SearchMatchMovie", results->Results[1]->ice_id());
+ auto movie = TMDb::SearchMatchMoviePtr::dynamicCast(results->Results[1]);
+ BOOST_REQUIRE_EQUAL("No Half Measures: Creating the Final Season of Breaking Bad", movie->Title);
+ BOOST_REQUIRE_EQUAL("/8OixSR45U5dbqv8F0tlspmTbXxN.jpg", movie->PosterPath);
+ BOOST_REQUIRE_EQUAL(239459, movie->Id);
+
+ BOOST_REQUIRE_EQUAL("::TMDb::SearchMatchPerson", results->Results[2]->ice_id());
+ auto person = TMDb::SearchMatchPersonPtr::dynamicCast(results->Results[2]);
+ BOOST_REQUIRE_EQUAL("Brad Pitt", person->Name);
+ BOOST_REQUIRE_EQUAL(287, person->Id);
+}
+
+BOOST_AUTO_TEST_CASE( deserialize_movie_json )
+{
+ auto movie = Slicer::Deserialize<Slicer::JsonFile, TMDb::Movie>("samples/movie_550.json");
+ BOOST_REQUIRE_EQUAL(550, movie->Id);
+ BOOST_REQUIRE_EQUAL(false, movie->Adult);
+ BOOST_REQUIRE_EQUAL("/8uO0gUM8aNqYLs1OsTBQiXu0fEv.jpg", movie->BackdropPath);
+ BOOST_REQUIRE_EQUAL(3, movie->Genres.size());
+ BOOST_REQUIRE_EQUAL(28, movie->Genres.front().Id);
+ BOOST_REQUIRE_EQUAL("Action", movie->Genres.front().Name);
+ BOOST_REQUIRE_EQUAL(63000000, movie->Budget);
+ BOOST_REQUIRE_EQUAL("http://www.fightclub.com/", movie->HomePage);
+ BOOST_REQUIRE_EQUAL("tt0137523", movie->ImdbId);
+ BOOST_REQUIRE_EQUAL("Fight Club", movie->OriginalTitle);
+ BOOST_REQUIRE_CLOSE(61151.75, movie->Popularity, 0.1);
+ BOOST_REQUIRE_EQUAL("/2lECpi35Hnbpa4y46JX0aY3AWTy.jpg", movie->PosterPath);
+ BOOST_REQUIRE_EQUAL(295, movie->Overview.length());
+ BOOST_REQUIRE_EQUAL(1, movie->ProductionCompanies.size());
+ BOOST_REQUIRE_EQUAL(25, movie->ProductionCompanies.front().Id);
+ BOOST_REQUIRE_EQUAL("20th Century Fox", movie->ProductionCompanies.front().Name);
+ BOOST_REQUIRE_EQUAL(2, movie->ProductionCountries.size());
+ BOOST_REQUIRE_EQUAL("DE", movie->ProductionCountries.front().Id);
+ BOOST_REQUIRE_EQUAL("Germany", movie->ProductionCountries.front().Name);
+ BOOST_REQUIRE_EQUAL(TMDb::Date({1999, 10, 15}), movie->ReleaseDate);
+ BOOST_REQUIRE_EQUAL(1, movie->SpokenLanguages.size());
+ BOOST_REQUIRE_EQUAL("en", movie->SpokenLanguages.front().Id);
+ BOOST_REQUIRE_EQUAL("English", movie->SpokenLanguages.front().Name);
+ BOOST_REQUIRE_EQUAL(100853753, movie->Revenue);
+ BOOST_REQUIRE_EQUAL(139, movie->Runtime);
+ BOOST_REQUIRE_EQUAL("Released", movie->Status);
+ BOOST_REQUIRE_EQUAL("How much can you know about yourself if you've never been in a fight?", movie->Tagline);
+ BOOST_REQUIRE_EQUAL("Fight Club", movie->Title);
+ BOOST_REQUIRE_CLOSE(9.0999999, movie->VoteAverage, 0.01);
+ BOOST_REQUIRE_EQUAL(174, movie->VoteCount);
+}
+
+BOOST_AUTO_TEST_CASE( deserialize_tvseries_json )
+{
+ auto tvSeries = Slicer::Deserialize<Slicer::JsonFile, TMDb::TvSeries>("samples/tv_1396.json");
+ BOOST_REQUIRE_EQUAL("/sIJyCJedGlZf1TId41gCtkblBGo.jpg", tvSeries->BackdropPath);
+ BOOST_REQUIRE_EQUAL(1, tvSeries->CreatedBy.size());
+ BOOST_REQUIRE_EQUAL(66633, tvSeries->CreatedBy.front().Id);
+ BOOST_REQUIRE_EQUAL("Vince Gilligan", tvSeries->CreatedBy.front().Name);
+ BOOST_REQUIRE_EQUAL("/rLSUjr725ez1cK7SKVxC9udO03Y.jpg", tvSeries->CreatedBy.front().ProfilePath);
+ BOOST_REQUIRE_EQUAL(2, tvSeries->EpisodeRunTimes.size());
+ BOOST_REQUIRE_EQUAL(45, tvSeries->EpisodeRunTimes.front());
+ BOOST_REQUIRE_EQUAL(TMDb::Date({2008, 1, 19}), tvSeries->FirstAirDate);
+ BOOST_REQUIRE_EQUAL(1, tvSeries->Genres.size());
+ BOOST_REQUIRE_EQUAL(18, tvSeries->Genres.front().Id);
+ BOOST_REQUIRE_EQUAL("Drama", tvSeries->Genres.front().Name);
+ BOOST_REQUIRE_EQUAL("http://www.amctv.com/shows/breaking-bad", tvSeries->HomePage);
+ BOOST_REQUIRE_EQUAL(1396, tvSeries->Id);
+ BOOST_REQUIRE_EQUAL(false, tvSeries->InProduction);
+ BOOST_REQUIRE_EQUAL(5, tvSeries->Languages.size());
+ BOOST_REQUIRE_EQUAL("en", tvSeries->Languages.front());
+ BOOST_REQUIRE_EQUAL(TMDb::Date({2013, 9, 29}), tvSeries->LastAirDate);
+ BOOST_REQUIRE_EQUAL("Breaking Bad", tvSeries->Name);
+ BOOST_REQUIRE_EQUAL(1, tvSeries->Networks.size());
+ BOOST_REQUIRE_EQUAL(174, tvSeries->Networks.front().Id);
+ BOOST_REQUIRE_EQUAL("AMC", tvSeries->Networks.front().Name);
+ BOOST_REQUIRE_EQUAL(62, tvSeries->NumberOfEpisodes);
+ BOOST_REQUIRE_EQUAL(5, tvSeries->NumberOfSeasons);
+ BOOST_REQUIRE_EQUAL(1, tvSeries->OriginCountries.size());
+ BOOST_REQUIRE_EQUAL("US", tvSeries->OriginCountries.front());
+ BOOST_REQUIRE_EQUAL(651, tvSeries->Overview.length());
+ BOOST_REQUIRE_CLOSE(7.684, tvSeries->Popularity, 0.01);
+ BOOST_REQUIRE_EQUAL("/4yMXf3DW6oCL0lVPZaZM2GypgwE.jpg", tvSeries->PosterPath);
+ BOOST_REQUIRE_EQUAL(6, tvSeries->Seasons.size());
+ BOOST_REQUIRE_EQUAL(TMDb::Date({2012, 7, 15}), tvSeries->Seasons.back().AirDate);
+ BOOST_REQUIRE_EQUAL(3578, tvSeries->Seasons.back().Id);
+ BOOST_REQUIRE_EQUAL("/ih1JKNxEzW56azeFpEQmdu4poA4.jpg", tvSeries->Seasons.back().PosterPath);
+ BOOST_REQUIRE_EQUAL(5, tvSeries->Seasons.back().SeasonNumber);
+ BOOST_REQUIRE_EQUAL("Ended", tvSeries->Status);
+ BOOST_REQUIRE_CLOSE(9, tvSeries->VoteAverage, 0.01);
+ BOOST_REQUIRE_EQUAL(72, tvSeries->VoteCount);
+}
+
diff --git a/libtmdb/tmdb-api.ice b/libtmdb/tmdb-api.ice
new file mode 100644
index 0000000..e116f2d
--- /dev/null
+++ b/libtmdb/tmdb-api.ice
@@ -0,0 +1,21 @@
+#ifndef TMDB_API_ICE
+#define TMDB_API_ICE
+
+#include "tmdb-models.ice"
+
+module TMDb {
+ exception HttpException {
+ int code;
+ };
+ interface API {
+ idempotent SearchMultiResults SearchMulti(string query, optional(0) int page);
+ idempotent SearchMovieResults SearchMovies(string query, optional(1) int year, optional(0) int page);
+ idempotent SearchPersonResults SearchPersons(string query, optional(0) int page);
+ idempotent SearchTvResults SearchTv(string query, optional(0) int page);
+ idempotent Movie GetMovie(int id);
+ idempotent TvSeries GetTvSeries(int id);
+ };
+};
+
+#endif
+
diff --git a/libtmdb/tmdb-common.ice b/libtmdb/tmdb-common.ice
new file mode 100644
index 0000000..532d65b
--- /dev/null
+++ b/libtmdb/tmdb-common.ice
@@ -0,0 +1,90 @@
+#ifndef TMDB_COMMON_ICE
+#define TMDB_COMMON_ICE
+
+module TMDb {
+ struct Date {
+ short Year;
+ short Month;
+ short Day;
+ };
+
+ struct CountryRef {
+ ["slicer:name:iso_3166_1"]
+ string Id;
+
+ ["slicer:name:name"]
+ string Name;
+ };
+ sequence<CountryRef> CountryRefList;
+
+ struct CompanyRef {
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:name"]
+ string Name;
+ };
+ sequence<CompanyRef> CompanyRefList;
+
+ struct Language {
+ ["slicer:name:iso_639_1"]
+ string Id;
+
+ ["slicer:name:name"]
+ string Name;
+ };
+ sequence<Language> LanguageList;
+
+ struct Genre {
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:name"]
+ string Name;
+ };
+ sequence<Genre> GenreList;
+
+ struct PersonRef {
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:name"]
+ string Name;
+
+ ["slicer:name:profile_path"]
+ string ProfilePath;
+ };
+ sequence<PersonRef> PersonRefList;
+
+ struct SeasonRef {
+ ["slicer:name:air_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date AirDate;
+
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:poster_path"]
+ string PosterPath;
+
+ ["slicer:name:season_number"]
+ short SeasonNumber;
+
+ };
+ sequence<SeasonRef> SeasonRefList;
+
+ struct NetworkRef {
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:name"]
+ string Name;
+ };
+ sequence<NetworkRef> NetworkRefList;
+
+ sequence<short> Runtimes;
+ sequence<string> StringList;
+};
+
+#endif
+
diff --git a/libtmdb/tmdb-models.ice b/libtmdb/tmdb-models.ice
new file mode 100644
index 0000000..3368b8d
--- /dev/null
+++ b/libtmdb/tmdb-models.ice
@@ -0,0 +1,269 @@
+#ifndef TMDB_MODELS_ICE
+#define TMDB_MODELS_ICE
+
+#include "tmdb-common.ice"
+
+module TMDb {
+ class SearchPaging {
+ ["slicer:name:page"]
+ int Page;
+
+ ["slicer:name:total_pages"]
+ int TotalPages;
+
+ ["slicer:name:total_results"]
+ int TotalResults;
+ };
+
+ ["slicer:typeid:media_type"]
+ class SearchMatch {
+ ["slicer:name:id"]
+ int Id;
+ };
+ sequence<SearchMatch> SearchMatchList;
+
+ ["slicer:typename:person"]
+ class SearchMatchPerson extends SearchMatch {
+ ["slicer:name:adult"]
+ bool Adult;
+
+ ["slicer:name:name"]
+ string Name;
+
+ ["slicer:name:profile_path"]
+ string ProfilePath;
+ };
+ sequence<SearchMatchPerson> SearchMatchPersonList;
+
+ ["slicer:typename:movie"]
+ class SearchMatchMovie extends SearchMatch {
+ ["slicer:name:adult"]
+ bool Adult;
+
+ ["slicer:name:backdrop_path"]
+ string BackdropPath;
+
+ ["slicer:name:original_title"]
+ string OriginalTitle;
+
+ ["slicer:name:release_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date ReleaseDate;
+
+ ["slicer:name:poster_path"]
+ string PosterPath;
+
+ ["slicer:name:popularity"]
+ double Popularity;
+
+ ["slicer:name:title"]
+ string Title;
+
+ ["slicer:name:vote_average"]
+ float VoteAverage;
+
+ ["slicer:name:vote_count"]
+ int VoteCount;
+ };
+ sequence<SearchMatchMovie> SearchMatchMovieList;
+
+ ["slicer:typename:tv"]
+ class SearchMatchTv extends SearchMatch {
+ ["slicer:name:backdrop_path"]
+ string BackdropPath;
+
+ ["slicer:name:original_name"]
+ string OriginalName;
+
+ ["slicer:name:first_air_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date FirstAirDate;
+
+ ["slicer:name:poster_path"]
+ string PosterPath;
+
+ ["slicer:name:popularity"]
+ double Popularity;
+
+ ["slicer:name:name"]
+ string Name;
+
+ ["slicer:name:vote_average"]
+ float VoteAverage;
+
+ ["slicer:name:vote_count"]
+ int VoteCount;
+
+ ["slicer:name:media_type"]
+ string MediaType;
+ };
+ sequence<SearchMatchTv> SearchMatchTvList;
+
+ class SearchMultiResults extends SearchPaging {
+ ["slicer:name:results"]
+ SearchMatchList Results;
+ };
+
+ class SearchTvResults extends SearchPaging {
+ ["slicer:name:results"]
+ SearchMatchTvList Results;
+ };
+
+ class SearchMovieResults extends SearchPaging {
+ ["slicer:name:results"]
+ SearchMatchMovieList Results;
+ };
+
+ class SearchPersonResults extends SearchPaging {
+ ["slicer:name:results"]
+ SearchMatchPersonList Results;
+ };
+
+ class Movie {
+ ["slicer:name:adult"]
+ bool Adult;
+
+ ["slicer:name:backdrop_path"]
+ string BackdropPath;
+
+ ["slicer:name:belongs_to_collection"]
+ optional(0) int BelongsToCollection;
+
+ ["slicer:name:budget"]
+ long Budget;
+
+ ["slicer:name:genres"]
+ GenreList Genres;
+
+ ["slicer:name:homepage"]
+ string HomePage;
+
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:imdb_id"]
+ string ImdbId;
+
+ ["slicer:name:original_title"]
+ string OriginalTitle;
+
+ ["slicer:name:overview"]
+ string Overview;
+
+ ["slicer:name:popularity"]
+ float Popularity;
+
+ ["slicer:name:poster_path"]
+ string PosterPath;
+
+ ["slicer:name:production_companies"]
+ CompanyRefList ProductionCompanies;
+
+ ["slicer:name:production_countries"]
+ CountryRefList ProductionCountries;
+
+ ["slicer:name:release_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date ReleaseDate;
+
+ ["slicer:name:revenue"]
+ long Revenue;
+
+ ["slicer:name:runtime"]
+ int Runtime;
+
+ ["slicer:name:spoken_languages"]
+ LanguageList SpokenLanguages;
+
+ ["slicer:name:status"]
+ string Status;
+
+ ["slicer:name:tagline"]
+ string Tagline;
+
+ ["slicer:name:title"]
+ string Title;
+
+ ["slicer:name:vote_average"]
+ float VoteAverage;
+
+ ["slicer:name:vote_count"]
+ int VoteCount;
+ };
+
+ class TvSeries {
+ ["slicer:name:backdrop_path"]
+ string BackdropPath;
+
+ ["slicer:name:created_by"]
+ PersonRefList CreatedBy;
+
+ ["slicer:name:episode_run_time"]
+ Runtimes EpisodeRunTimes;
+
+ ["slicer:name:first_air_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date FirstAirDate;
+
+ ["slicer:name:genres"]
+ GenreList Genres;
+
+ ["slicer:name:homepage"]
+ string HomePage;
+
+ ["slicer:name:id"]
+ int Id;
+
+ ["slicer:name:in_production"]
+ bool InProduction;
+
+ ["slicer:name:languages"]
+ StringList Languages;
+
+ ["slicer:name:last_air_date",
+ "slicer:conversion:std.string:stringToDate:dateToString"]
+ Date LastAirDate;
+
+ ["slicer:name:name"]
+ string Name;
+
+ ["slicer:name:networks"]
+ NetworkRefList Networks;
+
+ ["slicer:name:number_of_episodes"]
+ int NumberOfEpisodes;
+
+ ["slicer:name:number_of_seasons"]
+ int NumberOfSeasons;
+
+ ["slicer:name:original_name"]
+ string OriginalName;
+
+ ["slicer:name:origin_country"]
+ StringList OriginCountries;
+
+ ["slicer:name:overview"]
+ string Overview;
+
+ ["slicer:name:popularity"]
+ float Popularity;
+
+ ["slicer:name:poster_path"]
+ string PosterPath;
+
+ ["slicer:name:seasons"]
+ SeasonRefList Seasons;
+
+ ["slicer:name:status"]
+ string Status;
+
+ ["slicer:name:vote_average"]
+ float VoteAverage;
+
+ ["slicer:name:vote_count"]
+ int VoteCount;
+ };
+};
+
+#endif
+
diff --git a/libtmdb/tmdb-proxy.cpp b/libtmdb/tmdb-proxy.cpp
new file mode 100644
index 0000000..7a575e5
--- /dev/null
+++ b/libtmdb/tmdb-proxy.cpp
@@ -0,0 +1,46 @@
+#include "tmdb-proxy.h"
+
+namespace TMDb {
+ Proxy::Proxy(const std::string & bu, const std::string & k) :
+ HttpClient(bu, k)
+ {
+ }
+
+ SearchMultiResultsPtr
+ Proxy::SearchMulti(const std::string & query, const IceUtil::Optional<int> & page, const Ice::Current&)
+ {
+ return GetData<SearchMultiResults>("/search/multi", { { "query", query }, { "page", page } });
+ }
+
+ SearchMovieResultsPtr
+ Proxy::SearchMovies(const std::string & query, const IceUtil::Optional<int> & year, const IceUtil::Optional<int> & page, const Ice::Current&)
+ {
+ return GetData<SearchMovieResults>("/search/movies", { { "query", query }, { "page", page }, { "year", year } });
+ }
+
+ SearchPersonResultsPtr
+ Proxy::SearchPersons(const std::string & query, const IceUtil::Optional<int> & page, const Ice::Current&)
+ {
+ return GetData<SearchPersonResults>("/search/person", { { "query", query }, { "page", page } });
+ }
+
+ SearchTvResultsPtr
+ Proxy::SearchTv(const std::string & query, const IceUtil::Optional<int> & page, const Ice::Current&)
+ {
+ return GetData<SearchTvResults>("/search/tv", { { "query", query}, { "page", page } });
+ }
+
+ MoviePtr
+ Proxy::GetMovie(Ice::Int id, const Ice::Current&)
+ {
+ return GetData<Movie, Ice::Int>("/movie/%d", id, { });
+ }
+
+ TvSeriesPtr
+ Proxy::GetTvSeries(Ice::Int id, const Ice::Current&)
+ {
+ return GetData<TvSeries, Ice::Int>("/tv/%d", id, { });
+ }
+
+}
+
diff --git a/libtmdb/tmdb-proxy.h b/libtmdb/tmdb-proxy.h
new file mode 100644
index 0000000..6ff8b38
--- /dev/null
+++ b/libtmdb/tmdb-proxy.h
@@ -0,0 +1,17 @@
+#include <tmdb-api.h>
+#include "httpClient.h"
+
+namespace TMDb {
+ class Proxy : public API, private HttpClient {
+ public:
+ Proxy(const std::string & baseUrl, const std::string & apikey);
+
+ SearchMultiResultsPtr SearchMulti(const std::string&, const IceUtil::Optional<int>&, const Ice::Current&) override;
+ SearchMovieResultsPtr SearchMovies(const std::string&, const IceUtil::Optional<int>&, const IceUtil::Optional<int>&, const Ice::Current&) override;
+ SearchPersonResultsPtr SearchPersons(const std::string&, const IceUtil::Optional<int>&, const Ice::Current&) override;
+ SearchTvResultsPtr SearchTv(const std::string&, const IceUtil::Optional<int>&, const Ice::Current&) override;
+ MoviePtr GetMovie(Ice::Int, const Ice::Current&) override;
+ TvSeriesPtr GetTvSeries(Ice::Int, const Ice::Current&) override;
+ };
+};
+