Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TraktApi class dealing with caching/fetching trakt api methods #166

Merged
merged 12 commits into from
Apr 5, 2021
13 changes: 2 additions & 11 deletions plex_trakt_sync/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import click
from plex_trakt_sync.main import main
from plex_trakt_sync.clear_trakt_collections import clear_trakt_collections
from plex_trakt_sync.commands.sync import sync


@click.group(invoke_without_command=True)
Expand All @@ -10,16 +10,7 @@ def cli(ctx):
Plex-Trakt-Sync is a two-way-sync between trakt.tv and Plex Media Server
"""
if not ctx.invoked_subcommand:
main()


@click.command()
def sync():
"""
Perform sync between Plex and Trakt
"""

main()
sync()


@click.command()
Expand Down
62 changes: 62 additions & 0 deletions plex_trakt_sync/commands/sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import click

from plex_trakt_sync.requests_cache import requests_cache
from plex_trakt_sync.main import get_plex_server
from plex_trakt_sync.config import CONFIG
from plex_trakt_sync.decorators import measure_time
from plex_trakt_sync.main import process_movie_section, process_show_section
from plex_trakt_sync.plex_api import PlexApi
from plex_trakt_sync.trakt_api import TraktApi
from plex_trakt_sync.trakt_list_util import TraktListUtil
from plex_trakt_sync.logging import logging


def sync_all():
with requests_cache.disabled():
server = get_plex_server()
listutil = TraktListUtil()
plex = PlexApi(server)
trakt = TraktApi()

with measure_time("Loaded Trakt lists"):
trakt_watched_movies = trakt.watched_movies
trakt_watched_shows = trakt.watched_shows
trakt_movie_collection = trakt.movie_collection
trakt_ratings = trakt.ratings
trakt_watchlist_movies = trakt.watchlist_movies
trakt_liked_lists = trakt.liked_lists

if trakt_watchlist_movies:
listutil.addList(None, "Trakt Watchlist", traktid_list=trakt_watchlist_movies)

for lst in trakt_liked_lists:
listutil.addList(lst['username'], lst['listname'])

with requests_cache.disabled():
logging.info("Server version {} updated at: {}".format(server.version, server.updatedAt))
logging.info("Recently added: {}".format(server.library.recentlyAdded()[:5]))

for section in plex.library_sections:
if PlexApi.is_movie(section):
with measure_time("Processing section %s" % section.title):
process_movie_section(section, trakt_watched_movies, trakt_ratings, listutil, trakt_movie_collection)
elif PlexApi.is_show(section):
with measure_time("Processing section %s" % section.title):
process_show_section(section, trakt_watched_shows, listutil)
else:
continue

with measure_time("Updated plex watchlist"):
listutil.updatePlexLists(server)


@click.command()
def sync():
"""
Perform sync between Plex and Trakt
"""

logging.info("Starting sync Plex {} and Trakt {}".format(CONFIG['PLEX_USERNAME'], CONFIG['TRAKT_USERNAME']))

with measure_time("Completed full sync"):
sync_all()
73 changes: 1 addition & 72 deletions plex_trakt_sync/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import plexapi.server
import trakt
from plex_trakt_sync.path import pytrakt_file
from plex_trakt_sync.plex_api import PlexApi

trakt.core.CONFIG_PATH = pytrakt_file
import trakt.errors
Expand All @@ -15,7 +14,6 @@
from json.decoder import JSONDecodeError

from plex_trakt_sync import pytrakt_extensions
from plex_trakt_sync.trakt_list_util import TraktListUtil
from plex_trakt_sync.config import CONFIG
from plex_trakt_sync.logging import logging
from plex_trakt_sync.requests_cache import requests_cache
Expand Down Expand Up @@ -386,78 +384,9 @@ def get_plex_server():
exit(1)
return server


def respect_trakt_rate(last_time):
diff_time = time() - last_time
if diff_time < trakt_post_wait:
sleep(trakt_post_wait - diff_time)
return time()

def main():

start_time = time()
listutil = TraktListUtil()
logging.info("Starting sync Plex {} and Trakt {}".format(CONFIG['PLEX_USERNAME'], CONFIG['TRAKT_USERNAME']))
# do not use the cache for account specific stuff as this is subject to change
with requests_cache.disabled():
try:
trakt_user = trakt.users.User('me')
except trakt.errors.OAuthException as e:
m = "Trakt authentication error: {}".format(str(e))
logging.error(m)
exit(1)
if CONFIG['sync']['liked_lists']:
liked_lists = pytrakt_extensions.get_liked_lists()
trakt_watched_movies = set(
map(lambda m: m.trakt, trakt_user.watched_movies))
logging.debug("Watched movies from trakt: {}".format(
trakt_watched_movies))
trakt_movie_collection = set(
map(lambda m: m.trakt, trakt_user.movie_collection))
# logging.debug("Movie collection from trakt:", trakt_movie_collection)
trakt_watched_shows = pytrakt_extensions.allwatched()
if CONFIG['sync']['watchlist']:
listutil.addList(None, "Trakt Watchlist", traktid_list=list(
map(lambda m: m.trakt, trakt_user.watchlist_movies)))
# logging.debug("Movie watchlist from trakt:", trakt_movie_watchlist)
user_ratings = trakt_user.get_ratings(media_type='movies')
if CONFIG['sync']['liked_lists']:
for lst in liked_lists:
listutil.addList(lst['username'], lst['listname'])
ratings = {}
for r in user_ratings:
ratings[r['movie']['ids']['slug']] = r['rating']
logging.debug("Movie ratings from trakt: {}".format(ratings))
logging.info('Loaded Trakt lists.')

with requests_cache.disabled():
plex_server = get_plex_server()
plex = PlexApi(plex_server)
logging.info("Server version {} updated at: {}".format(
plex_server.version, plex_server.updatedAt))
logging.info("Recently added: {}".format(
plex_server.library.recentlyAdded()[:5]))

for section in plex.library_sections:
# process movie sections
section_start_time = time()
if type(section) is plexapi.library.MovieSection:
# clean_collections_in_section(section)
logging.info("Processing section {}".format(section.title))
process_movie_section(
section, trakt_watched_movies, ratings, listutil, trakt_movie_collection)
# process show sections
elif type(section) is plexapi.library.ShowSection:
logging.info("Processing section {}".format(section.title))
process_show_section(section, trakt_watched_shows, listutil)
else:
continue

timedelta = time() - section_start_time
m, s = divmod(timedelta, 60)
logging.warning("Completed section sync in " + (m>0) * "{:.0f} min ".format(m) + (s>0) * "{:.1f} seconds".format(s))

listutil.updatePlexLists(plex_server)
logging.info("Updated plex watchlist")
timedelta = time() - start_time
m, s = divmod(timedelta, 60)
logging.info("Completed full sync in " + (m>0) * "{:.0f} min ".format(m) + (s>0) * "{:.1f} seconds".format(s))
9 changes: 9 additions & 0 deletions plex_trakt_sync/plex_api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from plexapi.library import MovieSection, ShowSection
from plex_trakt_sync.decorators import memoize, nocache
from plex_trakt_sync.config import CONFIG

Expand All @@ -21,3 +22,11 @@ def library_sections(self):
result.append(section)

return result

@staticmethod
def is_movie(section):
return type(section) is MovieSection

@staticmethod
def is_show(section):
return type(section) is ShowSection
84 changes: 84 additions & 0 deletions plex_trakt_sync/trakt_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import trakt

from plex_trakt_sync import pytrakt_extensions
from plex_trakt_sync.path import pytrakt_file

trakt.core.CONFIG_PATH = pytrakt_file
import trakt.users
from trakt.errors import OAuthException, ForbiddenException

from plex_trakt_sync.logging import logging
from plex_trakt_sync.decorators import memoize, nocache
from plex_trakt_sync.config import CONFIG


class TraktApi:
"""
Trakt API class abstracting common data access and dealing with requests cache.
"""

@property
@memoize
@nocache
def me(self):
try:
return trakt.users.User('me')
except (OAuthException, ForbiddenException) as e:
logging.fatal("Trakt authentication error: {}".format(str(e)))
raise e

@property
@memoize
@nocache
def liked_lists(self):
if not CONFIG['sync']['liked_lists']:
return []
return pytrakt_extensions.get_liked_lists()

@property
@memoize
@nocache
def watched_movies(self):
return set(
map(lambda m: m.trakt, self.me.watched_movies)
)

@property
@memoize
@nocache
def movie_collection(self):
return set(
map(lambda m: m.trakt, self.me.movie_collection)
)

@property
@memoize
@nocache
def watched_shows(self):
return pytrakt_extensions.allwatched()

@property
@memoize
@nocache
def watchlist_movies(self):
if not CONFIG['sync']['watchlist']:
return []

return list(
map(lambda m: m.trakt, self.me.watchlist_movies)
)

@property
@memoize
@nocache
def movie_ratings(self):
return self.me.get_ratings(media_type='movies')

@property
@memoize
def ratings(self):
ratings = {}
for r in self.movie_ratings:
ratings[r['movie']['ids']['slug']] = r['rating']

return ratings