@ -1,4 +1,4 @@
import logging, os , requests
import base64, logging, os , requests
from datetime import datetime
from lxml import html
from modules import util , radarr , sonarr
@ -10,6 +10,7 @@ from modules.icheckmovies import ICheckMovies
from modules . imdb import IMDb
from modules . letterboxd import Letterboxd
from modules . mal import MyAnimeList
from modules . notifiarr import NotifiarrFactory
from modules . omdb import OMDb
from modules . plex import Plex
from modules . radarr import Radarr
@ -29,21 +30,22 @@ sync_modes = {"append": "Only Add Items to the Collection", "sync": "Add & Remov
mass_update_options = { " tmdb " : " Use TMDb Metadata " , " omdb " : " Use IMDb Metadata through OMDb " }
class Config :
def __init__ ( self , default_dir , config_p ath= None , is_ test= False , time_scheduled = None , reque sted_collections= None , requested_libraries = None , resume_from = None ) :
def __init__ ( self , default_dir , attrs) :
logger . info ( " Locating config... " )
if config_path and os . path . exists ( config_path ) : self . config_path = os . path . abspath ( config_path )
elif config_path and not os . path . exists ( config_path ) : raise Failed ( f " Config Error: config not found at { os . path . abspath ( config_path ) } " )
config_file = attrs [ " config_file " ]
if config_file and os . path . exists ( config_file ) : self . config_path = os . path . abspath ( config_file )
elif config_file and not os . path . exists ( config_file ) : raise Failed ( f " Config Error: config not found at { os . path . abspath ( config_file ) } " )
elif os . path . exists ( os . path . join ( default_dir , " config.yml " ) ) : self . config_path = os . path . abspath ( os . path . join ( default_dir , " config.yml " ) )
else : raise Failed ( f " Config Error: config not found at { os . path . abspath ( default_dir ) } " )
logger . info ( f " Using { self . config_path } as config " )
self . default_dir = default_dir
self . test_mode = is_test
self . run_start_time = time_scheduled
self . run_hour = datetime . strptime ( time_scheduled , " % H: % M " ) . hour
self . requested_collections = util . get_list ( requested_collections )
self . requested_libraries = util . get_list ( requested_libraries )
self . resume_from = resume_from
self . test_mode = attrs[ " test " ]
self . run_start_time = attrs[ " time " ]
self . run_hour = datetime . strptime ( attrs[ " time " ] , " % H: % M " ) . hour
self . requested_collections = util . get_list ( attrs[ " collections " ] )
self . requested_libraries = util . get_list ( attrs[ " libraries " ] )
self . resume_from = attrs[ " resume " ]
yaml . YAML ( ) . allow_duplicate_keys = True
try :
@ -87,6 +89,7 @@ class Config:
if " radarr " in new_config : new_config [ " radarr " ] = new_config . pop ( " radarr " )
if " sonarr " in new_config : new_config [ " sonarr " ] = new_config . pop ( " sonarr " )
if " omdb " in new_config : new_config [ " omdb " ] = new_config . pop ( " omdb " )
if " notifiarr " in new_config : new_config [ " notifiarr " ] = new_config . pop ( " notifiarr " )
if " trakt " in new_config : new_config [ " trakt " ] = new_config . pop ( " trakt " )
if " mal " in new_config : new_config [ " mal " ] = new_config . pop ( " mal " )
if " anidb " in new_config : new_config [ " anidb " ] = new_config . pop ( " anidb " )
@ -186,7 +189,10 @@ class Config:
" missing_only_released " : check_for_attribute ( self . data , " missing_only_released " , parent = " settings " , var_type = " bool " , default = False ) ,
" create_asset_folders " : check_for_attribute ( self . data , " create_asset_folders " , parent = " settings " , var_type = " bool " , default = False ) ,
" collection_minimum " : check_for_attribute ( self . data , " collection_minimum " , parent = " settings " , var_type = " int " , default = 1 ) ,
" delete_below_minimum " : check_for_attribute ( self . data , " delete_below_minimum " , parent = " settings " , var_type = " bool " , default = False )
" delete_below_minimum " : check_for_attribute ( self . data , " delete_below_minimum " , parent = " settings " , var_type = " bool " , default = False ) ,
" notifiarr_collection_creation " : check_for_attribute ( self . data , " notifiarr_collection_creation " , parent = " settings " , var_type = " bool " , default = False ) ,
" notifiarr_collection_addition " : check_for_attribute ( self . data , " notifiarr_collection_addition " , parent = " settings " , var_type = " bool " , default = False ) ,
" notifiarr_collection_removing " : check_for_attribute ( self . data , " notifiarr_collection_removing " , parent = " settings " , var_type = " bool " , default = False )
}
if self . general [ " cache " ] :
util . separator ( )
@ -196,6 +202,27 @@ class Config:
util . separator ( )
self . NotifiarrFactory = None
if " notifiarr " in self . data :
logger . info ( " Connecting to Notifiarr... " )
try :
self . NotifiarrFactory = NotifiarrFactory ( self , {
" apikey " : check_for_attribute ( self . data , " apikey " , parent = " notifiarr " , throw = True ) ,
" error_notification " : check_for_attribute ( self . data , " error_notification " , parent = " notifiarr " , var_type = " bool " , default = True ) ,
" develop " : check_for_attribute ( self . data , " develop " , parent = " notifiarr " , var_type = " bool " , default = False , do_print = False , save = False ) ,
" test " : check_for_attribute ( self . data , " test " , parent = " notifiarr " , var_type = " bool " , default = False , do_print = False , save = False )
} )
except Failed as e :
logger . error ( e )
logger . info ( f " Notifiarr Connection { ' Failed ' if self . NotifiarrFactory is None else ' Successful ' } " )
else :
logger . warning ( " notifiarr attribute not found " )
self . errors = [ ]
util . separator ( )
try :
self . TMDb = None
if " tmdb " in self . data :
logger . info ( " Connecting to TMDb... " )
@ -215,6 +242,7 @@ class Config:
try :
self . OMDb = OMDb ( self , { " apikey " : check_for_attribute ( self . data , " apikey " , parent = " omdb " , throw = True ) } )
except Failed as e :
self . errors . append ( e )
logger . error ( e )
logger . info ( f " OMDb Connection { ' Failed ' if self . OMDb is None else ' Successful ' } " )
else :
@ -233,6 +261,7 @@ class Config:
" authorization " : self . data [ " trakt " ] [ " authorization " ] if " authorization " in self . data [ " trakt " ] else None
} )
except Failed as e :
self . errors . append ( e )
logger . error ( e )
logger . info ( f " Trakt Connection { ' Failed ' if self . Trakt is None else ' Successful ' } " )
else :
@ -251,6 +280,7 @@ class Config:
" authorization " : self . data [ " mal " ] [ " authorization " ] if " authorization " in self . data [ " mal " ] else None
} )
except Failed as e :
self . errors . append ( e )
logger . error ( e )
logger . info ( f " My Anime List Connection { ' Failed ' if self . MyAnimeList is None else ' Successful ' } " )
else :
@ -268,6 +298,7 @@ class Config:
" password " : check_for_attribute ( self . data , " password " , parent = " anidb " , throw = True )
} )
except Failed as e :
self . errors . append ( e )
logger . error ( e )
logger . info ( f " My Anime List Connection { ' Failed Continuing as Guest ' if self . MyAnimeList is None else ' Successful ' } " )
if self . AniDB is None :
@ -357,26 +388,37 @@ class Config:
params [ " create_asset_folders " ] = check_for_attribute ( lib , " create_asset_folders " , parent = " settings " , var_type = " bool " , default = self . general [ " create_asset_folders " ] , do_print = False , save = False )
params [ " collection_minimum " ] = check_for_attribute ( lib , " collection_minimum " , parent = " settings " , var_type = " int " , default = self . general [ " collection_minimum " ] , do_print = False , save = False )
params [ " delete_below_minimum " ] = check_for_attribute ( lib , " delete_below_minimum " , parent = " settings " , var_type = " bool " , default = self . general [ " delete_below_minimum " ] , do_print = False , save = False )
params [ " notifiarr_collection_creation " ] = check_for_attribute ( lib , " notifiarr_collection_creation " , parent = " settings " , var_type = " bool " , default = self . general [ " notifiarr_collection_creation " ] , do_print = False , save = False )
params [ " notifiarr_collection_addition " ] = check_for_attribute ( lib , " notifiarr_collection_addition " , parent = " settings " , var_type = " bool " , default = self . general [ " notifiarr_collection_addition " ] , do_print = False , save = False )
params [ " notifiarr_collection_removing " ] = check_for_attribute ( lib , " notifiarr_collection_removing " , parent = " settings " , var_type = " bool " , default = self . general [ " notifiarr_collection_removing " ] , do_print = False , save = False )
params [ " mass_genre_update " ] = check_for_attribute ( lib , " mass_genre_update " , test_list = mass_update_options , default_is_none = True , save = False , do_print = lib and " mass_genre_update " in lib )
if self . OMDb is None and params [ " mass_genre_update " ] == " omdb " :
params [ " mass_genre_update " ] = None
logger . error ( " Config Error: mass_genre_update cannot be omdb without a successful OMDb Connection " )
e = " Config Error: mass_genre_update cannot be omdb without a successful OMDb Connection "
self . errors . append ( e )
logger . error ( e )
params [ " mass_audience_rating_update " ] = check_for_attribute ( lib , " mass_audience_rating_update " , test_list = mass_update_options , default_is_none = True , save = False , do_print = lib and " mass_audience_rating_update " in lib )
if self . OMDb is None and params [ " mass_audience_rating_update " ] == " omdb " :
params [ " mass_audience_rating_update " ] = None
logger . error ( " Config Error: mass_audience_rating_update cannot be omdb without a successful OMDb Connection " )
e = " Config Error: mass_audience_rating_update cannot be omdb without a successful OMDb Connection "
self . errors . append ( e )
logger . error ( e )
params [ " mass_critic_rating_update " ] = check_for_attribute ( lib , " mass_critic_rating_update " , test_list = mass_update_options , default_is_none = True , save = False , do_print = lib and " mass_audience_rating_update " in lib )
if self . OMDb is None and params [ " mass_critic_rating_update " ] == " omdb " :
params [ " mass_critic_rating_update " ] = None
logger . error ( " Config Error: mass_critic_rating_update cannot be omdb without a successful OMDb Connection " )
e = " Config Error: mass_critic_rating_update cannot be omdb without a successful OMDb Connection "
self . errors . append ( e )
logger . error ( e )
params [ " mass_trakt_rating_update " ] = check_for_attribute ( lib , " mass_trakt_rating_update " , var_type = " bool " , default = False , save = False , do_print = lib and " mass_trakt_rating_update " in lib )
if self . Trakt is None and params [ " mass_trakt_rating_update " ] :
params [ " mass_trakt_rating_update " ] = None
logger . error ( " Config Error: mass_trakt_rating_update cannot run without a successful Trakt Connection " )
e = " Config Error: mass_trakt_rating_update cannot run without a successful Trakt Connection "
self . errors . append ( e )
logger . error ( e )
params [ " split_duplicates " ] = check_for_attribute ( lib , " split_duplicates " , var_type = " bool " , default = False , save = False , do_print = lib and " split_duplicates " in lib )
params [ " radarr_add_all " ] = check_for_attribute ( lib , " radarr_add_all " , var_type = " bool " , default = False , save = False , do_print = lib and " radarr_add_all " in lib )
@ -393,7 +435,9 @@ class Config:
def check_dict ( attr , name ) :
if attr in path :
if path [ attr ] is None :
logger . error ( f " Config Error: metadata_path { attr } is blank " )
e = f " Config Error: metadata_path { attr } is blank "
self . errors . append ( e )
logger . error ( e )
else :
params [ " metadata_path " ] . append ( ( name , path [ attr ] ) )
check_dict ( " url " , " URL " )
@ -417,6 +461,7 @@ class Config:
logger . info ( " " )
logger . info ( f " { display_name } Library Connection Successful " )
except Failed as e :
self . errors . append ( e )
util . print_stacktrace ( )
util . print_multiline ( e , error = True )
logger . info ( f " { display_name } Library Connection Failed " )
@ -442,6 +487,7 @@ class Config:
" search " : check_for_attribute ( lib , " search " , parent = " radarr " , var_type = " bool " , default = self . general [ " radarr " ] [ " search " ] , save = False )
} )
except Failed as e :
self . errors . append ( e )
util . print_stacktrace ( )
util . print_multiline ( e , error = True )
logger . info ( " " )
@ -470,6 +516,7 @@ class Config:
" cutoff_search " : check_for_attribute ( lib , " cutoff_search " , parent = " sonarr " , var_type = " bool " , default = self . general [ " sonarr " ] [ " cutoff_search " ] , save = False )
} )
except Failed as e :
self . errors . append ( e )
util . print_stacktrace ( )
util . print_multiline ( e , error = True )
logger . info ( " " )
@ -487,11 +534,14 @@ class Config:
" apikey " : check_for_attribute ( lib , " apikey " , parent = " tautulli " , default = self . general [ " tautulli " ] [ " apikey " ] , req_default = True , save = False )
} )
except Failed as e :
self . errors . append ( e )
util . print_stacktrace ( )
util . print_multiline ( e , error = True )
logger . info ( " " )
logger . info ( f " { display_name } library ' s Tautulli Connection { ' Failed ' if library . Tautulli is None else ' Successful ' } " )
library . Notifiarr = self . NotifiarrFactory . getNotifiarr ( library ) if self . NotifiarrFactory else None
logger . info ( " " )
self . libraries . append ( library )
@ -504,15 +554,31 @@ class Config:
util . separator ( )
if self . errors :
self . notify ( self . errors )
except Exception as e :
self . notify ( e )
raise
def notify ( self , text , library = None , collection = None , critical = True ) :
if self . NotifiarrFactory :
if not isinstance ( text , list ) :
text = [ text ]
for t in text :
self . NotifiarrFactory . error ( t , library = library , collection = collection , critical = critical )
def get_html ( self , url , headers = None , params = None ) :
return html . fromstring ( self . get ( url , headers = headers , params = params ) . content )
def get_json ( self , url , headers = None ) :
return self . get ( url , headers = headers ) . json ( )
def get_json ( self , url , json= None , headers= None , param s= None ) :
return self . get ( url , json= json , headers= header s, params = param s) . json ( )
@retry ( stop_max_attempt_number = 6 , wait_fixed = 10000 )
def get ( self , url , headers = None , params = None ) :
return self . session . get ( url , headers = headers , params = params )
def get ( self , url , json = None , headers = None , params = None ) :
return self . session . get ( url , json = json , headers = headers , params = params )
def get_image_encoded ( self , url ) :
return base64 . b64encode ( self . get ( url ) . content ) . decode ( ' utf-8 ' )
def post_html ( self , url , data = None , json = None , headers = None ) :
return html . fromstring ( self . post ( url , data = data , json = json , headers = headers ) . content )