2022-02-13 16:33:57 +00:00
import os , re , time
2022-04-27 23:14:52 +00:00
from datetime import datetime
2022-03-19 05:16:25 +00:00
from modules import anidb , anilist , flixpatrol , icheckmovies , imdb , letterboxd , mal , plex , radarr , reciperr , sonarr , tautulli , tmdb , trakt , tvdb , mdblist , util
2022-01-06 19:16:12 +00:00
from modules . util import Failed , ImageData , NotScheduled , NotScheduledRange
2021-12-29 15:51:22 +00:00
from plexapi . audio import Artist , Album , Track
2021-03-01 20:59:10 +00:00
from plexapi . exceptions import BadRequest , NotFound
2021-08-22 15:54:33 +00:00
from plexapi . video import Movie , Show , Season , Episode
2021-05-05 18:01:41 +00:00
from urllib . parse import quote
2021-02-20 05:41:45 +00:00
2022-02-13 16:33:57 +00:00
logger = util . logger
2021-02-20 05:41:45 +00:00
2021-04-21 12:49:27 +00:00
advance_new_agent = [ " item_metadata_language " , " item_use_original_title " ]
advance_show = [ " item_episode_sorting " , " item_keep_episodes " , " item_delete_episodes " , " item_season_display " , " item_episode_sorting " ]
2021-11-27 00:30:41 +00:00
all_builders = anidb . builders + anilist . builders + flixpatrol . builders + icheckmovies . builders + imdb . builders + \
2022-03-19 05:16:25 +00:00
letterboxd . builders + mal . builders + plex . builders + reciperr . builders + tautulli . builders + \
2022-05-02 19:05:52 +00:00
tmdb . builders + trakt . builders + tvdb . builders + mdblist . builders + radarr . builders + sonarr . builders
2022-03-22 18:16:30 +00:00
show_only_builders = [
" tmdb_network " , " tmdb_show " , " tmdb_show_details " , " tvdb_show " , " tvdb_show_details " , " tmdb_airing_today " ,
2022-05-02 19:05:52 +00:00
" tmdb_on_the_air " , " collection_level " , " item_tmdb_season_titles " , " sonarr_all " , " sonarr_taglist "
2022-03-22 18:16:30 +00:00
]
2021-03-30 05:50:53 +00:00
movie_only_builders = [
2021-08-01 01:23:17 +00:00
" letterboxd_list " , " letterboxd_list_details " , " icheckmovies_list " , " icheckmovies_list_details " , " stevenlu_popular " ,
2021-07-23 19:44:21 +00:00
" tmdb_collection " , " tmdb_collection_details " , " tmdb_movie " , " tmdb_movie_details " , " tmdb_now_playing " ,
2022-05-02 19:05:52 +00:00
" tvdb_movie " , " tvdb_movie_details " , " tmdb_upcoming " , " trakt_boxoffice " , " reciperr_list " , " radarr_all " , " radarr_taglist "
2021-05-02 04:22:48 +00:00
]
2021-12-29 15:51:22 +00:00
music_only_builders = [ " item_album_sorting " ]
2021-06-22 20:28:12 +00:00
summary_details = [
" summary " , " tmdb_summary " , " tmdb_description " , " tmdb_biography " , " tvdb_summary " ,
2021-07-03 19:17:05 +00:00
" tvdb_description " , " trakt_description " , " letterboxd_description " , " icheckmovies_description "
2021-06-22 20:28:12 +00:00
]
2021-07-23 19:44:21 +00:00
poster_details = [ " url_poster " , " tmdb_poster " , " tmdb_profile " , " tvdb_poster " , " file_poster " ]
background_details = [ " url_background " , " tmdb_background " , " tvdb_background " , " file_background " ]
2021-12-19 16:17:03 +00:00
boolean_details = [
" show_filtered " , " show_missing " , " save_missing " , " missing_only_released " , " only_filter_missing " ,
2022-02-02 16:28:25 +00:00
" delete_below_minimum " , " asset_folders " , " create_asset_folders "
2021-12-19 16:17:03 +00:00
]
2021-12-19 05:41:58 +00:00
scheduled_boolean = [ " visible_library " , " visible_home " , " visible_shared " ]
2021-07-23 19:44:21 +00:00
string_details = [ " sort_title " , " content_rating " , " name_mapping " ]
2021-08-22 15:54:33 +00:00
ignored_details = [
2022-04-20 12:56:07 +00:00
" smart_filter " , " smart_label " , " smart_url " , " run_again " , " schedule " , " sync_mode " , " template " , " test " , " suppress_overlays " ,
2022-04-18 18:16:39 +00:00
" delete_not_scheduled " , " tmdb_person " , " build_collection " , " collection_order " , " collection_level " , " overlay " ,
2022-01-27 21:24:50 +00:00
" validate_builders " , " libraries " , " sync_to_users " , " collection_name " , " playlist_name " , " name " , " blank_collection "
2021-08-22 15:54:33 +00:00
]
2022-03-23 18:43:50 +00:00
details = [
2022-04-08 18:29:38 +00:00
" ignore_ids " , " ignore_imdb_ids " , " server_preroll " , " changes_webhooks " , " collection_filtering " , " collection_mode " , " limit " , " url_theme " ,
2022-03-23 18:43:50 +00:00
" file_theme " , " minimum_items " , " label " , " album_sorting " , " cache_builders " , " tmdb_region "
] + boolean_details + scheduled_boolean + string_details
2021-07-23 19:44:21 +00:00
collectionless_details = [ " collection_order " , " plex_collectionless " , " label " , " label_sync_mode " , " test " ] + \
poster_details + background_details + summary_details + string_details
2022-01-26 14:43:58 +00:00
item_false_details = [ " item_lock_background " , " item_lock_poster " , " item_lock_title " ]
2022-04-18 18:16:39 +00:00
item_bool_details = [ " item_tmdb_season_titles " , " revert_overlay " , " item_refresh " ] + item_false_details
2022-01-25 01:46:48 +00:00
item_details = [ " non_item_remove_label " , " item_label " , " item_radarr_tag " , " item_sonarr_tag " , " item_overlay " , " item_refresh_delay " ] + item_bool_details + list ( plex . item_advance_keys . keys ( ) )
2022-05-02 19:05:52 +00:00
none_details = [ " label.sync " , " item_label.sync " , " radarr_taglist " , " sonarr_taglist " ]
2022-04-30 23:57:26 +00:00
radarr_details = [
" radarr_add_missing " , " radarr_add_existing " , " radarr_upgrade_existing " , " radarr_folder " , " radarr_monitor " ,
" radarr_search " , " radarr_availability " , " radarr_quality " , " radarr_tag " , " item_radarr_tag "
]
2021-08-22 15:54:33 +00:00
sonarr_details = [
2022-04-30 23:57:26 +00:00
" sonarr_add_missing " , " sonarr_add_existing " , " sonarr_upgrade_existing " , " sonarr_folder " , " sonarr_monitor " , " sonarr_language " ,
" sonarr_series " , " sonarr_quality " , " sonarr_season " , " sonarr_search " , " sonarr_cutoff_search " , " sonarr_tag " , " item_sonarr_tag "
2021-08-22 15:54:33 +00:00
]
2022-01-25 07:45:31 +00:00
album_details = [ " non_item_remove_label " , " item_label " , " item_album_sorting " ]
2022-01-31 16:04:09 +00:00
discover_types = {
" Documentary " : " documentary " , " News " : " news " , " Miniseries " : " miniseries " ,
" Reality " : " reality " , " Scripted " : " scripted " , " Talk Show " : " talk_show " , " Video " : " video "
}
discover_status = {
" Returning Series " : " returning " , " Planned " : " planned " , " In Production " : " production " ,
2022-01-31 17:13:56 +00:00
" Ended " : " ended " , " Canceled " : " canceled " , " Pilot " : " pilot "
2022-01-31 16:04:09 +00:00
}
2021-12-29 15:51:22 +00:00
filters_by_type = {
2022-02-22 05:56:04 +00:00
" movie_show_season_episode_artist_album_track " : [ " title " , " summary " , " collection " , " has_collection " , " added " , " last_played " , " user_rating " , " plays " ] ,
2021-12-29 15:51:22 +00:00
" movie_show_season_episode_album_track " : [ " year " ] ,
" movie_show_episode_artist_track " : [ " filepath " ] ,
" movie_show_episode_album " : [ " release " , " critic_rating " , " history " ] ,
" movie_show_episode_track " : [ " duration " ] ,
" movie_show_artist_album " : [ " genre " ] ,
" movie_show_episode " : [ " actor " , " content_rating " , " audience_rating " ] ,
" movie_show_album " : [ " label " ] ,
" movie_episode_track " : [ " audio_track_title " ] ,
2022-02-22 17:20:50 +00:00
" movie_show " : [ " studio " , " original_language " , " has_overlay " , " tmdb_vote_count " , " tmdb_year " , " tmdb_genre " , " tmdb_title " , " tmdb_keyword " ] ,
2022-02-23 20:28:58 +00:00
" movie_episode " : [ " director " , " producer " , " writer " , " resolution " , " audio_language " , " subtitle_language " , " has_dolby_vision " ] ,
2021-12-29 15:51:22 +00:00
" movie_artist " : [ " country " ] ,
2022-04-27 23:14:52 +00:00
" show_season " : [ " episodes " ] ,
" artist_album " : [ " tracks " ] ,
" show " : [ " seasons " , " tmdb_status " , " tmdb_type " , " origin_country " , " network " , " first_episode_aired " , " last_episode_aired " ] ,
" artist " : [ " albums " ] ,
2021-12-29 15:51:22 +00:00
" album " : [ " record_label " ]
}
filters = {
" movie " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " movie " in check ] ,
" show " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " show " in check ] ,
" season " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " season " in check ] ,
" episode " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " episode " in check ] ,
" artist " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " artist " in check ] ,
" album " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " album " in check ] ,
" track " : [ item for check , sub in filters_by_type . items ( ) for item in sub if " track " in check ]
}
2022-03-11 07:07:37 +00:00
tmdb_filters = [
" original_language " , " origin_country " , " tmdb_vote_count " , " tmdb_year " , " tmdb_keyword " , " tmdb_genre " ,
" first_episode_aired " , " last_episode_aired " , " tmdb_status " , " tmdb_type " , " tmdb_title "
]
2022-02-22 17:20:50 +00:00
string_filters = [ " title " , " summary " , " studio " , " record_label " , " filepath " , " audio_track_title " , " tmdb_title " ]
2021-12-29 15:51:22 +00:00
string_modifiers = [ " " , " .not " , " .is " , " .isnot " , " .begins " , " .ends " , " .regex " ]
tag_filters = [
2022-03-11 07:07:37 +00:00
" actor " , " collection " , " content_rating " , " country " , " director " , " network " , " genre " , " label " , " producer " , " year " , " origin_country " ,
2022-04-20 16:03:08 +00:00
" writer " , " resolution " , " audio_language " , " subtitle_language " , " tmdb_keyword " , " tmdb_genre "
2021-03-30 05:50:53 +00:00
]
2022-04-20 16:03:08 +00:00
tag_modifiers = [ " " , " .not " , " .regex " , " .count_gt " , " .count_gte " , " .count_lt " , " .count_lte " ]
2022-02-23 20:28:58 +00:00
boolean_filters = [ " has_collection " , " has_overlay " , " has_dolby_vision " ]
2021-12-29 15:51:22 +00:00
date_filters = [ " release " , " added " , " last_played " , " first_episode_aired " , " last_episode_aired " ]
date_modifiers = [ " " , " .not " , " .before " , " .after " , " .regex " ]
number_filters = [ " year " , " tmdb_year " , " critic_rating " , " audience_rating " , " user_rating " , " tmdb_vote_count " , " plays " , " duration " ]
number_modifiers = [ " .gt " , " .gte " , " .lt " , " .lte " ]
2022-04-27 23:14:52 +00:00
special_filters = [
" history " , " episodes " , " seasons " , " albums " , " tracks " , " original_language " , " original_language.not " ,
" tmdb_status " , " tmdb_status.not " , " tmdb_type " , " tmdb_type.not "
]
2021-12-29 15:51:22 +00:00
all_filters = boolean_filters + special_filters + \
[ f " { f } { m } " for f in string_filters for m in string_modifiers ] + \
[ f " { f } { m } " for f in tag_filters for m in tag_modifiers ] + \
[ f " { f } { m } " for f in date_filters for m in date_modifiers ] + \
[ f " { f } { m } " for f in number_filters for m in number_modifiers ]
2022-04-20 16:03:08 +00:00
date_attributes = plex . date_attributes + [ " first_episode_aired " , " last_episode_aired " ]
year_attributes = plex . year_attributes + [ " tmdb_year " ]
number_attributes = plex . number_attributes + [ " tmdb_vote_count " ]
2021-08-22 15:54:33 +00:00
smart_invalid = [ " collection_order " , " collection_level " ]
2022-04-08 18:29:38 +00:00
smart_only = [ " collection_filtering " ]
2022-04-05 18:46:00 +00:00
smart_url_invalid = [ " filters " , " run_again " , " sync_mode " , " show_filtered " , " show_missing " , " save_missing " , " smart_label " ] + radarr_details + sonarr_details
2021-07-29 02:26:39 +00:00
custom_sort_builders = [
2022-03-12 21:59:37 +00:00
" plex_search " , " plex_pilots " , " tmdb_list " , " tmdb_popular " , " tmdb_now_playing " , " tmdb_top_rated " ,
2022-03-29 20:17:20 +00:00
" tmdb_trending_daily " , " tmdb_trending_weekly " , " tmdb_discover " , " reciperr_list " , " trakt_chart " , " trakt_userlist " ,
2022-03-22 18:16:30 +00:00
" tvdb_list " , " imdb_chart " , " imdb_list " , " stevenlu_popular " , " anidb_popular " , " tmdb_upcoming " , " tmdb_airing_today " ,
" tmdb_on_the_air " , " trakt_list " , " trakt_watchlist " , " trakt_collection " , " trakt_trending " , " trakt_popular " , " trakt_boxoffice " ,
2021-10-25 16:43:13 +00:00
" trakt_collected_daily " , " trakt_collected_weekly " , " trakt_collected_monthly " , " trakt_collected_yearly " , " trakt_collected_all " ,
2022-04-08 20:59:14 +00:00
" flixpatrol_url " , " flixpatrol_demographics " , " flixpatrol_popular " , " flixpatrol_top " , " trakt_recommendations " ,
2022-03-04 11:15:32 +00:00
" trakt_recommended_personal " , " trakt_recommended_daily " , " trakt_recommended_weekly " , " trakt_recommended_monthly " , " trakt_recommended_yearly " , " trakt_recommended_all " ,
2021-10-25 16:43:13 +00:00
" trakt_watched_daily " , " trakt_watched_weekly " , " trakt_watched_monthly " , " trakt_watched_yearly " , " trakt_watched_all " ,
2022-01-25 07:45:31 +00:00
" tautulli_popular " , " tautulli_watched " , " mdblist_list " , " letterboxd_list " , " icheckmovies_list " ,
2022-03-13 21:06:31 +00:00
" anilist_top_rated " , " anilist_popular " , " anilist_trending " , " anilist_search " , " anilist_userlist " ,
2022-05-03 18:44:19 +00:00
" mal_all " , " mal_airing " , " mal_upcoming " , " mal_tv " , " mal_movie " , " mal_ova " , " mal_special " , " mal_search " ,
2021-08-17 00:07:35 +00:00
" mal_popular " , " mal_favorite " , " mal_suggested " , " mal_userlist " , " mal_season " , " mal_genre " , " mal_studio "
2021-07-29 02:26:39 +00:00
]
2022-01-24 22:36:40 +00:00
episode_parts_only = [ " plex_pilots " ]
2022-04-20 12:56:07 +00:00
overlay_only = [ " overlay " , " suppress_overlays " ]
2022-04-18 18:16:39 +00:00
overlay_attributes = [
" filters " , " limit " , " show_missing " , " save_missing " , " missing_only_released " , " minimum_items " , " cache_builders " , " tmdb_region "
] + all_builders + overlay_only
2021-12-29 15:51:22 +00:00
parts_collection_valid = [
2022-04-08 18:29:38 +00:00
" filters " , " plex_all " , " plex_search " , " trakt_list " , " trakt_list_details " , " collection_filtering " , " collection_mode " , " label " , " visible_library " , " limit " ,
2022-01-24 22:36:40 +00:00
" visible_home " , " visible_shared " , " show_missing " , " save_missing " , " missing_only_released " , " server_preroll " , " changes_webhooks " ,
2022-03-06 06:09:03 +00:00
" item_lock_background " , " item_lock_poster " , " item_lock_title " , " item_refresh " , " item_refresh_delay " , " imdb_list " , " cache_builders " ,
2022-04-30 22:12:45 +00:00
" url_theme " , " file_theme " , " item_label "
2022-01-24 22:36:40 +00:00
] + episode_parts_only + summary_details + poster_details + background_details + string_details
2021-12-17 14:24:46 +00:00
playlist_attributes = [
2021-12-26 15:48:52 +00:00
" filters " , " name_mapping " , " show_filtered " , " show_missing " , " save_missing " ,
2021-12-17 14:24:46 +00:00
" missing_only_released " , " only_filter_missing " , " delete_below_minimum " , " ignore_ids " , " ignore_imdb_ids " ,
2022-02-02 15:38:38 +00:00
" server_preroll " , " changes_webhooks " , " minimum_items " , " cache_builders "
2021-12-26 15:48:52 +00:00
] + custom_sort_builders + summary_details + poster_details + radarr_details + sonarr_details
2021-12-29 15:51:22 +00:00
music_attributes = [
2022-04-18 18:16:39 +00:00
" non_item_remove_label " , " item_label " , " collection_filtering " , " item_lock_background " , " item_lock_poster " , " item_lock_title " ,
2022-01-11 18:51:49 +00:00
" item_refresh " , " item_refresh_delay " , " plex_search " , " plex_all " , " filters "
2021-12-29 15:51:22 +00:00
] + details + summary_details + poster_details + background_details
2021-03-30 05:50:53 +00:00
2021-02-20 05:41:45 +00:00
class CollectionBuilder :
2022-04-19 05:58:38 +00:00
def __init__ ( self , config , metadata , name , data , library = None , overlay = None , extra = None ) :
2021-02-20 05:41:45 +00:00
self . config = config
2021-05-07 15:29:29 +00:00
self . metadata = metadata
2021-09-13 00:05:38 +00:00
self . mapping_name = name
2021-02-20 05:41:45 +00:00
self . data = data
2022-02-08 22:57:36 +00:00
self . library = library
self . libraries = [ ]
self . playlist = library is None
2022-04-18 18:16:39 +00:00
self . overlay = overlay
2022-02-08 22:57:36 +00:00
methods = { m . lower ( ) : m for m in self . data }
2022-04-18 18:16:39 +00:00
if self . playlist :
self . type = " playlist "
elif self . overlay :
self . type = " overlay "
else :
self . type = " collection "
2022-02-08 22:57:36 +00:00
self . Type = self . type . capitalize ( )
2022-04-19 05:58:38 +00:00
logger . separator ( f " { self . mapping_name } { self . Type } { f ' in { self . library . name } ' if self . library else ' ' } " )
logger . info ( " " )
if extra :
logger . info ( extra )
logger . info ( " " )
logger . separator ( f " Validating { self . mapping_name } Attributes " , space = False , border = False )
2022-02-08 22:57:36 +00:00
if " name " in methods :
name = self . data [ methods [ " name " ] ]
elif f " { self . type } _name " in methods :
logger . warning ( f " Config Warning: Running { self . type } _name as name " )
name = self . data [ methods [ f " { self . type } _name " ] ]
else :
name = None
if name :
logger . debug ( " " )
logger . debug ( " Validating Method: name " )
if not name :
raise Failed ( f " { self . Type } Error: name attribute is blank " )
logger . debug ( f " Value: { name } " )
2022-02-13 18:28:53 +00:00
self . name = str ( name )
2022-02-08 22:57:36 +00:00
else :
2022-02-13 18:28:53 +00:00
self . name = str ( self . mapping_name )
2022-02-08 22:57:36 +00:00
if " template " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: template " )
new_attributes = self . metadata . apply_template ( self . name , self . data , self . data [ methods [ " template " ] ] )
for attr in new_attributes :
if attr . lower ( ) not in methods :
self . data [ attr ] = new_attributes [ attr ]
methods [ attr . lower ( ) ] = attr
2022-04-20 12:56:07 +00:00
self . suppress_overlays = [ ]
2022-04-21 20:56:32 +00:00
self . overlay_group = None
2022-04-22 15:24:44 +00:00
self . overlay_weight = None
2022-04-23 02:48:26 +00:00
self . overlay_path = None
2022-04-26 19:56:46 +00:00
self . overlay_coordinates = None
2022-04-18 18:16:39 +00:00
if self . overlay :
if " overlay " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: overlay " )
logger . debug ( f " Value: { data [ methods [ ' overlay ' ] ] } " )
if isinstance ( data [ methods [ " overlay " ] ] , dict ) :
if " name " not in data [ methods [ " overlay " ] ] or not data [ methods [ " overlay " ] ] [ " name " ] :
raise Failed ( f " { self . Type } Error: overlay must have the name attribute " )
2022-04-21 05:35:07 +00:00
self . overlay = str ( data [ methods [ " overlay " ] ] [ " name " ] )
2022-04-21 20:56:32 +00:00
if " group " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " group " ] :
self . overlay_group = str ( data [ methods [ " overlay " ] ] [ " group " ] )
2022-04-26 19:56:46 +00:00
if " weight " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " weight " ] is not None :
pri = util . check_num ( data [ methods [ " overlay " ] ] [ " weight " ] )
if pri is None :
raise Failed ( f " { self . Type } Error: overlay weight must be a number " )
self . overlay_weight = pri
if ( " group " in data [ methods [ " overlay " ] ] or " weight " in data [ methods [ " overlay " ] ] ) and ( not self . overlay_group or self . overlay_weight is None ) :
raise Failed ( f " { self . Type } Error: overlay group and overlay weight must be used together " )
x_coordinate = None
2022-04-26 20:29:03 +00:00
if " x_coordinate " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " x_coordinate " ] is not None :
2022-04-26 19:56:46 +00:00
x_coordinate = util . check_num ( data [ methods [ " overlay " ] ] [ " x_coordinate " ] )
if x_coordinate is None or x_coordinate < 0 :
raise Failed ( f " { self . Type } Error: overlay x_coordinate: { data [ methods [ ' overlay ' ] ] [ ' x_coordinate ' ] } invalid must be a number 0 or greater " )
y_coordinate = None
2022-04-26 20:29:03 +00:00
if " y_coordinate " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " y_coordinate " ] is not None :
2022-04-26 19:56:46 +00:00
y_coordinate = util . check_num ( data [ methods [ " overlay " ] ] [ " y_coordinate " ] )
if y_coordinate is None or y_coordinate < 0 :
raise Failed ( f " { self . Type } Error: overlay y_coordinate: { data [ methods [ ' overlay ' ] ] [ ' y_coordinate ' ] } invalid must be a number 0 or greater " )
if ( " x_coordinate " in data [ methods [ " overlay " ] ] or " y_coordinate " in data [ methods [ " overlay " ] ] ) and ( x_coordinate is None or y_coordinate is None ) :
raise Failed ( f " { self . Type } Error: overlay x_coordinate and overlay y_coordinate must be used together " )
2022-04-27 15:11:10 +00:00
if x_coordinate is not None or y_coordinate is not None :
2022-04-26 19:56:46 +00:00
self . overlay_coordinates = ( x_coordinate , y_coordinate )
2022-04-23 02:48:26 +00:00
def get_and_save_image ( image_url ) :
response = self . config . get ( image_url )
2022-04-18 18:16:39 +00:00
if response . status_code > = 400 :
2022-04-23 02:48:26 +00:00
raise Failed ( f " { self . Type } Error: Overlay Image not found at: { image_url } " )
2022-04-18 18:16:39 +00:00
if " Content-Type " not in response . headers or response . headers [ " Content-Type " ] != " image/png " :
2022-04-26 19:56:46 +00:00
raise Failed ( f " { self . Type } Error: Overlay Image not a png: { image_url } " )
2022-04-18 18:16:39 +00:00
if not os . path . exists ( library . overlay_folder ) or not os . path . isdir ( library . overlay_folder ) :
os . makedirs ( library . overlay_folder , exist_ok = False )
logger . info ( f " Creating Overlay Folder found at: { library . overlay_folder } " )
2022-04-23 02:48:26 +00:00
clean_image_name , _ = util . validate_filename ( self . overlay )
image_path = os . path . join ( library . overlay_folder , f " { clean_image_name } .png " )
if os . path . exists ( image_path ) :
os . remove ( image_path )
with open ( image_path , " wb " ) as handler :
2022-04-18 18:16:39 +00:00
handler . write ( response . content )
2022-04-23 02:48:26 +00:00
while util . is_locked ( image_path ) :
2022-04-18 18:16:39 +00:00
time . sleep ( 1 )
2022-04-23 02:48:26 +00:00
return image_path
if " file " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " file " ] :
self . overlay_path = data [ methods [ " overlay " ] ] [ " file " ]
elif " git " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " git " ] :
self . overlay_path = get_and_save_image ( f " { util . github_base } { data [ methods [ ' overlay ' ] ] [ ' git ' ] } .png " )
elif " repo " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " repo " ] :
self . overlay_path = get_and_save_image ( f " { self . config . custom_repo } { data [ methods [ ' overlay ' ] ] [ ' repo ' ] } .png " )
elif " url " in data [ methods [ " overlay " ] ] and data [ methods [ " overlay " ] ] [ " url " ] :
self . overlay_path = get_and_save_image ( data [ methods [ " overlay " ] ] [ " url " ] )
2022-04-18 18:16:39 +00:00
else :
2022-04-21 05:35:07 +00:00
self . overlay = str ( data [ methods [ " overlay " ] ] )
2022-04-18 18:16:39 +00:00
else :
2022-04-21 05:35:07 +00:00
self . overlay = str ( self . mapping_name )
2022-04-19 05:58:38 +00:00
logger . warning ( f " { self . Type } Warning: No overlay attribute using mapping name { self . mapping_name } as the overlay name " )
2022-04-20 22:56:37 +00:00
if self . overlay . startswith ( " blur " ) :
try :
match = re . search ( " \\ (([^)]+) \\ ) " , self . overlay )
if not match or 0 > = int ( match . group ( 1 ) ) > 100 :
raise ValueError
self . overlay = f " blur( { match . group ( 1 ) } ) "
except ValueError :
logger . error ( f " Overlay Error: failed to parse overlay blur name: { self . overlay } defaulting to blur(50) " )
self . overlay = " blur(50) "
2022-04-23 02:48:26 +00:00
else :
2022-04-26 19:56:46 +00:00
if " | " in self . overlay :
raise Failed ( f " { self . Type } Error: Overlay Name: { self . overlay } cannot contain ' | ' " )
2022-04-23 02:48:26 +00:00
if not self . overlay_path :
clean_name , _ = util . validate_filename ( self . overlay )
self . overlay_path = os . path . join ( library . overlay_folder , f " { clean_name } .png " )
if not os . path . exists ( self . overlay_path ) :
raise Failed ( f " { self . Type } Error: Overlay Image not found at: { self . overlay_path } " )
2022-04-18 18:16:39 +00:00
2022-04-20 12:56:07 +00:00
if " suppress_overlays " in methods :
2022-04-19 05:58:38 +00:00
logger . debug ( " " )
2022-04-20 12:56:07 +00:00
logger . debug ( " Validating Method: suppress_overlays " )
logger . debug ( f " Value: { data [ methods [ ' suppress_overlays ' ] ] } " )
if data [ methods [ " suppress_overlays " ] ] :
self . suppress_overlays = util . get_list ( data [ methods [ " suppress_overlays " ] ] )
2022-04-19 05:58:38 +00:00
else :
2022-04-20 12:56:07 +00:00
logger . error ( f " { self . Type } Error: suppress_overlays attribute is blank " )
2022-04-19 05:58:38 +00:00
2022-05-07 15:37:36 +00:00
self . sync_to_users = None
self . valid_users = [ ]
2022-02-08 22:57:36 +00:00
if self . playlist :
2022-05-07 21:20:22 +00:00
if " libraries " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: libraries " )
if not self . data [ methods [ " libraries " ] ] :
raise Failed ( f " { self . Type } Error: libraries attribute is blank " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' libraries ' ] ] } " )
for pl_library in util . get_list ( self . data [ methods [ " libraries " ] ] ) :
if str ( pl_library ) in config . library_map :
self . libraries . append ( config . library_map [ pl_library ] )
else :
raise Failed ( f " Playlist Error: Library: { pl_library } not defined " )
self . library = self . libraries [ 0 ]
else :
raise Failed ( " Playlist Error: libraries attribute is required " )
2022-05-07 15:37:36 +00:00
self . sync_to_users = config . general [ " playlist_sync_to_users " ]
if " sync_to_users " in methods or " sync_to_user " in methods :
s_attr = f " sync_to_user { ' s ' if ' sync_to_users ' in methods else ' ' } "
logger . debug ( " " )
logger . debug ( f " Validating Method: { s_attr } " )
logger . debug ( f " Value: { self . data [ methods [ s_attr ] ] } " )
if self . data [ methods [ s_attr ] ] :
self . sync_to_users = self . data [ methods [ s_attr ] ]
else :
logger . warning ( f " Playlist Error: sync_to_users attribute is blank defaulting to playlist_sync_to_users: { self . sync_to_users } " )
else :
logger . warning ( f " Playlist Error: sync_to_users attribute not found defaulting to playlist_sync_to_users: { self . sync_to_users } " )
plex_users = self . library . users
if self . sync_to_users :
if str ( self . sync_to_users ) == " all " :
self . valid_users = plex_users
else :
for user in util . get_list ( self . sync_to_users ) :
if user in plex_users :
self . valid_users . append ( user )
else :
raise Failed ( f " Playlist Error: User: { user } not found in plex \n Options: { plex_users } " )
if " delete_playlist " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: delete_not_scheduled " )
logger . debug ( f " Value: { data [ methods [ ' delete_not_scheduled ' ] ] } " )
if util . parse ( self . Type , " delete_not_scheduled " , self . data , datatype = " bool " , methods = methods , default = False ) :
self . obj = self . library . get_playlist ( self . name )
logger . info ( self . delete ( ) )
2022-02-08 22:57:36 +00:00
else :
self . libraries . append ( self . library )
2022-04-29 13:36:07 +00:00
self . asset_directory = metadata . asset_directory if metadata . asset_directory else self . library . asset_directory
2021-07-21 20:59:27 +00:00
self . language = self . library . Plex . language
2021-02-20 06:41:40 +00:00
self . details = {
2021-05-26 14:45:33 +00:00
" show_filtered " : self . library . show_filtered ,
2021-12-17 00:16:08 +00:00
" show_options " : self . library . show_options ,
2021-05-26 14:45:33 +00:00
" show_missing " : self . library . show_missing ,
2021-06-30 22:26:53 +00:00
" save_missing " : self . library . save_missing ,
2021-08-16 05:41:04 +00:00
" missing_only_released " : self . library . missing_only_released ,
2021-12-01 16:59:49 +00:00
" only_filter_missing " : self . library . only_filter_missing ,
2021-12-19 16:17:03 +00:00
" asset_folders " : self . library . asset_folders ,
2021-08-02 19:00:15 +00:00
" create_asset_folders " : self . library . create_asset_folders ,
2021-10-04 17:51:32 +00:00
" delete_below_minimum " : self . library . delete_below_minimum ,
2021-12-06 22:30:38 +00:00
" delete_not_scheduled " : self . library . delete_not_scheduled ,
2022-02-02 15:38:38 +00:00
" changes_webhooks " : self . library . changes_webhooks ,
2022-02-02 16:28:25 +00:00
" cache_builders " : 0
2021-02-20 06:41:40 +00:00
}
2022-04-20 21:30:31 +00:00
if self . library . mass_collection_mode :
self . details [ " collection_mode " ] = self . library . mass_collection_mode
2021-04-21 12:49:27 +00:00
self . item_details = { }
2021-08-16 05:41:04 +00:00
self . radarr_details = { }
self . sonarr_details = { }
2021-03-01 20:59:10 +00:00
self . missing_movies = [ ]
self . missing_shows = [ ]
2021-08-22 15:54:33 +00:00
self . missing_parts = [ ]
2022-01-25 06:30:07 +00:00
self . added_to_radarr = [ ]
self . added_to_sonarr = [ ]
2021-07-29 02:26:39 +00:00
self . builders = [ ]
2021-02-20 05:41:45 +00:00
self . filters = [ ]
2021-08-05 14:59:45 +00:00
self . tmdb_filters = [ ]
2021-12-17 14:24:46 +00:00
self . added_items = [ ]
2021-08-09 15:50:41 +00:00
self . filtered_keys = { }
2021-05-14 19:22:59 +00:00
self . run_again_movies = [ ]
self . run_again_shows = [ ]
2021-11-03 14:36:11 +00:00
self . notification_additions = [ ]
self . notification_removals = [ ]
2021-09-15 03:16:59 +00:00
self . items = [ ]
2022-02-09 02:31:29 +00:00
self . remove_item_map = { }
2021-02-28 23:06:04 +00:00
self . posters = { }
self . backgrounds = { }
self . summaries = { }
2021-02-26 18:15:05 +00:00
self . schedule = " "
2022-02-22 05:56:04 +00:00
self . limit = 0
2022-02-09 02:31:29 +00:00
self . beginning_count = 0
2021-12-29 19:51:07 +00:00
self . minimum = self . library . minimum_items
2022-03-23 18:43:50 +00:00
self . tmdb_region = None
2021-12-08 05:17:35 +00:00
self . ignore_ids = [ i for i in self . library . ignore_ids ]
self . ignore_imdb_ids = [ i for i in self . library . ignore_imdb_ids ]
2021-12-08 03:31:20 +00:00
self . server_preroll = None
2021-05-27 17:40:35 +00:00
self . current_time = datetime . now ( )
self . current_year = self . current_time . year
2022-03-08 02:37:40 +00:00
self . url_theme = None
self . file_theme = None
2022-05-09 06:58:09 +00:00
self . sync_to_trakt_list = None
2022-05-09 15:22:41 +00:00
self . sync_missing_to_trakt_list = False
2021-12-13 19:43:36 +00:00
self . collection_poster = None
self . collection_background = None
2021-10-04 17:51:32 +00:00
self . exists = False
self . created = False
2021-12-09 04:57:00 +00:00
self . deleted = False
2021-03-21 23:00:10 +00:00
2022-02-08 22:57:36 +00:00
if self . playlist :
server_check = None
for pl_library in self . libraries :
if server_check :
if pl_library . PlexServer . machineIdentifier != server_check :
raise Failed ( " Playlist Error: All defined libraries must be on the same server " )
else :
server_check = pl_library . PlexServer . machineIdentifier
2021-12-26 15:48:52 +00:00
2022-04-18 18:16:39 +00:00
if " delete_not_scheduled " in methods and not self . overlay :
2021-12-06 22:30:38 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: delete_not_scheduled " )
logger . debug ( f " Value: { data [ methods [ ' delete_not_scheduled ' ] ] } " )
2022-01-28 18:36:21 +00:00
self . details [ " delete_not_scheduled " ] = util . parse ( self . Type , " delete_not_scheduled " , self . data , datatype = " bool " , methods = methods , default = False )
2021-12-06 22:30:38 +00:00
2022-04-25 20:18:04 +00:00
if " schedule " in methods and not self . config . requested_collections and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: schedule " )
2021-05-24 15:23:21 +00:00
if not self . data [ methods [ " schedule " ] ] :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: schedule attribute is blank " )
2021-05-24 15:23:21 +00:00
else :
logger . debug ( f " Value: { self . data [ methods [ ' schedule ' ] ] } " )
2022-01-09 00:45:42 +00:00
err = None
2021-12-18 03:02:24 +00:00
try :
2022-04-25 20:18:04 +00:00
util . schedule_check ( " schedule " , self . data [ methods [ " schedule " ] ] , self . current_time , self . config . run_hour )
2022-01-06 19:16:12 +00:00
except NotScheduledRange as e :
2022-01-09 00:45:42 +00:00
err = e
2021-12-18 03:02:24 +00:00
except NotScheduled as e :
2022-01-06 19:16:12 +00:00
if not self . config . ignore_schedules :
2022-01-09 00:45:42 +00:00
err = e
if err :
2021-12-06 22:30:38 +00:00
suffix = " "
if self . details [ " delete_not_scheduled " ] :
try :
2021-12-26 15:48:52 +00:00
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name )
2022-02-13 16:33:57 +00:00
logger . info ( self . delete ( ) )
2021-12-09 04:57:00 +00:00
self . deleted = True
2021-12-06 22:30:38 +00:00
suffix = f " and was deleted "
except Failed :
suffix = f " and could not be found to delete "
2022-01-09 00:45:42 +00:00
raise NotScheduled ( f " { err } \n \n { self . Type } { self . name } not scheduled to run { suffix } " )
2021-02-20 05:41:45 +00:00
2022-04-18 18:16:39 +00:00
self . collectionless = " plex_collectionless " in methods and not self . playlist and not self . overlay
2021-05-02 04:22:48 +00:00
2021-07-30 19:19:43 +00:00
self . validate_builders = True
2022-04-18 18:16:39 +00:00
if " validate_builders " in methods and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: validate_builders " )
logger . debug ( f " Value: { data [ methods [ ' validate_builders ' ] ] } " )
2022-01-28 18:36:21 +00:00
self . validate_builders = util . parse ( self . Type , " validate_builders " , self . data , datatype = " bool " , methods = methods , default = True )
2021-07-30 19:19:43 +00:00
2021-05-24 15:23:21 +00:00
self . run_again = False
2022-04-18 18:16:39 +00:00
if " run_again " in methods and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: run_again " )
logger . debug ( f " Value: { data [ methods [ ' run_again ' ] ] } " )
2022-01-28 18:36:21 +00:00
self . run_again = util . parse ( self . Type , " run_again " , self . data , datatype = " bool " , methods = methods , default = False )
2021-07-30 19:19:43 +00:00
2022-04-18 18:16:39 +00:00
self . build_collection = False if self . overlay else True
if " build_collection " in methods and not self . playlist and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: build_collection " )
logger . debug ( f " Value: { data [ methods [ ' build_collection ' ] ] } " )
2022-01-28 18:36:21 +00:00
self . build_collection = util . parse ( self . Type , " build_collection " , self . data , datatype = " bool " , methods = methods , default = True )
2021-05-24 15:23:21 +00:00
2022-01-27 21:24:50 +00:00
self . blank_collection = False
2022-04-18 18:16:39 +00:00
if " blank_collection " in methods and not self . playlist and not self . overlay :
2022-01-27 21:24:50 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: blank_collection " )
logger . debug ( f " Value: { data [ methods [ ' blank_collection ' ] ] } " )
2022-01-28 18:36:21 +00:00
self . blank_collection = util . parse ( self . Type , " blank_collection " , self . data , datatype = " bool " , methods = methods , default = False )
2022-01-27 21:24:50 +00:00
2021-05-24 15:23:21 +00:00
self . sync = self . library . sync_mode == " sync "
2022-04-18 18:16:39 +00:00
if " sync_mode " in methods and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: sync_mode " )
2021-05-24 15:23:21 +00:00
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 "
2022-01-11 01:23:56 +00:00
if self . playlist : self . collection_level = " item "
elif self . library . is_show : self . collection_level = " show "
elif self . library . is_music : self . collection_level = " artist "
else : self . collection_level = " movie "
2021-12-17 14:24:46 +00:00
if " collection_level " in methods and not self . playlist :
2021-08-22 15:54:33 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: collection_level " )
2021-12-09 05:40:49 +00:00
if self . library . is_movie :
2021-12-29 15:51:22 +00:00
raise Failed ( f " { self . Type } Error: collection_level attribute only works for show and music libraries " )
2021-12-09 05:40:49 +00:00
elif self . data [ methods [ " collection_level " ] ] is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: collection_level attribute is blank " )
2021-08-22 15:54:33 +00:00
else :
logger . debug ( f " Value: { self . data [ methods [ ' collection_level ' ] ] } " )
2021-12-29 15:51:22 +00:00
if ( self . library . is_show and self . data [ methods [ " collection_level " ] ] . lower ( ) in plex . collection_level_show_options ) or \
( self . library . is_music and self . data [ methods [ " collection_level " ] ] . lower ( ) in plex . collection_level_music_options ) :
2021-08-22 15:54:33 +00:00
self . collection_level = self . data [ methods [ " collection_level " ] ] . lower ( )
else :
2021-12-29 15:51:22 +00:00
if self . library . is_show :
options = " \n \t season (Collection at the Season Level) \n \t episode (Collection at the Episode Level) "
else :
options = " \n \t album (Collection at the Album Level) \n \t track (Collection at the Track Level) "
raise Failed ( f " { self . Type } Error: { self . data [ methods [ ' collection_level ' ] ] } collection_level invalid { options } " )
self . parts_collection = self . collection_level in plex . collection_level_options
2021-08-22 15:54:33 +00:00
2021-05-05 18:01:41 +00:00
if " tmdb_person " in methods :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: tmdb_person " )
2021-05-24 15:23:21 +00:00
if not self . data [ methods [ " tmdb_person " ] ] :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: tmdb_person attribute is blank " )
2021-05-24 15:23:21 +00:00
else :
logger . debug ( f " Value: { self . data [ methods [ ' tmdb_person ' ] ] } " )
2021-05-05 18:01:41 +00:00
valid_names = [ ]
2022-04-05 18:46:00 +00:00
for tmdb_person in util . get_list ( self . data [ methods [ " tmdb_person " ] ] ) :
try :
person = self . config . TMDb . get_person ( util . regex_first_int ( tmdb_person , " TMDb Person ID " ) )
valid_names . append ( person . name )
if person . biography :
self . summaries [ " tmdb_person " ] = person . biography
if person . profile_url :
self . posters [ " tmdb_person " ] = person . profile_url
except Failed as e :
if str ( e ) . startswith ( " TMDb Error " ) :
logger . error ( e )
else :
try :
results = self . config . TMDb . search_people ( tmdb_person )
if results :
valid_names . append ( results [ 0 ] . name )
if results [ 0 ] . biography :
self . summaries [ " tmdb_person " ] = results [ 0 ] . biography
if results [ 0 ] . profile_url :
self . posters [ " tmdb_person " ] = results [ 0 ] . profile_url
except Failed as ee :
logger . error ( ee )
2021-05-05 18:01:41 +00:00
if len ( valid_names ) > 0 :
self . details [ " tmdb_person " ] = valid_names
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: No valid TMDb Person IDs in { self . data [ methods [ ' tmdb_person ' ] ] } " )
2021-05-05 18:01:41 +00:00
2022-02-13 18:28:53 +00:00
self . smart_filter_details = " "
2022-04-02 14:17:47 +00:00
self . smart_label = { " sort_by " : " random " , " all " : { " label " : [ self . name ] } }
2021-05-02 04:22:48 +00:00
self . smart_label_collection = False
2022-04-18 18:16:39 +00:00
if " smart_label " in methods and not self . playlist and not self . overlay and not self . library . is_music :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: smart_label " )
2021-05-02 04:22:48 +00:00
self . smart_label_collection = True
2021-05-24 15:23:21 +00:00
if not self . data [ methods [ " smart_label " ] ] :
2021-12-13 07:30:19 +00:00
logger . warning ( f " { self . Type } Error: smart_label attribute is blank defaulting to random " )
2021-05-24 15:23:21 +00:00
else :
logger . debug ( f " Value: { self . data [ methods [ ' smart_label ' ] ] } " )
2022-02-13 18:28:53 +00:00
if isinstance ( self . data [ methods [ " smart_label " ] ] , dict ) :
_data , replaced = util . replace_label ( self . name , self . data [ methods [ " smart_label " ] ] )
if not replaced :
raise Failed ( " Config Error: <<smart_label>> not found in the smart_label attribute data " )
self . smart_label = _data
elif ( self . library . is_movie and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . movie_sorts ) \
2021-05-29 02:33:55 +00:00
or ( self . library . is_show and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . show_sorts ) :
2022-02-13 18:28:53 +00:00
self . smart_label [ " sort_by " ] = str ( self . data [ methods [ " smart_label " ] ] ) . lower ( )
2021-05-02 04:22:48 +00:00
else :
2021-12-13 07:30:19 +00:00
logger . warning ( f " { self . Type } Error: smart_label attribute: { self . data [ methods [ ' smart_label ' ] ] } is invalid defaulting to random " )
2022-02-13 18:28:53 +00:00
if self . smart_label_collection and self . library . smart_label_check ( self . name ) :
_ , self . smart_filter_details , _ = self . build_filter ( " smart_label " , self . smart_label , default_sort = " random " )
2021-05-02 04:22:48 +00:00
self . smart_url = None
2021-05-05 18:01:41 +00:00
self . smart_type_key = None
2022-04-18 18:16:39 +00:00
if " smart_url " in methods and not self . playlist and not self . overlay :
2021-08-05 14:59:45 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: smart_url " )
2021-05-24 15:23:21 +00:00
if not self . data [ methods [ " smart_url " ] ] :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: smart_url attribute is blank " )
2021-05-24 15:23:21 +00:00
else :
logger . debug ( f " Value: { self . data [ methods [ ' smart_url ' ] ] } " )
2021-05-02 04:22:48 +00:00
try :
2021-05-26 14:45:33 +00:00
self . smart_url , self . smart_type_key = self . library . get_smart_filter_from_uri ( self . data [ methods [ " smart_url " ] ] )
2021-05-02 04:22:48 +00:00
except ValueError :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: smart_url is incorrectly formatted " )
2021-05-02 04:22:48 +00:00
2022-04-18 18:16:39 +00:00
if " smart_filter " in methods and not self . playlist and not self . overlay :
2022-02-13 18:28:53 +00:00
self . smart_type_key , self . smart_filter_details , self . smart_url = self . build_filter ( " smart_filter " , self . data [ methods [ " smart_filter " ] ] , display = True , default_sort = " random " )
2021-05-05 18:01:41 +00:00
2022-02-19 14:52:24 +00:00
if self . collectionless :
for x in [ " smart_label " , " smart_filter " , " smart_url " ] :
if x in methods :
self . collectionless = False
2021-05-05 18:01:41 +00:00
logger . info ( " " )
2022-02-19 14:52:24 +00:00
logger . warning ( f " { self . Type } Error: { x } is not compatible with plex_collectionless removing plex_collectionless " )
if self . run_again and self . smart_url :
self . run_again = False
logger . info ( " " )
logger . warning ( f " { self . Type } Error: smart_filter is not compatible with run_again removing run_again " )
if self . smart_url and self . smart_label_collection :
raise Failed ( f " { self . Type } Error: smart_filter is not compatible with smart_label " )
2021-05-05 18:01:41 +00:00
2022-02-19 14:52:24 +00:00
if self . parts_collection :
for x in [ " smart_label " , " smart_filter " , " smart_url " ] :
if x in methods :
raise Failed ( f " { self . Type } Error: { x } is not compatible with collection_level: { self . collection_level } " )
2021-05-05 18:01:41 +00:00
self . smart = self . smart_url or self . smart_label_collection
2021-02-20 05:41:45 +00:00
2022-01-07 16:09:12 +00:00
test_sort = None
2022-01-11 01:24:42 +00:00
if " collection_order " in methods and not self . playlist and self . build_collection :
2022-01-07 16:09:12 +00:00
if self . data [ methods [ " collection_order " ] ] is None :
raise Failed ( f " { self . Type } Warning: collection_order attribute is blank " )
else :
test_sort = self . data [ methods [ " collection_order " ] ]
2022-01-11 01:24:42 +00:00
elif " collection_order " not in methods and not self . playlist and self . build_collection and self . library . default_collection_order and not self . smart :
2022-01-07 16:09:12 +00:00
test_sort = self . library . default_collection_order
2022-01-11 01:24:42 +00:00
logger . warning ( " " )
2022-01-07 16:09:12 +00:00
logger . warning ( f " { self . Type } Warning: collection_order not found using library default_collection_order: { self . library . default_collection_order } " )
self . custom_sort = self . playlist
if test_sort :
2022-01-02 04:23:47 +00:00
if self . smart :
raise Failed ( f " { self . Type } Error: collection_order does not work with Smart Collections " )
logger . debug ( " " )
logger . debug ( " Validating Method: collection_order " )
2022-01-07 16:09:12 +00:00
logger . debug ( f " Value: { test_sort } " )
if test_sort . lower ( ) in plex . collection_order_options :
self . details [ " collection_order " ] = test_sort . lower ( )
if test_sort . lower ( ) == " custom " and self . build_collection :
self . custom_sort = True
elif ( self . library . is_movie and test_sort . lower ( ) in plex . movie_sorts ) or ( self . library . is_show and test_sort . lower ( ) in plex . show_sorts ) :
self . custom_sort = test_sort . lower ( )
2022-01-02 04:23:47 +00:00
else :
2022-01-07 16:09:12 +00:00
raise Failed ( f " { self . Type } Error: { test_sort } collection_order invalid \n \t release (Order Collection by release dates) \n \t alpha (Order Collection Alphabetically) \n \t custom (Custom Order Collection) \n \t Other sorting options can be found at https://github.com/meisnate12/Plex-Meta-Manager/wiki/Smart-Builders#sort-options " )
2022-01-02 04:23:47 +00:00
2022-01-28 16:11:09 +00:00
if self . smart_url or self . smart_label_collection :
2022-01-02 04:23:47 +00:00
self . custom_sort = False
2021-04-05 02:25:41 +00:00
for method_key , method_data in self . data . items ( ) :
2022-03-10 18:28:56 +00:00
if method_key . lower ( ) in ignored_details :
continue
logger . debug ( " " )
2022-04-27 23:14:52 +00:00
method_name , method_mod , method_final = self . library . split ( method_key )
2021-07-23 18:45:49 +00:00
if method_name in ignored_details :
2021-05-24 15:23:21 +00:00
continue
2021-07-23 18:45:49 +00:00
logger . debug ( f " Validating Method: { method_key } " )
logger . debug ( f " Value: { method_data } " )
2021-07-30 19:19:43 +00:00
try :
2021-12-29 15:51:22 +00:00
if method_data is None and method_name in all_builders + plex . searches :
raise Failed ( f " { self . Type } Error: { method_final } attribute is blank " )
elif method_data is None and method_final not in none_details :
logger . warning ( f " Collection Warning: { method_final } attribute is blank " )
elif self . playlist and method_name not in playlist_attributes :
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed when using playlists " )
elif not self . config . Trakt and " trakt " in method_name :
raise Failed ( f " { self . Type } Error: { method_final } requires Trakt to be configured " )
elif not self . library . Radarr and " radarr " in method_name :
2022-01-04 16:46:56 +00:00
logger . error ( f " { self . Type } Error: { method_final } requires Radarr to be configured " )
2021-12-29 15:51:22 +00:00
elif not self . library . Sonarr and " sonarr " in method_name :
2022-01-04 16:46:56 +00:00
logger . error ( f " { self . Type } Error: { method_final } requires Sonarr to be configured " )
2021-12-29 15:51:22 +00:00
elif not self . library . Tautulli and " tautulli " in method_name :
raise Failed ( f " { self . Type } Error: { method_final } requires Tautulli to be configured " )
elif not self . config . MyAnimeList and " mal " in method_name :
raise Failed ( f " { self . Type } Error: { method_final } requires MyAnimeList to be configured " )
elif self . library . is_movie and method_name in show_only_builders :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed for show libraries " )
elif self . library . is_show and method_name in movie_only_builders :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed for movie libraries " )
elif self . library . is_show and method_name in plex . movie_only_searches :
raise Failed ( f " { self . Type } Error: { method_final } plex search only allowed for movie libraries " )
elif self . library . is_movie and method_name in plex . show_only_searches :
raise Failed ( f " { self . Type } Error: { method_final } plex search only allowed for show libraries " )
elif self . library . is_music and method_name not in music_attributes :
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed for music libraries " )
elif self . library . is_music and method_name in album_details and self . collection_level != " album " :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed for album collections " )
elif not self . library . is_music and method_name in music_only_builders :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed for music libraries " )
2022-03-16 07:00:22 +00:00
elif not self . playlist and self . collection_level != " episode " and method_name in episode_parts_only :
2022-01-24 22:36:40 +00:00
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed with Collection Level: episode " )
2021-12-29 15:51:22 +00:00
elif self . parts_collection and method_name not in parts_collection_valid :
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed with Collection Level: { self . collection_level . capitalize ( ) } " )
elif self . smart and method_name in smart_invalid :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed with normal collections " )
2022-04-08 18:29:38 +00:00
elif not self . smart and method_name in smart_only :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed with smart collections " )
2021-12-29 15:51:22 +00:00
elif self . collectionless and method_name not in collectionless_details :
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed for Collectionless collection " )
elif self . smart_url and method_name in all_builders + smart_url_invalid :
raise Failed ( f " { self . Type } Error: { method_final } builder not allowed when using smart_filter " )
2022-04-18 18:16:39 +00:00
elif not self . overlay and method_name in overlay_only :
raise Failed ( f " { self . Type } Error: { method_final } attribute only allowed in an overlay file " )
elif self . overlay and method_name not in overlay_attributes :
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed in an overlay file " )
2021-12-29 15:51:22 +00:00
elif method_name in summary_details :
self . _summary ( method_name , method_data )
elif method_name in poster_details :
self . _poster ( method_name , method_data )
elif method_name in background_details :
self . _background ( method_name , method_data )
elif method_name in details :
self . _details ( method_name , method_data , method_final , methods )
elif method_name in item_details :
self . _item_details ( method_name , method_data , method_mod , method_final , methods )
2022-05-02 19:05:52 +00:00
elif method_name in radarr_details or method_name in radarr . builders :
2021-12-29 15:51:22 +00:00
self . _radarr ( method_name , method_data )
2022-05-02 19:05:52 +00:00
elif method_name in sonarr_details or method_name in sonarr . builders :
2021-12-29 15:51:22 +00:00
self . _sonarr ( method_name , method_data )
elif method_name in anidb . builders :
self . _anidb ( method_name , method_data )
elif method_name in anilist . builders :
self . _anilist ( method_name , method_data )
elif method_name in flixpatrol . builders :
self . _flixpatrol ( method_name , method_data )
elif method_name in icheckmovies . builders :
self . _icheckmovies ( method_name , method_data )
elif method_name in letterboxd . builders :
self . _letterboxd ( method_name , method_data )
elif method_name in imdb . builders :
self . _imdb ( method_name , method_data )
elif method_name in mal . builders :
self . _mal ( method_name , method_data )
elif method_name in plex . builders or method_final in plex . searches :
self . _plex ( method_name , method_data )
2022-03-19 05:16:25 +00:00
elif method_name in reciperr . builders :
self . _reciperr ( method_name , method_data )
2021-12-29 15:51:22 +00:00
elif method_name in tautulli . builders :
self . _tautulli ( method_name , method_data )
elif method_name in tmdb . builders :
self . _tmdb ( method_name , method_data )
2022-05-09 15:22:41 +00:00
elif method_name in trakt . builders or method_name in [ " sync_to_trakt_list " , " sync_missing_to_trakt_list " ] :
2021-12-29 15:51:22 +00:00
self . _trakt ( method_name , method_data )
elif method_name in tvdb . builders :
self . _tvdb ( method_name , method_data )
2022-01-15 22:40:59 +00:00
elif method_name in mdblist . builders :
self . _mdblist ( method_name , method_data )
2021-12-29 15:51:22 +00:00
elif method_name == " filters " :
self . _filters ( method_name , method_data )
else :
raise Failed ( f " { self . Type } Error: { method_final } attribute not supported " )
2021-07-30 19:19:43 +00:00
except Failed as e :
if self . validate_builders :
raise
else :
logger . error ( e )
2021-02-20 05:41:45 +00:00
2022-01-27 21:24:50 +00:00
if not self . server_preroll and not self . smart_url and not self . blank_collection and len ( self . builders ) == 0 :
2022-01-24 19:33:14 +00:00
raise Failed ( f " { self . Type } Error: No builders were found " )
2022-01-27 21:24:50 +00:00
if self . blank_collection and len ( self . builders ) > 0 :
raise Failed ( f " { self . Type } Error: No builders allowed with blank_collection " )
2021-12-21 18:48:27 +00:00
if self . custom_sort is True and ( len ( self . builders ) > 1 or self . builders [ 0 ] [ 0 ] not in custom_sort_builders ) :
2022-02-08 22:57:36 +00:00
raise Failed ( f " { self . Type } Error: " + ( ' Playlists ' if self . playlist else ' collection_order: custom ' ) +
2021-12-14 05:51:36 +00:00
( f " can only be used with a single builder per { self . type } " if len ( self . builders ) > 1 else f " cannot be used with { self . builders [ 0 ] [ 0 ] } " ) )
2021-07-29 02:26:39 +00:00
2021-12-30 17:13:01 +00:00
if " add_missing " not in self . radarr_details :
self . radarr_details [ " add_missing " ] = self . library . Radarr . add_missing if self . library . Radarr else False
2021-08-16 05:41:04 +00:00
if " add_existing " not in self . radarr_details :
self . radarr_details [ " add_existing " ] = self . library . Radarr . add_existing if self . library . Radarr else False
2021-12-30 17:13:01 +00:00
if " add_missing " not in self . sonarr_details :
self . sonarr_details [ " add_missing " ] = self . library . Sonarr . add_missing if self . library . Sonarr else False
2021-08-16 05:41:04 +00:00
if " add_existing " not in self . sonarr_details :
self . sonarr_details [ " add_existing " ] = self . library . Sonarr . add_existing if self . library . Sonarr else False
2021-05-02 04:22:48 +00:00
2021-12-29 15:51:22 +00:00
if self . smart_url or self . collectionless or self . library . is_music :
2021-12-30 17:13:01 +00:00
self . radarr_details [ " add_missing " ] = False
2021-08-16 05:41:04 +00:00
self . radarr_details [ " add_existing " ] = False
2021-12-30 17:13:01 +00:00
self . sonarr_details [ " add_missing " ] = False
2021-08-16 05:41:04 +00:00
self . sonarr_details [ " add_existing " ] = False
2021-02-20 05:41:45 +00:00
2022-01-28 16:11:09 +00:00
if ( self . radarr_details [ " add_existing " ] or self . sonarr_details [ " add_existing " ] ) and not self . parts_collection :
2021-11-22 20:47:26 +00:00
self . item_details [ " add_existing " ] = True
2021-03-16 15:49:05 +00:00
if self . collectionless :
self . details [ " collection_mode " ] = " hide "
self . sync = True
2022-04-13 04:30:59 +00:00
self . do_missing = not self . config . no_missing and ( self . details [ " show_missing " ] or self . details [ " save_missing " ]
or ( self . library . Radarr and self . radarr_details [ " add_missing " ] )
or ( self . library . Sonarr and self . sonarr_details [ " add_missing " ] ) )
2021-05-19 20:42:05 +00:00
if self . build_collection :
try :
2021-12-17 14:24:46 +00:00
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name )
2021-06-16 18:20:48 +00:00
if ( self . smart and not self . obj . smart ) or ( not self . smart and self . obj . smart ) :
2021-05-19 20:42:05 +00:00
logger . info ( " " )
2021-12-13 07:30:19 +00:00
logger . error ( f " { self . Type } Error: Converting { self . obj . title } to a { ' smart ' if self . smart else ' normal ' } collection " )
2021-05-26 14:45:33 +00:00
self . library . query ( self . obj . delete )
2021-05-19 20:42:05 +00:00
self . obj = None
except Failed :
2021-05-12 14:25:48 +00:00
self . obj = None
2021-10-04 17:51:32 +00:00
if self . obj :
self . exists = True
2022-02-21 20:56:38 +00:00
if not self . playlist :
self . beginning_count = self . obj . childCount
if self . sync or self . playlist :
2022-02-09 02:31:29 +00:00
self . remove_item_map = { i . ratingKey : i for i in self . library . get_collection_items ( self . obj , self . smart_label_collection ) }
2022-02-21 20:56:38 +00:00
if self . playlist :
self . beginning_count = len ( self . remove_item_map )
2021-05-19 20:42:05 +00:00
else :
2021-07-12 02:28:52 +00:00
self . obj = None
2021-05-19 20:42:05 +00:00
self . sync = False
self . run_again = False
2021-05-24 03:38:46 +00:00
logger . info ( " " )
logger . info ( " Validation Successful " )
2021-05-12 14:25:48 +00:00
2021-07-21 20:59:27 +00:00
def _summary ( self , method_name , method_data ) :
if method_name == " summary " :
self . summaries [ method_name ] = method_data
elif method_name == " tmdb_summary " :
self . summaries [ method_name ] = self . config . TMDb . get_movie_show_or_collection ( util . regex_first_int ( method_data , " TMDb ID " ) , self . library . is_movie ) . overview
elif method_name == " tmdb_description " :
self . summaries [ method_name ] = self . config . TMDb . get_list ( util . regex_first_int ( method_data , " TMDb List ID " ) ) . description
elif method_name == " tmdb_biography " :
self . summaries [ method_name ] = self . config . TMDb . get_person ( util . regex_first_int ( method_data , " TMDb Person ID " ) ) . biography
elif method_name == " tvdb_summary " :
2022-05-05 22:05:16 +00:00
self . summaries [ method_name ] = self . config . TVDb . get_tvdb_obj ( method_data , is_movie = self . library . is_movie ) . summary
2021-07-21 20:59:27 +00:00
elif method_name == " tvdb_description " :
2021-10-26 15:01:08 +00:00
self . summaries [ method_name ] = self . config . TVDb . get_list_description ( method_data )
2021-07-21 20:59:27 +00:00
elif method_name == " trakt_description " :
2022-03-27 06:26:08 +00:00
self . summaries [ method_name ] = self . config . Trakt . list_description ( self . config . Trakt . validate_list ( method_data , self . library . is_movie ) [ 0 ] )
2021-07-21 20:59:27 +00:00
elif method_name == " letterboxd_description " :
self . summaries [ method_name ] = self . config . Letterboxd . get_list_description ( method_data , self . language )
elif method_name == " icheckmovies_description " :
self . summaries [ method_name ] = self . config . ICheckMovies . get_list_description ( method_data , self . language )
def _poster ( self , method_name , method_data ) :
if method_name == " url_poster " :
self . posters [ method_name ] = method_data
elif method_name == " tmdb_poster " :
2022-01-21 16:34:19 +00:00
self . posters [ method_name ] = self . config . TMDb . get_movie_show_or_collection ( util . regex_first_int ( method_data , ' TMDb ID ' ) , self . library . is_movie ) . poster_url
2021-07-21 20:59:27 +00:00
elif method_name == " tmdb_profile " :
2022-01-21 16:34:19 +00:00
self . posters [ method_name ] = self . config . TMDb . get_person ( util . regex_first_int ( method_data , ' TMDb Person ID ' ) ) . profile_url
2021-07-21 20:59:27 +00:00
elif method_name == " tvdb_poster " :
2022-05-05 22:05:16 +00:00
self . posters [ method_name ] = f " { self . config . TVDb . get_tvdb_obj ( method_data , is_movie = self . library . is_movie ) . poster_url } "
2021-07-21 20:59:27 +00:00
elif method_name == " file_poster " :
2022-01-11 01:23:56 +00:00
if os . path . exists ( os . path . abspath ( method_data ) ) :
2021-07-21 20:59:27 +00:00
self . posters [ method_name ] = os . path . abspath ( method_data )
else :
2022-01-11 01:23:56 +00:00
logger . error ( f " { self . Type } Error: Poster Path Does Not Exist: { os . path . abspath ( method_data ) } " )
2021-07-21 20:59:27 +00:00
def _background ( self , method_name , method_data ) :
if method_name == " url_background " :
self . backgrounds [ method_name ] = method_data
elif method_name == " tmdb_background " :
2022-01-21 16:34:19 +00:00
self . backgrounds [ method_name ] = self . config . TMDb . get_movie_show_or_collection ( util . regex_first_int ( method_data , ' TMDb ID ' ) , self . library . is_movie ) . backdrop_url
2021-07-21 20:59:27 +00:00
elif method_name == " tvdb_background " :
2022-05-05 22:05:16 +00:00
self . posters [ method_name ] = f " { self . config . TVDb . get_tvdb_obj ( method_data , is_movie = self . library . is_movie ) . background_url } "
2021-07-21 20:59:27 +00:00
elif method_name == " file_background " :
2022-01-11 01:23:56 +00:00
if os . path . exists ( os . path . abspath ( method_data ) ) :
2021-07-21 20:59:27 +00:00
self . backgrounds [ method_name ] = os . path . abspath ( method_data )
else :
2022-01-11 01:23:56 +00:00
logger . error ( f " { self . Type } Error: Background Path Does Not Exist: { os . path . abspath ( method_data ) } " )
2021-07-21 20:59:27 +00:00
def _details ( self , method_name , method_data , method_final , methods ) :
2022-03-06 06:09:03 +00:00
if method_name == " url_theme " :
self . url_theme = method_data
elif method_name == " file_theme " :
if os . path . exists ( os . path . abspath ( method_data ) ) :
self . file_theme = os . path . abspath ( method_data )
else :
logger . error ( f " { self . Type } Error: Theme Path Does Not Exist: { os . path . abspath ( method_data ) } " )
2022-03-23 18:43:50 +00:00
elif method_name == " tmdb_region " :
2022-03-24 19:28:23 +00:00
self . tmdb_region = util . parse ( self . Type , method_name , method_data , options = self . config . TMDb . iso_3166_1 )
2022-03-08 02:37:40 +00:00
elif method_name == " collection_mode " :
2022-04-20 21:30:31 +00:00
try :
self . details [ method_name ] = util . check_collection_mode ( method_data )
except Failed as e :
logger . error ( e )
2022-04-08 18:29:38 +00:00
elif method_name == " collection_filtering " :
if method_data and str ( method_data ) . lower ( ) in plex . collection_filtering_options :
self . details [ method_name ] = str ( method_data ) . lower ( )
else :
logger . error ( f " Config Error: { method_data } collection_filtering invalid \n \t admin (Always the server admin user) \n \t user (User currently viewing the content) " )
2021-12-29 19:51:07 +00:00
elif method_name == " minimum_items " :
2022-01-28 18:36:21 +00:00
self . minimum = util . parse ( self . Type , method_name , method_data , datatype = " int " , minimum = 1 )
2022-02-22 05:56:04 +00:00
elif method_name == " limit " :
self . limit = util . parse ( self . Type , method_name , method_data , datatype = " int " , minimum = 1 )
2022-02-02 16:28:25 +00:00
elif method_name == " cache_builders " :
self . details [ method_name ] = util . parse ( self . Type , method_name , method_data , datatype = " int " , minimum = 0 )
2021-12-08 03:31:20 +00:00
elif method_name == " server_preroll " :
2022-01-28 18:36:21 +00:00
self . server_preroll = util . parse ( self . Type , method_name , method_data )
2021-12-08 04:00:17 +00:00
elif method_name == " ignore_ids " :
2022-01-28 18:36:21 +00:00
self . ignore_ids . extend ( util . parse ( self . Type , method_name , method_data , datatype = " intlist " ) )
2021-12-08 04:00:17 +00:00
elif method_name == " ignore_imdb_ids " :
2022-01-28 18:36:21 +00:00
self . ignore_imdb_ids . extend ( util . parse ( self . Type , method_name , method_data , datatype = " list " ) )
2021-07-21 20:59:27 +00:00
elif method_name == " label " :
if " label " in methods and " label.sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use label and label.sync together " )
2021-07-21 20:59:27 +00:00
if " label.remove " in methods and " label.sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use label.remove and label.sync together " )
2021-07-21 20:59:27 +00:00
if method_final == " label " and " label_sync_mode " in methods and self . data [ methods [ " label_sync_mode " ] ] == " sync " :
2021-12-03 04:23:22 +00:00
self . details [ " label.sync " ] = util . get_list ( method_data ) if method_data else [ ]
2021-07-21 20:59:27 +00:00
else :
2021-12-03 04:23:22 +00:00
self . details [ method_final ] = util . get_list ( method_data ) if method_data else [ ]
2021-12-26 15:48:52 +00:00
elif method_name == " changes_webhooks " :
2022-04-14 13:29:54 +00:00
self . details [ method_name ] = util . parse ( self . Type , method_name , method_data , datatype = " list " ) if method_data else None
2021-12-19 05:41:58 +00:00
elif method_name in scheduled_boolean :
2021-12-19 04:11:23 +00:00
if isinstance ( method_data , bool ) :
self . details [ method_name ] = method_data
elif isinstance ( method_data , ( int , float ) ) :
self . details [ method_name ] = method_data > 0
elif str ( method_data ) . lower ( ) in [ " t " , " true " ] :
self . details [ method_name ] = True
elif str ( method_data ) . lower ( ) in [ " f " , " false " ] :
self . details [ method_name ] = False
else :
2021-12-19 05:41:58 +00:00
try :
2022-01-28 18:36:21 +00:00
util . schedule_check ( method_name , util . parse ( self . Type , method_name , method_data ) , self . current_time , self . config . run_hour )
2021-12-19 05:41:58 +00:00
self . details [ method_name ] = True
except NotScheduled :
self . details [ method_name ] = False
2021-07-21 20:59:27 +00:00
elif method_name in boolean_details :
2021-08-05 15:56:27 +00:00
default = self . details [ method_name ] if method_name in self . details else None
2022-01-28 18:36:21 +00:00
self . details [ method_name ] = util . parse ( self . Type , method_name , method_data , datatype = " bool " , default = default )
2021-07-21 20:59:27 +00:00
elif method_name in string_details :
self . details [ method_name ] = str ( method_data )
def _item_details ( self , method_name , method_data , method_mod , method_final , methods ) :
if method_name == " item_label " :
if " item_label " in methods and " item_label.sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use item_label and item_label.sync together " )
2021-07-21 20:59:27 +00:00
if " item_label.remove " in methods and " item_label.sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use item_label.remove and item_label.sync together " )
2021-12-03 04:23:22 +00:00
self . item_details [ method_final ] = util . get_list ( method_data ) if method_data else [ ]
2022-01-25 01:46:48 +00:00
elif method_name == " non_item_remove_label " :
if not method_data :
raise Failed ( f " { self . Type } Error: non_item_remove_label is blank " )
self . item_details [ method_final ] = util . get_list ( method_data )
2021-07-21 20:59:27 +00:00
elif method_name in [ " item_radarr_tag " , " item_sonarr_tag " ] :
if method_name in methods and f " { method_name } .sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use { method_name } and { method_name } .sync together " )
2021-07-21 20:59:27 +00:00
if f " { method_name } .remove " in methods and f " { method_name } .sync " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use { method_name } .remove and { method_name } .sync together " )
2021-07-21 20:59:27 +00:00
if method_name in methods and f " { method_name } .remove " in methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot use { method_name } and { method_name } .remove together " )
2021-12-20 14:47:50 +00:00
self . item_details [ method_name ] = util . get_list ( method_data , lower = True )
2021-07-21 20:59:27 +00:00
self . item_details [ " apply_tags " ] = method_mod [ 1 : ] if method_mod else " "
elif method_name == " item_overlay " :
2022-01-05 15:42:38 +00:00
if isinstance ( method_data , dict ) :
2022-01-05 16:01:56 +00:00
if " name " not in method_data or not method_data [ " name " ] :
raise Failed ( f " { self . Type } Error: item_overlay must have the name attribute " )
if " git " in method_data and method_data [ " git " ] :
url = f " https://github.com/meisnate12/Plex-Meta-Manager-Configs/blob/master/ { method_data [ ' git ' ] } .png "
elif " url " in method_data and method_data [ " url " ] :
url = method_data [ " url " ]
else :
raise Failed ( f " { self . Type } Error: item_overlay must have either the git or url attribute " )
2022-01-05 15:42:38 +00:00
name = method_data [ " name " ]
2022-01-05 16:01:56 +00:00
response = self . config . get ( url )
2022-01-05 15:42:38 +00:00
if response . status_code > = 400 :
2022-01-05 16:01:56 +00:00
raise Failed ( f " { self . Type } Error: Overlay Image not found at: { url } " )
2022-01-05 15:42:38 +00:00
overlay_dir = os . path . join ( self . config . default_dir , " overlays " , name )
if not os . path . exists ( overlay_dir ) or not os . path . isdir ( overlay_dir ) :
2022-01-05 16:01:56 +00:00
os . makedirs ( overlay_dir , exist_ok = False )
logger . info ( f " Creating Overlay Folder found at: { overlay_dir } " )
2022-01-05 15:42:38 +00:00
overlay = os . path . join ( overlay_dir , " overlay.png " )
with open ( overlay , " wb " ) as handler :
handler . write ( response . content )
while util . is_locked ( overlay ) :
time . sleep ( 1 )
else :
overlay = os . path . join ( self . config . default_dir , " overlays " , method_data , " overlay.png " )
name = method_data
2021-07-21 20:59:27 +00:00
if not os . path . exists ( overlay ) :
2022-01-05 15:42:38 +00:00
raise Failed ( f " { self . Type } Error: { name } overlay image not found at { overlay } " )
2022-04-18 18:16:39 +00:00
if name in self . library . overlays_old :
2021-07-21 20:59:27 +00:00
raise Failed ( " Each Overlay can only be used once per Library " )
2022-04-18 18:16:39 +00:00
self . library . overlays_old . append ( name )
2022-01-05 15:42:38 +00:00
self . item_details [ method_name ] = name
2022-01-11 18:51:49 +00:00
elif method_name == " item_refresh_delay " :
2022-01-28 18:36:21 +00:00
self . item_details [ method_name ] = util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 0 , minimum = 0 )
2021-11-24 13:22:17 +00:00
elif method_name in item_bool_details :
2022-01-28 18:36:21 +00:00
if util . parse ( self . Type , method_name , method_data , datatype = " bool " , default = False ) :
2021-09-15 03:16:59 +00:00
self . item_details [ method_name ] = True
2022-01-26 14:43:58 +00:00
elif method_name in item_false_details :
self . item_details [ method_name ] = False
2021-07-21 20:59:27 +00:00
elif method_name in plex . item_advance_keys :
key , options = plex . item_advance_keys [ method_name ]
if method_name in advance_new_agent and self . library . agent not in plex . new_plex_agents :
2021-12-29 04:12:40 +00:00
logger . error ( f " Metadata Error: { method_name } attribute only works for with the New Plex Movie Agent and New Plex TV Agent " )
2021-07-21 20:59:27 +00:00
elif method_name in advance_show and not self . library . is_show :
logger . error ( f " Metadata Error: { method_name } attribute only works for show libraries " )
elif str ( method_data ) . lower ( ) not in options :
logger . error ( f " Metadata Error: { method_data } { method_name } attribute invalid " )
else :
self . item_details [ method_name ] = str ( method_data ) . lower ( )
def _radarr ( self , method_name , method_data ) :
2022-04-30 23:57:26 +00:00
if method_name in [ " radarr_add_missing " , " radarr_add_existing " , " radarr_upgrade_existing " , " radarr_monitor " , " radarr_search " ] :
2022-01-28 18:36:21 +00:00
self . radarr_details [ method_name [ 7 : ] ] = util . parse ( self . Type , method_name , method_data , datatype = " bool " )
2021-07-21 20:59:27 +00:00
elif method_name == " radarr_folder " :
2021-08-16 05:41:04 +00:00
self . radarr_details [ " folder " ] = method_data
2021-07-21 20:59:27 +00:00
elif method_name == " radarr_availability " :
if str ( method_data ) . lower ( ) in radarr . availability_translation :
2021-08-16 05:41:04 +00:00
self . radarr_details [ " availability " ] = str ( method_data ) . lower ( )
2021-07-21 20:59:27 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } attribute must be either announced, cinemas, released or db " )
2021-07-21 20:59:27 +00:00
elif method_name == " radarr_quality " :
2021-08-16 05:41:04 +00:00
self . radarr_details [ " quality " ] = method_data
2021-07-21 20:59:27 +00:00
elif method_name == " radarr_tag " :
2021-12-20 14:47:50 +00:00
self . radarr_details [ " tag " ] = util . get_list ( method_data , lower = True )
2022-05-02 19:05:52 +00:00
elif method_name == " radarr_taglist " :
self . builders . append ( ( method_name , util . get_list ( method_data , lower = True ) ) )
elif method_name == " radarr_all " :
self . builders . append ( ( method_name , True ) )
2021-07-21 20:59:27 +00:00
def _sonarr ( self , method_name , method_data ) :
2022-04-30 23:57:26 +00:00
if method_name in [ " sonarr_add_missing " , " sonarr_add_existing " , " sonarr_upgrade_existing " , " sonarr_season " , " sonarr_search " , " sonarr_cutoff_search " ] :
2022-01-28 18:36:21 +00:00
self . sonarr_details [ method_name [ 7 : ] ] = util . parse ( self . Type , method_name , method_data , datatype = " bool " )
2021-10-19 04:23:52 +00:00
elif method_name in [ " sonarr_folder " , " sonarr_quality " , " sonarr_language " ] :
self . sonarr_details [ method_name [ 7 : ] ] = method_data
2021-07-21 20:59:27 +00:00
elif method_name == " sonarr_monitor " :
if str ( method_data ) . lower ( ) in sonarr . monitor_translation :
2021-08-16 05:41:04 +00:00
self . sonarr_details [ " monitor " ] = str ( method_data ) . lower ( )
2021-07-21 20:59:27 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } attribute must be either all, future, missing, existing, pilot, first, latest or none " )
2021-07-21 20:59:27 +00:00
elif method_name == " sonarr_series " :
2021-12-13 14:28:46 +00:00
if str ( method_data ) . lower ( ) in sonarr . series_types :
2021-08-16 05:41:04 +00:00
self . sonarr_details [ " series " ] = str ( method_data ) . lower ( )
2021-07-21 20:59:27 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } attribute must be either standard, daily, or anime " )
2021-07-21 20:59:27 +00:00
elif method_name == " sonarr_tag " :
2021-12-20 14:47:50 +00:00
self . sonarr_details [ " tag " ] = util . get_list ( method_data , lower = True )
2022-05-02 19:05:52 +00:00
elif method_name == " sonarr_taglist " :
self . builders . append ( ( method_name , util . get_list ( method_data , lower = True ) ) )
elif method_name == " sonarr_all " :
self . builders . append ( ( method_name , True ) )
2021-07-21 20:59:27 +00:00
2021-07-22 21:00:45 +00:00
def _anidb ( self , method_name , method_data ) :
if method_name == " anidb_popular " :
2022-01-28 18:36:21 +00:00
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 30 , maximum = 30 ) ) )
2021-07-22 21:00:45 +00:00
elif method_name in [ " anidb_id " , " anidb_relation " ] :
2022-03-15 00:11:16 +00:00
for anidb_id in self . config . AniDB . validate_anidb_ids ( method_data ) :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , anidb_id ) )
2021-07-22 21:00:45 +00:00
elif method_name == " anidb_tag " :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-07-22 21:00:45 +00:00
new_dictionary = { }
if " tag " not in dict_methods :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: anidb_tag tag attribute is required " )
2021-07-22 21:00:45 +00:00
elif not dict_data [ dict_methods [ " tag " ] ] :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: anidb_tag tag attribute is blank " )
2021-07-22 21:00:45 +00:00
else :
2021-08-28 05:17:14 +00:00
new_dictionary [ " tag " ] = util . regex_first_int ( dict_data [ dict_methods [ " tag " ] ] , " AniDB Tag ID " )
2022-01-28 18:36:21 +00:00
new_dictionary [ " limit " ] = util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 0 , parent = method_name , minimum = 0 )
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , new_dictionary ) )
2021-07-22 21:00:45 +00:00
def _anilist ( self , method_name , method_data ) :
if method_name in [ " anilist_id " , " anilist_relations " , " anilist_studio " ] :
for anilist_id in self . config . AniList . validate_anilist_ids ( method_data , studio = method_name == " anilist_studio " ) :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , anilist_id ) )
2021-08-26 18:24:30 +00:00
elif method_name in [ " anilist_popular " , " anilist_trending " , " anilist_top_rated " ] :
2022-01-28 18:36:21 +00:00
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 10 ) ) )
2022-03-13 21:06:31 +00:00
elif method_name == " anilist_userlist " :
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2022-03-14 02:49:55 +00:00
self . builders . append ( ( method_name , self . config . AniList . validate_userlist ( {
2022-03-13 21:06:31 +00:00
" username " : util . parse ( self . Type , " username " , dict_data , methods = dict_methods , parent = method_name ) ,
2022-03-14 02:49:55 +00:00
" list_name " : util . parse ( self . Type , " list_name " , dict_data , methods = dict_methods , parent = method_name ) ,
" sort_by " : util . parse ( self . Type , " sort_by " , dict_data , methods = dict_methods , parent = method_name , default = " score " , options = anilist . userlist_sort_options ) ,
} ) ) )
2021-08-16 05:41:04 +00:00
elif method_name == " anilist_search " :
if self . current_time . month in [ 12 , 1 , 2 ] : current_season = " winter "
elif self . current_time . month in [ 3 , 4 , 5 ] : current_season = " spring "
elif self . current_time . month in [ 6 , 7 , 8 ] : current_season = " summer "
else : current_season = " fall "
2021-12-01 15:17:19 +00:00
default_year = self . current_year + 1 if self . current_time . month == 12 else self . current_year
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-07-22 21:00:45 +00:00
new_dictionary = { }
2021-08-16 05:41:04 +00:00
for search_method , search_data in dict_data . items ( ) :
2022-01-12 16:12:38 +00:00
search_attr , modifier = os . path . splitext ( str ( search_method ) . lower ( ) )
if search_method not in anilist . searches :
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute not supported " )
2021-08-16 05:41:04 +00:00
elif search_attr == " season " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , parent = method_name , default = current_season , options = util . seasons )
2022-03-03 14:43:00 +00:00
if new_dictionary [ search_attr ] == " current " :
new_dictionary [ search_attr ] = current_season
2021-08-16 05:41:04 +00:00
if " year " not in dict_methods :
2021-12-01 15:17:19 +00:00
logger . warning ( f " Collection Warning: { method_name } year attribute not found using this year: { default_year } by default " )
new_dictionary [ " year " ] = default_year
2021-08-16 05:41:04 +00:00
elif search_attr == " year " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , datatype = " int " , parent = method_name , default = default_year , minimum = 1917 , maximum = default_year + 1 )
2021-08-17 00:07:35 +00:00
elif search_data is None :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute is blank " )
2021-08-16 05:41:04 +00:00
elif search_attr == " adult " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , datatype = " bool " , parent = method_name )
2021-08-26 18:24:30 +00:00
elif search_attr == " country " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , options = anilist . country_codes , parent = method_name )
2021-08-26 18:24:30 +00:00
elif search_attr == " source " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , options = anilist . media_source , parent = method_name )
2021-08-16 05:41:04 +00:00
elif search_attr in [ " episodes " , " duration " , " score " , " popularity " ] :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_method ] = util . parse ( self . Type , search_method , search_data , datatype = " int " , parent = method_name )
2021-08-16 05:41:04 +00:00
elif search_attr in [ " format " , " status " , " genre " , " tag " , " tag_category " ] :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_method ] = self . config . AniList . validate ( search_attr . replace ( " _ " , " " ) . title ( ) , util . parse ( self . Type , search_method , search_data ) )
2021-08-16 05:41:04 +00:00
elif search_attr in [ " start " , " end " ] :
2022-01-12 16:12:38 +00:00
new_dictionary [ search_method ] = util . validate_date ( search_data , f " { method_name } { search_method } attribute " , return_as = " % m/ %d / % Y " )
2021-08-16 05:41:04 +00:00
elif search_attr == " min_tag_percent " :
2022-01-28 18:36:21 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , datatype = " int " , parent = method_name , minimum = 0 , maximum = 100 )
2021-08-16 05:41:04 +00:00
elif search_attr == " search " :
new_dictionary [ search_attr ] = str ( search_data )
2022-01-12 16:12:38 +00:00
elif search_method not in [ " sort_by " , " limit " ] :
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute not supported " )
2021-08-16 13:02:52 +00:00
if len ( new_dictionary ) == 0 :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } must have at least one valid search option " )
2022-01-28 18:36:21 +00:00
new_dictionary [ " sort_by " ] = util . parse ( self . Type , " sort_by " , dict_data , methods = dict_methods , parent = method_name , default = " score " , options = anilist . sort_options )
new_dictionary [ " limit " ] = util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 0 , parent = method_name )
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , new_dictionary ) )
2021-07-22 21:00:45 +00:00
2021-11-27 00:30:41 +00:00
def _flixpatrol ( self , method_name , method_data ) :
if method_name . startswith ( " flixpatrol_url " ) :
flixpatrol_lists = self . config . FlixPatrol . validate_flixpatrol_lists ( method_data , self . language , self . library . is_movie )
for flixpatrol_list in flixpatrol_lists :
self . builders . append ( ( " flixpatrol_url " , flixpatrol_list ) )
elif method_name in flixpatrol . builders :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-11-27 00:30:41 +00:00
if method_name == " flixpatrol_demographics " :
data = {
2022-01-28 18:36:21 +00:00
" generation " : util . parse ( self . Type , " generation " , dict_data , methods = dict_methods , parent = method_name , options = flixpatrol . generations ) ,
" gender " : util . parse ( self . Type , " gender " , dict_data , methods = dict_methods , parent = method_name , default = " all " , options = flixpatrol . gender ) ,
" location " : util . parse ( self . Type , " location " , dict_data , methods = dict_methods , parent = method_name , default = " world " , options = flixpatrol . demo_locations ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , parent = method_name , default = 10 )
2021-11-27 00:30:41 +00:00
}
elif method_name == " flixpatrol_popular " :
data = {
2022-01-28 18:36:21 +00:00
" source " : util . parse ( self . Type , " source " , dict_data , methods = dict_methods , parent = method_name , options = flixpatrol . popular ) ,
" time_window " : util . parse ( self . Type , " time_window " , dict_data , methods = dict_methods , parent = method_name , default = " today " ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , parent = method_name , default = 10 )
2021-11-27 00:30:41 +00:00
}
elif method_name == " flixpatrol_top " :
data = {
2022-01-28 18:36:21 +00:00
" platform " : util . parse ( self . Type , " platform " , dict_data , methods = dict_methods , parent = method_name , options = flixpatrol . platforms ) ,
" location " : util . parse ( self . Type , " location " , dict_data , methods = dict_methods , parent = method_name , default = " world " , options = flixpatrol . locations ) ,
" time_window " : util . parse ( self . Type , " time_window " , dict_data , methods = dict_methods , parent = method_name , default = " today " ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , parent = method_name , default = 10 )
2021-11-27 00:30:41 +00:00
}
else :
continue
if self . config . FlixPatrol . validate_flixpatrol_dict ( method_name , data , self . language , self . library . is_movie ) :
self . builders . append ( ( method_name , data ) )
2021-07-22 21:00:45 +00:00
def _icheckmovies ( self , method_name , method_data ) :
if method_name . startswith ( " icheckmovies_list " ) :
icheckmovies_lists = self . config . ICheckMovies . validate_icheckmovies_lists ( method_data , self . language )
for icheckmovies_list in icheckmovies_lists :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( " icheckmovies_list " , icheckmovies_list ) )
2021-07-22 21:00:45 +00:00
if method_name . endswith ( " _details " ) :
self . summaries [ method_name ] = self . config . ICheckMovies . get_list_description ( icheckmovies_lists [ 0 ] , self . language )
2021-07-23 18:45:49 +00:00
def _imdb ( self , method_name , method_data ) :
if method_name == " imdb_id " :
for value in util . get_list ( method_data ) :
if str ( value ) . startswith ( " tt " ) :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , value ) )
2021-07-23 18:45:49 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: imdb_id { value } must begin with tt " )
2021-07-23 18:45:49 +00:00
elif method_name == " imdb_list " :
2022-03-27 06:26:08 +00:00
for imdb_dict in self . config . IMDb . validate_imdb_lists ( self . Type , method_data , self . language ) :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , imdb_dict ) )
2021-12-10 08:13:26 +00:00
elif method_name == " imdb_chart " :
for value in util . get_list ( method_data ) :
if value in imdb . movie_charts and not self . library . is_movie :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: chart: { value } does not work with show libraries " )
2021-12-10 08:13:26 +00:00
elif value in imdb . show_charts and self . library . is_movie :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: chart: { value } does not work with movie libraries " )
2021-12-10 08:13:26 +00:00
elif value in imdb . charts :
self . builders . append ( ( method_name , value ) )
2021-12-10 08:17:24 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: chart: { value } is invalid options are { [ i for i in imdb . charts ] } " )
2021-07-23 18:45:49 +00:00
def _letterboxd ( self , method_name , method_data ) :
if method_name . startswith ( " letterboxd_list " ) :
2022-03-31 06:23:48 +00:00
letterboxd_lists = self . config . Letterboxd . validate_letterboxd_lists ( self . Type , method_data , self . language )
2021-07-23 18:45:49 +00:00
for letterboxd_list in letterboxd_lists :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( " letterboxd_list " , letterboxd_list ) )
2021-07-23 18:45:49 +00:00
if method_name . endswith ( " _details " ) :
2022-03-31 06:23:48 +00:00
self . summaries [ method_name ] = self . config . Letterboxd . get_list_description ( letterboxd_lists [ 0 ] [ " url " ] , self . language )
2021-07-23 18:45:49 +00:00
def _mal ( self , method_name , method_data ) :
if method_name == " mal_id " :
for mal_id in util . get_int_list ( method_data , " MyAnimeList ID " ) :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , mal_id ) )
2021-07-23 18:45:49 +00:00
elif method_name in [ " mal_all " , " mal_airing " , " mal_upcoming " , " mal_tv " , " mal_ova " , " mal_movie " , " mal_special " , " mal_popular " , " mal_favorite " , " mal_suggested " ] :
2022-01-28 18:36:21 +00:00
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 10 , maximum = 100 if method_name == " mal_suggested " else 500 ) ) )
2022-05-03 18:44:19 +00:00
elif method_name in [ " mal_season " , " mal_userlist " , " mal_search " ] :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-07-23 18:45:49 +00:00
if method_name == " mal_season " :
2021-08-10 13:34:13 +00:00
if self . current_time . month in [ 1 , 2 , 3 ] : default_season = " winter "
elif self . current_time . month in [ 4 , 5 , 6 ] : default_season = " spring "
elif self . current_time . month in [ 7 , 8 , 9 ] : default_season = " summer "
else : default_season = " fall "
2022-03-03 14:43:00 +00:00
season = util . parse ( self . Type , " season " , dict_data , methods = dict_methods , parent = method_name , default = default_season , options = util . seasons )
if season == " current " :
season = default_season
2021-08-10 13:34:13 +00:00
self . builders . append ( ( method_name , {
2022-03-03 14:43:00 +00:00
" season " : season ,
2022-01-28 18:36:21 +00:00
" sort_by " : util . parse ( self . Type , " sort_by " , dict_data , methods = dict_methods , parent = method_name , default = " members " , options = mal . season_sort_options , translation = mal . season_sort_translation ) ,
" year " : util . parse ( self . Type , " year " , dict_data , datatype = " int " , methods = dict_methods , default = self . current_year , parent = method_name , minimum = 1917 , maximum = self . current_year + 1 ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 100 , parent = method_name , maximum = 500 )
2021-08-10 13:34:13 +00:00
} ) )
2021-07-23 18:45:49 +00:00
elif method_name == " mal_userlist " :
2021-08-10 13:34:13 +00:00
self . builders . append ( ( method_name , {
2022-01-28 18:36:21 +00:00
" username " : util . parse ( self . Type , " username " , dict_data , methods = dict_methods , parent = method_name ) ,
" status " : util . parse ( self . Type , " status " , dict_data , methods = dict_methods , parent = method_name , default = " all " , options = mal . userlist_status ) ,
" sort_by " : util . parse ( self . Type , " sort_by " , dict_data , methods = dict_methods , parent = method_name , default = " score " , options = mal . userlist_sort_options , translation = mal . userlist_sort_translation ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 100 , parent = method_name , maximum = 1000 )
2021-08-10 13:34:13 +00:00
} ) )
2022-05-03 18:44:19 +00:00
elif method_name == " mal_search " :
final_attributes = { }
final_text = " MyAnimeList Search "
if " sort_by " in dict_methods :
sort = util . parse ( self . Type , " sort_by " , dict_data , methods = dict_methods , parent = method_name , options = mal . search_combos )
sort_type , sort_direction = sort . split ( " . " )
final_text + = f " \n Sorted By: { sort } "
final_attributes [ " order_by " ] = sort_type
final_attributes [ " sort " ] = sort_direction
limit = 0
if " limit " in dict_methods :
limit = util . parse ( self . Type , " limit " , dict_data , datatype = " int " , default = 0 , methods = dict_methods , parent = method_name )
final_text + = f " \n Limit: { limit if limit else ' None ' } "
if " query " in dict_methods :
final_attributes [ " q " ] = util . parse ( self . Type , " query " , dict_data , methods = dict_methods , parent = method_name )
final_text + = f " \n Query: { final_attributes [ ' q ' ] } "
if " prefix " in dict_methods :
final_attributes [ " letter " ] = util . parse ( self . Type , " prefix " , dict_data , methods = dict_methods , parent = method_name )
final_text + = f " \n Prefix: { final_attributes [ ' letter ' ] } "
if " type " in dict_methods :
type_list = util . parse ( self . Type , " type " , dict_data , datatype = " commalist " , methods = dict_methods , parent = method_name , options = mal . search_types )
final_attributes [ " type " ] = " , " . join ( type_list )
final_text + = f " \n Type: { ' or ' . join ( type_list ) } "
if " status " in dict_methods :
final_attributes [ " status " ] = util . parse ( self . Type , " status " , dict_data , methods = dict_methods , parent = method_name , options = mal . search_status )
final_text + = f " \n Status: { final_attributes [ ' status ' ] } "
if " genre " in dict_methods :
genre_list = util . parse ( self . Type , " genre " , dict_data , datatype = " commalist " , methods = dict_methods , parent = method_name )
final_genres = [ self . config . MyAnimeList . genres [ g ] for g in genre_list if g in self . config . MyAnimeList . genres ]
final_attributes [ " genres " ] = " , " . join ( final_genres )
final_text + = f " \n Genre: { ' or ' . join ( [ self . config . MyAnimeList . genres [ g ] for g in final_genres ] ) } "
if " genre.not " in dict_methods :
genre_list = util . parse ( self . Type , " genre.not " , dict_data , datatype = " commalist " , methods = dict_methods , parent = method_name )
final_genres = [ self . config . MyAnimeList . genres [ g ] for g in genre_list if g in self . config . MyAnimeList . genres ]
final_attributes [ " genres_exclude " ] = " , " . join ( final_genres )
final_text + = f " \n Not Genre: { ' or ' . join ( [ self . config . MyAnimeList . genres [ g ] for g in final_genres ] ) } "
if " studio " in dict_methods :
studio_list = util . parse ( self . Type , " studio " , dict_data , datatype = " commalist " , methods = dict_methods , parent = method_name )
final_studios = [ self . config . MyAnimeList . studios [ s ] for s in studio_list if s in self . config . MyAnimeList . studios ]
final_attributes [ " producers " ] = " , " . join ( final_studios )
final_text + = f " \n Studio: { ' or ' . join ( [ self . config . MyAnimeList . studios [ s ] for s in final_studios ] ) } "
if " content_rating " in dict_methods :
final_attributes [ " rating " ] = util . parse ( self . Type , " content_rating " , dict_data , methods = dict_methods , parent = method_name , options = mal . search_ratings )
final_text + = f " \n Content Rating: { final_attributes [ ' rating ' ] } "
if " score.gte " in dict_methods :
final_attributes [ " min_score " ] = util . parse ( self . Type , " score.gte " , dict_data , datatype = " float " , methods = dict_methods , parent = method_name , minimum = 0 , maximum = 10 )
final_text + = f " \n Score Greater Than or Equal: { final_attributes [ ' min_score ' ] } "
elif " score.gt " in dict_methods :
original_score = util . parse ( self . Type , " score.gt " , dict_data , datatype = " float " , methods = dict_methods , parent = method_name , minimum = 0 , maximum = 10 )
final_attributes [ " min_score " ] = original_score + 0.01
final_text + = f " \n Score Greater Than: { original_score } "
if " score.lte " in dict_methods :
final_attributes [ " max_score " ] = util . parse ( self . Type , " score.lte " , dict_data , datatype = " float " , methods = dict_methods , parent = method_name , minimum = 0 , maximum = 10 )
final_text + = f " \n Score Less Than or Equal: { final_attributes [ ' max_score ' ] } "
elif " score.lt " in dict_methods :
original_score = util . parse ( self . Type , " score.lt " , dict_data , datatype = " float " , methods = dict_methods , parent = method_name , minimum = 0 , maximum = 10 )
final_attributes [ " max_score " ] = original_score - 0.01
final_text + = f " \n Score Less Than: { original_score } "
if " min_score " in final_attributes and " max_score " in final_attributes and final_attributes [ " max_score " ] < = final_attributes [ " min_score " ] :
raise Failed ( f " { self . Type } Error: mal_search score.lte/score.lt attribute must be greater then score.gte/score.gt " )
if " sfw " in dict_methods :
sfw = util . parse ( self . Type , " sfw " , dict_data , datatype = " bool " , methods = dict_methods , parent = method_name )
if sfw :
final_attributes [ " sfw " ] = 1
final_text + = f " \n Safe for Work: { final_attributes [ ' sfw ' ] } "
if not final_attributes :
raise Failed ( f " { self . Type } Error: no mal_search attributes found " )
self . builders . append ( ( method_name , ( final_attributes , final_text , limit ) ) )
2021-08-17 00:07:35 +00:00
elif method_name in [ " mal_genre " , " mal_studio " ] :
2021-08-10 13:34:13 +00:00
id_name = f " { method_name [ 4 : ] } _id "
final_data = [ ]
for data in util . get_list ( method_data ) :
final_data . append ( data if isinstance ( data , dict ) else { id_name : data , " limit " : 0 } )
2022-05-03 18:44:19 +00:00
for dict_data in final_data :
2022-02-13 22:47:08 +00:00
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-08-10 13:34:13 +00:00
self . builders . append ( ( method_name , {
2022-01-28 18:36:21 +00:00
id_name : util . parse ( self . Type , id_name , dict_data , datatype = " int " , methods = dict_methods , parent = method_name , maximum = 999999 ) ,
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 0 , parent = method_name )
2021-08-10 13:34:13 +00:00
} ) )
2021-07-23 18:45:49 +00:00
def _plex ( self , method_name , method_data ) :
2022-01-24 22:36:40 +00:00
if method_name in [ " plex_all " , " plex_pilots " ] :
2022-01-11 01:23:56 +00:00
self . builders . append ( ( method_name , self . collection_level ) )
2021-07-23 18:45:49 +00:00
elif method_name in [ " plex_search " , " plex_collectionless " ] :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-07-23 18:45:49 +00:00
new_dictionary = { }
if method_name == " plex_search " :
2021-08-22 15:54:33 +00:00
type_override = f " { self . collection_level } s " if self . collection_level in plex . collection_level_options else None
2022-04-27 23:32:48 +00:00
new_dictionary = self . build_filter ( " plex_search " , dict_data , type_override = type_override )
2021-07-23 18:45:49 +00:00
elif method_name == " plex_collectionless " :
2022-02-01 03:19:49 +00:00
prefix_list = util . parse ( self . Type , " exclude_prefix " , dict_data , datatype = " list " , methods = dict_methods ) if " exclude_prefix " in dict_methods else [ ]
exact_list = util . parse ( self . Type , " exclude " , dict_data , datatype = " list " , methods = dict_methods ) if " exclude " in dict_methods else [ ]
2021-07-23 18:45:49 +00:00
if len ( prefix_list ) == 0 and len ( exact_list ) == 0 :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: you must have at least one exclusion " )
2021-07-23 18:45:49 +00:00
exact_list . append ( self . name )
new_dictionary [ " exclude_prefix " ] = prefix_list
new_dictionary [ " exclude " ] = exact_list
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , new_dictionary ) )
2021-07-23 18:45:49 +00:00
else :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( " plex_search " , self . build_filter ( " plex_search " , { " any " : { method_name : method_data } } ) ) )
2021-07-23 18:45:49 +00:00
2022-03-19 05:16:25 +00:00
def _reciperr ( self , method_name , method_data ) :
if method_name == " reciperr_list " :
for reciperr_list in self . config . Reciperr . validate_list ( method_data ) :
self . builders . append ( ( method_name , reciperr_list ) )
elif method_name == " stevenlu_popular " :
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , " bool " ) ) )
2021-08-01 01:23:17 +00:00
2022-01-15 22:40:59 +00:00
def _mdblist ( self , method_name , method_data ) :
2022-03-03 14:43:00 +00:00
for mdb_dict in self . config . Mdblist . validate_mdblist_lists ( self . Type , method_data ) :
2022-01-24 09:20:07 +00:00
self . builders . append ( ( method_name , mdb_dict ) )
2022-01-15 22:40:59 +00:00
2021-07-23 18:45:49 +00:00
def _tautulli ( self , method_name , method_data ) :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , {
2021-07-23 18:45:49 +00:00
" list_type " : " popular " if method_name == " tautulli_popular " else " watched " ,
2022-01-28 18:36:21 +00:00
" list_days " : util . parse ( self . Type , " list_days " , dict_data , datatype = " int " , methods = dict_methods , default = 30 , parent = method_name ) ,
" list_size " : util . parse ( self . Type , " list_size " , dict_data , datatype = " int " , methods = dict_methods , default = 10 , parent = method_name ) ,
" list_buffer " : util . parse ( self . Type , " list_buffer " , dict_data , datatype = " int " , methods = dict_methods , default = 20 , parent = method_name ) ,
" list_minimum " : util . parse ( self . Type , " list_minimum " , dict_data , datatype = " int " , methods = dict_methods , default = 0 , parent = method_name )
2021-07-23 18:45:49 +00:00
} ) )
def _tmdb ( self , method_name , method_data ) :
if method_name == " tmdb_discover " :
2022-02-13 22:47:08 +00:00
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
2022-01-28 18:36:21 +00:00
new_dictionary = { " limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 100 , parent = method_name ) }
2021-08-14 22:59:35 +00:00
for discover_method , discover_data in dict_data . items ( ) :
2022-01-12 16:12:38 +00:00
discover_attr , modifier = os . path . splitext ( str ( discover_method ) . lower ( ) )
2021-08-14 22:59:35 +00:00
if discover_data is None :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute is blank " )
elif discover_method not in tmdb . discover_all :
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute not supported " )
2021-08-14 22:59:35 +00:00
elif self . library . is_movie and discover_attr in tmdb . discover_tv_only :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute only works for show libraries " )
2021-08-14 22:59:35 +00:00
elif self . library . is_show and discover_attr in tmdb . discover_movie_only :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute only works for movie libraries " )
2022-03-21 17:40:32 +00:00
elif discover_attr == " region " :
new_dictionary [ discover_attr ] = util . parse ( self . Type , discover_attr , discover_data , parent = method_name , regex = ( " ^[A-Z] {2} $ " , " US " ) )
2022-01-24 06:42:56 +00:00
elif discover_attr == " sort_by " :
2021-08-14 22:59:35 +00:00
options = tmdb . discover_movie_sort if self . library . is_movie else tmdb . discover_tv_sort
2022-01-28 18:36:21 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , parent = method_name , options = options )
2021-08-14 22:59:35 +00:00
elif discover_attr == " certification_country " :
if " certification " in dict_data or " certification.lte " in dict_data or " certification.gte " in dict_data :
2022-01-12 16:12:38 +00:00
new_dictionary [ discover_method ] = discover_data
2021-07-23 18:45:49 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_attr } attribute: must be used with either certification, certification.lte, or certification.gte " )
2021-08-14 22:59:35 +00:00
elif discover_attr == " certification " :
if " certification_country " in dict_data :
2022-01-12 16:12:38 +00:00
new_dictionary [ discover_method ] = discover_data
2021-08-14 22:59:35 +00:00
else :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute: must be used with certification_country " )
2021-11-26 08:24:36 +00:00
elif discover_attr == " watch_region " :
2021-11-29 00:27:15 +00:00
if " with_watch_providers " in dict_data or " without_watch_providers " in dict_data or " with_watch_monetization_types " in dict_data :
2022-01-12 16:12:38 +00:00
new_dictionary [ discover_method ] = discover_data
2021-11-26 08:24:36 +00:00
else :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute: must be used with either with_watch_providers, without_watch_providers, or with_watch_monetization_types " )
2021-11-26 08:24:36 +00:00
elif discover_attr == " with_watch_monetization_types " :
if " watch_region " in dict_data :
2022-01-28 18:36:21 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , parent = method_name , options = tmdb . discover_monetization_types )
2021-11-26 08:24:36 +00:00
else :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute: must be used with watch_region " )
2021-11-29 16:02:16 +00:00
elif discover_attr in tmdb . discover_booleans :
2022-03-21 17:40:32 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , datatype = " bool " , parent = method_name )
2021-11-29 00:27:15 +00:00
elif discover_attr == " vote_average " :
2022-01-28 18:36:21 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " float " , parent = method_name )
2021-11-26 08:24:36 +00:00
elif discover_attr == " with_status " :
2022-03-21 17:40:32 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , datatype = " int " , parent = method_name , minimum = 0 , maximum = 5 )
2021-11-26 08:24:36 +00:00
elif discover_attr == " with_type " :
2022-03-21 17:40:32 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , datatype = " int " , parent = method_name , minimum = 0 , maximum = 6 )
elif discover_attr in tmdb . discover_dates :
2022-01-12 16:12:38 +00:00
new_dictionary [ discover_method ] = util . validate_date ( discover_data , f " { method_name } { discover_method } attribute " , return_as = " % m/ %d / % Y " )
2021-11-29 00:27:15 +00:00
elif discover_attr in tmdb . discover_years :
2022-03-21 17:40:32 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_attr , discover_data , datatype = " int " , parent = method_name , minimum = 1800 , maximum = self . current_year + 1 )
2021-11-29 00:27:15 +00:00
elif discover_attr in tmdb . discover_ints :
2022-01-28 18:36:21 +00:00
new_dictionary [ discover_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " int " , parent = method_name )
2022-03-21 17:40:32 +00:00
elif discover_attr in tmdb . discover_strings :
2022-01-12 16:12:38 +00:00
new_dictionary [ discover_method ] = discover_data
2021-08-14 22:59:35 +00:00
elif discover_attr != " limit " :
2022-01-12 16:12:38 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { discover_method } attribute not supported " )
2021-07-23 18:45:49 +00:00
if len ( new_dictionary ) > 1 :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name , new_dictionary ) )
2021-07-23 18:45:49 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } had no valid fields " )
2022-03-22 18:16:30 +00:00
elif method_name in tmdb . int_builders :
2022-01-28 18:36:21 +00:00
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 10 ) ) )
2021-07-23 18:45:49 +00:00
else :
values = self . config . TMDb . validate_tmdb_ids ( method_data , method_name )
2022-03-22 18:16:30 +00:00
if method_name in tmdb . details_builders :
2021-07-26 15:46:36 +00:00
if method_name . startswith ( ( " tmdb_collection " , " tmdb_movie " , " tmdb_show " ) ) :
2021-07-23 18:45:49 +00:00
item = self . config . TMDb . get_movie_show_or_collection ( values [ 0 ] , self . library . is_movie )
2022-01-21 16:34:19 +00:00
if item . overview :
2021-07-23 18:45:49 +00:00
self . summaries [ method_name ] = item . overview
2022-01-21 16:34:19 +00:00
if item . backdrop_url :
self . backgrounds [ method_name ] = item . backdrop_url
2022-04-30 21:58:46 +00:00
if item . poster_url :
2022-01-21 16:34:19 +00:00
self . posters [ method_name ] = item . poster_url
2021-07-26 15:46:36 +00:00
elif method_name . startswith ( ( " tmdb_actor " , " tmdb_crew " , " tmdb_director " , " tmdb_producer " , " tmdb_writer " ) ) :
2021-07-23 18:45:49 +00:00
item = self . config . TMDb . get_person ( values [ 0 ] )
2022-01-21 16:34:19 +00:00
if item . biography :
2021-07-23 18:45:49 +00:00
self . summaries [ method_name ] = item . biography
2022-01-21 16:34:19 +00:00
if item . profile_path :
self . posters [ method_name ] = item . profile_url
2021-07-26 15:46:36 +00:00
elif method_name . startswith ( " tmdb_list " ) :
2021-07-23 18:45:49 +00:00
item = self . config . TMDb . get_list ( values [ 0 ] )
2022-01-21 16:34:19 +00:00
if item . description :
2021-07-23 18:45:49 +00:00
self . summaries [ method_name ] = item . description
for value in values :
2022-03-22 18:16:30 +00:00
self . builders . append ( ( method_name [ : - 8 ] if method_name in tmdb . details_builders else method_name , value ) )
2021-07-23 18:45:49 +00:00
def _trakt ( self , method_name , method_data ) :
if method_name . startswith ( " trakt_list " ) :
2022-03-27 06:26:08 +00:00
trakt_lists = self . config . Trakt . validate_list ( method_data )
2021-07-23 18:45:49 +00:00
for trakt_list in trakt_lists :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( " trakt_list " , trakt_list ) )
2021-07-23 18:45:49 +00:00
if method_name . endswith ( " _details " ) :
self . summaries [ method_name ] = self . config . Trakt . list_description ( trakt_lists [ 0 ] )
2021-11-13 23:51:12 +00:00
elif method_name == " trakt_boxoffice " :
2022-01-28 18:36:21 +00:00
if util . parse ( self . Type , method_name , method_data , datatype = " bool " , default = False ) :
2021-11-13 23:51:12 +00:00
self . builders . append ( ( method_name , 10 ) )
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method_name } must be set to true " )
2022-03-27 06:26:08 +00:00
elif method_name == " trakt_recommendations " :
self . builders . append ( ( method_name , util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 10 , maximum = 100 ) ) )
2022-05-09 06:58:09 +00:00
elif method_name == " sync_to_trakt_list " :
if method_data not in self . config . Trakt . slugs :
raise Failed ( f " { self . Type } Error: { method_data } invalid. Options { ' , ' . join ( self . config . Trakt . slugs ) } " )
self . sync_to_trakt_list = method_data
2022-05-09 15:22:41 +00:00
elif method_name == " sync_missing_to_trakt_list " :
self . sync_missing_to_trakt_list = util . parse ( self . Type , method_name , method_data , datatype = " bool " , default = False )
2021-11-13 23:51:12 +00:00
elif method_name in trakt . builders :
2022-03-27 06:26:08 +00:00
if method_name in [ " trakt_chart " , " trakt_userlist " ] :
trakt_dicts = method_data
final_method = method_name
elif method_name in [ " trakt_watchlist " , " trakt_collection " ] :
trakt_dicts = [ ]
for trakt_user in util . get_list ( method_data , split = False ) :
trakt_dicts . append ( { " userlist " : " watchlist " if " trakt_watchlist " else " collected " , " user " : trakt_user } )
final_method = " trakt_userlist "
else :
terms = method_name . split ( " _ " )
trakt_dicts = {
" chart " : terms [ 1 ] ,
2022-04-02 14:17:47 +00:00
" limit " : util . parse ( self . Type , method_name , method_data , datatype = " int " , default = 10 ) ,
2022-03-27 06:26:08 +00:00
" time_period " : terms [ 2 ] if len ( terms ) > 2 else None
}
final_method = " trakt_chart "
2022-03-28 19:30:46 +00:00
if method_name != final_method :
logger . warning ( f " { self . Type } Warning: { method_name } will run as { final_method } " )
2022-03-28 16:15:34 +00:00
for trakt_dict in self . config . Trakt . validate_chart ( self . Type , final_method , trakt_dicts , self . library . is_movie ) :
2022-03-29 19:16:05 +00:00
self . builders . append ( ( final_method , trakt_dict ) )
2021-07-23 18:45:49 +00:00
def _tvdb ( self , method_name , method_data ) :
values = util . get_list ( method_data )
if method_name . endswith ( " _details " ) :
if method_name . startswith ( ( " tvdb_movie " , " tvdb_show " ) ) :
2022-05-05 22:05:16 +00:00
item = self . config . TVDb . get_tvdb_obj ( values [ 0 ] , is_movie = method_name . startswith ( " tvdb_movie " ) )
if item . background_url :
self . backgrounds [ method_name ] = item . background_url
if item . poster_url :
self . posters [ method_name ] = item . poster_url
2021-07-23 18:45:49 +00:00
elif method_name . startswith ( " tvdb_list " ) :
2021-10-26 15:01:08 +00:00
self . summaries [ method_name ] = self . config . TVDb . get_list_description ( values [ 0 ] )
2021-07-23 18:45:49 +00:00
for value in values :
2021-07-29 02:26:39 +00:00
self . builders . append ( ( method_name [ : - 8 ] if method_name . endswith ( " _details " ) else method_name , value ) )
2021-07-23 18:45:49 +00:00
def _filters ( self , method_name , method_data ) :
2022-02-13 22:47:08 +00:00
dict_data = util . parse ( self . Type , method_name , method_data , datatype = " dict " )
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
validate = True
if " validate " in dict_methods :
if dict_data [ dict_methods [ " validate " ] ] is None :
raise Failed ( f " { self . Type } Error: validate filter attribute is blank " )
if not isinstance ( dict_data [ dict_methods [ " validate " ] ] , bool ) :
raise Failed ( f " { self . Type } Error: validate filter attribute must be either true or false " )
validate = dict_data . pop ( dict_methods [ " validate " ] )
for filter_method , filter_data in dict_data . items ( ) :
2022-04-27 23:14:52 +00:00
filter_attr , modifier , filter_final = self . library . split ( filter_method )
2022-02-13 22:47:08 +00:00
message = None
if filter_final not in all_filters :
message = f " { self . Type } Error: { filter_final } is not a valid filter attribute "
elif self . collection_level in filters and filter_attr not in filters [ self . collection_level ] :
message = f " { self . Type } Error: { filter_final } is not a valid { self . collection_level } filter attribute "
elif filter_final is None :
message = f " { self . Type } Error: { filter_final } filter attribute is blank "
elif filter_attr in tmdb_filters :
self . tmdb_filters . append ( ( filter_final , self . validate_attribute ( filter_attr , modifier , f " { filter_final } filter " , filter_data , validate ) ) )
else :
self . filters . append ( ( filter_final , self . validate_attribute ( filter_attr , modifier , f " { filter_final } filter " , filter_data , validate ) ) )
if message :
if validate :
raise Failed ( message )
2021-07-23 18:45:49 +00:00
else :
2022-02-13 22:47:08 +00:00
logger . error ( message )
2021-07-23 18:45:49 +00:00
2021-12-14 05:51:36 +00:00
def gather_ids ( self , method , value ) :
2022-02-02 15:38:38 +00:00
expired = None
list_key = None
if self . config . Cache and self . details [ " cache_builders " ] :
2022-04-26 03:09:42 +00:00
list_key , expired = self . config . Cache . query_list_cache ( f " { self . library . type } : { method } " , str ( value ) , self . details [ " cache_builders " ] )
2022-02-02 15:38:38 +00:00
if list_key and expired is False :
2022-02-03 20:21:52 +00:00
logger . info ( f " Builder: { method } loaded from Cache " )
2022-02-02 15:38:38 +00:00
return self . config . Cache . query_list_ids ( list_key )
2021-12-14 05:51:36 +00:00
if " plex " in method :
2022-02-02 15:38:38 +00:00
ids = self . library . get_rating_keys ( method , value )
2021-12-14 05:51:36 +00:00
elif " tautulli " in method :
2022-05-02 19:05:52 +00:00
ids = self . library . Tautulli . get_rating_keys ( value , self . playlist )
2021-12-14 05:51:36 +00:00
elif " anidb " in method :
2022-03-15 00:11:16 +00:00
anidb_ids = self . config . AniDB . get_anidb_ids ( method , value )
2022-02-02 15:38:38 +00:00
ids = self . config . Convert . anidb_to_ids ( anidb_ids , self . library )
2021-12-14 05:51:36 +00:00
elif " anilist " in method :
anilist_ids = self . config . AniList . get_anilist_ids ( method , value )
2022-02-02 15:38:38 +00:00
ids = self . config . Convert . anilist_to_ids ( anilist_ids , self . library )
2021-12-14 05:51:36 +00:00
elif " mal " in method :
mal_ids = self . config . MyAnimeList . get_mal_ids ( method , value )
2022-02-02 15:38:38 +00:00
ids = self . config . Convert . myanimelist_to_ids ( mal_ids , self . library )
2021-12-14 05:51:36 +00:00
elif " tvdb " in method :
2022-02-02 15:38:38 +00:00
ids = self . config . TVDb . get_tvdb_ids ( method , value )
2021-12-14 05:51:36 +00:00
elif " imdb " in method :
2022-02-02 15:38:38 +00:00
ids = self . config . IMDb . get_imdb_ids ( method , value , self . language )
2021-12-14 05:51:36 +00:00
elif " flixpatrol " in method :
2022-03-19 05:16:25 +00:00
ids = self . config . FlixPatrol . get_tmdb_ids ( method , value , self . language , self . library . is_movie )
2021-12-14 05:51:36 +00:00
elif " icheckmovies " in method :
2022-03-19 05:16:25 +00:00
ids = self . config . ICheckMovies . get_imdb_ids ( method , value , self . language )
2021-12-14 05:51:36 +00:00
elif " letterboxd " in method :
2022-02-02 15:38:38 +00:00
ids = self . config . Letterboxd . get_tmdb_ids ( method , value , self . language )
2022-03-19 05:16:25 +00:00
elif " reciperr " in method or " stevenlu " in method :
ids = self . config . Reciperr . get_imdb_ids ( method , value )
2022-01-15 22:40:59 +00:00
elif " mdblist " in method :
2022-03-19 05:16:25 +00:00
ids = self . config . Mdblist . get_imdb_ids ( method , value )
2021-12-14 05:51:36 +00:00
elif " tmdb " in method :
2022-03-23 18:43:50 +00:00
ids = self . config . TMDb . get_tmdb_ids ( method , value , self . library . is_movie , self . tmdb_region )
2021-12-14 05:51:36 +00:00
elif " trakt " in method :
2022-02-02 15:38:38 +00:00
ids = self . config . Trakt . get_trakt_ids ( method , value , self . library . is_movie )
2022-05-02 19:05:52 +00:00
elif " radarr " in method :
ids = self . library . Radarr . get_tmdb_ids ( method , value )
elif " sonarr " in method :
ids = self . library . Sonarr . get_tvdb_ids ( method , value )
2021-12-14 05:51:36 +00:00
else :
2022-02-02 15:38:38 +00:00
ids = [ ]
2021-12-14 05:51:36 +00:00
logger . error ( f " { self . Type } Error: { method } method not supported " )
2022-02-02 15:38:38 +00:00
if self . config . Cache and self . details [ " cache_builders " ] and ids :
if list_key :
self . config . Cache . delete_list_ids ( list_key )
2022-04-26 03:09:42 +00:00
list_key = self . config . Cache . update_list_cache ( f " { self . library . type } : { method } " , str ( value ) , expired , self . details [ " cache_builders " ] )
2022-02-02 15:38:38 +00:00
self . config . Cache . update_list_ids ( list_key , ids )
return ids
2021-12-14 05:51:36 +00:00
2022-02-08 22:57:36 +00:00
def filter_and_save_items ( self , ids ) :
items = [ ]
if len ( ids ) > 0 :
total_ids = len ( ids )
2021-07-21 17:40:05 +00:00
logger . debug ( " " )
2022-02-08 22:57:36 +00:00
logger . debug ( f " { total_ids } IDs Found: { ids } " )
logger . debug ( " " )
for i , input_data in enumerate ( ids , 1 ) :
input_id , id_type = input_data
2022-02-13 16:33:57 +00:00
logger . ghost ( f " Parsing ID { i } / { total_ids } " )
2022-02-08 22:57:36 +00:00
rating_keys = [ ]
if id_type == " ratingKey " :
rating_keys = int ( input_id )
elif id_type == " imdb " :
if input_id not in self . ignore_imdb_ids :
found = False
for pl_library in self . libraries :
if input_id in pl_library . imdb_map :
found = True
rating_keys = pl_library . imdb_map [ input_id ]
break
if not found and ( self . collection_level == " episode " or self . playlist or self . do_missing ) :
2021-12-18 05:59:53 +00:00
try :
_id , tmdb_type = self . config . Convert . imdb_to_tmdb ( input_id , fail = True )
2022-02-08 22:57:36 +00:00
if tmdb_type == " episode " and ( self . collection_level == " episode " or self . playlist ) :
2022-01-06 06:25:23 +00:00
try :
2022-02-08 22:57:36 +00:00
tmdb_id , season_num , episode_num = _id . split ( " _ " )
tvdb_id = self . config . Convert . tmdb_to_tvdb ( tmdb_id , fail = True )
tvdb_id = int ( tvdb_id )
except Failed as e :
try :
if not self . config . OMDb :
raise Failed ( " " )
if self . config . OMDb . limit :
raise Failed ( " and OMDb limit reached. " )
omdb_item = self . config . OMDb . get_omdb ( input_id )
tvdb_id = omdb_item . series_id
season_num = omdb_item . season_num
episode_num = omdb_item . episode_num
if not tvdb_id or not season_num or not episode_num :
raise Failed ( f " and OMDb metadata lookup Failed for IMDb ID: { input_id } " )
except Failed as ee :
logger . error ( f " { e } { ee } " )
continue
for pl_library in self . libraries :
if tvdb_id in pl_library . show_map :
found = True
show_item = pl_library . fetchItem ( pl_library . show_map [ tvdb_id ] [ 0 ] )
try :
items . append ( show_item . episode ( season = int ( season_num ) , episode = int ( episode_num ) ) )
except NotFound :
self . missing_parts . append ( f " { show_item . title } Season: { season_num } Episode: { episode_num } Missing " )
break
if not found and tvdb_id not in self . missing_shows and self . do_missing :
self . missing_shows . append ( tvdb_id )
elif tmdb_type == " movie " and self . do_missing and _id not in self . missing_movies :
self . missing_movies . append ( _id )
elif tmdb_type == " show " and self . do_missing :
tvdb_id = self . config . Convert . tmdb_to_tvdb ( _id , fail = True )
if tvdb_id not in self . missing_shows :
self . missing_shows . append ( tvdb_id )
except Failed as e :
logger . warning ( e )
continue
elif id_type == " tmdb " and not self . parts_collection :
input_id = int ( input_id )
if input_id not in self . ignore_ids :
found = False
for pl_library in self . libraries :
if input_id in pl_library . movie_map :
found = True
rating_keys = pl_library . movie_map [ input_id ]
break
if not found and input_id not in self . missing_movies :
self . missing_movies . append ( input_id )
elif id_type in [ " tvdb " , " tmdb_show " ] and not self . parts_collection :
if id_type == " tmdb_show " :
try :
tvdb_id = self . config . Convert . tmdb_to_tvdb ( input_id , fail = True )
except Failed as e :
logger . warning ( e )
2021-12-18 05:59:53 +00:00
continue
2022-02-08 22:57:36 +00:00
else :
tvdb_id = int ( input_id )
if tvdb_id not in self . ignore_ids :
found = False
for pl_library in self . libraries :
if tvdb_id in pl_library . show_map :
found = True
rating_keys = pl_library . show_map [ tvdb_id ]
break
if not found and tvdb_id not in self . missing_shows :
2022-05-06 13:32:37 +00:00
self . missing_shows . append ( tvdb_id )
2022-02-08 22:57:36 +00:00
elif id_type == " tvdb_season " and ( self . collection_level == " season " or self . playlist ) :
tvdb_id , season_num = input_id . split ( " _ " )
tvdb_id = int ( tvdb_id )
found = False
for pl_library in self . libraries :
if tvdb_id in pl_library . show_map :
found = True
show_item = pl_library . fetchItem ( pl_library . show_map [ tvdb_id ] [ 0 ] )
try :
season_obj = show_item . season ( season = int ( season_num ) )
if self . playlist :
items . extend ( season_obj . episodes ( ) )
else :
items . append ( season_obj )
except NotFound :
self . missing_parts . append ( f " { show_item . title } Season: { season_num } Missing " )
break
if not found and tvdb_id not in self . missing_shows :
self . missing_shows . append ( tvdb_id )
elif id_type == " tvdb_episode " and ( self . collection_level == " episode " or self . playlist ) :
tvdb_id , season_num , episode_num = input_id . split ( " _ " )
tvdb_id = int ( tvdb_id )
found = False
for pl_library in self . libraries :
if tvdb_id in pl_library . show_map :
found = True
show_item = pl_library . fetchItem ( pl_library . show_map [ tvdb_id ] [ 0 ] )
2021-12-14 05:51:36 +00:00
try :
2021-12-15 15:50:20 +00:00
items . append ( show_item . episode ( season = int ( season_num ) , episode = int ( episode_num ) ) )
2021-12-14 05:51:36 +00:00
except NotFound :
2021-12-18 05:59:53 +00:00
self . missing_parts . append ( f " { show_item . title } Season: { season_num } Episode: { episode_num } Missing " )
2022-02-08 22:57:36 +00:00
if not found and tvdb_id not in self . missing_shows and self . do_missing :
self . missing_shows . append ( tvdb_id )
else :
continue
2021-12-15 15:50:20 +00:00
2022-02-08 22:57:36 +00:00
if not isinstance ( rating_keys , list ) :
rating_keys = [ rating_keys ]
for rk in rating_keys :
try :
item = self . fetch_item ( rk )
if self . playlist and isinstance ( item , ( Show , Season ) ) :
items . extend ( item . episodes ( ) )
else :
items . append ( item )
except Failed as e :
logger . error ( e )
2022-02-13 16:33:57 +00:00
logger . exorcise ( )
2022-02-08 22:57:36 +00:00
if not items :
return None
2021-12-15 15:50:20 +00:00
name = self . obj . title if self . obj else self . name
total = len ( items )
max_length = len ( str ( total ) )
if ( self . filters or self . tmdb_filters ) and self . details [ " show_filtered " ] is True :
logger . info ( " " )
logger . info ( " Filtering Builders: " )
for i , item in enumerate ( items , 1 ) :
2021-12-29 15:51:22 +00:00
if not isinstance ( item , ( Movie , Show , Season , Episode , Artist , Album , Track ) ) :
logger . error ( f " { self . Type } Error: Item: { item } is an invalid type " )
2021-12-15 15:50:20 +00:00
continue
2021-12-17 14:24:46 +00:00
if item not in self . added_items :
2021-12-15 15:50:20 +00:00
if item . ratingKey in self . filtered_keys :
if self . details [ " show_filtered " ] is True :
logger . info ( f " { name } { self . Type } | X | { self . filtered_keys [ item . ratingKey ] } " )
else :
2021-12-17 14:24:46 +00:00
current_title = util . item_title ( item )
2021-12-15 15:50:20 +00:00
if self . check_filters ( item , f " { ( ' ' * ( max_length - len ( str ( i ) ) ) ) } { i } / { total } " ) :
2021-12-17 14:24:46 +00:00
self . added_items . append ( item )
2021-12-15 15:50:20 +00:00
else :
self . filtered_keys [ item . ratingKey ] = current_title
if self . details [ " show_filtered " ] is True :
logger . info ( f " { name } { self . Type } | X | { current_title } " )
2021-08-05 14:59:45 +00:00
2022-04-27 23:32:48 +00:00
def build_filter ( self , method , plex_filter , display = False , default_sort = None , type_override = None ) :
2022-02-13 18:28:53 +00:00
if display :
2021-05-29 02:33:55 +00:00
logger . info ( " " )
logger . info ( f " Validating Method: { method } " )
if plex_filter is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method } attribute is blank " )
2021-05-29 02:33:55 +00:00
if not isinstance ( plex_filter , dict ) :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { method } must be a dictionary: { plex_filter } " )
2022-02-13 18:28:53 +00:00
if display :
2021-05-29 02:33:55 +00:00
logger . debug ( f " Value: { plex_filter } " )
filter_alias = { m . lower ( ) : m for m in plex_filter }
if " any " in filter_alias and " all " in filter_alias :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Cannot have more then one base " )
2021-05-29 02:33:55 +00:00
2021-08-22 15:54:33 +00:00
if type_override :
sort_type = type_override
2022-02-05 05:49:14 +00:00
elif " type " in filter_alias and self . library . is_show :
2021-05-29 02:33:55 +00:00
if plex_filter [ filter_alias [ " type " ] ] not in [ " shows " , " seasons " , " episodes " ] :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: type: { plex_filter [ filter_alias [ ' type ' ] ] } is invalid, must be either shows, season, or episodes " )
2021-05-29 02:33:55 +00:00
sort_type = plex_filter [ filter_alias [ " type " ] ]
2022-02-05 05:49:14 +00:00
elif " type " in filter_alias and self . library . is_music :
2021-12-29 15:51:22 +00:00
if plex_filter [ filter_alias [ " type " ] ] not in [ " artists " , " albums " , " tracks " ] :
raise Failed ( f " { self . Type } Error: type: { plex_filter [ filter_alias [ ' type ' ] ] } is invalid, must be either artists, albums, or tracks " )
sort_type = plex_filter [ filter_alias [ " type " ] ]
2021-05-29 02:33:55 +00:00
elif self . library . is_show :
sort_type = " shows "
2021-12-29 15:51:22 +00:00
elif self . library . is_music :
sort_type = " artists "
2021-05-29 02:33:55 +00:00
else :
sort_type = " movies "
ms = method . split ( " _ " )
filter_details = f " { ms [ 0 ] . capitalize ( ) } { sort_type . capitalize ( ) [ : - 1 ] } { ms [ 1 ] . capitalize ( ) } \n "
2022-04-27 23:32:48 +00:00
type_default_sort , type_key , sorts = plex . sort_types [ sort_type ]
2021-05-29 02:33:55 +00:00
2022-04-27 23:32:48 +00:00
sort = default_sort if default_sort else type_default_sort
2021-05-29 02:33:55 +00:00
if " sort_by " in filter_alias :
if plex_filter [ filter_alias [ " sort_by " ] ] is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: sort_by attribute is blank " )
2021-05-29 02:33:55 +00:00
if plex_filter [ filter_alias [ " sort_by " ] ] not in sorts :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: sort_by: { plex_filter [ filter_alias [ ' sort_by ' ] ] } is invalid " )
2021-05-29 02:33:55 +00:00
sort = plex_filter [ filter_alias [ " sort_by " ] ]
filter_details + = f " Sort By: { sort } \n "
limit = None
if " limit " in filter_alias :
if plex_filter [ filter_alias [ " limit " ] ] is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: limit attribute is blank " )
2022-03-22 20:06:59 +00:00
elif str ( plex_filter [ filter_alias [ " limit " ] ] ) . lower ( ) == " all " :
filter_details + = " Limit: all \n "
elif not isinstance ( plex_filter [ filter_alias [ " limit " ] ] , int ) or plex_filter [ filter_alias [ " limit " ] ] < 1 :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: limit attribute must be an integer greater then 0 " )
2022-03-22 20:06:59 +00:00
else :
limit = plex_filter [ filter_alias [ " limit " ] ]
filter_details + = f " Limit: { limit } \n "
2021-05-29 02:33:55 +00:00
validate = True
if " validate " in filter_alias :
if plex_filter [ filter_alias [ " validate " ] ] is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: validate attribute is blank " )
2021-05-29 02:33:55 +00:00
if not isinstance ( plex_filter [ filter_alias [ " validate " ] ] , bool ) :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: validate attribute must be either true or false " )
2021-05-29 02:33:55 +00:00
validate = plex_filter [ filter_alias [ " validate " ] ]
filter_details + = f " Validate: { validate } \n "
def _filter ( filter_dict , is_all = True , level = 1 ) :
output = " "
2022-02-13 18:28:53 +00:00
display_out = f " \n { ' ' * level } Match { ' all ' if is_all else ' any ' } of the following: "
2021-05-29 02:33:55 +00:00
level + = 1
indent = f " \n { ' ' * level } "
conjunction = f " { ' and ' if is_all else ' or ' } =1& "
for _key , _data in filter_dict . items ( ) :
2022-04-27 23:14:52 +00:00
attr , modifier , final_attr = self . library . split ( _key )
2021-05-29 02:33:55 +00:00
def build_url_arg ( arg , mod = None , arg_s = None , mod_s = None ) :
arg_key = plex . search_translation [ attr ] if attr in plex . search_translation else attr
2021-07-26 18:52:32 +00:00
arg_key = plex . show_translation [ arg_key ] if self . library . is_show and arg_key in plex . show_translation else arg_key
2021-05-29 02:33:55 +00:00
if mod is None :
mod = plex . modifier_translation [ modifier ] if modifier in plex . modifier_translation else modifier
if arg_s is None :
arg_s = arg
2021-12-29 15:51:22 +00:00
if attr in plex . string_attributes and modifier in [ " " , " .not " ] :
2021-05-29 02:33:55 +00:00
mod_s = " does not contain " if modifier == " .not " else " contains "
elif mod_s is None :
2021-08-14 22:59:35 +00:00
mod_s = util . mod_displays [ modifier ]
2021-05-29 02:33:55 +00:00
param_s = plex . search_display [ attr ] if attr in plex . search_display else attr . title ( ) . replace ( ' _ ' , ' ' )
display_line = f " { indent } { param_s } { mod_s } { arg_s } "
return f " { arg_key } { mod } = { arg } & " , display_line
2022-01-23 19:40:16 +00:00
error = None
2021-07-14 14:47:20 +00:00
if final_attr not in plex . searches and not final_attr . startswith ( ( " any " , " all " ) ) :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } is not a valid { method } attribute "
2021-12-29 15:51:22 +00:00
elif self . library . is_show and final_attr in plex . movie_only_searches :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } { method } attribute only works for movie libraries "
2021-12-29 15:51:22 +00:00
elif self . library . is_movie and final_attr in plex . show_only_searches :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } { method } attribute only works for show libraries "
2022-03-14 19:41:07 +00:00
elif self . library . is_music and final_attr not in plex . music_searches + [ " all " , " any " ] :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } { method } attribute does not work for music libraries "
2021-12-29 15:51:22 +00:00
elif not self . library . is_music and final_attr in plex . music_searches :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } { method } attribute only works for music libraries "
2022-01-23 22:20:43 +00:00
elif _data is not False and not _data :
2022-01-23 19:40:16 +00:00
error = f " { self . Type } Error: { final_attr } { method } attribute is blank "
2021-05-29 02:33:55 +00:00
else :
2022-01-23 19:40:16 +00:00
if final_attr . startswith ( ( " any " , " all " ) ) :
dicts = util . get_list ( _data )
2021-05-29 02:33:55 +00:00
results = " "
display_add = " "
2022-01-23 19:40:16 +00:00
for dict_data in dicts :
if not isinstance ( dict_data , dict ) :
2022-01-27 14:55:18 +00:00
raise Failed ( f " { self . Type } Error: { attr } must be either a dictionary or list of dictionaries " )
2022-01-23 19:40:16 +00:00
inside_filter , inside_display = _filter ( dict_data , is_all = attr == " all " , level = level )
if len ( inside_filter ) > 0 :
display_add + = inside_display
results + = f " { conjunction if len ( results ) > 0 else ' ' } push=1& { inside_filter } pop=1& "
else :
2022-04-20 16:03:08 +00:00
validation = self . validate_attribute ( attr , modifier , final_attr , _data , validate , plex_search = True )
2022-01-27 14:55:18 +00:00
if validation is not False and not validation :
2022-01-23 19:40:16 +00:00
continue
elif attr in plex . date_attributes and modifier in [ " " , " .not " ] :
last_text = " is not in the last " if modifier == " .not " else " is in the last "
last_mod = " %3E %3E " if modifier == " " else " % 3C % 3C "
results , display_add = build_url_arg ( f " - { validation } d " , mod = last_mod , arg_s = f " { validation } Days " , mod_s = last_text )
elif attr == " duration " and modifier in [ " .gt " , " .gte " , " .lt " , " .lte " ] :
results , display_add = build_url_arg ( validation * 60000 )
elif attr in plex . boolean_attributes :
bool_mod = " " if validation else " ! "
bool_arg = " true " if validation else " false "
results , display_add = build_url_arg ( 1 , mod = bool_mod , arg_s = bool_arg , mod_s = " is " )
2022-04-20 16:03:08 +00:00
elif ( attr in plex . tag_attributes + plex . string_attributes + plex . year_attributes ) and modifier in [ " " , " .is " , " .isnot " , " .not " , " .begins " , " .ends " , " .regex " ] :
2022-01-23 19:40:16 +00:00
results = " "
display_add = " "
for og_value , result in validation :
built_arg = build_url_arg ( quote ( str ( result ) ) if attr in plex . string_attributes else result , arg_s = og_value )
display_add + = built_arg [ 1 ]
results + = f " { conjunction if len ( results ) > 0 else ' ' } { built_arg [ 0 ] } "
else :
results , display_add = build_url_arg ( validation )
2022-02-13 18:28:53 +00:00
display_out + = display_add
2022-01-23 19:40:16 +00:00
output + = f " { conjunction if len ( output ) > 0 else ' ' } { results } "
if error :
if validate :
raise Failed ( error )
2021-05-29 02:33:55 +00:00
else :
2022-01-23 19:40:16 +00:00
logger . error ( error )
continue
2022-02-13 18:28:53 +00:00
return output , display_out
2021-05-29 02:33:55 +00:00
if " any " not in filter_alias and " all " not in filter_alias :
base_dict = { }
2021-05-30 02:19:38 +00:00
any_dicts = [ ]
2021-05-29 02:33:55 +00:00
for alias_key , alias_value in filter_alias . items ( ) :
2022-04-27 23:14:52 +00:00
_ , _ , final = self . library . split ( alias_key )
2021-06-22 14:54:11 +00:00
if final in plex . and_searches :
2021-05-30 02:19:38 +00:00
base_dict [ alias_value [ : - 4 ] ] = plex_filter [ alias_value ]
2021-06-22 14:54:11 +00:00
elif final in plex . or_searches :
2021-05-30 02:19:38 +00:00
any_dicts . append ( { alias_value : plex_filter [ alias_value ] } )
2021-06-22 14:54:11 +00:00
elif final in plex . searches :
2021-05-29 02:33:55 +00:00
base_dict [ alias_value ] = plex_filter [ alias_value ]
2021-05-30 02:19:38 +00:00
if len ( any_dicts ) > 0 :
base_dict [ " any " ] = any_dicts
2021-05-29 02:33:55 +00:00
base_all = True
if len ( base_dict ) == 0 :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Must have either any or all as a base for { method } " )
2021-05-29 02:33:55 +00:00
else :
base = " all " if " all " in filter_alias else " any "
base_all = base == " all "
if plex_filter [ filter_alias [ base ] ] is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { base } attribute is blank " )
2021-05-29 02:33:55 +00:00
if not isinstance ( plex_filter [ filter_alias [ base ] ] , dict ) :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { base } must be a dictionary: { plex_filter [ filter_alias [ base ] ] } " )
2021-05-29 02:33:55 +00:00
base_dict = plex_filter [ filter_alias [ base ] ]
built_filter , filter_text = _filter ( base_dict , is_all = base_all )
filter_details = f " { filter_details } Filter: { filter_text } "
if len ( built_filter ) > 0 :
final_filter = built_filter [ : - 1 ] if base_all else f " push=1& { built_filter } pop=1 "
filter_url = f " ?type= { type_key } & { f ' limit= { limit } & ' if limit else ' ' } sort= { sorts [ sort ] } & { final_filter } "
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: No Filter Created " )
2021-05-29 02:33:55 +00:00
return type_key , filter_details , filter_url
2022-04-20 16:03:08 +00:00
def validate_attribute ( self , attribute , modifier , final , data , validate , plex_search = False ) :
2021-05-27 17:40:35 +00:00
def smart_pair ( list_to_pair ) :
2022-04-20 16:03:08 +00:00
return [ ( t , t ) for t in list_to_pair ] if plex_search else list_to_pair
if modifier == " .regex " and not plex_search :
return util . validate_regex ( data , self . Type , validate = validate )
2022-01-06 14:39:21 +00:00
elif attribute in plex . string_attributes + string_filters and modifier in [ " " , " .not " , " .is " , " .isnot " , " .begins " , " .ends " ] :
2021-05-27 17:40:35 +00:00
return smart_pair ( util . get_list ( data , split = False ) )
2022-03-11 14:10:23 +00:00
elif attribute == " origin_country " :
return util . get_list ( data , upper = True )
elif attribute in [ " original_language " , " tmdb_keyword " ] :
2021-05-28 23:18:28 +00:00
return util . get_list ( data , lower = True )
2022-01-25 07:24:46 +00:00
elif attribute in [ " filepath " , " tmdb_genre " ] :
2021-05-28 23:18:28 +00:00
return util . get_list ( data )
2021-06-03 04:20:49 +00:00
elif attribute == " history " :
try :
2022-01-28 18:36:21 +00:00
return util . parse ( self . Type , final , data , datatype = " int " , maximum = 30 )
2021-06-03 04:20:49 +00:00
except Failed :
if str ( data ) . lower ( ) in [ " day " , " month " ] :
return data . lower ( )
2022-04-20 16:03:08 +00:00
else :
raise Failed ( f " { self . Type } Error: history attribute invalid: { data } must be a number between 1-30, day, or month " )
2022-01-28 21:20:09 +00:00
elif attribute == " tmdb_type " :
2022-01-29 16:06:53 +00:00
return util . parse ( self . Type , final , data , datatype = " commalist " , options = [ v for k , v in discover_types . items ( ) ] )
2022-01-28 21:20:09 +00:00
elif attribute == " tmdb_status " :
2022-01-29 16:06:53 +00:00
return util . parse ( self . Type , final , data , datatype = " commalist " , options = [ v for k , v in discover_status . items ( ) ] )
2022-04-20 16:03:08 +00:00
elif attribute in plex . tag_attributes and modifier in [ " .regex " ] :
_ , names = self . library . get_search_choices ( attribute , title = not plex_search , name_pairs = True )
valid_list = [ ]
used = [ ]
if plex_search and modifier == " .regex " :
for reg in util . validate_regex ( data , self . Type , validate = validate ) :
for name , key in names :
if name not in used and re . compile ( reg ) . search ( name ) :
valid_list . append ( ( name , key ) if plex_search else key )
2022-04-21 20:56:32 +00:00
if not valid_list :
error = f " Plex Error: { attribute } : No matches found with regex pattern { data } "
if validate :
raise Failed ( error )
else :
logger . error ( error )
2022-04-20 16:03:08 +00:00
return valid_list
2022-04-21 20:56:32 +00:00
elif attribute in plex . tag_attributes and modifier in [ " " , " .not " ] :
2021-05-28 23:18:28 +00:00
if attribute in plex . tmdb_attributes :
2021-05-27 17:40:35 +00:00
final_values = [ ]
for value in util . get_list ( data ) :
if value . lower ( ) == " tmdb " and " tmdb_person " in self . details :
for name in self . details [ " tmdb_person " ] :
final_values . append ( name )
else :
final_values . append ( value )
else :
2022-05-09 15:22:41 +00:00
final_values = util . get_list ( data , trim = False )
2022-04-20 16:03:08 +00:00
search_choices , names = self . library . get_search_choices ( attribute , title = not plex_search )
2021-06-16 03:20:45 +00:00
valid_list = [ ]
for value in final_values :
if str ( value ) . lower ( ) in search_choices :
2022-04-20 16:03:08 +00:00
if plex_search :
2021-06-16 03:20:45 +00:00
valid_list . append ( ( value , search_choices [ str ( value ) . lower ( ) ] ) )
else :
valid_list . append ( search_choices [ str ( value ) . lower ( ) ] )
2021-05-27 17:40:35 +00:00
else :
2022-04-06 02:14:32 +00:00
actor_id = None
if attribute in [ " actor " , " director " , " producer " , " writer " ] :
actor_id = self . library . get_actor_id ( value )
if actor_id :
2022-04-20 16:03:08 +00:00
if plex_search :
2022-04-06 02:14:32 +00:00
valid_list . append ( ( value , actor_id ) )
else :
valid_list . append ( actor_id )
if not actor_id :
error = f " Plex Error: { attribute } : { value } not found "
if self . details [ " show_options " ] :
error + = f " \n Options: { names } "
if validate :
raise Failed ( error )
else :
logger . error ( error )
2021-06-16 03:20:45 +00:00
return valid_list
2022-04-20 16:03:08 +00:00
elif attribute in date_attributes and modifier in [ " .before " , " .after " ] :
2022-02-03 04:22:47 +00:00
if data == " today " :
return datetime . strftime ( datetime . now ( ) , " % Y- % m- %d " )
else :
return util . validate_date ( data , final , return_as = " % Y- % m- %d " )
2022-04-20 16:03:08 +00:00
elif attribute in year_attributes and modifier in [ " " , " .not " ] :
2021-07-30 19:19:43 +00:00
final_years = [ ]
values = util . get_list ( data )
for value in values :
2022-01-28 18:36:21 +00:00
final_years . append ( util . parse ( self . Type , final , value , datatype = " int " ) )
2021-07-30 19:19:43 +00:00
return smart_pair ( final_years )
2022-04-20 16:03:08 +00:00
elif ( attribute in number_attributes + date_attributes + year_attributes and modifier in [ " " , " .not " , " .gt " , " .gte " , " .lt " , " .lte " ] ) \
2022-01-27 06:21:47 +00:00
or ( attribute in plex . tag_attributes and modifier in [ " .count_gt " , " .count_gte " , " .count_lt " , " .count_lte " ] ) :
2022-01-28 18:36:21 +00:00
return util . parse ( self . Type , final , data , datatype = " int " )
2021-12-29 15:51:22 +00:00
elif attribute in plex . float_attributes and modifier in [ " .gt " , " .gte " , " .lt " , " .lte " ] :
2022-02-02 15:37:16 +00:00
return util . parse ( self . Type , final , data , datatype = " float " , minimum = 0 , maximum = None if attribute == " duration " else 10 )
2021-12-18 05:59:53 +00:00
elif attribute in plex . boolean_attributes + boolean_filters :
2022-01-28 18:36:21 +00:00
return util . parse ( self . Type , attribute , data , datatype = " bool " )
2022-04-27 23:14:52 +00:00
elif attribute in [ " seasons " , " episodes " , " albums " , " tracks " ] :
if isinstance ( data , dict ) and data :
percentage = 60
if " percentage " in data :
if data [ " percentage " ] is None :
logger . warning ( f " { self . Type } Warning: percentage filter attribute is blank using 60 as default " )
else :
maybe = util . check_num ( data [ " percentage " ] )
if maybe < 0 or maybe > 100 :
logger . warning ( f " { self . Type } Warning: percentage filter attribute must be a number 0-100 using 60 as default " )
else :
percentage = maybe
final_filters = { " percentage " : percentage }
for filter_method , filter_data in data . items ( ) :
filter_attr , filter_modifier , filter_final = self . library . split ( filter_method )
message = None
2022-04-27 23:40:11 +00:00
if filter_final == " percentage " :
continue
2022-04-27 23:14:52 +00:00
if filter_final not in all_filters :
message = f " { self . Type } Error: { filter_final } is not a valid filter attribute "
elif filter_attr not in filters [ attribute [ : - 1 ] ] or filter_attr in [ " seasons " , " episodes " , " albums " , " tracks " ] :
message = f " { self . Type } Error: { filter_final } is not a valid { attribute [ : - 1 ] } filter attribute "
elif filter_final is None :
message = f " { self . Type } Error: { filter_final } filter attribute is blank "
2022-04-27 23:40:11 +00:00
else :
2022-04-27 23:14:52 +00:00
final_filters [ filter_final ] = self . validate_attribute ( filter_attr , filter_modifier , f " { attribute } { filter_final } filter " , filter_data , validate )
if message :
if validate :
raise Failed ( message )
else :
logger . error ( message )
if not final_filters :
raise Failed ( f " { self . Type } Error: no filters found under { attribute } " )
return final_filters
else :
raise Failed ( f " { self . Type } Error: { final } attribute must be a dictionary " )
2021-05-27 17:40:35 +00:00
else :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: { final } attribute not supported " )
2021-05-27 17:40:35 +00:00
2021-05-26 19:14:51 +00:00
def fetch_item ( self , item ) :
2022-04-22 02:57:26 +00:00
if isinstance ( item , ( Movie , Show , Season , Episode , Artist , Album , Track ) ) :
if item . ratingKey not in self . library . cached_items :
self . library . cached_items [ item . ratingKey ] = ( item , False )
return item
key = int ( item )
if key in self . library . cached_items :
cached_item , full_obj = self . library . cached_items [ key ]
return cached_item
2021-05-26 19:14:51 +00:00
try :
2022-04-20 18:19:58 +00:00
current = self . library . fetchItem ( key )
2021-12-29 15:51:22 +00:00
if not isinstance ( current , ( Movie , Show , Season , Episode , Artist , Album , Track ) ) :
2021-05-26 19:14:51 +00:00
raise NotFound
2022-04-22 02:57:26 +00:00
self . library . cached_items [ key ] = ( current , True )
2021-05-27 20:09:39 +00:00
return current
2021-05-26 19:14:51 +00:00
except ( BadRequest , NotFound ) :
raise Failed ( f " Plex Error: Item { item } not found " )
2021-05-26 14:45:33 +00:00
def add_to_collection ( self ) :
2021-12-30 00:37:31 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Adding to { self . name } { self . Type } " , space = False , border = False )
2021-12-30 00:37:31 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
name , collection_items = self . library . get_collection_name_and_items ( self . obj if self . obj else self . name , self . smart_label_collection )
2022-02-22 14:03:24 +00:00
total = self . limit if self . limit and len ( self . added_items ) > self . limit else len ( self . added_items )
2021-12-30 00:37:31 +00:00
spacing = len ( str ( total ) ) * 2 + 1
2021-11-03 14:36:11 +00:00
amount_added = 0
2021-12-30 21:29:16 +00:00
amount_unchanged = 0
2021-12-17 14:24:46 +00:00
playlist_adds = [ ]
2021-12-30 00:37:31 +00:00
for i , item in enumerate ( self . added_items , 1 ) :
2022-02-22 05:56:04 +00:00
if self . limit and amount_added + self . beginning_count - len ( [ r for _ , r in self . remove_item_map . items ( ) if r is not None ] ) > = self . limit :
logger . info ( f " { self . Type } Limit reached " )
2022-02-22 14:03:24 +00:00
self . added_items = self . added_items [ : i - 1 ]
2022-02-22 05:56:04 +00:00
break
2021-12-17 14:24:46 +00:00
current_operation = " = " if item in collection_items else " + "
2021-12-30 00:37:31 +00:00
number_text = f " { i } / { total } "
2022-02-13 16:33:57 +00:00
logger . info ( f " { number_text : > { spacing } } | { name } { self . Type } | { current_operation } | { util . item_title ( item ) } " )
2021-12-17 14:24:46 +00:00
if item in collection_items :
2022-02-09 02:31:29 +00:00
self . remove_item_map [ item . ratingKey ] = None
2021-12-30 21:29:16 +00:00
amount_unchanged + = 1
2021-07-07 16:16:00 +00:00
else :
2021-12-17 14:24:46 +00:00
if self . playlist :
playlist_adds . append ( item )
else :
self . library . alter_collection ( item , name , smart_label_collection = self . smart_label_collection )
2021-11-03 14:36:11 +00:00
amount_added + = 1
2021-12-26 15:48:52 +00:00
if self . details [ " changes_webhooks " ] :
2021-12-17 23:18:34 +00:00
if item . ratingKey in self . library . movie_rating_key_map :
2021-12-17 14:24:46 +00:00
add_id = self . library . movie_rating_key_map [ item . ratingKey ]
2021-12-17 23:18:34 +00:00
elif item . ratingKey in self . library . show_rating_key_map :
2021-12-17 14:24:46 +00:00
add_id = self . library . show_rating_key_map [ item . ratingKey ]
2021-10-04 17:51:32 +00:00
else :
add_id = None
2021-12-17 23:18:34 +00:00
self . notification_additions . append ( util . item_set ( item , add_id ) )
2021-12-17 14:24:46 +00:00
if self . playlist and playlist_adds and not self . obj :
self . obj = self . library . create_playlist ( self . name , playlist_adds )
logger . info ( " " )
logger . info ( f " Playlist: { self . name } created " )
elif self . playlist and playlist_adds :
self . obj . addItems ( playlist_adds )
2022-02-13 16:33:57 +00:00
logger . exorcise ( )
2021-05-24 03:38:46 +00:00
logger . info ( " " )
2021-08-22 15:54:33 +00:00
logger . info ( f " { total } { self . collection_level . capitalize ( ) } { ' s ' if total > 1 else ' ' } Processed " )
2021-12-30 21:29:16 +00:00
return amount_added , amount_unchanged
2021-02-20 05:41:45 +00:00
2021-10-20 19:58:25 +00:00
def sync_collection ( self ) :
2021-11-03 14:36:11 +00:00
amount_removed = 0
2021-12-17 14:24:46 +00:00
playlist_removes = [ ]
2022-02-09 02:31:29 +00:00
items = [ item for _ , item in self . remove_item_map . items ( ) if item is not None ]
2021-12-30 00:37:31 +00:00
if items :
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Removed from { self . name } { self . Type } " , space = False , border = False )
2021-12-30 00:37:31 +00:00
logger . info ( " " )
total = len ( items )
spacing = len ( str ( total ) ) * 2 + 1
for i , item in enumerate ( items , 1 ) :
number_text = f " { i } / { total } "
logger . info ( f " { number_text : > { spacing } } | { self . name } { self . Type } | - | { util . item_title ( item ) } " )
2021-12-17 14:24:46 +00:00
if self . playlist :
playlist_removes . append ( item )
else :
self . library . alter_collection ( item , self . name , smart_label_collection = self . smart_label_collection , add = False )
amount_removed + = 1
2021-12-26 15:48:52 +00:00
if self . details [ " changes_webhooks " ] :
2021-12-17 23:18:34 +00:00
if item . ratingKey in self . library . movie_rating_key_map :
2021-10-20 19:58:25 +00:00
remove_id = self . library . movie_rating_key_map [ item . ratingKey ]
2021-12-17 23:18:34 +00:00
elif item . ratingKey in self . library . show_rating_key_map :
2021-10-20 19:58:25 +00:00
remove_id = self . library . show_rating_key_map [ item . ratingKey ]
else :
remove_id = None
2021-12-17 23:18:34 +00:00
self . notification_removals . append ( util . item_set ( item , remove_id ) )
2021-12-30 00:37:31 +00:00
if self . playlist and playlist_removes :
self . obj . reload ( )
self . obj . removeItems ( playlist_removes )
2021-10-20 19:58:25 +00:00
logger . info ( " " )
2021-11-03 14:36:11 +00:00
logger . info ( f " { amount_removed } { self . collection_level . capitalize ( ) } { ' s ' if amount_removed == 1 else ' ' } Removed " )
return amount_removed
2021-10-20 19:58:25 +00:00
2021-08-07 06:01:21 +00:00
def check_tmdb_filter ( self , item_id , is_movie , item = None , check_released = False ) :
if self . tmdb_filters or check_released :
try :
if item is None :
2022-02-22 17:20:50 +00:00
if is_movie :
2022-03-13 19:39:34 +00:00
item = self . config . TMDb . get_movie ( item_id )
2022-02-22 17:20:50 +00:00
else :
2022-05-06 13:32:37 +00:00
item = self . config . TMDb . get_show ( self . config . Convert . tvdb_to_tmdb ( item_id , fail = True ) )
2021-08-07 06:01:21 +00:00
if check_released :
2022-01-21 16:34:19 +00:00
date_to_check = item . release_date if is_movie else item . first_air_date
2022-01-23 00:47:29 +00:00
if not date_to_check or date_to_check > self . current_time :
2021-08-07 06:01:21 +00:00
return False
for filter_method , filter_data in self . tmdb_filters :
2022-04-27 23:14:52 +00:00
filter_attr , modifier , filter_final = self . library . split ( filter_method )
2022-01-31 16:04:09 +00:00
if filter_attr in [ " tmdb_status " , " tmdb_type " , " original_language " ] :
2022-01-28 21:20:09 +00:00
if filter_attr == " tmdb_status " :
check_value = discover_status [ item . status ]
elif filter_attr == " tmdb_type " :
check_value = discover_types [ item . type ]
elif filter_attr == " original_language " :
2022-03-13 19:39:34 +00:00
check_value = item . language_iso
2022-03-11 07:07:37 +00:00
else :
raise Failed
2022-01-28 21:20:09 +00:00
if ( modifier == " .not " and check_value in filter_data ) or ( modifier == " " and check_value not in filter_data ) :
2021-08-07 06:01:21 +00:00
return False
elif filter_attr in [ " first_episode_aired " , " last_episode_aired " ] :
tmdb_date = None
if filter_attr == " first_episode_aired " :
2022-01-21 16:34:19 +00:00
tmdb_date = item . first_air_date
2021-08-07 06:01:21 +00:00
elif filter_attr == " last_episode_aired " :
2022-01-21 16:34:19 +00:00
tmdb_date = item . last_air_date
2021-08-07 06:01:21 +00:00
if util . is_date_filter ( tmdb_date , modifier , filter_data , filter_final , self . current_time ) :
return False
elif modifier in [ " .gt " , " .gte " , " .lt " , " .lte " ] :
attr = None
if filter_attr == " tmdb_vote_count " :
attr = item . vote_count
2022-02-22 19:43:02 +00:00
elif filter_attr == " tmdb_year " :
attr = item . release_date . year if is_movie else item . first_air_date . year
2021-08-07 06:01:21 +00:00
if util . is_number_filter ( attr , modifier , filter_data ) :
return False
2022-03-11 07:07:37 +00:00
elif filter_attr in [ " tmdb_genre " , " tmdb_keyword " , " origin_country " ] :
if filter_attr == " tmdb_genre " :
2022-03-13 19:39:34 +00:00
attrs = item . genres
2022-03-11 07:07:37 +00:00
elif filter_attr == " tmdb_keyword " :
2022-03-13 19:39:34 +00:00
attrs = item . keywords
2022-03-11 07:07:37 +00:00
elif filter_attr == " origin_country " :
2022-03-13 19:39:34 +00:00
attrs = [ c . iso_3166_1 for c in item . countries ]
2022-03-11 07:07:37 +00:00
else :
raise Failed
2022-04-20 16:03:08 +00:00
if modifier == " .regex " :
has_match = False
for reg in filter_data :
for name in attrs :
if re . compile ( reg ) . search ( name ) :
has_match = True
if has_match is False :
return False
elif ( not list ( set ( filter_data ) & set ( attrs ) ) and modifier == " " ) \
2022-02-22 17:20:50 +00:00
or ( list ( set ( filter_data ) & set ( attrs ) ) and modifier == " .not " ) :
return False
elif filter_attr == " tmdb_title " :
if util . is_string_filter ( [ item . title ] , modifier , filter_data ) :
return False
2021-08-07 06:01:21 +00:00
except Failed :
return False
return True
2021-12-29 15:51:22 +00:00
def check_filters ( self , item , display ) :
2021-12-01 16:59:49 +00:00
if ( self . filters or self . tmdb_filters ) and not self . details [ " only_filter_missing " ] :
2022-02-13 16:33:57 +00:00
logger . ghost ( f " Filtering { display } { item . title } " )
2022-04-22 15:24:44 +00:00
item = self . library . reload ( item )
2021-12-29 15:51:22 +00:00
if self . tmdb_filters and isinstance ( item , ( Movie , Show ) ) :
if item . ratingKey not in self . library . movie_rating_key_map and item . ratingKey not in self . library . show_rating_key_map :
logger . warning ( f " Filter Error: No { ' TMDb ' if self . library . is_movie else ' TVDb ' } ID found for { item . title } " )
2021-08-09 15:40:44 +00:00
return False
try :
2021-12-29 15:51:22 +00:00
if item . ratingKey in self . library . movie_rating_key_map :
t_id = self . library . movie_rating_key_map [ item . ratingKey ]
2021-08-09 15:40:44 +00:00
else :
2021-12-29 15:51:22 +00:00
t_id = self . library . show_rating_key_map [ item . ratingKey ]
2021-08-09 15:40:44 +00:00
except Failed as e :
logger . error ( e )
return False
2021-12-29 15:51:22 +00:00
if not self . check_tmdb_filter ( t_id , item . ratingKey in self . library . movie_rating_key_map ) :
2021-08-09 15:40:44 +00:00
return False
2022-04-27 23:14:52 +00:00
if self . library . check_filters ( item , self . filters , self . current_time ) is False :
return False
2021-05-26 14:58:04 +00:00
return True
2021-05-14 19:22:59 +00:00
def run_missing ( self ) :
2021-11-03 14:36:11 +00:00
added_to_radarr = 0
added_to_sonarr = 0
2021-05-14 19:22:59 +00:00
if len ( self . missing_movies ) > 0 :
2021-12-30 00:37:31 +00:00
if self . details [ " show_missing " ] is True :
logger . info ( " " )
2022-02-21 20:56:38 +00:00
logger . separator ( f " Missing Movies from Library: { self . library . name } " , space = False , border = False )
2021-12-30 00:37:31 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
missing_movies_with_names = [ ]
2021-05-14 19:22:59 +00:00
for missing_id in self . missing_movies :
2021-05-12 14:25:48 +00:00
try :
movie = self . config . TMDb . get_movie ( missing_id )
except Failed as e :
logger . error ( e )
continue
2022-01-21 16:34:19 +00:00
current_title = f " { movie . title } ( { movie . release_date . year } ) " if movie . release_date else movie . title
2021-08-16 05:41:04 +00:00
if self . check_tmdb_filter ( missing_id , True , item = movie , check_released = self . details [ " missing_only_released " ] ) :
2021-06-27 23:21:49 +00:00
missing_movies_with_names . append ( ( current_title , missing_id ) )
2021-05-12 14:25:48 +00:00
if self . details [ " show_missing " ] is True :
2021-12-17 14:24:46 +00:00
logger . info ( f " { self . name } { self . Type } | ? | { current_title } (TMDb: { missing_id } ) " )
2021-08-06 17:46:13 +00:00
else :
if self . details [ " show_filtered " ] is True and self . details [ " show_missing " ] is True :
2021-12-17 14:24:46 +00:00
logger . info ( f " { self . name } { self . Type } | X | { current_title } (TMDb: { missing_id } ) " )
2021-05-24 03:38:46 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
logger . info ( f " { len ( missing_movies_with_names ) } Movie { ' s ' if len ( missing_movies_with_names ) > 1 else ' ' } Missing " )
2021-08-12 13:37:30 +00:00
if len ( missing_movies_with_names ) > 0 :
if self . details [ " save_missing " ] is True :
self . library . add_missing ( self . name , missing_movies_with_names , True )
2021-12-30 17:13:01 +00:00
if self . run_again or ( self . library . Radarr and ( self . radarr_details [ " add_missing " ] or " item_radarr_tag " in self . item_details ) ) :
2021-08-12 13:37:30 +00:00
missing_tmdb_ids = [ missing_id for title , missing_id in missing_movies_with_names ]
if self . library . Radarr :
2021-12-30 17:13:01 +00:00
if self . radarr_details [ " add_missing " ] :
2021-08-12 13:37:30 +00:00
try :
2022-01-25 06:30:07 +00:00
added = self . library . Radarr . add_tmdb ( missing_tmdb_ids , * * self . radarr_details )
2022-01-26 14:22:16 +00:00
self . added_to_radarr . extend ( [ { " title " : movie . title , " id " : movie . tmdbId } for movie in added ] )
2022-01-25 06:30:07 +00:00
added_to_radarr + = len ( added )
2021-08-12 13:37:30 +00:00
except Failed as e :
logger . error ( e )
if " item_radarr_tag " in self . item_details :
try :
self . library . Radarr . edit_tags ( missing_tmdb_ids , self . item_details [ " item_radarr_tag " ] , self . item_details [ " apply_tags " ] )
except Failed as e :
logger . error ( e )
if self . run_again :
self . run_again_movies . extend ( missing_tmdb_ids )
2021-05-14 19:22:59 +00:00
if len ( self . missing_shows ) > 0 and self . library . is_show :
2021-12-30 00:37:31 +00:00
if self . details [ " show_missing " ] is True :
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Missing Shows from Library: { self . name } " , space = False , border = False )
2021-12-30 00:37:31 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
missing_shows_with_names = [ ]
2021-05-14 19:22:59 +00:00
for missing_id in self . missing_shows :
2021-05-12 14:25:48 +00:00
try :
2022-05-05 22:05:16 +00:00
title = self . config . TVDb . get_tvdb_obj ( missing_id ) . title
2021-05-12 14:25:48 +00:00
except Failed as e :
logger . error ( e )
continue
2021-08-16 05:41:04 +00:00
if self . check_tmdb_filter ( missing_id , False , check_released = self . details [ " missing_only_released " ] ) :
2022-05-05 22:05:16 +00:00
missing_shows_with_names . append ( ( title , missing_id ) )
2021-05-12 14:25:48 +00:00
if self . details [ " show_missing " ] is True :
2022-05-05 22:05:16 +00:00
logger . info ( f " { self . name } { self . Type } | ? | { title } (TVDb: { missing_id } ) " )
2021-08-06 17:46:13 +00:00
else :
if self . details [ " show_filtered " ] is True and self . details [ " show_missing " ] is True :
2022-05-05 22:05:16 +00:00
logger . info ( f " { self . name } { self . Type } | X | { title } (TVDb: { missing_id } ) " )
2021-05-24 03:38:46 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
logger . info ( f " { len ( missing_shows_with_names ) } Show { ' s ' if len ( missing_shows_with_names ) > 1 else ' ' } Missing " )
2021-08-12 13:37:30 +00:00
if len ( missing_shows_with_names ) > 0 :
if self . details [ " save_missing " ] is True :
self . library . add_missing ( self . name , missing_shows_with_names , False )
2021-12-30 17:13:01 +00:00
if self . run_again or ( self . library . Sonarr and ( self . sonarr_details [ " add_missing " ] or " item_sonarr_tag " in self . item_details ) ) :
2021-08-12 13:37:30 +00:00
missing_tvdb_ids = [ missing_id for title , missing_id in missing_shows_with_names ]
if self . library . Sonarr :
2021-12-30 17:13:01 +00:00
if self . sonarr_details [ " add_missing " ] :
2021-08-12 13:37:30 +00:00
try :
2022-01-25 06:30:07 +00:00
added = self . library . Sonarr . add_tvdb ( missing_tvdb_ids , * * self . sonarr_details )
2022-01-26 14:22:16 +00:00
self . added_to_sonarr . extend ( [ { " title " : show . title , " id " : show . tvdbId } for show in added ] )
2022-01-25 06:30:07 +00:00
added_to_sonarr + = len ( added )
2021-08-12 13:37:30 +00:00
except Failed as e :
logger . error ( e )
if " item_sonarr_tag " in self . item_details :
try :
self . library . Sonarr . edit_tags ( missing_tvdb_ids , self . item_details [ " item_sonarr_tag " ] , self . item_details [ " apply_tags " ] )
except Failed as e :
logger . error ( e )
if self . run_again :
self . run_again_shows . extend ( missing_tvdb_ids )
2021-08-22 15:54:33 +00:00
if len ( self . missing_parts ) > 0 and self . library . is_show and self . details [ " save_missing " ] is True :
for missing in self . missing_parts :
2021-12-17 14:24:46 +00:00
logger . info ( f " { self . name } { self . Type } | X | { missing } " )
2021-11-03 14:36:11 +00:00
return added_to_radarr , added_to_sonarr
2021-08-22 15:54:33 +00:00
2021-09-15 03:16:59 +00:00
def load_collection_items ( self ) :
if self . build_collection and self . obj :
self . items = self . library . get_collection_items ( self . obj , self . smart_label_collection )
elif not self . build_collection :
2021-08-05 18:55:39 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Items Found for { self . name } { self . Type } " , space = False , border = False )
2021-08-05 18:55:39 +00:00
logger . info ( " " )
2021-12-17 14:24:46 +00:00
self . items = self . added_items
2021-09-15 03:16:59 +00:00
if not self . items :
2021-12-17 14:24:46 +00:00
raise Failed ( f " Plex Error: No { self . Type } items found " )
2021-05-26 19:14:51 +00:00
2021-09-15 03:16:59 +00:00
def update_item_details ( self ) :
2021-08-05 18:55:39 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Updating Details of the Items in { self . name } { self . Type } " , space = False , border = False )
2021-08-05 18:55:39 +00:00
logger . info ( " " )
2021-08-22 15:55:37 +00:00
2021-09-15 03:16:59 +00:00
add_tags = self . item_details [ " item_label " ] if " item_label " in self . item_details else None
remove_tags = self . item_details [ " item_label.remove " ] if " item_label.remove " in self . item_details else None
sync_tags = self . item_details [ " item_label.sync " ] if " item_label.sync " in self . item_details else None
2022-01-25 01:46:48 +00:00
if " non_item_remove_label " in self . item_details :
2022-01-26 21:16:46 +00:00
rk_compare = [ item . ratingKey for item in self . items ]
2022-01-25 01:46:48 +00:00
for remove_label in self . item_details [ " non_item_remove_label " ] :
2022-04-18 18:16:39 +00:00
for non_item in self . library . search ( label = remove_label , libtype = self . collection_level ) :
2022-01-25 01:46:48 +00:00
if non_item . ratingKey not in rk_compare :
self . library . edit_tags ( " label " , non_item , remove_tags = [ remove_label ] )
2021-11-24 13:22:17 +00:00
tmdb_paths = [ ]
tvdb_paths = [ ]
2021-09-15 03:16:59 +00:00
for item in self . items :
2021-05-26 19:14:51 +00:00
self . library . edit_tags ( " label " , item , add_tags = add_tags , remove_tags = remove_tags , sync_tags = sync_tags )
2021-11-24 13:22:17 +00:00
path = os . path . dirname ( str ( item . locations [ 0 ] ) ) if self . library . is_movie else str ( item . locations [ 0 ] )
2021-11-29 14:11:23 +00:00
if self . library . Radarr and item . ratingKey in self . library . movie_rating_key_map :
2021-12-09 15:23:51 +00:00
path = path . replace ( self . library . Radarr . plex_path , self . library . Radarr . radarr_path )
path = path [ : - 1 ] if path . endswith ( ( ' / ' , ' \\ ' ) ) else path
tmdb_paths . append ( ( self . library . movie_rating_key_map [ item . ratingKey ] , path ) )
2021-11-29 14:11:23 +00:00
if self . library . Sonarr and item . ratingKey in self . library . show_rating_key_map :
2021-12-09 15:23:51 +00:00
path = path . replace ( self . library . Sonarr . plex_path , self . library . Sonarr . sonarr_path )
path = path [ : - 1 ] if path . endswith ( ( ' / ' , ' \\ ' ) ) else path
tvdb_paths . append ( ( self . library . show_rating_key_map [ item . ratingKey ] , path ) )
2022-04-20 21:30:31 +00:00
if any ( [ mn in plex . item_advance_keys for mn in self . item_details ] ) and hasattr ( item , " preferences " ) :
advance_edits = { }
2022-01-24 22:36:40 +00:00
prefs = [ p . id for p in item . preferences ( ) ]
for method_name , method_data in self . item_details . items ( ) :
if method_name in plex . item_advance_keys :
key , options = plex . item_advance_keys [ method_name ]
if key in prefs and getattr ( item , key ) != options [ method_data ] :
advance_edits [ key ] = options [ method_data ]
2022-04-20 21:30:31 +00:00
self . library . edit_item ( item , item . title , self . collection_level . capitalize ( ) , advance_edits , advanced = True )
2021-11-23 19:45:41 +00:00
2021-12-09 06:37:11 +00:00
if " item_tmdb_season_titles " in self . item_details and item . ratingKey in self . library . show_rating_key_map :
try :
tmdb_id = self . config . Convert . tvdb_to_tmdb ( self . library . show_rating_key_map [ item . ratingKey ] )
2022-01-21 16:34:19 +00:00
names = { s . season_number : s . name for s in self . config . TMDb . get_show ( tmdb_id ) . seasons }
2021-12-09 06:37:11 +00:00
for season in self . library . query ( item . seasons ) :
2022-03-25 20:13:51 +00:00
if season . index in names and season . title != names [ season . index ] :
season . editTitle ( names [ season . index ] )
2021-12-09 06:37:11 +00:00
except Failed as e :
logger . error ( e )
2021-11-23 19:45:41 +00:00
# Locking should come before refreshing since refreshing can change metadata (i.e. if specified to both lock
2021-12-29 15:51:22 +00:00
# background/poster and also refreshing, assume that the item background/poster should be kept)
2021-11-21 00:15:45 +00:00
if " item_lock_background " in self . item_details :
2022-01-26 14:23:19 +00:00
self . library . query ( item . lockArt if self . item_details [ " item_lock_background " ] else item . unlockArt )
2021-11-20 23:50:17 +00:00
if " item_lock_poster " in self . item_details :
2022-01-26 14:23:19 +00:00
self . library . query ( item . lockPoster if self . item_details [ " item_lock_poster " ] else item . unlockPoster )
2021-11-23 19:45:41 +00:00
if " item_lock_title " in self . item_details :
2022-01-26 14:23:19 +00:00
self . library . edit_query ( item , { " title.locked " : 1 if self . item_details [ " item_lock_title " ] else 0 } )
2021-10-20 20:09:51 +00:00
if " item_refresh " in self . item_details :
2022-01-11 19:37:48 +00:00
delay = self . item_details [ " item_refresh_delay " ] if " item_refresh_delay " in self . item_details else self . library . item_refresh_delay
if delay > 0 :
time . sleep ( delay )
2021-12-09 06:37:11 +00:00
self . library . query ( item . refresh )
2021-05-26 19:14:51 +00:00
2021-11-29 14:11:23 +00:00
if self . library . Radarr and tmdb_paths :
2021-08-16 05:41:04 +00:00
if " item_radarr_tag " in self . item_details :
2021-11-29 14:11:23 +00:00
self . library . Radarr . edit_tags ( [ t [ 0 ] if isinstance ( t , tuple ) else t for t in tmdb_paths ] , self . item_details [ " item_radarr_tag " ] , self . item_details [ " apply_tags " ] )
2021-08-16 05:41:04 +00:00
if self . radarr_details [ " add_existing " ] :
2022-01-25 06:30:07 +00:00
added = self . library . Radarr . add_tmdb ( tmdb_paths , * * self . radarr_details )
2022-01-26 14:22:16 +00:00
self . added_to_radarr . extend ( [ { " title " : movie . title , " id " : movie . tmdbId } for movie in added ] )
2021-07-03 04:18:09 +00:00
2021-11-29 14:11:23 +00:00
if self . library . Sonarr and tvdb_paths :
2021-08-16 05:41:04 +00:00
if " item_sonarr_tag " in self . item_details :
2021-11-29 14:11:23 +00:00
self . library . Sonarr . edit_tags ( [ t [ 0 ] if isinstance ( t , tuple ) else t for t in tvdb_paths ] , self . item_details [ " item_sonarr_tag " ] , self . item_details [ " apply_tags " ] )
2021-08-16 05:41:04 +00:00
if self . sonarr_details [ " add_existing " ] :
2022-01-25 06:30:07 +00:00
added = self . library . Sonarr . add_tvdb ( tvdb_paths , * * self . sonarr_details )
2022-01-26 14:22:16 +00:00
self . added_to_sonarr . extend ( [ { " title " : show . title , " id " : show . tvdbId } for show in added ] )
2021-07-03 04:18:09 +00:00
2021-09-15 03:16:59 +00:00
def load_collection ( self ) :
2022-04-18 18:16:39 +00:00
if self . obj is None and self . smart_url :
2021-05-12 14:25:48 +00:00
self . library . create_smart_collection ( self . name , self . smart_type_key , self . smart_url )
2022-04-18 18:16:39 +00:00
elif self . obj is None and self . blank_collection :
2022-01-27 21:24:50 +00:00
self . library . create_blank_collection ( self . name )
2021-05-17 14:35:47 +00:00
elif self . smart_label_collection :
2021-05-16 22:06:51 +00:00
try :
2022-02-13 18:28:53 +00:00
if not self . library . smart_label_check ( self . name ) :
raise Failed
smart_type , _ , self . smart_url = self . build_filter ( " smart_label " , self . smart_label , default_sort = " random " )
2021-05-17 14:35:47 +00:00
if not self . obj :
self . library . create_smart_collection ( self . name , smart_type , self . smart_url )
2021-05-16 22:06:51 +00:00
except Failed :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: Label: { self . name } was not added to any items in the Library " )
2021-12-17 14:24:46 +00:00
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name )
2021-10-04 17:51:32 +00:00
if not self . exists :
self . created = True
2021-05-12 14:25:48 +00:00
2021-09-15 03:16:59 +00:00
def update_details ( self ) :
2022-05-04 16:27:17 +00:00
updated_details = [ ]
2021-09-15 03:16:59 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Updating Details of { self . name } { self . Type } " , space = False , border = False )
2021-09-15 03:16:59 +00:00
logger . info ( " " )
2021-05-12 14:25:48 +00:00
if self . smart_url and self . smart_url != self . library . smart_filter ( self . obj ) :
self . library . update_smart_collection ( self . obj , self . smart_url )
2021-05-02 04:22:48 +00:00
logger . info ( f " Detail: Smart Filter updated to { self . smart_url } " )
2022-05-04 16:27:17 +00:00
updated_details . append ( " Smart Filter " )
if " summary " in self . summaries : summary = ( " summary " , self . summaries [ " summary " ] )
elif " tmdb_description " in self . summaries : summary = ( " tmdb_description " , self . summaries [ " tmdb_description " ] )
elif " letterboxd_description " in self . summaries : summary = ( " letterboxd_description " , self . summaries [ " letterboxd_description " ] )
elif " tmdb_summary " in self . summaries : summary = ( " tmdb_summary " , self . summaries [ " tmdb_summary " ] )
elif " tvdb_summary " in self . summaries : summary = ( " tvdb_summary " , self . summaries [ " tvdb_summary " ] )
elif " tmdb_biography " in self . summaries : summary = ( " tmdb_biography " , self . summaries [ " tmdb_biography " ] )
elif " tmdb_person " in self . summaries : summary = ( " tmdb_person " , self . summaries [ " tmdb_person " ] )
elif " tmdb_collection_details " in self . summaries : summary = ( " tmdb_collection_details " , self . summaries [ " tmdb_collection_details " ] )
elif " trakt_list_details " in self . summaries : summary = ( " trakt_list_details " , self . summaries [ " trakt_list_details " ] )
elif " tmdb_list_details " in self . summaries : summary = ( " tmdb_list_details " , self . summaries [ " tmdb_list_details " ] )
elif " letterboxd_list_details " in self . summaries : summary = ( " letterboxd_list_details " , self . summaries [ " letterboxd_list_details " ] )
elif " icheckmovies_list_details " in self . summaries : summary = ( " icheckmovies_list_details " , self . summaries [ " icheckmovies_list_details " ] )
elif " tmdb_actor_details " in self . summaries : summary = ( " tmdb_actor_details " , self . summaries [ " tmdb_actor_details " ] )
elif " tmdb_crew_details " in self . summaries : summary = ( " tmdb_crew_details " , self . summaries [ " tmdb_crew_details " ] )
elif " tmdb_director_details " in self . summaries : summary = ( " tmdb_director_details " , self . summaries [ " tmdb_director_details " ] )
elif " tmdb_producer_details " in self . summaries : summary = ( " tmdb_producer_details " , self . summaries [ " tmdb_producer_details " ] )
elif " tmdb_writer_details " in self . summaries : summary = ( " tmdb_writer_details " , self . summaries [ " tmdb_writer_details " ] )
elif " tmdb_movie_details " in self . summaries : summary = ( " tmdb_movie_details " , self . summaries [ " tmdb_movie_details " ] )
elif " tvdb_movie_details " in self . summaries : summary = ( " tvdb_movie_details " , self . summaries [ " tvdb_movie_details " ] )
elif " tvdb_show_details " in self . summaries : summary = ( " tvdb_show_details " , self . summaries [ " tvdb_show_details " ] )
elif " tmdb_show_details " in self . summaries : summary = ( " tmdb_show_details " , self . summaries [ " tmdb_show_details " ] )
2021-02-28 23:06:04 +00:00
else : summary = None
2021-03-01 04:16:08 +00:00
2022-03-25 20:13:51 +00:00
if self . playlist :
2022-05-04 16:27:17 +00:00
if summary and str ( summary [ 1 ] ) != str ( self . obj . summary ) :
2022-03-25 20:13:51 +00:00
try :
2022-05-04 16:27:17 +00:00
self . obj . edit ( summary = str ( summary [ 1 ] ) )
logger . info ( f " Summary ( { summary [ 0 ] } ) | { summary [ 1 ] : <25 } " )
2022-03-25 20:13:51 +00:00
logger . info ( " Details: have been updated " )
2022-05-04 16:27:17 +00:00
updated_details . append ( " Metadata " )
2022-03-25 20:13:51 +00:00
except NotFound :
logger . error ( " Details: Failed to Update Please delete the collection and run again " )
logger . info ( " " )
else :
self . obj . batchEdits ( )
batch_display = " Collection Metadata Edits "
2022-05-04 16:27:17 +00:00
if summary and str ( summary [ 1 ] ) != str ( self . obj . summary ) :
self . obj . editSummary ( summary [ 1 ] )
batch_display + = f " \n Summary ( { summary [ 0 ] } ) | { summary [ 1 ] : <25 } "
2022-03-25 20:13:51 +00:00
if " sort_title " in self . details and str ( self . details [ " sort_title " ] ) != str ( self . obj . titleSort ) :
self . obj . editSortTitle ( self . details [ " sort_title " ] )
batch_display + = f " \n Sort Title | { self . details [ ' sort_title ' ] } "
if " content_rating " in self . details and str ( self . details [ " content_rating " ] ) != str ( self . obj . contentRating ) :
self . obj . editContentRating ( self . details [ " content_rating " ] )
batch_display + = f " \n Content Rating | { self . details [ ' content_rating ' ] } "
add_tags = self . details [ " label " ] if " label " in self . details else None
remove_tags = self . details [ " label.remove " ] if " label.remove " in self . details else None
sync_tags = self . details [ " label.sync " ] if " label.sync " in self . details else None
2022-05-06 13:32:37 +00:00
tag_results = self . library . edit_tags ( ' label ' , self . obj , add_tags = add_tags , remove_tags = remove_tags , sync_tags = sync_tags , do_print = False ) [ 28 : ]
if tag_results :
batch_display + = f " \n { tag_results } "
2022-03-25 20:13:51 +00:00
logger . info ( batch_display )
if len ( batch_display ) > 25 :
try :
self . obj . saveEdits ( )
logger . info ( " Details: have been updated " )
2022-05-04 16:27:17 +00:00
updated_details . append ( " Metadata " )
2022-03-25 20:13:51 +00:00
except NotFound :
logger . error ( " Details: Failed to Update Please delete the collection and run again " )
logger . info ( " " )
2021-03-01 04:16:08 +00:00
2022-05-04 16:27:17 +00:00
advance_update = False
2022-03-25 20:13:51 +00:00
if " collection_mode " in self . details :
2022-05-04 16:27:17 +00:00
if int ( self . obj . collectionMode ) not in plex . collection_mode_keys \
or plex . collection_mode_keys [ int ( self . obj . collectionMode ) ] != self . details [ " collection_mode " ] :
self . library . collection_mode_query ( self . obj , self . details [ " collection_mode " ] )
logger . info ( f " Collection Mode | { self . details [ ' collection_mode ' ] } " )
advance_update = True
2021-02-20 05:41:45 +00:00
2022-04-08 18:29:38 +00:00
if " collection_filtering " in self . details :
self . library . edit_query ( self . obj , { " collectionFilterBasedOnUser " : 0 if self . details [ " collection_filtering " ] == " admin " else 1 } , advanced = True )
2022-05-04 16:27:17 +00:00
advance_update = True
2022-04-08 18:29:38 +00:00
2022-03-25 20:13:51 +00:00
if " collection_order " in self . details :
2022-05-04 16:27:17 +00:00
if int ( self . obj . collectionSort ) not in plex . collection_order_keys \
2022-03-25 20:13:51 +00:00
or plex . collection_order_keys [ int ( self . obj . collectionSort ) ] != self . details [ " collection_order " ] :
self . library . collection_order_query ( self . obj , self . details [ " collection_order " ] )
logger . info ( f " Collection Order | { self . details [ ' collection_order ' ] } " )
2022-05-04 16:27:17 +00:00
advance_update = True
2021-06-30 15:07:02 +00:00
2022-03-25 20:13:51 +00:00
if " visible_library " in self . details or " visible_home " in self . details or " visible_shared " in self . details :
visibility = self . library . collection_visibility ( self . obj )
visible_library = None
visible_home = None
visible_shared = None
2021-06-30 15:07:02 +00:00
2022-03-25 20:13:51 +00:00
if " visible_library " in self . details and self . details [ " visible_library " ] != visibility [ " library " ] :
visible_library = self . details [ " visible_library " ]
2021-06-30 15:07:02 +00:00
2022-03-25 20:13:51 +00:00
if " visible_home " in self . details and self . details [ " visible_home " ] != visibility [ " home " ] :
visible_home = self . details [ " visible_home " ]
2021-06-30 15:07:02 +00:00
2022-03-25 20:13:51 +00:00
if " visible_shared " in self . details and self . details [ " visible_shared " ] != visibility [ " shared " ] :
visible_shared = self . details [ " visible_shared " ]
2021-06-30 15:07:02 +00:00
2022-03-25 20:13:51 +00:00
if visible_library is not None or visible_home is not None or visible_shared is not None :
self . library . collection_visibility_update ( self . obj , visibility = visibility , library = visible_library , home = visible_home , shared = visible_shared )
2022-05-04 16:27:17 +00:00
advance_update = True
2022-03-25 20:13:51 +00:00
logger . info ( " Collection Visibility Updated " )
2021-05-20 19:26:56 +00:00
2022-05-04 16:27:17 +00:00
if advance_update and " Metadata " not in updated_details :
updated_details . append ( " Metadata " )
2022-01-06 20:23:50 +00:00
poster_image = None
background_image = None
asset_location = None
2022-04-21 05:35:07 +00:00
if self . asset_directory :
2021-02-20 05:41:45 +00:00
name_mapping = self . name
if " name_mapping " in self . details :
2021-02-28 23:06:04 +00:00
if self . details [ " name_mapping " ] : name_mapping = self . details [ " name_mapping " ]
2021-12-13 07:30:19 +00:00
else : logger . error ( f " { self . Type } Error: name_mapping attribute is blank " )
2022-04-25 22:55:59 +00:00
try :
poster_image , background_image , asset_location , _ = self . library . find_item_assets ( name_mapping , asset_directory = self . asset_directory )
if poster_image :
self . posters [ " asset_directory " ] = poster_image
if background_image :
self . backgrounds [ " asset_directory " ] = background_image
except Failed as e :
if self . library . asset_folders and ( self . library . show_missing_assets or self . library . create_asset_folders ) :
2022-04-27 15:11:10 +00:00
logger . warning ( e )
2021-02-21 08:13:07 +00:00
2021-12-13 19:43:36 +00:00
self . collection_poster = None
2021-07-02 04:04:24 +00:00
if len ( self . posters ) > 0 :
logger . debug ( f " { len ( self . posters ) } posters found: " )
2021-02-28 23:06:04 +00:00
for p in self . posters :
2021-07-02 04:04:24 +00:00
logger . debug ( f " Method: { p } Poster: { self . posters [ p ] } " )
2021-02-28 23:06:04 +00:00
2022-01-06 20:23:50 +00:00
if " url_poster " in self . posters :
if self . library . download_url_assets and asset_location :
if poster_image :
self . collection_poster = self . posters [ " asset_directory " ]
else :
response = self . config . get ( self . posters [ " url_poster " ] , headers = util . header ( ) )
if response . status_code > = 400 or " Content-Type " not in response . headers or response . headers [ " Content-Type " ] not in [ " image/png " , " image/jpeg " ] :
logger . error ( f " Image Error: Failed to parse Image at { self . posters [ ' url_poster ' ] } " )
else :
new_image = os . path . join ( asset_location , f " poster { ' .png ' if response . headers [ ' Content-Type ' ] == ' image/png ' else ' .jpg ' } " )
with open ( new_image , " wb " ) as handler :
handler . write ( response . content )
self . collection_poster = ImageData ( " asset_directory " , new_image , prefix = f " { self . obj . title } ' s " , is_url = False )
if not self . collection_poster :
self . collection_poster = ImageData ( " url_poster " , self . posters [ " url_poster " ] )
2021-12-13 19:43:36 +00:00
elif " file_poster " in self . posters : self . collection_poster = ImageData ( " file_poster " , self . posters [ " file_poster " ] , is_url = False )
elif " tmdb_poster " in self . posters : self . collection_poster = ImageData ( " tmdb_poster " , self . posters [ " tmdb_poster " ] )
elif " tmdb_profile " in self . posters : self . collection_poster = ImageData ( " tmdb_poster " , self . posters [ " tmdb_profile " ] )
elif " tvdb_poster " in self . posters : self . collection_poster = ImageData ( " tvdb_poster " , self . posters [ " tvdb_poster " ] )
elif " asset_directory " in self . posters : self . collection_poster = self . posters [ " asset_directory " ]
elif " tmdb_person " in self . posters : self . collection_poster = ImageData ( " tmdb_person " , self . posters [ " tmdb_person " ] )
elif " tmdb_collection_details " in self . posters : self . collection_poster = ImageData ( " tmdb_collection_details " , self . posters [ " tmdb_collection_details " ] )
elif " tmdb_actor_details " in self . posters : self . collection_poster = ImageData ( " tmdb_actor_details " , self . posters [ " tmdb_actor_details " ] )
elif " tmdb_crew_details " in self . posters : self . collection_poster = ImageData ( " tmdb_crew_details " , self . posters [ " tmdb_crew_details " ] )
elif " tmdb_director_details " in self . posters : self . collection_poster = ImageData ( " tmdb_director_details " , self . posters [ " tmdb_director_details " ] )
elif " tmdb_producer_details " in self . posters : self . collection_poster = ImageData ( " tmdb_producer_details " , self . posters [ " tmdb_producer_details " ] )
elif " tmdb_writer_details " in self . posters : self . collection_poster = ImageData ( " tmdb_writer_details " , self . posters [ " tmdb_writer_details " ] )
elif " tmdb_movie_details " in self . posters : self . collection_poster = ImageData ( " tmdb_movie_details " , self . posters [ " tmdb_movie_details " ] )
elif " tvdb_movie_details " in self . posters : self . collection_poster = ImageData ( " tvdb_movie_details " , self . posters [ " tvdb_movie_details " ] )
elif " tvdb_show_details " in self . posters : self . collection_poster = ImageData ( " tvdb_show_details " , self . posters [ " tvdb_show_details " ] )
elif " tmdb_show_details " in self . posters : self . collection_poster = ImageData ( " tmdb_show_details " , self . posters [ " tmdb_show_details " ] )
2021-07-01 18:21:14 +00:00
else :
2021-12-17 14:24:46 +00:00
logger . info ( f " No poster { self . type } detail or asset folder found " )
2021-07-01 18:21:14 +00:00
2021-12-13 19:43:36 +00:00
self . collection_background = None
2021-07-02 04:04:24 +00:00
if len ( self . backgrounds ) > 0 :
logger . debug ( f " { len ( self . backgrounds ) } backgrounds found: " )
2021-02-28 23:06:04 +00:00
for b in self . backgrounds :
2021-07-02 04:04:24 +00:00
logger . debug ( f " Method: { b } Background: { self . backgrounds [ b ] } " )
2021-02-28 23:06:04 +00:00
2022-01-06 20:23:50 +00:00
if " url_background " in self . backgrounds :
if self . library . download_url_assets and asset_location :
if background_image :
self . collection_background = self . backgrounds [ " asset_directory " ]
else :
response = self . config . get ( self . backgrounds [ " url_background " ] , headers = util . header ( ) )
if response . status_code > = 400 or " Content-Type " not in response . headers or response . headers [ " Content-Type " ] not in [ " image/png " , " image/jpeg " ] :
logger . error ( f " Image Error: Failed to parse Image at { self . backgrounds [ ' url_background ' ] } " )
else :
new_image = os . path . join ( asset_location , f " background { ' .png ' if response . headers [ ' Content-Type ' ] == ' image/png ' else ' .jpg ' } " )
with open ( new_image , " wb " ) as handler :
handler . write ( response . content )
self . collection_background = ImageData ( " asset_directory " , new_image , prefix = f " { self . obj . title } ' s " , is_url = False , is_poster = False )
if not self . collection_background :
self . collection_background = ImageData ( " url_background " , self . backgrounds [ " url_background " ] , is_poster = False )
2021-12-13 19:43:36 +00:00
elif " file_background " in self . backgrounds : self . collection_background = ImageData ( " file_background " , self . backgrounds [ " file_background " ] , is_poster = False , is_url = False )
elif " tmdb_background " in self . backgrounds : self . collection_background = ImageData ( " tmdb_background " , self . backgrounds [ " tmdb_background " ] , is_poster = False )
elif " tvdb_background " in self . backgrounds : self . collection_background = ImageData ( " tvdb_background " , self . backgrounds [ " tvdb_background " ] , is_poster = False )
elif " asset_directory " in self . backgrounds : self . collection_background = self . backgrounds [ " asset_directory " ]
elif " tmdb_collection_details " in self . backgrounds : self . collection_background = ImageData ( " tmdb_collection_details " , self . backgrounds [ " tmdb_collection_details " ] , is_poster = False )
elif " tmdb_movie_details " in self . backgrounds : self . collection_background = ImageData ( " tmdb_movie_details " , self . backgrounds [ " tmdb_movie_details " ] , is_poster = False )
elif " tvdb_movie_details " in self . backgrounds : self . collection_background = ImageData ( " tvdb_movie_details " , self . backgrounds [ " tvdb_movie_details " ] , is_poster = False )
elif " tvdb_show_details " in self . backgrounds : self . collection_background = ImageData ( " tvdb_show_details " , self . backgrounds [ " tvdb_show_details " ] , is_poster = False )
elif " tmdb_show_details " in self . backgrounds : self . collection_background = ImageData ( " tmdb_show_details " , self . backgrounds [ " tmdb_show_details " ] , is_poster = False )
2021-07-01 18:21:14 +00:00
else :
2021-12-17 14:24:46 +00:00
logger . info ( f " No background { self . type } detail or asset folder found " )
2021-03-01 20:59:10 +00:00
2021-12-13 19:43:36 +00:00
if self . collection_poster or self . collection_background :
2022-05-04 16:27:17 +00:00
pu , bu = self . library . upload_images ( self . obj , poster = self . collection_poster , background = self . collection_background )
if pu or bu :
updated_details . append ( " Image " )
2021-06-12 15:29:17 +00:00
2022-05-04 16:27:17 +00:00
if self . url_theme : # TODO: cache theme path to not constantly upload
2022-03-19 18:29:03 +00:00
self . library . upload_theme ( self . obj , url = self . url_theme )
2022-03-06 06:09:03 +00:00
elif self . file_theme :
2022-03-19 18:29:03 +00:00
self . library . upload_theme ( self . obj , filepath = self . file_theme )
2022-05-04 16:27:17 +00:00
return updated_details
2022-03-06 06:09:03 +00:00
2021-07-29 13:36:30 +00:00
def sort_collection ( self ) :
2021-09-15 03:16:59 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Sorting { self . name } { self . Type } " , space = False , border = False )
2021-09-15 03:16:59 +00:00
logger . info ( " " )
2021-12-21 18:48:27 +00:00
if self . custom_sort is True :
2021-12-17 14:24:46 +00:00
items = self . added_items
2021-12-21 18:48:27 +00:00
else :
2022-03-27 20:59:14 +00:00
plex_search = { " sort_by " : self . custom_sort }
if self . collection_level in [ " season " , " episode " ] :
plex_search [ " type " ] = f " { self . collection_level } s "
plex_search [ " any " ] = { f " { self . collection_level } _collection " : self . name }
else :
plex_search [ " any " ] = { " collection " : self . name }
search_data = self . build_filter ( " plex_search " , plex_search )
2021-12-21 18:48:27 +00:00
items = self . library . get_filter_items ( search_data [ 2 ] )
2021-07-29 13:36:30 +00:00
previous = None
2022-03-28 19:30:46 +00:00
for i , item in enumerate ( items , 0 ) :
2022-03-31 06:23:48 +00:00
if len ( self . items ) < = i or item . ratingKey != self . items [ i ] . ratingKey :
2022-03-28 19:30:46 +00:00
text = f " after { util . item_title ( previous ) } " if previous else " to the beginning "
logger . info ( f " Moving { util . item_title ( item ) } { text } " )
self . library . moveItem ( self . obj , item , previous )
2021-12-17 14:24:46 +00:00
previous = item
2022-05-09 06:58:09 +00:00
def sync_trakt_list ( self ) :
logger . info ( " " )
logger . separator ( f " Syncing { self . name } { self . Type } to Trakt List { self . sync_to_trakt_list } " , space = False , border = False )
logger . info ( " " )
if self . obj :
self . obj . reload ( )
self . load_collection_items ( )
current_ids = [ ]
for item in self . items :
for pl_library in self . libraries :
new_id = None
if isinstance ( item , Movie ) and item . ratingKey in pl_library . movie_rating_key_map :
new_id = ( pl_library . movie_rating_key_map [ item . ratingKey ] , " tmdb " )
elif isinstance ( item , Show ) and item . ratingKey in pl_library . show_rating_key_map :
new_id = ( pl_library . show_rating_key_map [ item . ratingKey ] , " tvdb " )
elif isinstance ( item , Season ) and item . parentRatingKey in pl_library . show_rating_key_map :
new_id = ( f " { pl_library . show_rating_key_map [ item . parentRatingKey ] } _ { item . seasonNumber } " , " tvdb_season " )
elif isinstance ( item , Episode ) and item . grandparentRatingKey in pl_library . show_rating_key_map :
new_id = ( f " { pl_library . show_rating_key_map [ item . grandparentRatingKey ] } _ { item . seasonNumber } _ { item . episodeNumber } " , " tvdb_episode " )
if new_id :
current_ids . append ( new_id )
break
2022-05-09 15:22:41 +00:00
if self . sync_missing_to_trakt_list :
current_ids . extend ( [ ( mm , " tmdb " ) for mm in self . missing_movies ] )
current_ids . extend ( [ ( ms , " tvdb " ) for ms in self . missing_shows ] )
2022-05-09 06:58:09 +00:00
self . config . Trakt . sync_list ( self . sync_to_trakt_list , current_ids )
2021-12-26 15:48:52 +00:00
def delete ( self ) :
output = " "
2021-12-23 17:02:07 +00:00
if self . obj :
self . library . query ( self . obj . delete )
2021-12-26 15:48:52 +00:00
output = f " { self . Type } { self . obj . title } deleted "
if self . playlist :
if self . valid_users :
for user in self . valid_users :
try :
2022-05-07 15:37:36 +00:00
self . library . delete_user_playlist ( self . obj . title , user )
2021-12-26 15:48:52 +00:00
output + = f " \n Playlist { self . obj . title } deleted on User { user } "
except NotFound :
output + = f " \n Playlist { self . obj . title } not found on User { user } "
return output
def sync_playlist ( self ) :
if self . obj and self . valid_users :
2021-12-23 17:02:07 +00:00
logger . info ( " " )
2022-02-13 16:33:57 +00:00
logger . separator ( f " Syncing Playlist to Users " , space = False , border = False )
2021-12-23 17:02:07 +00:00
logger . info ( " " )
2021-12-26 15:48:52 +00:00
for user in self . valid_users :
2021-12-23 17:02:07 +00:00
try :
2022-05-07 15:37:36 +00:00
self . library . delete_user_playlist ( self . obj . title , user )
2021-12-23 17:02:07 +00:00
except NotFound :
pass
self . obj . copyToUser ( user )
logger . info ( f " Playlist: { self . name } synced to { user } " )
2021-12-17 14:24:46 +00:00
def send_notifications ( self , playlist = False ) :
2021-12-26 15:48:52 +00:00
if self . obj and self . details [ " changes_webhooks " ] and \
2021-12-07 06:29:41 +00:00
( self . created or len ( self . notification_additions ) > 0 or len ( self . notification_removals ) > 0 ) :
2021-10-04 17:51:32 +00:00
self . obj . reload ( )
2021-11-28 07:14:35 +00:00
try :
self . library . Webhooks . collection_hooks (
2021-12-26 15:48:52 +00:00
self . details [ " changes_webhooks " ] ,
2021-11-28 07:14:35 +00:00
self . obj ,
2021-12-17 23:18:34 +00:00
poster_url = self . collection_poster . location if self . collection_poster and self . collection_poster . is_url else None ,
background_url = self . collection_background . location if self . collection_background and self . collection_background . is_url else None ,
2021-11-28 07:14:35 +00:00
created = self . created ,
2021-12-09 04:57:00 +00:00
deleted = self . deleted ,
2021-11-28 07:14:35 +00:00
additions = self . notification_additions ,
2021-12-17 14:24:46 +00:00
removals = self . notification_removals ,
2022-01-25 06:30:07 +00:00
radarr = self . added_to_radarr ,
sonarr = self . added_to_sonarr ,
2021-12-17 14:24:46 +00:00
playlist = playlist
2021-11-28 07:14:35 +00:00
)
except Failed as e :
2022-02-13 16:33:57 +00:00
logger . stacktrace ( )
2021-11-28 07:14:35 +00:00
logger . error ( f " Webhooks Error: { e } " )
2021-10-04 17:51:32 +00:00
2021-05-26 14:45:33 +00:00
def run_collections_again ( self ) :
2021-05-12 14:25:48 +00:00
self . obj = self . library . get_collection ( self . name )
name , collection_items = self . library . get_collection_name_and_items ( self . obj , self . smart_label_collection )
2021-10-04 17:51:32 +00:00
self . created = False
2021-04-22 03:13:40 +00:00
rating_keys = [ ]
2021-12-30 21:29:16 +00:00
amount_added = 0
2021-11-03 14:36:11 +00:00
self . notification_additions = [ ]
2022-01-25 06:30:07 +00:00
self . added_to_radarr = [ ]
self . added_to_sonarr = [ ]
2021-05-14 19:22:59 +00:00
for mm in self . run_again_movies :
2021-05-26 14:45:33 +00:00
if mm in self . library . movie_map :
rating_keys . extend ( self . library . movie_map [ mm ] )
2021-03-05 16:04:28 +00:00
if self . library . is_show :
2021-05-14 19:22:59 +00:00
for sm in self . run_again_shows :
2021-05-26 14:45:33 +00:00
if sm in self . library . show_map :
rating_keys . extend ( self . library . show_map [ sm ] )
2021-03-01 20:59:10 +00:00
if len ( rating_keys ) > 0 :
for rating_key in rating_keys :
try :
2021-03-05 16:04:28 +00:00
current = self . library . fetchItem ( int ( rating_key ) )
2021-03-01 20:59:10 +00:00
except ( BadRequest , NotFound ) :
logger . error ( f " Plex Error: Item { rating_key } not found " )
continue
if current in collection_items :
2021-12-17 14:24:46 +00:00
logger . info ( f " { name } { self . Type } | = | { util . item_title ( current ) } " )
2021-03-01 20:59:10 +00:00
else :
2021-09-30 20:55:29 +00:00
self . library . alter_collection ( current , name , smart_label_collection = self . smart_label_collection )
2021-12-30 21:29:16 +00:00
amount_added + = 1
2021-12-17 14:24:46 +00:00
logger . info ( f " { name } { self . Type } | + | { util . item_title ( current ) } " )
2021-10-04 17:51:32 +00:00
if self . library . is_movie and current . ratingKey in self . library . movie_rating_key_map :
add_id = self . library . movie_rating_key_map [ current . ratingKey ]
elif self . library . is_show and current . ratingKey in self . library . show_rating_key_map :
add_id = self . library . show_rating_key_map [ current . ratingKey ]
else :
add_id = None
2021-12-17 23:18:34 +00:00
self . notification_additions . append ( util . item_set ( current , add_id ) )
2021-10-04 17:51:32 +00:00
self . send_notifications ( )
2021-08-22 15:54:33 +00:00
logger . info ( f " { len ( rating_keys ) } { self . collection_level . capitalize ( ) } { ' s ' if len ( rating_keys ) > 1 else ' ' } Processed " )
2021-03-01 20:59:10 +00:00
2021-05-14 19:22:59 +00:00
if len ( self . run_again_movies ) > 0 :
2021-03-01 20:59:10 +00:00
logger . info ( " " )
2021-05-14 19:22:59 +00:00
for missing_id in self . run_again_movies :
2021-05-26 14:45:33 +00:00
if missing_id not in self . library . movie_map :
2021-03-01 20:59:10 +00:00
try :
movie = self . config . TMDb . get_movie ( missing_id )
except Failed as e :
logger . error ( e )
continue
if self . details [ " show_missing " ] is True :
2022-01-21 16:34:19 +00:00
current_title = f " { movie . title } ( { movie . release_date . year } ) " if movie . release_date else movie . title
2021-12-17 14:24:46 +00:00
logger . info ( f " { name } { self . Type } | ? | { current_title } (TMDb: { missing_id } ) " )
2021-03-01 20:59:10 +00:00
logger . info ( " " )
2021-05-14 19:22:59 +00:00
logger . info ( f " { len ( self . run_again_movies ) } Movie { ' s ' if len ( self . run_again_movies ) > 1 else ' ' } Missing " )
2021-03-01 20:59:10 +00:00
2021-05-14 19:22:59 +00:00
if len ( self . run_again_shows ) > 0 and self . library . is_show :
2021-03-01 20:59:10 +00:00
logger . info ( " " )
2021-05-14 19:22:59 +00:00
for missing_id in self . run_again_shows :
2021-05-26 14:45:33 +00:00
if missing_id not in self . library . show_map :
2021-03-01 20:59:10 +00:00
try :
2022-05-05 22:05:16 +00:00
title = self . config . TVDb . get_tvdb_obj ( missing_id ) . title
2021-03-01 20:59:10 +00:00
except Failed as e :
logger . error ( e )
continue
if self . details [ " show_missing " ] is True :
2021-12-17 14:24:46 +00:00
logger . info ( f " { name } { self . Type } | ? | { title } (TVDb: { missing_id } ) " )
2021-05-14 19:22:59 +00:00
logger . info ( f " { len ( self . run_again_shows ) } Show { ' s ' if len ( self . run_again_shows ) > 1 else ' ' } Missing " )
2021-12-30 21:29:16 +00:00
2022-01-25 07:45:31 +00:00
return amount_added