@ -28,7 +28,7 @@ method_alias = {
" writers " : " writer " ,
" writers " : " writer " ,
" years " : " year "
" years " : " year "
}
}
filter_ alias = {
filter_ translation = {
" actor " : " actors " ,
" actor " : " actors " ,
" audience_rating " : " audienceRating " ,
" audience_rating " : " audienceRating " ,
" collection " : " collections " ,
" collection " : " collections " ,
@ -220,11 +220,14 @@ class CollectionBuilder:
methods = { m . lower ( ) : m for m in self . data }
methods = { m . lower ( ) : m for m in self . data }
if " template " in methods :
if " template " in methods :
logger . info ( " " )
logger . info ( " Validating Method: template " )
if not self . metadata . templates :
if not self . metadata . templates :
raise Failed ( " Collection Error: No templates found " )
raise Failed ( " Collection Error: No templates found " )
elif not self . data [ methods [ " template " ] ] :
elif not self . data [ methods [ " template " ] ] :
raise Failed ( " Collection Error: template attribute is blank " )
raise Failed ( " Collection Error: template attribute is blank " )
else :
else :
logger . debug ( f " Value: { self . data [ methods [ ' template ' ] ] } " )
for variables in util . get_list ( self . data [ methods [ " template " ] ] , split = False ) :
for variables in util . get_list ( self . data [ methods [ " template " ] ] , split = False ) :
if not isinstance ( variables , dict ) :
if not isinstance ( variables , dict ) :
raise Failed ( " Collection Error: template attribute is not a dictionary " )
raise Failed ( " Collection Error: template attribute is not a dictionary " )
@ -329,32 +332,45 @@ class CollectionBuilder:
except Failed :
except Failed :
continue
continue
skip_collection = True
if " schedule " in methods :
if " schedule " not in methods :
logger . info ( " " )
skip_collection = False
logger . info ( " Validating Method: schedule " )
elif not self . data [ methods [ " schedule " ] ] :
if not self . data [ methods [ " schedule " ] ] :
logger . error ( " Collection Error: schedule attribute is blank. Running daily " )
raise Failed ( " Collection Error: schedule attribute is blank " )
skip_collection = False
else :
else :
logger . debug ( f " Value: { self . data [ methods [ ' schedule ' ] ] } " )
schedule_list = util . get_list ( self . data [ methods [ " schedule " ] ] )
skip_collection = True
next_month = current_time . replace ( day = 28 ) + timedelta ( days = 4 )
schedule_list = util . get_list ( self . data [ methods [ " schedule " ] ] )
last_day = next_month - timedelta ( days = next_month . day )
next_month = current_time . replace ( day = 28 ) + timedelta ( days = 4 )
for schedule in schedule_list :
last_day = next_month - timedelta ( days = next_month . day )
run_time = str ( schedule ) . lower ( )
for schedule in schedule_list :
if run_time . startswith ( " day " ) or run_time . startswith ( " daily " ) :
run_time = str ( schedule ) . lower ( )
skip_collection = False
if run_time . startswith ( ( " day " , " daily " ) ) :
elif run_time . startswith ( " week " ) or run_time . startswith ( " month " ) or run_time . startswith ( " year " ) :
skip_collection = False
match = re . search ( " \\ (([^)]+) \\ ) " , run_time )
elif run_time . startswith ( ( " hour " , " week " , " month " , " year " ) ) :
if match :
match = re . search ( " \\ (([^)]+) \\ ) " , run_time )
if not match :
logger . error ( f " Collection Error: failed to parse schedule: { schedule } " )
continue
param = match . group ( 1 )
param = match . group ( 1 )
if run_time . startswith ( " week " ) :
if run_time . startswith ( " hour " ) :
if param . lower ( ) in util . days_alias :
try :
weekday = util . days_alias [ param . lower ( ) ]
if 0 < = int ( param ) < = 23 :
self . schedule + = f " \n Scheduled weekly on { util . pretty_days [ weekday ] } "
self . schedule + = f " \n Scheduled to run only on the { util . make_ordinal ( param ) } hour "
if weekday == current_time . weekday ( ) :
if config . run_hour == int ( param ) :
skip_collection = False
skip_collection = False
else :
else :
raise ValueError
except ValueError :
logger . error ( f " Collection Error: hourly schedule attribute { schedule } invalid must be an integer between 0 and 23 " )
elif run_time . startswith ( " week " ) :
if param . lower ( ) not in util . days_alias :
logger . error ( f " Collection Error: weekly schedule attribute { schedule } invalid must be a day of the week i.e. weekly(Monday) " )
logger . error ( f " Collection Error: weekly schedule attribute { schedule } invalid must be a day of the week i.e. weekly(Monday) " )
continue
weekday = util . days_alias [ param . lower ( ) ]
self . schedule + = f " \n Scheduled weekly on { util . pretty_days [ weekday ] } "
if weekday == current_time . weekday ( ) :
skip_collection = False
elif run_time . startswith ( " month " ) :
elif run_time . startswith ( " month " ) :
try :
try :
if 1 < = int ( param ) < = 31 :
if 1 < = int ( param ) < = 31 :
@ -362,35 +378,69 @@ class CollectionBuilder:
if current_time . day == int ( param ) or ( current_time . day == last_day . day and int ( param ) > last_day . day ) :
if current_time . day == int ( param ) or ( current_time . day == last_day . day and int ( param ) > last_day . day ) :
skip_collection = False
skip_collection = False
else :
else :
logger . error ( f " Collection Error: monthly schedule attribute { schedule } invalid must be between 1 and 31 " )
raise ValueError
except ValueError :
except ValueError :
logger . error ( f " Collection Error: monthly schedule attribute { schedule } invalid must be an integer " )
logger . error ( f " Collection Error: monthly schedule attribute { schedule } invalid must be an integer between 1 and 31 " )
elif run_time . startswith ( " year " ) :
elif run_time . startswith ( " year " ) :
match = re . match ( " ^(1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])$ " , param )
match = re . match ( " ^(1[0-2]|0?[1-9])/(3[01]|[12][0-9]|0?[1-9])$ " , param )
if match :
if not match :
month = int ( match . group ( 1 ) )
day = int ( match . group ( 2 ) )
self . schedule + = f " \n Scheduled yearly on { util . pretty_months [ month ] } { util . make_ordinal ( day ) } "
if current_time . month == month and ( current_time . day == day or ( current_time . day == last_day . day and day > last_day . day ) ) :
skip_collection = False
else :
logger . error ( f " Collection Error: yearly schedule attribute { schedule } invalid must be in the MM/DD format i.e. yearly(11/22) " )
logger . error ( f " Collection Error: yearly schedule attribute { schedule } invalid must be in the MM/DD format i.e. yearly(11/22) " )
continue
month = int ( match . group ( 1 ) )
day = int ( match . group ( 2 ) )
self . schedule + = f " \n Scheduled yearly on { util . pretty_months [ month ] } { util . make_ordinal ( day ) } "
if current_time . month == month and ( current_time . day == day or ( current_time . day == last_day . day and day > last_day . day ) ) :
skip_collection = False
else :
else :
logger . error ( f " Collection Error: failed to parse schedule: { schedule } " )
logger . error ( f " Collection Error: schedule attribute { schedule } invalid " )
else :
if len ( self . schedule ) == 0 :
logger . error ( f " Collection Error: schedule attribute { schedule } invalid " )
skip_collection = False
if len ( self . schedule ) == 0 :
if skip_collection :
skip_collection = False
raise Failed ( f " { self . schedule } \n \n Collection { self . name } not scheduled to run " )
if skip_collection :
raise Failed ( f " { self . schedule } \n \n Collection { self . name } not scheduled to run " )
logger . info ( f " Scanning { self . name } Collection " )
self . run_again = " run_again " in methods
self . run_again = " run_again " in methods
self . collectionless = " plex_collectionless " in methods
self . collectionless = " plex_collectionless " in methods
self . run_again = False
if " run_again " in methods :
logger . info ( " " )
logger . info ( " Validating Method: run_again " )
if not self . data [ methods [ " run_again " ] ] :
logger . warning ( f " Collection Warning: run_again attribute is blank defaulting to false " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' run_again ' ] ] } " )
self . run_again = util . get_bool ( " run_again " , self . data [ methods [ " run_again " ] ] )
self . sync = self . library . sync_mode == " sync "
if " sync_mode " in methods :
logger . info ( " " )
logger . info ( " Validating Method: sync_mode " )
if not self . data [ methods [ " sync_mode " ] ] :
logger . warning ( f " Collection Warning: sync_mode attribute is blank using general: { self . library . sync_mode } " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' sync_mode ' ] ] } " )
if self . data [ methods [ " sync_mode " ] ] . lower ( ) not in [ " append " , " sync " ] :
logger . warning ( f " Collection Warning: { self . data [ methods [ ' sync_mode ' ] ] } sync_mode invalid using general: { self . library . sync_mode } " )
else :
self . sync = self . data [ methods [ " sync_mode " ] ] . lower ( ) == " sync "
self . build_collection = True
if " build_collection " in methods :
logger . info ( " " )
logger . info ( " Validating Method: build_collection " )
if not self . data [ methods [ " build_collection " ] ] :
logger . warning ( f " Collection Warning: build_collection attribute is blank defaulting to true " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' build_collection ' ] ] } " )
self . build_collection = util . get_bool ( " build_collection " , self . data [ methods [ " build_collection " ] ] )
if " tmdb_person " in methods :
if " tmdb_person " in methods :
if self . data [ methods [ " tmdb_person " ] ] :
logger . info ( " " )
logger . info ( " Validating Method: build_collection " )
if not self . data [ methods [ " tmdb_person " ] ] :
raise Failed ( " Collection Error: tmdb_person attribute is blank " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' tmdb_person ' ] ] } " )
valid_names = [ ]
valid_names = [ ]
for tmdb_id in util . get_int_list ( self . data [ methods [ " tmdb_person " ] ] , " TMDb Person ID " ) :
for tmdb_id in util . get_int_list ( self . data [ methods [ " tmdb_person " ] ] , " TMDb Person ID " ) :
person = config . TMDb . get_person ( tmdb_id )
person = config . TMDb . get_person ( tmdb_id )
@ -403,42 +453,48 @@ class CollectionBuilder:
self . details [ " tmdb_person " ] = valid_names
self . details [ " tmdb_person " ] = valid_names
else :
else :
raise Failed ( f " Collection Error: No valid TMDb Person IDs in { self . data [ methods [ ' tmdb_person ' ] ] } " )
raise Failed ( f " Collection Error: No valid TMDb Person IDs in { self . data [ methods [ ' tmdb_person ' ] ] } " )
else :
raise Failed ( " Collection Error: tmdb_person attribute is blank " )
self . smart_sort = " random "
self . smart_sort = " random "
self . smart_label_collection = False
self . smart_label_collection = False
if " smart_label " in methods :
if " smart_label " in methods :
logger . info ( " " )
logger . info ( " Validating Method: smart_label " )
self . smart_label_collection = True
self . smart_label_collection = True
if self . data [ methods [ " smart_label " ] ] :
if not self . data [ methods [ " smart_label " ] ] :
logger . warning ( " Collection Error: smart_label attribute is blank defaulting to random " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' smart_label ' ] ] } " )
if ( self . library . is_movie and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . movie_smart_sorts ) \
if ( self . library . is_movie and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . movie_smart_sorts ) \
or ( self . library . is_show and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . show_smart_sorts ) :
or ( self . library . is_show and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . show_smart_sorts ) :
self . smart_sort = str ( self . data [ methods [ " smart_label " ] ] ) . lower ( )
self . smart_sort = str ( self . data [ methods [ " smart_label " ] ] ) . lower ( )
else :
else :
logger . info ( " " )
logger . warning ( f " Collection Error: smart_label attribute: { self . data [ methods [ ' smart_label ' ] ] } is invalid defaulting to random " )
logger . warning ( f " Collection Error: smart_label attribute: { self . data [ methods [ ' smart_label ' ] ] } is invalid defaulting to random " )
else :
logger . info ( " " )
logger . warning ( " Collection Error: smart_label attribute is blank defaulting to random " )
self . smart_url = None
self . smart_url = None
self . smart_type_key = None
self . smart_type_key = None
if " smart_url " in methods :
if " smart_url " in methods :
if self . data [ methods [ " smart_url " ] ] :
logger . info ( " " )
logger . info ( " Validating Method: smart_url " )
if not self . data [ methods [ " smart_url " ] ] :
raise Failed ( " Collection Error: smart_url attribute is blank " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' smart_url ' ] ] } " )
try :
try :
self . smart_url , self . smart_type_key = library . get_smart_filter_from_uri ( self . data [ methods [ " smart_url " ] ] )
self . smart_url , self . smart_type_key = library . get_smart_filter_from_uri ( self . data [ methods [ " smart_url " ] ] )
except ValueError :
except ValueError :
raise Failed ( " Collection Error: smart_url is incorrectly formatted " )
raise Failed ( " Collection Error: smart_url is incorrectly formatted " )
else :
raise Failed ( " Collection Error: smart_url attribute is blank " )
self . smart_filter_details = " "
if " smart_filter " in methods :
if " smart_filter " in methods :
logger . info ( " " )
logger . info ( " " )
logger . info ( " Validating Method: smart_filter " )
filter_details = " \n "
smart_filter = self . data [ methods [ " smart_filter " ] ]
smart_filter = self . data [ methods [ " smart_filter " ] ]
if smart_filter is None :
if smart_filter is None :
raise Failed ( f " Collection Error: smart_filter attribute is blank " )
raise Failed ( f " Collection Error: smart_filter attribute is blank " )
if not isinstance ( smart_filter , dict ) :
if not isinstance ( smart_filter , dict ) :
raise Failed ( f " Collection Error: smart_filter must be a dictionary: { smart_filter } " )
raise Failed ( f " Collection Error: smart_filter must be a dictionary: { smart_filter } " )
logger . debug ( f " Value: { self . data [ methods [ ' smart_filter ' ] ] } " )
smart_methods = { m . lower ( ) : m for m in smart_filter }
smart_methods = { m . lower ( ) : m for m in smart_filter }
if " any " in smart_methods and " all " in smart_methods :
if " any " in smart_methods and " all " in smart_methods :
raise Failed ( f " Collection Error: Cannot have more then one base " )
raise Failed ( f " Collection Error: Cannot have more then one base " )
@ -453,7 +509,7 @@ class CollectionBuilder:
smart_type = " shows "
smart_type = " shows "
else :
else :
smart_type = " movies "
smart_type = " movies "
logger. info ( f " Smart { smart_type . capitalize ( ) [ : - 1 ] } Filter " )
filter_details + = f " Smart { smart_type . capitalize ( ) [ : - 1 ] } Filter \n "
self . smart_type_key , smart_sorts = plex . smart_types [ smart_type ]
self . smart_type_key , smart_sorts = plex . smart_types [ smart_type ]
smart_sort = " random "
smart_sort = " random "
@ -463,7 +519,7 @@ class CollectionBuilder:
if smart_filter [ smart_methods [ " sort_by " ] ] not in smart_sorts :
if smart_filter [ smart_methods [ " sort_by " ] ] not in smart_sorts :
raise Failed ( f " Collection Error: sort_by: { smart_filter [ smart_methods [ ' sort_by ' ] ] } is invalid " )
raise Failed ( f " Collection Error: sort_by: { smart_filter [ smart_methods [ ' sort_by ' ] ] } is invalid " )
smart_sort = smart_filter [ smart_methods [ " sort_by " ] ]
smart_sort = smart_filter [ smart_methods [ " sort_by " ] ]
logger. info ( f " Sort By: { smart_sort } " )
filter_details + = f " Sort By: { smart_sort } \n "
limit = None
limit = None
if " limit " in smart_methods :
if " limit " in smart_methods :
@ -472,7 +528,7 @@ class CollectionBuilder:
if not isinstance ( smart_filter [ smart_methods [ " limit " ] ] , int ) or smart_filter [ smart_methods [ " limit " ] ] < 1 :
if not isinstance ( smart_filter [ smart_methods [ " limit " ] ] , int ) or smart_filter [ smart_methods [ " limit " ] ] < 1 :
raise Failed ( " Collection Error: limit attribute must be an integer greater then 0 " )
raise Failed ( " Collection Error: limit attribute must be an integer greater then 0 " )
limit = smart_filter [ smart_methods [ " limit " ] ]
limit = smart_filter [ smart_methods [ " limit " ] ]
logger. info ( f " Limit: { limit } " )
filter_details + = f " Limit: { limit } \n "
validate = True
validate = True
if " validate " in smart_methods :
if " validate " in smart_methods :
@ -481,7 +537,7 @@ class CollectionBuilder:
if not isinstance ( smart_filter [ smart_methods [ " validate " ] ] , bool ) :
if not isinstance ( smart_filter [ smart_methods [ " validate " ] ] , bool ) :
raise Failed ( " Collection Error: validate attribute must be either true or false " )
raise Failed ( " Collection Error: validate attribute must be either true or false " )
validate = smart_filter [ smart_methods [ " validate " ] ]
validate = smart_filter [ smart_methods [ " validate " ] ]
logger. info ( f " Validate: { validate } " )
filter_details + = f " Validate: { validate } \n "
def _filter ( filter_dict , fail , is_all = True , level = 1 ) :
def _filter ( filter_dict , fail , is_all = True , level = 1 ) :
output = " "
output = " "
@ -590,7 +646,7 @@ class CollectionBuilder:
if not isinstance ( smart_filter [ smart_methods [ base ] ] , dict ) :
if not isinstance ( smart_filter [ smart_methods [ base ] ] , dict ) :
raise Failed ( f " Collection Error: { base } must be a dictionary: { smart_filter [ smart_methods [ base ] ] } " )
raise Failed ( f " Collection Error: { base } must be a dictionary: { smart_filter [ smart_methods [ base ] ] } " )
built_filter , filter_text = _filter ( smart_filter [ smart_methods [ base ] ] , validate , is_all = base_all )
built_filter , filter_text = _filter ( smart_filter [ smart_methods [ base ] ] , validate , is_all = base_all )
util . print_multiline ( f " Filter: { filter_text } " )
self . smart_filter_details = f " { filter_details } Filter: { filter_text } "
final_filter = built_filter [ : - 1 ] if base_all else f " push=1& { built_filter } pop=1 "
final_filter = built_filter [ : - 1 ] if base_all else f " push=1& { built_filter } pop=1 "
self . smart_url = f " ?type= { self . smart_type_key } & { f ' limit= { limit } & ' if limit else ' ' } sort= { smart_sorts [ smart_sort ] } & { final_filter } "
self . smart_url = f " ?type= { self . smart_type_key } & { f ' limit= { limit } & ' if limit else ' ' } sort= { smart_sorts [ smart_sort ] } & { final_filter } "
@ -612,6 +668,10 @@ class CollectionBuilder:
self . smart = self . smart_url or self . smart_label_collection
self . smart = self . smart_url or self . smart_label_collection
for method_key , method_data in self . data . items ( ) :
for method_key , method_data in self . data . items ( ) :
if method_key . lower ( ) in ignored_details :
continue
logger . info ( " " )
logger . info ( f " Validating Method: { method_key } " )
if " trakt " in method_key . lower ( ) and not config . Trakt : raise Failed ( f " Collection Error: { method_key } requires Trakt todo be configured " )
if " trakt " in method_key . lower ( ) and not config . Trakt : raise Failed ( f " Collection Error: { method_key } requires Trakt todo be configured " )
elif " imdb " in method_key . lower ( ) and not config . IMDb : raise Failed ( f " Collection Error: { method_key } requires TMDb or Trakt to be configured " )
elif " imdb " in method_key . lower ( ) and not config . IMDb : raise Failed ( f " Collection Error: { method_key } requires TMDb or Trakt to be configured " )
elif " radarr " in method_key . lower ( ) and not self . library . Radarr : raise Failed ( f " Collection Error: { method_key } requires Radarr to be configured " )
elif " radarr " in method_key . lower ( ) and not self . library . Radarr : raise Failed ( f " Collection Error: { method_key } requires Radarr to be configured " )
@ -619,8 +679,6 @@ class CollectionBuilder:
elif " tautulli " in method_key . lower ( ) and not self . library . Tautulli : raise Failed ( f " Collection Error: { method_key } requires Tautulli to be configured " )
elif " tautulli " in method_key . lower ( ) and not self . library . Tautulli : raise Failed ( f " Collection Error: { method_key } requires Tautulli to be configured " )
elif " mal " in method_key . lower ( ) and not config . MyAnimeList : raise Failed ( f " Collection Error: { method_key } requires MyAnimeList to be configured " )
elif " mal " in method_key . lower ( ) and not config . MyAnimeList : raise Failed ( f " Collection Error: { method_key } requires MyAnimeList to be configured " )
elif method_data is not None :
elif method_data is not None :
logger . debug ( " " )
logger . debug ( f " Validating Method: { method_key } " )
logger . debug ( f " Value: { method_data } " )
logger . debug ( f " Value: { method_data } " )
if method_key . lower ( ) in method_alias :
if method_key . lower ( ) in method_alias :
method_name = method_alias [ method_key . lower ( ) ]
method_name = method_alias [ method_key . lower ( ) ]
@ -1227,15 +1285,6 @@ class CollectionBuilder:
else :
else :
logger . warning ( f " Collection Warning: { method_key } attribute is blank " )
logger . warning ( f " Collection Warning: { method_key } attribute is blank " )
self . sync = self . library . sync_mode == " sync "
if " sync_mode " in methods :
if not self . data [ methods [ " sync_mode " ] ] :
logger . warning ( f " Collection Warning: sync_mode attribute is blank using general: { self . library . sync_mode } " )
elif self . data [ methods [ " sync_mode " ] ] . lower ( ) not in [ " append " , " sync " ] :
logger . warning ( f " Collection Warning: { self . data [ methods [ ' sync_mode ' ] ] } sync_mode invalid using general: { self . library . sync_mode } " )
else :
self . sync = self . data [ methods [ " sync_mode " ] ] . lower ( ) == " sync "
if self . add_to_radarr is None :
if self . add_to_radarr is None :
self . add_to_radarr = self . library . Radarr . add if self . library . Radarr else False
self . add_to_radarr = self . library . Radarr . add if self . library . Radarr else False
if self . add_to_sonarr is None :
if self . add_to_sonarr is None :
@ -1251,13 +1300,6 @@ class CollectionBuilder:
self . details [ " collection_mode " ] = " hide "
self . details [ " collection_mode " ] = " hide "
self . sync = True
self . sync = True
self . build_collection = True
if " build_collection " in methods :
if not self . data [ methods [ " build_collection " ] ] :
logger . warning ( f " Collection Warning: build_collection attribute is blank defaulting to true " )
else :
self . build_collection = util . get_bool ( " build_collection " , self . data [ methods [ " build_collection " ] ] )
if self . build_collection :
if self . build_collection :
try :
try :
self . obj = library . get_collection ( self . name )
self . obj = library . get_collection ( self . name )
@ -1277,6 +1319,8 @@ class CollectionBuilder:
else :
else :
self . sync = False
self . sync = False
self . run_again = False
self . run_again = False
logger . info ( " " )
logger . info ( " Validation Successful " )
def collect_rating_keys ( self , movie_map , show_map ) :
def collect_rating_keys ( self , movie_map , show_map ) :
def add_rating_keys ( keys ) :
def add_rating_keys ( keys ) :
@ -1315,7 +1359,7 @@ class CollectionBuilder:
elif " anilist " in method : check_map ( self . config . AniList . get_items ( method , value ) )
elif " anilist " in method : check_map ( self . config . AniList . get_items ( method , value ) )
elif " mal " in method : check_map ( self . config . MyAnimeList . get_items ( method , value ) )
elif " mal " in method : check_map ( self . config . MyAnimeList . get_items ( method , value ) )
elif " tvdb " in method : check_map ( self . config . TVDb . get_items ( method , value , self . library . Plex . language ) )
elif " tvdb " in method : check_map ( self . config . TVDb . get_items ( method , value , self . library . Plex . language ) )
elif " imdb " in method : check_map ( self . config . IMDb . get_items ( method , value , self . library . Plex . language ))
elif " imdb " in method : check_map ( self . config . IMDb . get_items ( method , value , self . library . Plex . language , self . library . is_movie ))
elif " letterboxd " in method : check_map ( self . config . Letterboxd . get_items ( method , value , self . library . Plex . language ) )
elif " letterboxd " in method : check_map ( self . config . Letterboxd . get_items ( method , value , self . library . Plex . language ) )
elif " tmdb " in method : check_map ( self . config . TMDb . get_items ( method , value , self . library . is_movie ) )
elif " tmdb " in method : check_map ( self . config . TMDb . get_items ( method , value , self . library . is_movie ) )
elif " trakt " in method : check_map ( self . config . Trakt . get_items ( method , value , self . library . is_movie ) )
elif " trakt " in method : check_map ( self . config . Trakt . get_items ( method , value , self . library . is_movie ) )
@ -1340,7 +1384,7 @@ class CollectionBuilder:
for filter_method , filter_data in self . filters :
for filter_method , filter_data in self . filters :
modifier = filter_method [ - 4 : ]
modifier = filter_method [ - 4 : ]
method = filter_method [ : - 4 ] if modifier in [ " .not " , " .lte " , " .gte " ] else filter_method
method = filter_method [ : - 4 ] if modifier in [ " .not " , " .lte " , " .gte " ] else filter_method
method_name = filter_ alias[ method ] if method in filter_alias else method
method_name = filter_ translation[ method ] if method in filter_translation else method
if method_name == " max_age " :
if method_name == " max_age " :
threshold_date = datetime . now ( ) - timedelta ( days = filter_data )
threshold_date = datetime . now ( ) - timedelta ( days = filter_data )
if current . originallyAvailableAt is None or current . originallyAvailableAt < threshold_date :
if current . originallyAvailableAt is None or current . originallyAvailableAt < threshold_date :
@ -1358,8 +1402,8 @@ class CollectionBuilder:
if movie is None :
if movie is None :
logger . warning ( f " Filter Error: No TMDb ID found for { current . title } " )
logger . warning ( f " Filter Error: No TMDb ID found for { current . title } " )
continue
continue
if ( modifier == " .not " and movie . original_language in filter_data ) or (
if ( modifier == " .not " and movie . original_language in filter_data ) \
modifier != " .not " and movie . original_language not in filter_data ) :
or ( modifier != " .not " and movie . original_language not in filter_data ) :
match = False
match = False
break
break
elif method_name == " audio_track_title " :
elif method_name == " audio_track_title " :
@ -1432,7 +1476,7 @@ class CollectionBuilder:
break
break
length = util . print_return ( length , f " Filtering { ( ' ' * ( max_length - len ( str ( i ) ) ) ) + str ( i ) } / { total } { current . title } " )
length = util . print_return ( length , f " Filtering { ( ' ' * ( max_length - len ( str ( i ) ) ) ) + str ( i ) } / { total } { current . title } " )
if match :
if match :
util. print_end ( length , f " { name } Collection | { ' = ' if current in collection_items else ' + ' } | { current . title } " )
logger. info ( util . adjust_space ( length , f " { name } Collection | { ' = ' if current in collection_items else ' + ' } | { current . title } " ) )
if current in collection_items :
if current in collection_items :
self . plex_map [ current . ratingKey ] = None
self . plex_map [ current . ratingKey ] = None
elif self . smart_label_collection :
elif self . smart_label_collection :
@ -1442,10 +1486,11 @@ class CollectionBuilder:
elif self . details [ " show_filtered " ] is True :
elif self . details [ " show_filtered " ] is True :
logger . info ( f " { name } Collection | X | { current . title } " )
logger . info ( f " { name } Collection | X | { current . title } " )
media_type = f " { ' Movie ' if self . library . is_movie else ' Show ' } { ' s ' if total > 1 else ' ' } "
media_type = f " { ' Movie ' if self . library . is_movie else ' Show ' } { ' s ' if total > 1 else ' ' } "
util . print_end ( length , f " { total } { media_type } Processed " )
util . print_end ( length )
logger . info ( " " )
logger . info ( f " { total } { media_type } Processed " )
def run_missing ( self ) :
def run_missing ( self ) :
logger . info ( " " )
arr_filters = [ ]
arr_filters = [ ]
for filter_method , filter_data in self . filters :
for filter_method , filter_data in self . filters :
if ( filter_method . startswith ( " original_language " ) and self . library . is_movie ) or filter_method . startswith ( " tmdb_vote_count " ) :
if ( filter_method . startswith ( " original_language " ) and self . library . is_movie ) or filter_method . startswith ( " tmdb_vote_count " ) :
@ -1472,6 +1517,7 @@ class CollectionBuilder:
logger . info ( f " { self . name } Collection | ? | { movie . title } (TMDb: { missing_id } ) " )
logger . info ( f " { self . name } Collection | ? | { movie . title } (TMDb: { missing_id } ) " )
elif self . details [ " show_filtered " ] is True :
elif self . details [ " show_filtered " ] is True :
logger . info ( f " { self . name } Collection | X | { movie . title } (TMDb: { missing_id } ) " )
logger . info ( f " { self . name } Collection | X | { movie . title } (TMDb: { missing_id } ) " )
logger . info ( " " )
logger . info ( f " { len ( missing_movies_with_names ) } Movie { ' s ' if len ( missing_movies_with_names ) > 1 else ' ' } Missing " )
logger . info ( f " { len ( missing_movies_with_names ) } Movie { ' s ' if len ( missing_movies_with_names ) > 1 else ' ' } Missing " )
if self . details [ " save_missing " ] is True :
if self . details [ " save_missing " ] is True :
self . library . add_missing ( self . name , missing_movies_with_names , True )
self . library . add_missing ( self . name , missing_movies_with_names , True )
@ -1506,6 +1552,7 @@ class CollectionBuilder:
logger . info ( f " { self . name } Collection | ? | { title } (TVDB: { missing_id } ) " )
logger . info ( f " { self . name } Collection | ? | { title } (TVDB: { missing_id } ) " )
elif self . details [ " show_filtered " ] is True :
elif self . details [ " show_filtered " ] is True :
logger . info ( f " { self . name } Collection | X | { title } (TVDb: { missing_id } ) " )
logger . info ( f " { self . name } Collection | X | { title } (TVDb: { missing_id } ) " )
logger . info ( " " )
logger . info ( f " { len ( missing_shows_with_names ) } Show { ' s ' if len ( missing_shows_with_names ) > 1 else ' ' } Missing " )
logger . info ( f " { len ( missing_shows_with_names ) } Show { ' s ' if len ( missing_shows_with_names ) > 1 else ' ' } Missing " )
if self . details [ " save_missing " ] is True :
if self . details [ " save_missing " ] is True :
self . library . add_missing ( self . name , missing_shows_with_names , False )
self . library . add_missing ( self . name , missing_shows_with_names , False )
@ -1520,17 +1567,22 @@ class CollectionBuilder:
self . run_again_shows . extend ( missing_tvdb_ids )
self . run_again_shows . extend ( missing_tvdb_ids )
def sync_collection ( self ) :
def sync_collection ( self ) :
logger . info ( " " )
count_removed = 0
count_removed = 0
for ratingKey , item in self . plex_map . items ( ) :
for ratingKey , item in self . plex_map . items ( ) :
if item is not None :
if item is not None :
if count_removed == 0 :
logger . info ( " " )
util . separator ( f " Removed from { self . name } Collection " , space = False , border = False )
logger . info ( " " )
logger . info ( f " { self . name } Collection | - | { item . title } " )
logger . info ( f " { self . name } Collection | - | { item . title } " )
if self . smart_label_collection :
if self . smart_label_collection :
self . library . query_data ( item . removeLabel , self . name )
self . library . query_data ( item . removeLabel , self . name )
else :
else :
self . library . query_data ( item . removeCollection , self . name )
self . library . query_data ( item . removeCollection , self . name )
count_removed + = 1
count_removed + = 1
logger . info ( f " { count_removed } { ' Movie ' if self . library . is_movie else ' Show ' } { ' s ' if count_removed == 1 else ' ' } Removed " )
if count_removed > 0 :
logger . info ( " " )
logger . info ( f " { count_removed } { ' Movie ' if self . library . is_movie else ' Show ' } { ' s ' if count_removed == 1 else ' ' } Removed " )
def update_details ( self ) :
def update_details ( self ) :
if not self . obj and self . smart_url :
if not self . obj and self . smart_url :
@ -1601,43 +1653,17 @@ class CollectionBuilder:
self . library . collection_order_query ( self . obj , self . details [ " collection_order " ] )
self . library . collection_order_query ( self . obj , self . details [ " collection_order " ] )
logger . info ( f " Detail: collection_order updated Collection Order to { self . details [ ' collection_order ' ] } " )
logger . info ( f " Detail: collection_order updated Collection Order to { self . details [ ' collection_order ' ] } " )
if " label " in self . details or " label.remove " in self . details or " label.sync " in self . details :
add_tags = self . details [ " label " ] if " label " in self . details else None
item_labels = [ label . tag for label in self . obj . labels ]
remove_tags = self . details [ " label.remove " ] if " label.remove " in self . details else None
labels = self . details [ " label " if " label " in self . details else " label.sync " ]
sync_tags = self . details [ " label.sync " ] if " label.sync " in self . details else None
if " label.sync " in self . details :
self . library . edit_tags ( " label " , self . obj , add_tags = add_tags , remove_tags = remove_tags , sync_tags = sync_tags )
for label in ( la for la in item_labels if la not in labels ) :
self . library . query_data ( self . obj . removeLabel , label )
logger . info ( f " Detail: Label { label } removed " )
if " label " in self . details or " label.sync " in self . details :
for label in ( la for la in labels if la not in item_labels ) :
self . library . query_data ( self . obj . addLabel , label )
logger . info ( f " Detail: Label { label } added " )
if " label.remove " in self . details :
for label in self . details [ " label.remove " ] :
if label in item_labels :
self . library . query_data ( self . obj . removeLabel , label )
logger . info ( f " Detail: Label { label } removed " )
if len ( self . item_details ) > 0 :
if len ( self . item_details ) > 0 :
labels = None
add_tags = self . item_details [ " item_label " ] if " item_label " in self . item_details else None
if " item_label " in self . item_details or " item_label.remove " in self . item_details or " item_label.sync " in self . item_details :
remove_tags = self . item_details [ " item_label.remove " ] if " item_label.remove " in self . item_details else None
label s = self . item_details [ " item_label " if " item_label " in self . item_details else " item_label.sync " ]
sync_tags = self . item_details [ " item_label.sync " ] if " item_label.sync " in self . item_details else None
for item in self . library . get_collection_items ( self . obj , self . smart_label_collection ) :
for item in self . library . get_collection_items ( self . obj , self . smart_label_collection ) :
if labels is not None :
self . library . edit_tags ( " label " , item , add_tags = add_tags , remove_tags = remove_tags , sync_tags = sync_tags )
item_labels = [ label . tag for label in item . labels ]
if " item_label.sync " in self . item_details :
for label in ( la for la in item_labels if la not in labels ) :
self . library . query_data ( item . removeLabel , label )
logger . info ( f " Detail: Label { label } removed from { item . title } " )
if " item_label " in self . item_details or " item_label.sync " in self . item_details :
for label in ( la for la in labels if la not in item_labels ) :
self . library . query_data ( item . addLabel , label )
logger . info ( f " Detail: Label { label } added to { item . title } " )
if " item_label.remove " in self . item_details :
for label in self . item_details [ " item_label.remove " ] :
if label in item_labels :
self . library . query_data ( self . obj . removeLabel , label )
logger . info ( f " Detail: Label { label } removed from { item . title } " )
advance_edits = { }
advance_edits = { }
for method_name , method_data in self . item_details . items ( ) :
for method_name , method_data in self . item_details . items ( ) :
if method_name in plex . item_advance_keys :
if method_name in plex . item_advance_keys :
@ -1670,9 +1696,6 @@ class CollectionBuilder:
except BadRequest :
except BadRequest :
logger . error ( f " Detail: { image_method } failed to update { message } " )
logger . error ( f " Detail: { image_method } failed to update { message } " )
if len ( self . posters ) > 0 :
logger . info ( " " )
if len ( self . posters ) > 1 :
if len ( self . posters ) > 1 :
logger . info ( f " { len ( self . posters ) } posters found: " )
logger . info ( f " { len ( self . posters ) } posters found: " )
for p in self . posters :
for p in self . posters :
@ -1697,9 +1720,6 @@ class CollectionBuilder:
elif " tmdb_show_details " in self . posters : set_image ( " tmdb_show_details " , self . posters )
elif " tmdb_show_details " in self . posters : set_image ( " tmdb_show_details " , self . posters )
else : logger . info ( " No poster to update " )
else : logger . info ( " No poster to update " )
if len ( self . backgrounds ) > 0 :
logger . info ( " " )
if len ( self . backgrounds ) > 1 :
if len ( self . backgrounds ) > 1 :
logger . info ( f " { len ( self . backgrounds ) } backgrounds found: " )
logger . info ( f " { len ( self . backgrounds ) } backgrounds found: " )
for b in self . backgrounds :
for b in self . backgrounds :