Newer
Older
Import / applications / Photoframe / src / videolist.cpp
@John Ryland John Ryland on 11 Jan 2021 12 KB save wip
#include "videolist.h"
#include "devicelist.h"
#include "text.h"
#include <unordered_map>
#include <string>
#include <QKeyEvent>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>


using KeyValuePairs = std::unordered_map<std::string, std::string>;
using Json = QJsonDocument;


class Response
{
public:
	Response() {}
	Response(QByteArray _ba) : ba(_ba) {}
	Response(const Response& other) : ba(other.ba) {}
	Json json() {
		return QJsonDocument::fromJson(ba);
	}
private:
	QByteArray ba;
};


class Requests : public QObject
{
public:
	Requests()
	{
		manager = new QNetworkAccessManager();

		QObject::connect(manager, &QNetworkAccessManager::finished,
			this, [=](QNetworkReply* reply)
			{
				if (reply->error()) {
					qDebug() << reply->errorString();
					return;
				}

				if (continuationMap.count(reply))
				{
					Response resp(reply->readAll());
					continuationMap[reply](resp);
				}
			});
	}

	~Requests()
	{
		delete manager;
	}

	// sessions?
	void get(const std::string& url, const KeyValuePairs& headers, std::function<void(Response&)> continuation)
	{
		QNetworkRequest request;
		request.setUrl(QUrl(url.c_str()));
		auto resp = manager->get(request);
		continuationMap[resp] = continuation;
	}

private:
	QNetworkAccessManager* manager;
	std::unordered_map<QNetworkReply*, std::function<void(Response&)>> continuationMap;
};


class MovieAggregator : public QObject
{
};


class JustWatch : public MovieAggregator
{
	static std::string api_base_template;
public:
	JustWatch(const char* _country = "US")//, bool _use_sessions = true)
	{
		requests = new Requests;
		// self.kwargs = kwargs
		country = _country;
		//self.kwargs_cinema = []
		setupLocale();
	}

	~JustWatch()
	{
		delete requests;
	}

	void setupLocale();

	void searchForItem(std::string query, KeyValuePairs kwargs);
	void getProviders();
	void getGenres();
	void getTitle(std::string title_id, std::string content_type = "movie");
	void searchTitleId(std::string query);
	void getSeason(std::string season_id);
	void getCinemaTimes(std::string title_id, std::string content_type = "movie", KeyValuePairs kwargs = {});
	void getCinemaDetails(KeyValuePairs kwargs);
	void getUpcomingCinema(int weeks_offset, bool nationwide_cinema_releases_only = true);
	void getCertifications(std::string content_type = "movie");
	void getPersonCetail(std::string person_id);

private:
	Requests* requests;
	std::string country;
	std::string locale;
};


void JustWatch::setupLocale()
{
	std::string default_locale = "en_AU";
	std::string api_url = api_base_template + "locales/state";
	KeyValuePairs headers = { { "User - Agent", "JustWatch client(github.com / dawoudt / JustWatchAPI)" } };
	requests->get(api_url, headers, [=](Response& resp)
	{
		std::string str = resp.json().toJson(QJsonDocument::Indented).toStdString();
		qDebug() << str.c_str();

		locale = default_locale;
		auto results = resp.json();
		//QJsonArray arr;
		for (auto result : results.array())
			if ((result.toObject()["iso_3166_2"].toString().toStdString() == country)
				|| (result.toObject()["country"].toString().toStdString() == country))
				locale = result.toObject()["full_locale"].toString().toStdString();
	});

	// warn = '\nWARN: Unable to locale for {}! Defaulting to en_AU\n'
	//sys.stderr.write(warn.format(self.country))
	//return default_locale;
}


std::string JustWatch::api_base_template = "http://apis.justwatch.com/content/";// {path}";


class VideoManager : public QObject
{
public:
	VideoManager();
	~VideoManager() {}

	void getList();

private:
	JustWatch justWatch;
};


VideoManager::VideoManager()
{
}


void VideoManager::getList()
{
}


VideoList::VideoList(QWidget *parent) : QWidget(parent)
{
	resize(800,480);
	devices = new DeviceList(this, QT_TRANSLATE_NOOP("DPF","Videos"), "videos");
	connect(devices, SIGNAL(setStage(int)), this, SIGNAL(setStage(int)));
	connect(devices, SIGNAL(switchFocus()), this, SLOT(switchFocus()));
	setFocusProxy(devices);
	devices->setFocusPolicy(Qt::StrongFocus);
	devices->setFocus();
	new Text(this, 380+2, 230+2, "Not Implemented", 20, QColor(0,0,0,128));
	new Text(this, 380, 230, "Not Implemented", 20);

	manager = new VideoManager;
}


VideoList::~VideoList()
{
	delete manager;
}


void VideoList::switchFocus()
{
	//focusNextPrevChild(true);
	devices->setFocus();
}




#if 0

JustWatch API


from datetime import datetime
from datetime import timedelta
import requests
import sys


HEADER = { 'User-Agent':'JustWatch client (github.com/dawoudt/JustWatchAPI)' }


class JustWatch :
	api_base_template = "https://apis.justwatch.com/content/{path}"

	def __init__(self, country = 'AU', use_sessions = True, **kwargs) :
	self.kwargs = kwargs
	self.country = country
	self.kwargs_cinema = []
	self.requests = requests.Session() if use_sessions else requests
	self.locale = self.set_locale()


	def __del__(self) :
	''' Should really use context manager
	but this should do without changing functionality.
	'''
	if isinstance(self.requests, requests.Session) :
		self.requests.close()


		def set_locale(self) :
		warn = '\nWARN: Unable to locale for {}! Defaulting to en_AU\n'
		default_locale = 'en_AU'
		path = 'locales/state'
		api_url = self.api_base_template.format(path = path)

		r = self.requests.get(api_url, headers = HEADER)
		try :
		r.raise_for_status()
		except requests.exceptions.HTTPError :
		sys.stderr.write(warn.format(self.country))
		return default_locale
	else:
results = r.json()

for result in results :
if result['iso_3166_2'] == self.country or \
result['country'] == self.country:

return result['full_locale']

sys.stderr.write(warn.format(self.country))
return default_locale

def search_for_item(self, query = None, **kwargs) :

	path = 'titles/{}/popular'.format(self.locale)
	api_url = self.api_base_template.format(path = path)

	if kwargs :
		self.kwargs = kwargs
		if query :
			self.kwargs.update({ 'query': query })
			null = None
			payload = {
				"age_certifications":null,
				"content_types" : null,
				"presentation_types" : null,
				"providers" : null,
				"genres" : null,
				"languages" : null,
				"release_year_from" : null,
				"release_year_until" : null,
				"monetization_types" : null,
				"min_price" : null,
				"max_price" : null,
				"nationwide_cinema_releases_only" : null,
				"scoring_filter_types" : null,
				"cinema_release" : null,
				"query" : null,
				"page" : null,
				"page_size" : null,
				"timeline_type" : null,
				"person_id" : null
		}
			for key, value in self.kwargs.items() :
				if key in payload.keys() :
					payload[key] = value
				else :
					print('{} is not a valid keyword'.format(key))
					r = self.requests.post(api_url, json = payload, headers = HEADER)

					# Client should deal with rate - limiting.JustWatch may send a 429 Too Many Requests response.
					r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

					return r.json()

					def get_providers(self) :
					path = 'providers/locale/{}'.format(self.locale)
					api_url = self.api_base_template.format(path = path)
					r = self.requests.get(api_url, headers = HEADER)
					r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

					return r.json()

					def get_genres(self) :
					path = 'genres/locale/{}'.format(self.locale)
					api_url = self.api_base_template.format(path = path)
					r = self.requests.get(api_url, headers = HEADER)
					r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

					return r.json()

					def get_title(self, title_id, content_type = 'movie') :
					path = 'titles/{content_type}/{title_id}/locale/{locale}'.format(content_type = content_type,
						title_id = title_id,
						locale = self.locale)

					api_url = self.api_base_template.format(path = path)
					r = self.requests.get(api_url, headers = HEADER)
					r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

					return r.json()

					def search_title_id(self, query) :
					''' Returns a dictionary of titles returned 
					from search and their respective ID's

					>> > ...
					>> > just_watch.get_title_id('The Matrix')
				{
					'The Matrix': 10, ...
				}

'''

results = self.search_for_item(query)
return { item['id']: item['title'] for item in results['items'] }


def get_season(self, season_id) :

	header = HEADER
	api_url = 'https://apis.justwatch.com/content/titles/show_season/{}/locale/{}'.format(season_id, self.locale)
	r = self.requests.get(api_url, headers = header)

	# Client should deal with rate - limiting.JustWatch may send a 429 Too Many Requests response.
	r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

	return r.json()


	def get_cinema_times(self, title_id, content_type = 'movie', **kwargs) :

	if kwargs :
		self.kwargs_cinema = kwargs

		null = None
		payload = {
			"date":null,
			"latitude" : null,
			"longitude" : null,
			"radius" : 20000
	}
		for key, value in self.kwargs_cinema.items() :
			if key in payload.keys() :
				payload[key] = value
			else :
				print('{} is not a valid keyword'.format(key))


				header = HEADER
				api_url = 'https://apis.justwatch.com/content/titles/{}/{}/showtimes'.format(content_type, title_id)
				r = self.requests.get(api_url, params = payload, headers = header)

				r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

				return r.json()


				def get_cinema_details(self, **kwargs) :

				if kwargs :
					self.kwargs_cinema = kwargs

					null = None
					payload = {
						"latitude":null,
						"longitude" : null,
						"radius" : 20000
				}
					for key, value in self.kwargs_cinema.items() :
						if key in payload.keys() :
							payload[key] = value
							elif key == 'date' :
							#ignore the date value if passed
							pass
							else:
print('{} is not a valid keyword'.format(key))


header = HEADER
api_url = 'https://apis.justwatch.com/content/cinemas/{}'.format(self.locale)
r = self.requests.get(api_url, params = payload, headers = header)

r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

return r.json()



def get_upcoming_cinema(self, weeks_offset, nationwide_cinema_releases_only = True) :

	header = HEADER
	payload = { 'nationwide_cinema_releases_only': nationwide_cinema_releases_only,
					'body' : {} }
	now_date = datetime.now()
	td = timedelta(weeks = weeks_offset)
	year_month_day = (now_date + td).isocalendar()
	api_url = 'https://apis.justwatch.com/content/titles/movie/upcoming/{}/{}/locale/{}'
	api_url = api_url.format(year_month_day[0], year_month_day[1], self.locale)

	#this throws an error if you go too many weeks forward, so return a blank payload if we hit an error
	try :
	r = self.requests.get(api_url, params = payload, headers = header)

	# Client should deal with rate - limiting.JustWatch may send a 429 Too Many Requests response.
	r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

	return r.json()
	except:
return { 'page': 0, 'page_size' : 0, 'total_pages' : 1, 'total_results' : 0,  'items' : [] }

def get_certifications(self, content_type = 'movie') :

	header = HEADER
	payload = { 'country': self.country, 'object_type' : content_type }
	api_url = 'https://apis.justwatch.com/content/age_certifications'
	r = self.requests.get(api_url, params = payload, headers = header)

	# Client should deal with rate - limiting.JustWatch may send a 429 Too Many Requests response.
	r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

	return r.json()

	def get_person_detail(self, person_id) :
	path = 'titles/person/{person_id}/locale/{locale}'.format(person_id = person_id, locale = self.locale)
	api_url = self.api_base_template.format(path = path)

	r = self.requests.get(api_url, headers = HEADER)
	r.raise_for_status()   # Raises requests.exceptions.HTTPError if r.status_code != 200

	return r.json()

#endif