# -*- coding: utf-8 -*- # Copyright: (c) 2019, Dag Wieers (@dagwieers) <dag@wieers.com> # GNU General Public License v3.0 (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """Implementation of Favorites class""" from __future__ import absolute_import, division, unicode_literals try: # Python 3 from urllib.error import HTTPError from urllib.parse import unquote from urllib.request import build_opener, install_opener, ProxyHandler except ImportError: # Python 2 from urllib2 import build_opener, HTTPError, install_opener, ProxyHandler, unquote from kodiutils import (container_refresh, get_cache, get_proxies, get_setting_bool, get_url_json, has_credentials, input_down, invalidate_caches, localize, log_error, multiselect, notification, ok_dialog, update_cache) from utils import program_to_id class Favorites: """Track, cache and manage VRT favorites""" def __init__(self): """Initialize favorites, relies on XBMC vfs and a special VRT token""" self._data = dict() # Our internal representation install_opener(build_opener(ProxyHandler(get_proxies()))) @staticmethod def is_activated(): """Is favorites activated in the menu and do we have credentials ?""" return get_setting_bool('usefavorites', default=True) and has_credentials() def refresh(self, ttl=None): """Get a cached copy or a newer favorites from VRT, or fall back to a cached file""" if not self.is_activated(): return favorites_json = get_cache('favorites.json', ttl) if not favorites_json: from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_token('X-VRT-Token', variant='user') if xvrttoken: headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be/vrtnu', } favorites_url = 'https://video-user-data.vrt.be/favorites' favorites_json = get_url_json(url=favorites_url, cache='favorites.json', headers=headers) if favorites_json is not None: self._data = favorites_json def update(self, program, title, value=True): """Set a program as favorite, and update local copy""" # Survive any recent updates self.refresh(ttl=5) if value is self.is_favorite(program): # Already followed/unfollowed, nothing to do return True from tokenresolver import TokenResolver xvrttoken = TokenResolver().get_token('X-VRT-Token', variant='user') if xvrttoken is None: log_error('Failed to get favorites token from VRT NU') notification(message=localize(30975)) return False headers = { 'authorization': 'Bearer ' + xvrttoken, 'content-type': 'application/json', 'Referer': 'https://www.vrt.be/vrtnu', } from json import dumps from utils import program_to_url payload = dict(isFavorite=value, programUrl=program_to_url(program, 'short'), title=title) data = dumps(payload).encode('utf-8') program_id = program_to_id(program) try: get_url_json('https://video-user-data.vrt.be/favorites/{program_id}'.format(program_id=program_id), headers=headers, data=data) except HTTPError as exc: log_error("Failed to (un)follow program '{program}' at VRT NU ({error})", program=program, error=exc) notification(message=localize(30976, program=program)) return False # NOTE: Updates to favorites take a longer time to take effect, so we keep our own cache and use it self._data[program_id] = dict(value=payload) update_cache('favorites.json', dumps(self._data)) invalidate_caches('my-offline-*.json', 'my-recent-*.json') return True def is_favorite(self, program): """Is a program a favorite ?""" value = False favorite = self._data.get(program_to_id(program)) if favorite: value = favorite.get('value', {}).get('isFavorite') return value is True def follow(self, program, title): """Follow your favorite program""" succeeded = self.update(program, title, True) if succeeded: notification(message=localize(30411, title=title)) container_refresh() def unfollow(self, program, title, move_down=False): """Unfollow your favorite program""" succeeded = self.update(program, title, False) if succeeded: notification(message=localize(30412, title=title)) # If the current item is selected and we need to move down before removing if move_down: input_down() container_refresh() def titles(self): """Return all favorite titles""" return [value.get('value').get('title') for value in list(self._data.values()) if value.get('value').get('isFavorite')] def programs(self): """Return all favorite programs""" from utils import url_to_program return [url_to_program(value.get('value').get('programUrl')) for value in list(self._data.values()) if value.get('value').get('isFavorite')] def manage(self): """Allow the user to unselect favorites to be removed from the listing""" from utils import url_to_program self.refresh(ttl=0) if not self._data: ok_dialog(heading=localize(30418), message=localize(30419)) # No favorites found return def by_title(item): """Sort by title""" return item.get('value').get('title') items = [dict(program=url_to_program(value.get('value').get('programUrl')), title=unquote(value.get('value').get('title')), enabled=value.get('value').get('isFavorite')) for value in list(sorted(list(self._data.values()), key=by_title))] titles = [item['title'] for item in items] preselect = [idx for idx in range(0, len(items) - 1) if items[idx]['enabled']] selected = multiselect(localize(30420), options=titles, preselect=preselect) # Please select/unselect to follow/unfollow if selected is not None: for idx in set(preselect).difference(set(selected)): self.unfollow(program=items[idx]['program'], title=items[idx]['title']) for idx in set(selected).difference(set(preselect)): self.follow(program=items[idx]['program'], title=items[idx]['title'])