2022-05-09 06:58:09 +00:00
import requests , time , webbrowser
2021-01-20 21:37:59 +00:00
from modules import util
2022-05-12 19:10:03 +00:00
from modules . util import Failed , TimeoutExpired , YAML
2022-05-09 06:58:09 +00:00
from retrying import retry
2021-01-20 21:37:59 +00:00
2022-02-13 16:33:57 +00:00
logger = util . logger
2021-01-20 21:37:59 +00:00
2021-07-15 17:42:28 +00:00
redirect_uri = " urn:ietf:wg:oauth:2.0:oob "
base_url = " https://api.trakt.tv "
2021-03-30 05:50:53 +00:00
builders = [
2022-03-27 06:26:08 +00:00
" trakt_list " , " trakt_list_details " , " trakt_chart " , " trakt_userlist " , " trakt_boxoffice " , " trakt_recommendations " ,
2021-10-25 15:40:49 +00:00
" trakt_collected_daily " , " trakt_collected_weekly " , " trakt_collected_monthly " , " trakt_collected_yearly " , " trakt_collected_all " ,
2022-03-27 06:26:08 +00:00
" trakt_recommended_daily " , " trakt_recommended_weekly " , " trakt_recommended_monthly " , " trakt_recommended_yearly " , " trakt_recommended_all " ,
2021-10-25 15:40:49 +00:00
" trakt_watched_daily " , " trakt_watched_weekly " , " trakt_watched_monthly " , " trakt_watched_yearly " , " trakt_watched_all " ,
2023-10-30 06:56:57 +00:00
" trakt_collection " , " trakt_anticipated " , " trakt_popular " , " trakt_trending " , " trakt_watchlist "
2021-03-30 05:50:53 +00:00
]
2021-07-29 21:36:26 +00:00
sorts = [
" rank " , " added " , " title " , " released " , " runtime " , " popularity " ,
" percentage " , " votes " , " random " , " my_rating " , " watched " , " collected "
]
2022-03-28 16:15:34 +00:00
status = [ " returning " , " production " , " planned " , " canceled " , " ended " ]
status_translation = {
" returning " : " returning series " , " production " : " in production " ,
" planned " : " planned " , " canceled " : " canceled " , " ended " : " ended "
}
2024-01-24 18:41:53 +00:00
userlist_options = [ " favorites " , " watched " , " collection " , " watchlist " ]
2022-03-27 06:26:08 +00:00
periods = [ " daily " , " weekly " , " monthly " , " yearly " , " all " ]
2022-01-31 08:37:49 +00:00
id_translation = { " movie " : " movie " , " show " : " show " , " season " : " show " , " episode " : " show " , " person " : " person " , " list " : " list " }
id_types = {
" movie " : ( " tmdb " , " TMDb ID " ) ,
" person " : ( " tmdb " , " TMDb ID " ) ,
" show " : ( " tvdb " , " TVDb ID " ) ,
" season " : ( " tvdb " , " TVDb ID " ) ,
" episode " : ( " tvdb " , " TVDb ID " ) ,
" list " : ( " slug " , " Trakt Slug " )
}
2021-03-30 05:50:53 +00:00
2021-06-14 15:24:11 +00:00
class Trakt :
2021-07-26 19:03:17 +00:00
def __init__ ( self , config , params ) :
2021-07-14 14:47:20 +00:00
self . config = config
2021-01-20 21:37:59 +00:00
self . client_id = params [ " client_id " ]
self . client_secret = params [ " client_secret " ]
2022-03-24 14:10:27 +00:00
self . pin = params [ " pin " ]
2021-01-20 21:37:59 +00:00
self . config_path = params [ " config_path " ]
2021-07-26 19:03:17 +00:00
self . authorization = params [ " authorization " ]
2022-02-13 16:33:57 +00:00
logger . secret ( self . client_secret )
2021-05-07 19:53:54 +00:00
if not self . _save ( self . authorization ) :
if not self . _refresh ( ) :
self . _authorization ( )
2022-05-09 06:58:09 +00:00
self . _slugs = None
2022-03-28 16:15:34 +00:00
self . _movie_genres = None
self . _show_genres = None
self . _movie_languages = None
self . _show_languages = None
self . _movie_countries = None
self . _show_countries = None
self . _movie_certifications = None
self . _show_certifications = None
2022-05-09 06:58:09 +00:00
@property
def slugs ( self ) :
if self . _slugs is None :
items = [ ]
try :
items = [ i [ " ids " ] [ " slug " ] for i in self . _request ( f " /users/me/lists " ) ]
except Failed :
pass
self . _slugs = items
return self . _slugs
2022-03-28 16:15:34 +00:00
@property
def movie_genres ( self ) :
if not self . _movie_genres :
self . _movie_genres = [ g [ " slug " ] for g in self . _request ( " /genres/movies " ) ]
return self . _movie_genres
@property
def show_genres ( self ) :
if not self . _show_genres :
self . _show_genres = [ g [ " slug " ] for g in self . _request ( " /genres/shows " ) ]
return self . _show_genres
@property
def movie_languages ( self ) :
if not self . _movie_languages :
self . _movie_languages = [ g [ " code " ] for g in self . _request ( " /languages/movies " ) ]
return self . _movie_languages
@property
def show_languages ( self ) :
if not self . _show_languages :
self . _show_languages = [ g [ " code " ] for g in self . _request ( " /languages/shows " ) ]
return self . _show_languages
@property
def movie_countries ( self ) :
if not self . _movie_countries :
self . _movie_countries = [ g [ " code " ] for g in self . _request ( " /countries/movies " ) ]
return self . _movie_countries
@property
def show_countries ( self ) :
if not self . _show_countries :
self . _show_countries = [ g [ " code " ] for g in self . _request ( " /countries/shows " ) ]
return self . _show_countries
@property
def movie_certifications ( self ) :
if not self . _movie_certifications :
self . _movie_certifications = [ g [ " slug " ] for g in self . _request ( " /certifications/movies " ) [ " us " ] ]
return self . _movie_certifications
@property
def show_certifications ( self ) :
if not self . _show_certifications :
self . _show_certifications = [ g [ " slug " ] for g in self . _request ( " /certifications/shows " ) [ " us " ] ]
return self . _show_certifications
2021-01-20 21:37:59 +00:00
2021-05-07 19:53:54 +00:00
def _authorization ( self ) :
2022-03-24 14:10:27 +00:00
if self . pin :
pin = self . pin
else :
url = f " https://trakt.tv/oauth/authorize?response_type=code&redirect_uri= { redirect_uri } &client_id= { self . client_id } "
logger . info ( f " Navigate to: { url } " )
logger . info ( " If you get an OAuth error your client_id or client_secret is invalid " )
webbrowser . open ( url , new = 2 )
2022-08-23 15:55:07 +00:00
try :
pin = util . logger_input ( " Trakt pin (case insensitive) " , timeout = 300 ) . strip ( )
except TimeoutExpired :
raise Failed ( " Input Timeout: Trakt pin required. " )
if not pin :
raise Failed ( " Trakt Error: Trakt pin required. " )
2024-01-11 20:02:42 +00:00
json_data = {
2021-07-15 17:42:28 +00:00
" code " : pin ,
" client_id " : self . client_id ,
" client_secret " : self . client_secret ,
" redirect_uri " : redirect_uri ,
" grant_type " : " authorization_code "
}
2024-01-11 20:02:42 +00:00
response = self . config . post ( f " { base_url } /oauth/token " , json = json_data , headers = { " Content-Type " : " application/json " } )
2021-07-15 17:42:28 +00:00
if response . status_code != 200 :
2022-08-23 15:55:07 +00:00
raise Failed ( f " Trakt Error: ( { response . status_code } ) { response . reason } " )
#raise Failed("Trakt Error: Invalid trakt pin. If you're sure you typed it in correctly your client_id or client_secret may be invalid")
2022-08-23 15:57:27 +00:00
response_json = response . json ( )
2022-09-07 19:32:52 +00:00
logger . trace ( response_json )
2022-08-23 15:57:27 +00:00
if not self . _save ( response_json ) :
2021-01-20 21:37:59 +00:00
raise Failed ( " Trakt Error: New Authorization Failed " )
2021-07-15 17:42:28 +00:00
def _check ( self , authorization = None ) :
2022-02-13 16:33:57 +00:00
token = self . authorization [ ' access_token ' ] if authorization is None else authorization [ ' access_token ' ]
2021-07-15 17:42:28 +00:00
headers = {
" Content-Type " : " application/json " ,
2022-02-13 16:33:57 +00:00
" Authorization " : f " Bearer { token } " ,
2021-07-15 17:42:28 +00:00
" trakt-api-version " : " 2 " ,
" trakt-api-key " : self . client_id
}
2022-02-13 16:33:57 +00:00
logger . secret ( token )
2021-07-15 17:42:28 +00:00
response = self . config . get ( f " { base_url } /users/settings " , headers = headers )
2022-06-15 16:03:16 +00:00
if response . status_code == 423 :
raise Failed ( " Trakt Error: Account is Locked please Contact Trakt Support " )
2022-08-23 15:55:07 +00:00
if response . status_code != 200 :
logger . debug ( f " Trakt Error: ( { response . status_code } ) { response . reason } " )
2021-07-15 17:42:28 +00:00
return response . status_code == 200
2021-01-20 21:37:59 +00:00
2021-05-07 19:53:54 +00:00
def _refresh ( self ) :
2021-01-20 21:37:59 +00:00
if self . authorization and " refresh_token " in self . authorization and self . authorization [ " refresh_token " ] :
logger . info ( " Refreshing Access Token... " )
2024-01-11 20:02:42 +00:00
json_data = {
2021-07-15 17:42:28 +00:00
" refresh_token " : self . authorization [ " refresh_token " ] ,
" client_id " : self . client_id ,
" client_secret " : self . client_secret ,
" redirect_uri " : redirect_uri ,
" grant_type " : " refresh_token "
}
2024-01-11 20:02:42 +00:00
response = self . config . post ( f " { base_url } /oauth/token " , json = json_data , headers = { " Content-Type " : " application/json " } )
2021-07-15 17:42:28 +00:00
if response . status_code != 200 :
return False
return self . _save ( response . json ( ) )
2021-01-20 21:37:59 +00:00
return False
2021-05-07 19:53:54 +00:00
def _save ( self , authorization ) :
if authorization and self . _check ( authorization ) :
2021-12-28 00:01:43 +00:00
if self . authorization != authorization and not self . config . read_only :
2022-05-12 19:10:03 +00:00
yaml = YAML ( self . config_path )
yaml . data [ " trakt " ] [ " pin " ] = None
yaml . data [ " trakt " ] [ " authorization " ] = {
2021-01-20 21:37:59 +00:00
" access_token " : authorization [ " access_token " ] ,
" token_type " : authorization [ " token_type " ] ,
" expires_in " : authorization [ " expires_in " ] ,
" refresh_token " : authorization [ " refresh_token " ] ,
" scope " : authorization [ " scope " ] ,
" created_at " : authorization [ " created_at " ]
}
2021-02-24 06:44:06 +00:00
logger . info ( f " Saving authorization information to { self . config_path } " )
2022-05-12 19:10:03 +00:00
yaml . save ( )
2021-07-15 17:42:28 +00:00
self . authorization = authorization
2021-01-20 21:37:59 +00:00
return True
return False
2022-05-09 06:58:09 +00:00
@retry ( stop_max_attempt_number = 6 , wait_fixed = 10000 , retry_on_exception = util . retry_if_not_failed )
2024-01-11 20:02:42 +00:00
def _request ( self , url , params = None , json_data = None ) :
2021-07-15 17:42:28 +00:00
headers = {
" Content-Type " : " application/json " ,
" Authorization " : f " Bearer { self . authorization [ ' access_token ' ] } " ,
" trakt-api-version " : " 2 " ,
" trakt-api-key " : self . client_id
}
2021-08-02 21:01:19 +00:00
output_json = [ ]
2022-03-28 16:15:34 +00:00
if params is None :
params = { }
2021-08-02 21:01:19 +00:00
pages = 1
current = 1
2022-09-07 19:32:52 +00:00
logger . trace ( f " URL: { base_url } { url } " )
if params :
logger . trace ( f " Params: { params } " )
2024-01-11 20:02:42 +00:00
if json_data :
logger . trace ( f " JSON: { json_data } " )
2021-08-02 21:01:19 +00:00
while current < = pages :
2022-05-09 06:58:09 +00:00
if pages > 1 :
2022-03-28 16:15:34 +00:00
params [ " page " ] = current
2024-01-11 20:02:42 +00:00
if json_data is not None :
response = self . config . post ( f " { base_url } { url } " , json = json_data , headers = headers )
2021-08-02 21:01:19 +00:00
else :
2022-05-09 06:58:09 +00:00
response = self . config . get ( f " { base_url } { url } " , headers = headers , params = params )
2022-05-11 05:17:30 +00:00
if pages == 1 and " X-Pagination-Page-Count " in response . headers and not params :
2022-05-09 06:58:09 +00:00
pages = int ( response . headers [ " X-Pagination-Page-Count " ] )
if response . status_code > = 400 :
2021-08-02 21:01:19 +00:00
raise Failed ( f " ( { response . status_code } ) { response . reason } " )
2024-01-11 20:02:42 +00:00
response_json = response . json ( )
2022-09-07 19:32:52 +00:00
logger . trace ( f " Headers: { response . headers } " )
2024-01-11 20:02:42 +00:00
logger . trace ( f " Response: { response_json } " )
if isinstance ( response_json , dict ) :
return response_json
2022-05-09 06:58:09 +00:00
else :
2024-01-11 20:02:42 +00:00
output_json . extend ( response_json )
2021-08-02 21:01:19 +00:00
current + = 1
return output_json
def user_ratings ( self , is_movie ) :
media = " movie " if is_movie else " show "
id_type = " tmdb " if is_movie else " tvdb "
return { int ( i [ media ] [ " ids " ] [ id_type ] ) : i [ " rating " ] for i in self . _request ( f " /users/me/ratings/ { media } s " ) }
2021-07-15 17:42:28 +00:00
2021-05-08 00:40:07 +00:00
def convert ( self , external_id , from_source , to_source , media_type ) :
2021-07-15 17:42:28 +00:00
path = f " /search/ { from_source } / { external_id } "
2022-03-28 16:15:34 +00:00
params = { " type " : media_type } if from_source in [ " tmdb " , " tvdb " ] else None
lookup = self . _request ( path , params = params )
2021-07-15 17:42:28 +00:00
if lookup and media_type in lookup [ 0 ] and to_source in lookup [ 0 ] [ media_type ] [ " ids " ] :
return lookup [ 0 ] [ media_type ] [ " ids " ] [ to_source ]
2021-05-08 04:05:10 +00:00
raise Failed ( f " Trakt Error: No { to_source . upper ( ) . replace ( ' B ' , ' b ' ) } ID found for { from_source . upper ( ) . replace ( ' B ' , ' b ' ) } ID: { external_id } " )
2021-01-20 21:37:59 +00:00
2021-07-15 17:42:28 +00:00
def list_description ( self , data ) :
try :
2021-08-02 21:01:19 +00:00
return self . _request ( requests . utils . urlparse ( data ) . path ) [ " description " ]
2021-07-15 17:42:28 +00:00
except Failed :
2023-09-01 14:11:00 +00:00
raise Failed ( data )
2021-03-30 05:49:10 +00:00
2023-02-06 20:34:40 +00:00
def _parse ( self , items , typeless = False , item_type = None , trakt_ids = False , ignore_other = False ) :
2021-08-04 13:42:21 +00:00
ids = [ ]
for item in items :
2021-08-22 15:54:33 +00:00
if typeless :
2021-08-07 06:01:21 +00:00
data = item
2021-11-15 21:12:06 +00:00
current_type = item_type
2021-08-22 15:54:33 +00:00
elif item_type :
data = item [ item_type ]
current_type = item_type
elif " type " in item and item [ " type " ] in id_translation :
2022-01-29 16:06:53 +00:00
data = item [ id_translation [ item [ " type " ] ] ]
2021-08-22 15:54:33 +00:00
current_type = item [ " type " ]
2021-08-07 06:01:21 +00:00
else :
2021-08-22 15:54:33 +00:00
continue
2023-02-06 20:34:40 +00:00
if current_type in [ " person " , " list " ] and ignore_other :
continue
2022-01-31 08:37:49 +00:00
id_type , id_display = id_types [ current_type ]
2021-09-18 02:07:30 +00:00
if id_type in data [ " ids " ] and data [ " ids " ] [ id_type ] :
2021-08-22 15:54:33 +00:00
final_id = data [ " ids " ] [ id_type ]
if current_type == " episode " :
final_id = f " { final_id } _ { item [ current_type ] [ ' season ' ] } "
if current_type in [ " episode " , " season " ] :
final_id = f " { final_id } _ { item [ current_type ] [ ' number ' ] } "
2022-01-31 08:37:49 +00:00
if current_type in [ " person " , " list " ] :
2022-01-29 16:06:53 +00:00
final_id = ( final_id , data [ " name " ] )
final_type = f " { id_type } _ { current_type } " if current_type in [ " episode " , " season " , " person " ] else id_type
2022-05-09 06:58:09 +00:00
ids . append ( ( int ( item [ " id " ] ) , final_id , final_type ) if trakt_ids else ( final_id , final_type ) )
2021-08-04 13:42:21 +00:00
else :
2022-01-31 08:37:49 +00:00
name = data [ " name " ] if current_type in [ " person " , " list " ] else f " { data [ ' title ' ] } ( { data [ ' year ' ] } ) "
2023-01-27 13:33:58 +00:00
logger . warning ( f " Trakt Error: No { id_display } found for { name } " )
2021-08-07 06:01:21 +00:00
return ids
2021-08-04 13:42:21 +00:00
2022-05-09 06:58:09 +00:00
def _build_item_json ( self , ids ) :
data = { }
for input_id , id_type in ids :
movies = id_type in [ " imdb " , " tmdb " ]
shows = id_type in [ " imdb " , " tvdb " , " tmdb_show " , " tvdb_season " , " tvdb_episode " ]
if not movies and not shows :
continue
type_set = str ( id_type ) . split ( " _ " )
id_set = str ( input_id ) . split ( " _ " )
item = { " ids " : { type_set [ 0 ] : id_set [ 0 ] if type_set [ 0 ] == " imdb " else int ( id_set [ 0 ] ) } }
if id_type in [ " tvdb_season " , " tvdb_episode " ] :
season_data = { " number " : int ( id_set [ 1 ] ) }
if id_type == " tvdb_episode " :
season_data [ " episodes " ] = [ { " number " : int ( id_set [ 2 ] ) } ]
item [ " seasons " ] = [ season_data ]
if movies :
if " movies " not in data :
data [ " movies " ] = [ ]
data [ " movies " ] . append ( item )
if shows :
if " shows " not in data :
data [ " shows " ] = [ ]
data [ " shows " ] . append ( item )
return data
def sync_list ( self , slug , ids ) :
2022-05-28 05:32:02 +00:00
current_ids = self . _list ( slug , urlparse = False , fail = False )
2022-05-09 06:58:09 +00:00
2022-05-09 15:22:41 +00:00
def read_result ( data , obj_type , result_type , result_str = None ) :
result_str = result_str if result_str else result_type . capitalize ( )
if data [ result_type ] [ obj_type ] > 0 :
logger . info ( f " { data [ result_type ] [ obj_type ] } { obj_type . capitalize ( ) } { result_str } " )
def read_not_found ( data , result_str ) :
not_found = [ ]
for item in data [ " not_found " ] [ " movies " ] :
not_found . append ( ( item [ " ids " ] [ " tmdb " ] , " tmdb " ) )
for item in data [ " not_found " ] [ " shows " ] :
not_found . append ( ( item [ " ids " ] [ " tvdb " ] , " tvdb " ) )
for item in data [ " not_found " ] [ " seasons " ] :
not_found . append ( ( f " { item [ ' ids ' ] [ ' tvdb ' ] } _ { item [ ' seasons ' ] [ 0 ] [ ' number ' ] } " , " tvdb_season " ) )
for item in data [ " not_found " ] [ " episodes " ] :
not_found . append ( ( f " { item [ ' ids ' ] [ ' tvdb ' ] } _ { item [ ' seasons ' ] [ 0 ] [ ' number ' ] } _ { item [ ' seasons ' ] [ 0 ] [ ' episodes ' ] [ 0 ] [ ' number ' ] } " , " tvdb_episode " ) )
if not_found :
logger . error ( f " { len ( not_found ) } Items Unable to { result_str } : { not_found } " )
2022-05-09 06:58:09 +00:00
add_ids = [ id_set for id_set in ids if id_set not in current_ids ]
if add_ids :
2022-05-09 15:22:41 +00:00
logger . info ( " " )
2024-01-11 20:02:42 +00:00
results = self . _request ( f " /users/me/lists/ { slug } /items " , json_data = self . _build_item_json ( add_ids ) )
2022-05-09 15:22:41 +00:00
for object_type in [ " movies " , " shows " , " seasons " , " episodes " ] :
read_result ( results , object_type , " added " )
read_not_found ( results , " Add " )
2022-05-09 06:58:09 +00:00
time . sleep ( 1 )
remove_ids = [ id_set for id_set in current_ids if id_set not in ids ]
if remove_ids :
2022-05-09 15:22:41 +00:00
logger . info ( " " )
2024-01-11 20:02:42 +00:00
results = self . _request ( f " /users/me/lists/ { slug } /items/remove " , json_data = self . _build_item_json ( remove_ids ) )
2022-05-09 15:22:41 +00:00
for object_type in [ " movies " , " shows " , " seasons " , " episodes " ] :
read_result ( results , object_type , " deleted " , " Removed " )
read_not_found ( results , " Remove " )
2022-05-09 06:58:09 +00:00
time . sleep ( 1 )
trakt_ids = self . _list ( slug , urlparse = False , trakt_ids = True )
trakt_lookup = { f " { ty } _ { i_id } " : t_id for t_id , i_id , ty in trakt_ids }
rank_ids = [ trakt_lookup [ f " { ty } _ { i_id } " ] for i_id , ty in ids if f " { ty } _ { i_id } " in trakt_lookup ]
2024-01-11 20:02:42 +00:00
self . _request ( f " /users/me/lists/ { slug } /items/reorder " , json_data = { " rank " : rank_ids } )
2022-05-09 15:22:41 +00:00
logger . info ( " " )
logger . info ( " Trakt List Ordered Successfully " )
2022-05-09 06:58:09 +00:00
2022-05-06 13:32:37 +00:00
def all_user_lists ( self , user = " me " ) :
2022-01-29 16:06:53 +00:00
try :
2022-03-27 06:26:08 +00:00
items = self . _request ( f " /users/ { user } /lists " )
2022-01-29 16:06:53 +00:00
except Failed :
2022-03-27 06:26:08 +00:00
raise Failed ( f " Trakt Error: User { user } not found " )
2022-01-29 16:06:53 +00:00
if len ( items ) == 0 :
2022-03-27 06:26:08 +00:00
raise Failed ( f " Trakt Error: User { user } has no lists " )
2022-05-06 13:32:37 +00:00
return [ ( user , i [ " ids " ] [ " slug " ] , i [ " name " ] ) for i in items ]
2022-01-31 08:37:49 +00:00
2022-03-27 06:26:08 +00:00
def all_liked_lists ( self ) :
2022-01-31 08:37:49 +00:00
items = self . _request ( f " /users/likes/lists " )
if len ( items ) == 0 :
raise Failed ( f " Trakt Error: No Liked lists found " )
return { self . build_user_url ( i [ ' list ' ] [ ' user ' ] [ ' ids ' ] [ ' slug ' ] , i [ ' list ' ] [ ' ids ' ] [ ' slug ' ] ) : i [ " list " ] [ " name " ] for i in items }
def build_user_url ( self , user , name ) :
return f " { base_url . replace ( ' api. ' , ' ' ) } /users/ { user } /lists/ { name } "
2022-01-29 16:06:53 +00:00
2023-02-06 20:34:40 +00:00
def _list ( self , data , urlparse = True , trakt_ids = False , fail = True , ignore_other = False ) :
2021-07-15 17:42:28 +00:00
try :
2023-02-21 20:01:45 +00:00
url = requests . utils . urlparse ( data ) . path . replace ( " /official/ " , " / " ) if urlparse else f " /users/me/lists/ { data } "
2022-05-06 13:32:37 +00:00
items = self . _request ( f " { url } /items " )
2021-07-15 17:42:28 +00:00
except Failed :
2021-08-07 06:01:21 +00:00
raise Failed ( f " Trakt Error: List { data } not found " )
2021-07-15 17:42:28 +00:00
if len ( items ) == 0 :
2022-05-28 05:32:02 +00:00
if fail :
raise Failed ( f " Trakt Error: List { data } is empty " )
else :
return [ ]
2023-02-06 20:34:40 +00:00
return self . _parse ( items , trakt_ids = trakt_ids , ignore_other = ignore_other )
2021-08-07 06:01:21 +00:00
2023-02-06 20:34:40 +00:00
def _userlist ( self , list_type , user , is_movie , sort_by = None , ignore_other = False ) :
2021-08-07 06:01:21 +00:00
try :
2022-03-27 06:26:08 +00:00
url_end = " movies " if is_movie else " shows "
if sort_by :
url_end = f " { url_end } / { sort_by } "
items = self . _request ( f " /users/ { user } / { list_type } / { url_end } " )
2021-08-07 06:01:21 +00:00
except Failed :
2022-03-27 06:26:08 +00:00
raise Failed ( f " Trakt Error: User { user } not found " )
2021-08-07 06:01:21 +00:00
if len ( items ) == 0 :
2022-03-27 06:26:08 +00:00
raise Failed ( f " Trakt Error: { user } ' s { list_type . capitalize ( ) } is empty " )
2023-02-06 20:34:40 +00:00
return self . _parse ( items , item_type = " movie " if is_movie else " show " , ignore_other = ignore_other )
2021-03-27 07:30:07 +00:00
2022-03-27 06:26:08 +00:00
def _recommendations ( self , limit , is_movie ) :
2022-03-04 11:15:32 +00:00
media_type = " Movie " if is_movie else " Show "
try :
2022-03-28 16:15:34 +00:00
items = self . _request ( f " /recommendations/ { ' movies ' if is_movie else ' shows ' } " , params = { " limit " : limit } )
2022-03-04 11:15:32 +00:00
except Failed :
raise Failed ( f " Trakt Error: failed to fetch { media_type } Recommendations " )
if len ( items ) == 0 :
raise Failed ( f " Trakt Error: no { media_type } Recommendations were found " )
2022-03-06 08:39:42 +00:00
return self . _parse ( items , typeless = True , item_type = " movie " if is_movie else " show " )
2022-03-04 11:15:32 +00:00
2023-02-06 20:34:40 +00:00
def _charts ( self , chart_type , is_movie , params , time_period = None , ignore_other = False ) :
2022-03-27 06:26:08 +00:00
chart_url = f " { chart_type } / { time_period } " if time_period else chart_type
2022-03-28 16:15:34 +00:00
items = self . _request ( f " / { ' movies ' if is_movie else ' shows ' } / { chart_url } " , params = params )
2023-02-06 20:34:40 +00:00
return self . _parse ( items , typeless = chart_type == " popular " , item_type = " movie " if is_movie else " show " , ignore_other = ignore_other )
2021-03-09 07:50:44 +00:00
2022-01-29 16:06:53 +00:00
def get_people ( self , data ) :
2022-03-27 06:26:08 +00:00
return { str ( i [ 0 ] [ 0 ] ) : i [ 0 ] [ 1 ] for i in self . _list ( data ) if i [ 1 ] == " tmdb_person " }
2022-01-29 16:06:53 +00:00
2022-03-27 06:26:08 +00:00
def validate_list ( self , trakt_lists ) :
2021-07-29 21:36:26 +00:00
values = util . get_list ( trakt_lists , split = False )
2021-01-20 21:37:59 +00:00
trakt_values = [ ]
for value in values :
2021-12-02 06:39:46 +00:00
if isinstance ( value , dict ) :
raise Failed ( " Trakt Error: List cannot be a dictionary " )
2021-01-20 21:37:59 +00:00
try :
2022-03-27 06:26:08 +00:00
self . _list ( value )
2021-01-20 21:37:59 +00:00
trakt_values . append ( value )
except Failed as e :
logger . error ( e )
if len ( trakt_values ) == 0 :
2022-03-27 06:26:08 +00:00
raise Failed ( f " Trakt Error: No valid Trakt Lists in { values } " )
2021-01-20 21:37:59 +00:00
return trakt_values
2022-03-28 16:15:34 +00:00
def validate_chart ( self , err_type , method_name , data , is_movie ) :
2022-03-27 06:26:08 +00:00
valid_dicts = [ ]
for trakt_dict in util . get_list ( data , split = False ) :
if not isinstance ( trakt_dict , dict ) :
raise Failed ( f " { err_type } Error: { method_name } must be a dictionary " )
dict_methods = { dm . lower ( ) : dm for dm in trakt_dict }
try :
if method_name == " trakt_chart " :
2022-03-28 16:15:34 +00:00
final_dict = { }
2023-10-30 06:56:57 +00:00
final_dict [ " chart " ] = util . parse ( err_type , " chart " , trakt_dict , methods = dict_methods , parent = method_name , options = [ " recommended " , " watched " , " anticipated " , " collected " , " trending " , " popular " ] )
2022-03-28 16:15:34 +00:00
final_dict [ " limit " ] = util . parse ( err_type , " limit " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , default = 10 )
final_dict [ " time_period " ] = None
if final_dict [ " chart " ] in [ " recommended " , " watched " , " collected " ] and " time_period " in dict_methods :
final_dict [ " time_period " ] = util . parse ( err_type , " time_period " , trakt_dict , methods = dict_methods , parent = method_name , default = " weekly " , options = periods )
if " query " in dict_methods :
final_dict [ " query " ] = util . parse ( err_type , " query " , trakt_dict , methods = dict_methods , parent = method_name )
2022-06-26 19:18:09 +00:00
if " years " in dict_methods :
2022-03-28 16:15:34 +00:00
try :
2022-06-26 19:18:09 +00:00
if trakt_dict [ dict_methods [ " years " ] ] and len ( str ( trakt_dict [ dict_methods [ " years " ] ] ) ) == 4 :
final_dict [ " years " ] = util . parse ( err_type , " years " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 1000 , maximum = 3000 )
2022-03-28 16:15:34 +00:00
else :
2022-06-26 19:18:09 +00:00
final_dict [ " years " ] = util . parse ( err_type , " years " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 1000 , maximum = 3000 , range_split = " - " )
2022-03-28 16:15:34 +00:00
except Failed :
raise Failed ( f " { err_type } Error: trakt_chart year attribute must be either a 4 digit year or a range of two 4 digit year with a ' - ' i.e. 1950 or 1950-1959 " )
if " runtimes " in dict_methods :
final_dict [ " runtimes " ] = util . parse ( err_type , " runtimes " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , range_split = " - " )
if " ratings " in dict_methods :
final_dict [ " ratings " ] = util . parse ( err_type , " ratings " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100 , range_split = " - " )
2024-01-11 20:02:42 +00:00
if " votes " in dict_methods :
final_dict [ " votes " ] = util . parse ( err_type , " votes " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100000 , range_split = " - " )
if " tmdb_ratings " in dict_methods :
final_dict [ " tmdb_ratings " ] = util . parse ( err_type , " tmdb_ratings " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " float " , minimum = 0 , maximum = 10 , range_split = " - " )
if " tmdb_votes " in dict_methods :
final_dict [ " tmdb_votes " ] = util . parse ( err_type , " tmdb_votes " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100000 , range_split = " - " )
if " imdb_ratings " in dict_methods :
final_dict [ " imdb_ratings " ] = util . parse ( err_type , " imdb_ratings " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " float " , minimum = 0 , maximum = 10 , range_split = " - " )
if " imdb_votes " in dict_methods :
final_dict [ " imdb_votes " ] = util . parse ( err_type , " imdb_votes " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 3000000 , range_split = " - " )
if " rt_meters " in dict_methods :
final_dict [ " rt_meters " ] = util . parse ( err_type , " rt_meters " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100 , range_split = " - " )
if " rt_user_meters " in dict_methods :
final_dict [ " rt_user_meters " ] = util . parse ( err_type , " rt_user_meters " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100 , range_split = " - " )
if " metascores " in dict_methods :
final_dict [ " metascores " ] = util . parse ( err_type , " metascores " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " int " , minimum = 0 , maximum = 100 , range_split = " - " )
2022-03-28 16:15:34 +00:00
if " genres " in dict_methods :
2022-04-03 21:22:58 +00:00
final_dict [ " genres " ] = util . parse ( err_type , " genres " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " , options = self . movie_genres if is_movie else self . show_genres )
2022-03-28 16:15:34 +00:00
if " languages " in dict_methods :
2022-04-03 21:22:58 +00:00
final_dict [ " languages " ] = util . parse ( err_type , " languages " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " , options = self . movie_languages if is_movie else self . show_languages )
2022-03-28 16:15:34 +00:00
if " countries " in dict_methods :
2022-04-03 21:22:58 +00:00
final_dict [ " countries " ] = util . parse ( err_type , " countries " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " , options = self . movie_countries if is_movie else self . show_countries )
2022-03-28 16:15:34 +00:00
if " certifications " in dict_methods :
2022-04-03 21:22:58 +00:00
final_dict [ " certifications " ] = util . parse ( err_type , " certifications " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " , options = self . movie_certifications if is_movie else self . show_certifications )
2024-01-11 20:02:42 +00:00
if " studio_ids " in dict_methods and not is_movie :
final_dict [ " studio_ids " ] = util . parse ( err_type , " studio_ids " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " )
if " network_ids " in dict_methods and not is_movie :
final_dict [ " network_ids " ] = util . parse ( err_type , " network_ids " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " )
2022-03-28 16:15:34 +00:00
if " status " in dict_methods and not is_movie :
2022-04-03 21:22:58 +00:00
final_dict [ " status " ] = util . parse ( err_type , " status " , trakt_dict , methods = dict_methods , parent = method_name , datatype = " commalist " , options = status )
2022-03-28 16:15:34 +00:00
valid_dicts . append ( final_dict )
2022-03-27 06:26:08 +00:00
else :
2024-01-24 18:41:53 +00:00
if " userlist " not in dict_methods :
raise Failed ( f " { err_type } Error: { method_name } userlist attribute not found " )
2024-02-28 20:42:02 +00:00
og_list = trakt_dict [ dict_methods [ " userlist " ] ]
2024-01-24 18:41:53 +00:00
if not og_list :
raise Failed ( f " { err_type } Error: { method_name } userlist attribute is blank " )
if og_list == " collected " :
logger . warning ( f " { err_type } Warning: userlist value collected has been deprecated using collection " )
userlist = " collection "
elif og_list == " recommendations " :
raise Failed ( f " { err_type } Error: { method_name } userlist value recommendations has been deprecated " )
else :
userlist = util . parse ( err_type , " userlist " , trakt_dict , methods = dict_methods , parent = method_name , options = userlist_options )
2022-03-27 06:26:08 +00:00
user = util . parse ( err_type , " user " , trakt_dict , methods = dict_methods , parent = method_name , default = " me " )
sort_by = None
2024-01-24 18:41:53 +00:00
if userlist in [ " favorites " , " watchlist " ] and " sort_by " in dict_methods :
2022-03-27 06:26:08 +00:00
sort_by = util . parse ( err_type , " sort_by " , trakt_dict , methods = dict_methods , parent = method_name , default = " rank " , options = [ " rank " , " added " , " released " , " title " ] )
2024-01-24 18:41:53 +00:00
self . _userlist ( userlist , user , is_movie , sort_by = sort_by )
2022-03-27 06:26:08 +00:00
valid_dicts . append ( { " userlist " : userlist , " user " : user , " sort_by " : sort_by } )
except Failed as e :
logger . error ( e )
if len ( valid_dicts ) == 0 :
2024-01-24 18:41:53 +00:00
raise Failed ( f " { err_type } Error: No valid Trakt { method_name [ 6 : ] . capitalize ( ) } " )
2022-03-27 06:26:08 +00:00
return valid_dicts
2021-08-07 06:01:21 +00:00
def get_trakt_ids ( self , method , data , is_movie ) :
2021-08-01 04:35:42 +00:00
pretty = method . replace ( " _ " , " " ) . title ( )
2021-01-20 21:37:59 +00:00
media_type = " Movie " if is_movie else " Show "
2022-03-27 06:26:08 +00:00
if method == " trakt_list " :
2021-05-09 05:37:45 +00:00
logger . info ( f " Processing { pretty } : { data } " )
2023-02-06 20:34:40 +00:00
return self . _list ( data , ignore_other = True )
2022-03-27 06:26:08 +00:00
elif method == " trakt_recommendations " :
2021-11-13 23:51:12 +00:00
logger . info ( f " Processing { pretty } : { data } { media_type } { ' ' if data == 1 else ' s ' } " )
2022-03-27 06:26:08 +00:00
return self . _recommendations ( data , is_movie )
elif method == " trakt_chart " :
2022-03-28 16:15:34 +00:00
params = { " limit " : data [ " limit " ] }
chart_limit = f " { data [ ' limit ' ] } { data [ ' time_period ' ] . capitalize ( ) } " if data [ " time_period " ] else data [ " limit " ]
logger . info ( f " Processing { pretty } : { chart_limit } { data [ ' chart ' ] . capitalize ( ) } { media_type } { ' ' if data == 1 else ' s ' } " )
2024-01-11 20:02:42 +00:00
for attr in [ " query " , " years " , " runtimes " , " ratings " , " genres " , " languages " , " countries " , " certifications " , " network_ids " , " studio_ids " , " status " , " votes " , " tmdb_ratings " , " tmdb_votes " , " imdb_ratings " , " imdb_votes " , " rt_meters " , " rt_user_meters " , " metascores " ] :
2022-03-28 16:15:34 +00:00
if attr in data :
logger . info ( f " { attr : >22 } : { ' , ' . join ( data [ attr ] ) if isinstance ( data [ attr ] , list ) else data [ attr ] } " )
values = [ status_translation [ v ] for v in data [ attr ] ] if attr == " status " else data [ attr ]
params [ attr ] = " , " . join ( values ) if isinstance ( values , list ) else values
2023-02-06 20:34:40 +00:00
return self . _charts ( data [ " chart " ] , is_movie , params , time_period = data [ " time_period " ] , ignore_other = True )
2022-03-27 06:26:08 +00:00
elif method == " trakt_userlist " :
logger . info ( f " Processing { pretty } { media_type } s from { data [ ' user ' ] } ' s { data [ ' userlist ' ] . capitalize ( ) } " )
2023-02-06 20:34:40 +00:00
return self . _userlist ( data [ " userlist " ] , data [ " user " ] , is_movie , sort_by = data [ " sort_by " ] , ignore_other = True )
2022-04-05 18:46:00 +00:00
elif method == " trakt_boxoffice " :
logger . info ( f " Processing { pretty } : { data } { media_type } { ' ' if data == 1 else ' s ' } " )
return self . _charts ( " boxoffice " , is_movie , { " limit " : data } )
2021-07-15 17:42:28 +00:00
else :
raise Failed ( f " Trakt Error: Method { method } not supported " )