Skip to content

Commit

Permalink
Merge pull request #166 from glensc/sync-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
glensc authored Apr 5, 2021
2 parents 8a29929 + e24bcf5 commit f5da78d
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 83 deletions.
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

0 comments on commit f5da78d

Please sign in to comment.