Fix empty My List due to unknown items (#102)

* Ignore unavailable items on My List

* Rework My List API
This commit is contained in:
Michaël Arnauts 2022-02-03 18:38:34 +01:00 committed by GitHub
parent 21f877fb8d
commit 02b2d4dbb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 48 deletions

View File

@ -4,9 +4,6 @@
from __future__ import absolute_import, division, unicode_literals from __future__ import absolute_import, division, unicode_literals
import logging import logging
from datetime import datetime
import dateutil.tz
from resources.lib import kodiutils from resources.lib import kodiutils
from resources.lib.kodiutils import TitleItem from resources.lib.kodiutils import TitleItem
@ -253,22 +250,10 @@ class Catalog:
kodiutils.show_listing(listing, 30005, content='tvshows') kodiutils.show_listing(listing, 30005, content='tvshows')
def show_mylist(self): def show_mylist(self):
""" Show all the programs of all channels """ """ Show the programs of My List """
try: mylist = self._api.get_mylist()
mylist, _ = self._auth.get_dataset('myList', 'myList')
except Exception as ex:
kodiutils.notification(message=str(ex))
raise
items = [] listing = [Menu.generate_titleitem(item) for item in mylist]
if mylist:
for item in mylist:
program = self._api.get_program_by_uuid(item.get('id'))
if program:
program.my_list = True
items.append(program)
listing = [Menu.generate_titleitem(item) for item in items]
# Sort items by title # Sort items by title
# Used for A-Z listing or when movies and episodes are mixed. # Used for A-Z listing or when movies and episodes are mixed.
@ -280,23 +265,7 @@ class Catalog:
kodiutils.end_of_directory() kodiutils.end_of_directory()
return return
mylist, sync_info = self._auth.get_dataset('myList', 'myList') self._api.mylist_add(uuid)
if not mylist:
mylist = []
if uuid not in [item.get('id') for item in mylist]:
# Python 2.7 doesn't support .timestamp(), and windows doesn't do '%s', so we need to calculate it ourself
epoch = datetime(1970, 1, 1, tzinfo=dateutil.tz.gettz('UTC'))
now = datetime.now(tz=dateutil.tz.gettz('UTC'))
timestamp = int((now - epoch).total_seconds()) * 1000
mylist.append({
'id': uuid,
'timestamp': timestamp,
})
self._auth.put_dataset('myList', 'myList', mylist, sync_info)
kodiutils.end_of_directory() kodiutils.end_of_directory()
@ -306,12 +275,6 @@ class Catalog:
kodiutils.end_of_directory() kodiutils.end_of_directory()
return return
mylist, sync_info = self._auth.get_dataset('myList', 'myList') self._api.mylist_del(uuid)
if not mylist:
mylist = []
new_mylist = [item for item in mylist if item.get('id') != uuid]
self._auth.put_dataset('myList', 'myList', new_mylist, sync_info)
kodiutils.end_of_directory() kodiutils.end_of_directory()

View File

@ -354,7 +354,7 @@ class ContentApi:
:type uuid: str :type uuid: str
:rtype str :rtype str
""" """
response = self._get_url(self.API_VIERVIJFZES + '/content/%s' % uuid, authentication=True) response = self._get_url(self.API_VIERVIJFZES + '/content/%s' % uuid, authentication=self._auth.get_token())
data = json.loads(response) data = json.loads(response)
if not data: if not data:
@ -366,7 +366,7 @@ class ContentApi:
drm_key = data['drmKey']['S'] drm_key = data['drmKey']['S']
_LOGGER.debug('Fetching Authentication XML with drm_key %s', drm_key) _LOGGER.debug('Fetching Authentication XML with drm_key %s', drm_key)
response_drm = self._get_url(self.API_GOPLAY + '/video/xml/%s' % drm_key, authentication=True) response_drm = self._get_url(self.API_GOPLAY + '/video/xml/%s' % drm_key, authentication=self._auth.get_token())
data_drm = json.loads(response_drm) data_drm = json.loads(response_drm)
return ResolvedStream( return ResolvedStream(
@ -484,6 +484,33 @@ class ContentApi:
return categories return categories
def get_mylist(self):
""" Get the content of My List
:rtype list[Program]
"""
data = self._get_url(self.API_GOPLAY + '/my-list', authentication='Bearer %s' % self._auth.get_token())
result = json.loads(data)
items = []
for item in result:
try:
program = self.get_program_by_uuid(item.get('programId'))
if program:
program.my_list = True
items.append(program)
except Exception as exc: # pylint: disable=broad-except
_LOGGER.warning(exc)
return items
def mylist_add(self, program_id):
""" Add a program on My List """
self._post_url(self.API_GOPLAY + '/my-list', data={'programId': program_id}, authentication='Bearer %s' % self._auth.get_token())
def mylist_del(self, program_id):
""" Remove a program on My List """
self._delete_url(self.API_GOPLAY + '/my-list-item', params={'programId': program_id}, authentication='Bearer %s' % self._auth.get_token())
@staticmethod @staticmethod
def _extract_programs(html): def _extract_programs(html):
""" Extract Programs from HTML code """ Extract Programs from HTML code
@ -669,16 +696,15 @@ class ContentApi:
) )
return episode return episode
def _get_url(self, url, params=None, authentication=False): def _get_url(self, url, params=None, authentication=None):
""" Makes a GET request for the specified URL. """ Makes a GET request for the specified URL.
:type url: str :type url: str
:type authentication: str
:rtype str :rtype str
""" """
if authentication: if authentication:
if not self._auth:
raise Exception('Requested to authenticate, but not auth object passed')
response = self._session.get(url, params=params, headers={ response = self._session.get(url, params=params, headers={
'authorization': self._auth.get_token(), 'authorization': authentication,
}) })
else: else:
response = self._session.get(url, params=params) response = self._session.get(url, params=params)
@ -689,6 +715,44 @@ class ContentApi:
return response.text return response.text
def _post_url(self, url, params=None, data=None, authentication=None):
""" Makes a POST request for the specified URL.
:type url: str
:type authentication: str
:rtype str
"""
if authentication:
response = self._session.post(url, params=params, json=data, headers={
'authorization': authentication,
})
else:
response = self._session.post(url, params=params, json=data)
if response.status_code != 200:
_LOGGER.error(response.text)
raise Exception('Could not fetch data')
return response.text
def _delete_url(self, url, params=None, authentication=None):
""" Makes a DELETE request for the specified URL.
:type url: str
:type authentication: str
:rtype str
"""
if authentication:
response = self._session.delete(url, params=params, headers={
'authorization': authentication,
})
else:
response = self._session.delete(url, params=params)
if response.status_code != 200:
_LOGGER.error(response.text)
raise Exception('Could not fetch data')
return response.text
def _handle_cache(self, key, cache_mode, update, ttl=30 * 24 * 60 * 60): def _handle_cache(self, key, cache_mode, update, ttl=30 * 24 * 60 * 60):
""" Fetch something from the cache, and update if needed """ """ Fetch something from the cache, and update if needed """
if cache_mode in [CACHE_AUTO, CACHE_ONLY]: if cache_mode in [CACHE_AUTO, CACHE_ONLY]: