Skip to content

Commit

Permalink
🐎 Dont wait for Trakt auth if weve got cached data
Browse files Browse the repository at this point in the history
🔖 Version bump v5.1.49 and dependency bump

🚑 Hotfix for inverted condition
  • Loading branch information
jurialmunkey committed Nov 6, 2023
1 parent 93232c1 commit e4ce6d6
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 46 deletions.
4 changes: 2 additions & 2 deletions addon.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<addon id="plugin.video.themoviedb.helper"
version="5.1.48~nexus"
version="5.1.50~nexus"
name="TheMovieDb Helper"
provider-name="jurialmunkey">
<requires>
<import addon="xbmc.python" version="3.0.1"/>
<import addon="script.module.requests" version="2.9.1"/>
<import addon="script.module.pil" version="1.1.7"/>
<import addon="script.module.addon.signals" version="0.0.6" />
<import addon="script.module.jurialmunkey" version="0.1.10" />
<import addon="script.module.jurialmunkey" version="0.1.13" />
<import addon="script.module.infotagger" version="0.0.5" />
<import addon="script.module.beautifulsoup4" version="4.9.3" />
</requires>
Expand Down
43 changes: 25 additions & 18 deletions resources/tmdbhelper/lib/api/trakt/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,11 @@ def _filter_inprogress(self, items):

@use_simple_cache(cache_days=CACHE_SHORT)
def get_sorted_list(
self, path, sort_by=None, sort_how=None, extended=None, trakt_type=None, permitted_types=None, cache_refresh=False,
self, path, sort_by=None, sort_how=None, extended=None, trakt_type=None, permitted_types=None, cache_refresh=False, cache_only=False,
genres=None, years=None, query=None, languages=None, countries=None, runtimes=None, studio_ids=None
):
response = self.get_response(
path, extended=extended, limit=4095,
path, extended=extended, limit=4095, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
)

Expand Down Expand Up @@ -161,7 +161,7 @@ def get_simple_list(self, *args, trakt_type=None, **kwargs):

@is_authorized
def get_mixed_list(
self, path, trakt_types: list, limit: int = None, extended: str = None, authorize=False,
self, path, trakt_types: list, limit: int = None, extended: str = None, authorize=False, cache_only=False,
genres=None, years=None, query=None, languages=None, countries=None, runtimes=None, studio_ids=None
):
""" Returns a randomised simple list which combines movies and shows
Expand All @@ -173,7 +173,7 @@ def get_mixed_list(
for trakt_type in trakt_types:
response = self.get_simple_list(
path.format(trakt_type=trakt_type),
extended=extended, page=1, limit=limit * 2, trakt_type=trakt_type,
extended=extended, page=1, limit=limit * 2, trakt_type=trakt_type, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
) or {}
items += response.get('items') or []
Expand All @@ -189,7 +189,7 @@ def get_mixed_list(
@is_authorized
def get_basic_list(
self, path, trakt_type, page: int = 1, limit: int = None, params=None,
sort_by=None, sort_how=None, extended=None, authorize=False, randomise=False, always_refresh=True,
sort_by=None, sort_how=None, extended=None, authorize=False, cache_only=False, randomise=False, always_refresh=True,
genres=None, years=None, query=None, languages=None, countries=None, runtimes=None, studio_ids=None
):

Expand All @@ -198,20 +198,20 @@ def get_basic_list(

if randomise:
response = self.get_simple_list(
path, extended=extended, page=1, limit=limit * 2, trakt_type=trakt_type,
path, extended=extended, page=1, limit=limit * 2, trakt_type=trakt_type, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
)

elif sort_by is not None: # Sorted list manually paginated because need to sort first
response = self.get_sorted_list(
path, sort_by, sort_how, extended, cache_refresh=cache_refresh,
path, sort_by, sort_how, extended, cache_refresh=cache_refresh, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
)
response = PaginatedItems(items=response['items'], page=page, limit=limit).get_dict()

else: # Unsorted lists can be paginated by the API
response = self.get_simple_list(
path, extended=extended, page=page, limit=limit, trakt_type=trakt_type,
path, extended=extended, page=page, limit=limit, trakt_type=trakt_type, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
)

Expand All @@ -226,7 +226,7 @@ def get_basic_list(
@is_authorized
def get_stacked_list(
self, path, trakt_type, page: int = 1, limit: int = None, params=None, sort_by=None, sort_how=None,
extended=None, authorize=False, always_refresh=True,
extended=None, authorize=False, always_refresh=True, cache_only=False,
genres=None, years=None, query=None, languages=None, countries=None, runtimes=None, studio_ids=None,
**kwargs
):
Expand All @@ -235,10 +235,13 @@ def get_stacked_list(
cache_refresh = True if always_refresh and try_int(page, fallback=1) == 1 else False

response = self.get_simple_list(
path, extended=extended, limit=4095, trakt_type=trakt_type, cache_refresh=cache_refresh,
path, extended=extended, limit=4095, trakt_type=trakt_type, cache_refresh=cache_refresh, cache_only=cache_only,
genres=genres, years=years, query=query, languages=languages, countries=countries, runtimes=runtimes, studio_ids=studio_ids
)

if not response:
return

response['items'] = self._stack_calendar_tvshows(response['items'])
response = PaginatedItems(items=response['items'], page=page, limit=limit).get_dict()

Expand All @@ -250,7 +253,7 @@ def get_stacked_list(
@is_authorized
def get_custom_list(
self, list_slug, user_slug=None, page: int = 1, limit: int = None, params=None, authorize=False,
sort_by=None, sort_how=None, extended=None, owner=False, always_refresh=True
sort_by=None, sort_how=None, extended=None, owner=False, always_refresh=True, cache_only=False
):

limit = limit or self.item_limit
Expand All @@ -266,7 +269,7 @@ def get_custom_list(
sorted_items = self.get_sorted_list(
path, sort_by, sort_how, extended,
permitted_types=['movie', 'show', 'person', 'episode'],
cache_refresh=cache_refresh
cache_refresh=cache_refresh, cache_only=cache_only
) or {}

paginated_items = PaginatedItems(
Expand All @@ -279,22 +282,22 @@ def get_custom_list(
'persons': sorted_items.get('persons', []),
'next_page': paginated_items.next_page}

@use_activity_cache(cache_days=CACHE_SHORT)
def _get_sync_list(
self, sync_type, trakt_type, sort_by=None, sort_how=None, decorator_cache_refresh=False, extended=None, filters=None
):
get_property('TraktSyncLastActivities.Expires', clear_property=True) # Wipe last activities cache to update now
func = TraktItems(items=self.get_sync(sync_type, trakt_type, extended=extended), trakt_type=trakt_type).build_items
return func(sort_by, sort_how, filters=filters)

@is_authorized
def get_sync_list(
self, sync_type, trakt_type, page: int = 1, limit: int = None, params=None, sort_by=None, sort_how=None, next_page=True,
always_refresh=True, extended=None, filters=None
):
limit = limit or self.sync_item_limit
cache_refresh = True if always_refresh and try_int(page, fallback=1) == 1 else False
response = self._get_sync_list(sync_type, trakt_type, sort_by=sort_by, sort_how=sort_how, decorator_cache_refresh=cache_refresh, extended=extended, filters=filters)
response = self._get_sync_list(
sync_type, trakt_type, sort_by=sort_by, sort_how=sort_how,
decorator_cache_refresh=cache_refresh, extended=extended, filters=filters)
if not response:
return
response = PaginatedItems(items=response['items'], page=page, limit=limit)
Expand Down Expand Up @@ -346,7 +349,7 @@ def _add_icon(i):
return items

@is_authorized
def get_list_of_lists(self, path, page: int = 1, limit: int = 250, authorize=False, next_page=True, sort_likes=False):
def get_list_of_lists(self, path, page: int = 1, limit: int = 250, authorize=False, next_page=True, sort_likes=False, **kwargs):
response = self.get_response(path, page=page, limit=limit)
if not response:
return
Expand Down Expand Up @@ -467,7 +470,7 @@ def __init__(
self.item_limit = 20 * max(get_setting('pagemulti_trakt', 'int'), page_length)
self.login(force)

def authorize(self, login=False):
def authorize(self, login=False, confirmation=False):
def _get_token():
token = self.get_stored_token()
if not token.get('access_token'):
Expand Down Expand Up @@ -512,11 +515,15 @@ def _ask_login():
if not token and login:
_ask_login()

if not confirmation:
return self.authorization

# First time authorization in this session so let's confirm
if (
self.authorization
and not boolean(get_property('TraktIsAuth'))
and not get_timestamp(get_property('TraktRefreshTimeStamp', is_type=float) or 0)):
and not get_timestamp(get_property('TraktRefreshTimeStamp', is_type=float) or 0)
):

# Wait if another thread is checking authorization
if has_property_lock('TraktCheckingAuth'):
Expand Down
34 changes: 26 additions & 8 deletions resources/tmdbhelper/lib/api/trakt/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,21 @@ def wrapper(self, *args, **kwargs):
if boolean(get_property('TraktIsAuth')) and self.authorize():
return func(self, *args, **kwargs)

# Ask user to login because they want to use a method requiring authorization:
if not self.attempted_login and self.authorize(login=True):
# User not authorized or not authorized yet so get cached data instead
params = {}
params.update(kwargs)
params['cache_only'] = True
try:
content = None
content = func(self, *args, **params)
except TypeError:
pass

# Ask user to login because they want to use a method requiring authorization and theres no cached data
if not content and not self.attempted_login and self.authorize(login=True):
return func(self, *args, **kwargs)

return
return content
return wrapper


Expand Down Expand Up @@ -57,12 +67,11 @@ def use_lastupdated_cache(cache, func, *args, sync_info=None, cache_name='', **k
def use_activity_cache(activity_type=None, activity_key=None, cache_days=None):
"""
Decorator to cache and refresh if last activity changes
Optionally can pickle instead of cache if necessary (useful for large objects like sync lists)
Optionally send decorator_cache_refresh=True in func kwargs to force refresh
Optionally send decorator_cache_refresh=True in func kwargs to force refresh as long as authorized
If not authorized the decoractor will only return cached object
"""
def decorator(func):

@is_authorized
def wrapper(self, *args, allow_fallback=False, decorator_cache_refresh=None, **kwargs):
# Setup getter/setter cache funcs
func_get = self._cache.get_cache
Expand All @@ -73,9 +82,18 @@ def wrapper(self, *args, allow_fallback=False, decorator_cache_refresh=None, **k
cache_name = f'{self.__class__.__name__}.{cache_name}'
cache_name = format_name(cache_name, *args, **kwargs)

# Cached response last_activity timestamp matches last_activity from trakt so no need to refresh
# Check last activity from Trakt
last_activity = self._get_last_activity(activity_type, activity_key)
cache_object = func_get(cache_name) if last_activity and not decorator_cache_refresh else None

# Trakt not authorized yet so lets use or cached object only
if last_activity == -1:
cache_object = func_get(cache_name) or {}
return cache_object.get('response')

# Get our cached object
cache_object = None
if last_activity and not decorator_cache_refresh:
cache_object = func_get(cache_name)
if cache_object and cache_object.get('last_activity') == last_activity:
if cache_object.get('response') and cache_object.get('last_activity'):
return cache_object['response']
Expand Down
17 changes: 5 additions & 12 deletions resources/tmdbhelper/lib/api/trakt/progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@


class _TraktProgress():
@is_authorized
def get_ondeck_list(self, page=1, limit=None, sort_by=None, sort_how=None, trakt_type=None):
limit = limit or self.sync_item_limit
get_property('TraktSyncLastActivities.Expires', clear_property=True) # Wipe last activities cache to update now
Expand All @@ -23,7 +22,6 @@ def get_ondeck_list(self, page=1, limit=None, sort_by=None, sort_how=None, trakt
response = PaginatedItems(response['items'], page=page, limit=limit)
return response.items + response.next_page

@is_authorized
def get_towatch_list(self, trakt_type, page=1, limit=None):
limit = limit or self.sync_item_limit
get_property('TraktSyncLastActivities.Expires', clear_property=True) # Wipe last activities cache to update now
Expand All @@ -38,7 +36,6 @@ def _get_inprogress_items(self, sync_type, lowest=5, highest=95):
response = self.get_sync('playback', sync_type)
return [i for i in response if lowest <= try_int(i.get('progress', 0)) <= highest]

@is_authorized
def get_inprogress_shows_list(self, page=1, limit=None, params=None, next_page=True, sort_by=None, sort_how=None):
limit = limit or self.sync_item_limit
get_property('TraktSyncLastActivities.Expires', clear_property=True) # Wipe last activities cache to update now
Expand Down Expand Up @@ -174,7 +171,6 @@ def _get_comp_item(i):
hidden_items = {j for j in (_get_comp_item(i) for i in response) if j}
return list(hidden_items)

@is_authorized
def get_upnext_list(self, unique_id, id_type=None, page=1, limit=None):
""" Gets the next episodes for a show that user should watch next """
limit = limit or self.sync_item_limit
Expand All @@ -187,7 +183,6 @@ def get_upnext_list(self, unique_id, id_type=None, page=1, limit=None):
response = PaginatedItems(response['items'], page=page, limit=limit)
return response.items + response.next_page

@is_authorized
def get_upnext_episodes_list(self, page=1, sort_by=None, sort_how='desc', limit=None):
""" Gets a list of episodes for in-progress shows that user should watch next """
limit = limit or self.sync_item_limit
Expand All @@ -197,8 +192,10 @@ def get_upnext_episodes_list(self, page=1, sort_by=None, sort_how='desc', limit=
response = PaginatedItems(response['items'], page=page, limit=limit)
return response.items + response.next_page

@is_authorized
def _get_upnext_episodes_list(self, sort_by=None, sort_how='desc'):

shows = self._get_inprogress_shows() or []

def _get_upnext_episodes(i, get_single_episode=True):
""" Helper func for upnext episodes to pass through threaded """
try:
Expand All @@ -207,7 +204,6 @@ def _get_upnext_episodes(i, get_single_episode=True):
except (AttributeError, KeyError):
return
return self.get_upnext_episodes(slug, show, get_single_episode=get_single_episode)
shows = self._get_inprogress_shows() or []

# Get upnext episodes threaded
with ParallelThread(shows, _get_upnext_episodes) as pt:
Expand Down Expand Up @@ -284,14 +280,12 @@ def get_upnext_episodes(self, slug, show, get_single_episode=False):
except StopIteration:
return

@is_authorized
def get_movie_playcount(self, unique_id, id_type):
try:
return self.get_sync('watched', 'movie', id_type)[unique_id]['plays']
except (AttributeError, KeyError):
return

@is_authorized
def get_movie_playprogress(self, unique_id, id_type, key='progress'):
try:
return self.get_sync('playback', 'movie', id_type)[unique_id][key]
Expand Down Expand Up @@ -369,7 +363,6 @@ def get_episode_playcount(self, unique_id, id_type, season, episode):
if j.get('number', -1) == episode:
return j.get('plays', 1)

@is_authorized
def get_episodes_airedcount(self, unique_id, id_type, season=None):
""" Gets the number of aired episodes for a tvshow """
try:
Expand All @@ -394,11 +387,11 @@ def get_season_episodes_airedcount(self, unique_id, id_type, season, trakt_id=No
if i.get('number', -1) == season:
return i.get('aired_episodes')

def get_calendar(self, trakt_type, user=True, start_date=None, days=None, endpoint=None):
@is_authorized
def get_calendar(self, trakt_type, user=True, start_date=None, days=None, endpoint=None, **kwargs):
user = 'my' if user else 'all'
return self.get_response_json('calendars', user, trakt_type, endpoint, start_date, days, extended='full')

@is_authorized
@use_simple_cache(cache_days=0.25)
def get_calendar_episodes(self, startdate=0, days=1, user=True, endpoint=None):
# Broaden date range in case utc conversion bumps into different day
Expand Down
5 changes: 4 additions & 1 deletion resources/tmdbhelper/lib/api/trakt/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def _get_activity_timestamp(self, activities, activity_type=None, activity_key=N
return activities.get(activity_type, {}).get(activity_key)

@is_authorized
def _get_last_activity(self, activity_type=None, activity_key=None, cache_refresh=False):
def _get_last_activity(self, activity_type=None, activity_key=None, cache_refresh=False, cache_only=False):
def _cache_expired():
""" Check if the cached last_activities has expired """
last_exp = get_property('TraktSyncLastActivities.Expires', is_type=int)
Expand All @@ -75,6 +75,9 @@ def _cache_router():
return data_loads(get_property('TraktSyncLastActivities'))
return _cache_activity()

if cache_only:
return -1

if not self.last_activities:
self.last_activities = _cache_router()

Expand Down
15 changes: 10 additions & 5 deletions resources/tmdbhelper/lib/monitor/cronjob.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
from xbmc import Monitor
from jurialmunkey.parser import try_int
from threading import Thread
from tmdbhelper.lib.addon.tmdate import convert_timestamp, get_datetime_now, get_timedelta, get_datetime_today, get_datetime_time, get_datetime_combine
from tmdbhelper.lib.addon.plugin import get_setting, executebuiltin, get_infolabel
from tmdbhelper.lib.script.method.maintenance import recache_kodidb, clean_old_databases


class CronJobMonitor(Thread):
def __init__(self, update_hour=0):
from xbmc import Monitor
Thread.__init__(self)
self.exit = False
self.poll_time = 1800 # Poll every 30 mins since we don't need to get exact time for update
self.update_hour = update_hour
self.xbmc_monitor = Monitor()

def run(self):
from jurialmunkey.parser import try_int
from tmdbhelper.lib.api.trakt.api import TraktAPI
from tmdbhelper.lib.addon.tmdate import convert_timestamp, get_datetime_now, get_timedelta, get_datetime_today, get_datetime_time, get_datetime_combine
from tmdbhelper.lib.addon.plugin import get_setting, executebuiltin, get_infolabel
from tmdbhelper.lib.script.method.maintenance import recache_kodidb, clean_old_databases

# Check Trakt authorization
TraktAPI().authorize(confirmation=True)

clean_old_databases()
recache_kodidb(notification=False)

Expand Down

0 comments on commit e4ce6d6

Please sign in to comment.