2022-02-13 16:33:57 +00:00
import os , re , time
2023-02-05 19:10:08 +00:00
from arrapi import ArrException
2024-01-15 21:38:54 +00:00
from datetime import datetime , timedelta
from dateutil . relativedelta import relativedelta
2024-03-06 16:31:44 +00:00
from modules import anidb , anilist , icheckmovies , imdb , letterboxd , mal , mojo , plex , radarr , reciperr , sonarr , tautulli , tmdb , trakt , tvdb , mdblist , util
2022-11-11 16:59:39 +00:00
from modules . util import Failed , FilterFailed , NonExisting , NotScheduled , NotScheduledRange , Deleted
2022-07-13 02:02:38 +00:00
from modules . overlay import Overlay
2024-04-22 14:20:12 +00:00
from modules . poster import KometaImage
2021-12-29 15:51:22 +00:00
from plexapi . audio import Artist , Album , Track
2023-02-27 20:10:06 +00:00
from plexapi . exceptions import NotFound
2021-08-22 15:54:33 +00:00
from plexapi . video import Movie , Show , Season , Episode
2022-05-31 15:37:55 +00:00
from requests . exceptions import ConnectionError
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 " ]
2024-03-06 16:31:44 +00:00
all_builders = anidb . builders + anilist . builders + icheckmovies . builders + imdb . builders + \
2024-01-15 21:38:54 +00:00
letterboxd . builders + mal . builders + mojo . 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-07-26 19:58:53 +00:00
" tmdb_on_the_air " , " builder_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 " ,
2022-09-20 02:46:52 +00:00
" tmdb_collection " , " tmdb_collection_details " , " tmdb_movie " , " tmdb_movie_details " , " tmdb_now_playing " , " item_edition " ,
2024-01-15 21:38:54 +00:00
" tvdb_movie " , " tvdb_movie_details " , " tmdb_upcoming " , " trakt_boxoffice " , " reciperr_list " , " radarr_all " , " radarr_taglist " ,
" mojo_world " , " mojo_domestic " , " mojo_international " , " mojo_record " , " mojo_all_time " , " mojo_never "
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 = [
2022-05-10 18:50:19 +00:00
" show_filtered " , " show_missing " , " save_report " , " 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-09-20 19:56:03 +00:00
" smart_filter " , " smart_label " , " smart_url " , " run_again " , " schedule " , " sync_mode " , " template " , " variables " , " test " , " suppress_overlays " ,
2024-04-22 14:20:12 +00:00
" delete_not_scheduled " , " tmdb_person " , " build_collection " , " collection_order " , " builder_level " , " overlay " , " kometa_poster " ,
2023-04-01 19:02:32 +00:00
" validate_builders " , " libraries " , " sync_to_users " , " exclude_users " , " collection_name " , " playlist_name " , " name " , " limit " ,
2023-02-23 03:34:12 +00:00
" blank_collection " , " allowed_library_types " , " run_definition " , " delete_playlist " , " ignore_blank_results " , " only_run_on_create " ,
2023-04-18 15:37:05 +00:00
" delete_collections_named " , " tmdb_person_offset " , " append_label " , " key_name " , " translation_key " , " translation_prefix " , " tmdb_birthday "
2021-08-22 15:54:33 +00:00
]
2022-03-23 18:43:50 +00:00
details = [
2023-04-01 19:02:32 +00:00
" ignore_ids " , " ignore_imdb_ids " , " server_preroll " , " changes_webhooks " , " collection_filtering " , " collection_mode " , " url_theme " ,
2022-05-17 13:35:37 +00:00
" file_theme " , " minimum_items " , " label " , " album_sorting " , " cache_builders " , " tmdb_region " , " default_percent "
2022-03-23 18:43:50 +00:00
] + boolean_details + scheduled_boolean + string_details
2023-03-01 15:14:20 +00:00
collectionless_details = [ " collection_order " , " plex_collectionless " , " label " , " label_sync_mode " , " test " , " item_label " ] + \
2023-03-20 21:03:24 +00:00
poster_details + background_details + summary_details + string_details + all_builders
2022-01-26 14:43:58 +00:00
item_false_details = [ " item_lock_background " , " item_lock_poster " , " item_lock_title " ]
2022-05-26 16:16:04 +00:00
item_bool_details = [ " item_tmdb_season_titles " , " revert_overlay " , " item_assets " , " item_refresh " ] + item_false_details
2022-09-20 02:46:52 +00:00
item_details = [ " non_item_remove_label " , " item_label " , " item_genre " , " item_edition " , " item_radarr_tag " , " item_sonarr_tag " , " item_refresh_delay " ] + item_bool_details + list ( plex . item_advance_keys . keys ( ) )
none_details = [ " label.sync " , " item_label.sync " , " item_genre.sync " , " radarr_taglist " , " sonarr_taglist " , " item_edition " ]
2024-02-02 20:41:44 +00:00
none_builders = [ " radarr_tag_list " , " sonarr_taglist " ]
2022-04-30 23:57:26 +00:00
radarr_details = [
2024-01-19 00:04:03 +00:00
" radarr_add_missing " , " radarr_add_existing " , " radarr_upgrade_existing " , " radarr_monitor_existing " , " radarr_folder " , " radarr_monitor " ,
2024-01-13 21:36:09 +00:00
" radarr_search " , " radarr_availability " , " radarr_quality " , " radarr_tag " , " item_radarr_tag " , " radarr_ignore_cache " ,
2022-04-30 23:57:26 +00:00
]
2021-08-22 15:54:33 +00:00
sonarr_details = [
2024-01-19 00:04:03 +00:00
" sonarr_add_missing " , " sonarr_add_existing " , " sonarr_upgrade_existing " , " sonarr_monitor_existing " , " sonarr_folder " , " sonarr_monitor " , " sonarr_language " ,
2023-02-07 03:20:05 +00:00
" sonarr_series " , " sonarr_quality " , " sonarr_season " , " sonarr_search " , " sonarr_cutoff_search " , " sonarr_tag " , " item_sonarr_tag " , " sonarr_ignore_cache "
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-06-16 03:20:40 +00:00
sub_filters = [
2023-08-19 15:30:47 +00:00
" filepath " , " audio_track_title " , " subtitle_track_title " , " resolution " , " audio_language " , " subtitle_language " , " has_dolby_vision " ,
2022-09-19 15:38:32 +00:00
" channels " , " height " , " width " , " aspect " , " audio_codec " , " audio_profile " , " video_codec " , " video_profile " , " versions "
2022-06-16 03:20:40 +00:00
]
2021-12-29 15:51:22 +00:00
filters_by_type = {
2023-08-19 15:30:47 +00:00
" movie_show_season_episode_artist_album_track " : [ " title " , " summary " , " collection " , " has_collection " , " added " , " last_played " , " user_rating " , " plays " , " filepath " , " label " , " audio_track_title " , " subtitle_track_title " , " versions " ] ,
2021-12-29 15:51:22 +00:00
" movie_show_season_episode_album_track " : [ " year " ] ,
2022-05-17 07:25:11 +00:00
" movie_show_season_episode_artist_album " : [ " has_overlay " ] ,
2022-06-16 03:20:40 +00:00
" movie_show_season_episode " : [ " resolution " , " audio_language " , " subtitle_language " , " has_dolby_vision " , " channels " , " height " , " width " , " aspect " , " audio_codec " , " audio_profile " , " video_codec " , " video_profile " ] ,
2021-12-29 15:51:22 +00:00
" 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 " ] ,
2023-04-18 14:55:32 +00:00
" movie_show " : [ " studio " , " original_language " , " tmdb_vote_count " , " tmdb_vote_average " , " tmdb_year " , " tmdb_genre " , " tmdb_title " , " tmdb_keyword " , " imdb_keyword " ] ,
2022-05-17 07:25:11 +00:00
" movie_episode " : [ " director " , " producer " , " writer " ] ,
2021-12-29 15:51:22 +00:00
" movie_artist " : [ " country " ] ,
2022-05-17 07:25:11 +00:00
" show_artist " : [ " folder " ] ,
2022-04-27 23:14:52 +00:00
" show_season " : [ " episodes " ] ,
" artist_album " : [ " tracks " ] ,
2023-03-05 03:22:07 +00:00
" movie " : [ " edition " , " has_edition " , " stinger_rating " , " has_stinger " ] ,
2024-04-03 12:45:43 +00:00
" show " : [ " seasons " , " tmdb_status " , " tmdb_type " , " origin_country " , " network " , " first_episode_aired " , " last_episode_aired " , " last_episode_aired_or_never " , " tvdb_title " , " tvdb_status " , " tvdb_genre " ] ,
2022-04-27 23:14:52 +00:00
" 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 = [
2023-04-18 14:55:32 +00:00
" original_language " , " origin_country " , " tmdb_vote_count " , " tmdb_vote_average " , " tmdb_year " , " tmdb_keyword " , " tmdb_genre " ,
2022-08-21 20:54:59 +00:00
" first_episode_aired " , " last_episode_aired " , " last_episode_aired_or_never " , " tmdb_status " , " tmdb_type " , " tmdb_title "
2022-03-11 07:07:37 +00:00
]
2024-04-03 12:45:43 +00:00
tvdb_filters = [ " tvdb_title " , " tvdb_status " , " tvdb_genre " ]
2023-01-27 15:16:00 +00:00
imdb_filters = [ " imdb_keyword " ]
2022-09-21 14:54:38 +00:00
string_filters = [
2023-08-19 15:30:47 +00:00
" title " , " summary " , " studio " , " edition " , " record_label " , " folder " , " filepath " , " audio_track_title " , " subtitle_track_title " , " tmdb_title " ,
2024-04-03 12:45:43 +00:00
" audio_codec " , " audio_profile " , " video_codec " , " video_profile " , " tvdb_title " , " tvdb_status "
2022-09-21 14:54:38 +00:00
]
2021-12-29 15:51:22 +00:00
string_modifiers = [ " " , " .not " , " .is " , " .isnot " , " .begins " , " .ends " , " .regex " ]
tag_filters = [
2022-06-16 03:20:40 +00:00
" actor " , " collection " , " content_rating " , " country " , " director " , " network " , " genre " , " label " , " producer " , " year " ,
2024-04-03 12:45:43 +00:00
" origin_country " , " writer " , " resolution " , " audio_language " , " subtitle_language " , " tmdb_keyword " , " tmdb_genre " , " imdb_keyword " , " tvdb_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 " ]
2023-03-05 03:22:07 +00:00
boolean_filters = [ " has_collection " , " has_edition " , " has_overlay " , " has_dolby_vision " , " has_stinger " ]
2022-08-21 20:54:59 +00:00
date_filters = [ " release " , " added " , " last_played " , " first_episode_aired " , " last_episode_aired " , " last_episode_aired_or_never " ]
2021-12-29 15:51:22 +00:00
date_modifiers = [ " " , " .not " , " .before " , " .after " , " .regex " ]
2022-06-16 03:20:40 +00:00
number_filters = [
2023-04-18 14:55:32 +00:00
" year " , " tmdb_year " , " critic_rating " , " audience_rating " , " user_rating " , " tmdb_vote_count " , " tmdb_vote_average " , " plays " , " duration " ,
2023-03-05 03:22:07 +00:00
" channels " , " height " , " width " , " aspect " , " versions " , " stinger_rating " ]
2022-06-16 03:20:40 +00:00
number_modifiers = [ " " , " .not " , " .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-08-21 20:54:59 +00:00
date_attributes = plex . date_attributes + [ " first_episode_aired " , " last_episode_aired " , " last_episode_aired_or_never " ]
2022-04-20 16:03:08 +00:00
year_attributes = plex . year_attributes + [ " tmdb_year " ]
2023-02-24 16:05:52 +00:00
number_attributes = plex . number_attributes + [ " channels " , " height " , " width " , " tmdb_vote_count " ]
2022-09-21 14:54:38 +00:00
tag_attributes = plex . tag_attributes
string_attributes = plex . string_attributes + string_filters
2023-04-18 14:55:32 +00:00
float_attributes = plex . float_attributes + [ " aspect " , " tmdb_vote_average " ]
2022-06-16 03:20:40 +00:00
boolean_attributes = plex . boolean_attributes + boolean_filters
2022-07-26 19:58:53 +00:00
smart_invalid = [ " collection_order " , " builder_level " ]
2022-04-08 18:29:38 +00:00
smart_only = [ " collection_filtering " ]
2022-05-10 18:50:19 +00:00
smart_url_invalid = [ " filters " , " run_again " , " sync_mode " , " show_filtered " , " show_missing " , " save_report " , " smart_label " ] + radarr_details + sonarr_details
2021-07-29 02:26:39 +00:00
custom_sort_builders = [
2022-09-08 20:11:11 +00:00
" plex_search " , " plex_watchlist " , " 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 " ,
2024-01-03 13:24:48 +00:00
" tvdb_list " , " imdb_chart " , " imdb_list " , " imdb_award " , " imdb_search " , " imdb_watchlist " , " stevenlu_popular " , " anidb_popular " ,
2023-12-11 21:34:50 +00:00
" tmdb_upcoming " , " tmdb_airing_today " , " tmdb_on_the_air " , " trakt_list " , " trakt_watchlist " , " trakt_collection " ,
" trakt_trending " , " trakt_popular " , " trakt_boxoffice " , " trakt_collected_daily " , " trakt_collected_weekly " ,
" trakt_collected_monthly " , " trakt_collected_yearly " , " trakt_collected_all " , " trakt_recommendations " ,
" trakt_recommended_personal " , " trakt_recommended_daily " , " trakt_recommended_weekly " , " trakt_recommended_monthly " ,
" trakt_recommended_yearly " , " trakt_recommended_all " , " trakt_watched_daily " , " trakt_watched_weekly " ,
" trakt_watched_monthly " , " trakt_watched_yearly " , " trakt_watched_all " ,
2024-03-06 16:31:44 +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 " ,
2024-01-15 21:38:54 +00:00
" mal_popular " , " mal_favorite " , " mal_suggested " , " mal_userlist " , " mal_season " , " mal_genre " , " mal_studio " ,
" mojo_world " , " mojo_domestic " , " mojo_international " , " mojo_record " , " mojo_all_time " , " mojo_never "
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 = [
2022-07-19 20:33:57 +00:00
" filters " , " limit " , " show_missing " , " save_report " , " missing_only_released " , " minimum_items " , " cache_builders " , " tmdb_region " , " default_percent "
2022-04-18 18:16:39 +00:00
] + 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-05-10 18:50:19 +00:00
" visible_home " , " visible_shared " , " show_missing " , " save_report " , " missing_only_released " , " server_preroll " , " changes_webhooks " ,
2023-12-11 21:34:50 +00:00
" item_lock_background " , " item_lock_poster " , " item_lock_title " , " item_refresh " , " item_refresh_delay " , " imdb_list " , " imdb_search " ,
2024-01-03 21:57:07 +00:00
" cache_builders " , " url_theme " , " file_theme " , " item_label " , " default_percent " , " non_item_remove_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 = [
2023-02-23 03:34:12 +00:00
" filters " , " name_mapping " , " show_filtered " , " show_missing " , " save_report " , " allowed_library_types " , " run_definition " ,
2021-12-17 14:24:46 +00:00
" missing_only_released " , " only_filter_missing " , " delete_below_minimum " , " ignore_ids " , " ignore_imdb_ids " ,
2022-05-17 13:35:37 +00:00
" server_preroll " , " changes_webhooks " , " minimum_items " , " cache_builders " , " default_percent "
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-05-26 16:16:04 +00:00
" item_assets " , " 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 = [ ]
2023-03-30 19:51:04 +00:00
self . summaries = { }
2022-02-08 22:57:36 +00:00
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 ( " " )
2022-07-14 19:37:10 +00:00
if f " { self . type } _name " in methods :
2022-02-08 22:57:36 +00:00
logger . warning ( f " Config Warning: Running { self . type } _name as name " )
2022-07-14 19:37:10 +00:00
self . data [ " name " ] = self . data [ methods [ f " { self . type } _name " ] ]
methods [ " name " ] = " name "
2022-02-08 22:57:36 +00:00
if " template " in methods :
2022-12-02 17:27:27 +00:00
logger . separator ( f " Building Definition From Templates " , space = False , border = False )
logger . debug ( " " )
named_templates = [ ]
for original_variables in util . get_list ( self . data [ methods [ " template " ] ] , split = False ) :
if not isinstance ( original_variables , dict ) :
raise Failed ( f " { self . Type } Error: template attribute is not a dictionary " )
elif " name " not in original_variables :
raise Failed ( f " { self . Type } Error: template sub-attribute name is required " )
elif not original_variables [ " name " ] :
raise Failed ( f " { self . Type } Error: template sub-attribute name cannot be blank " )
named_templates . append ( original_variables [ " name " ] )
2022-12-06 16:34:31 +00:00
logger . debug ( f " Templates Called: { ' , ' . join ( named_templates ) } " )
2022-12-02 17:27:27 +00:00
logger . debug ( " " )
2022-09-20 21:29:57 +00:00
new_variables = { }
2022-09-20 19:56:03 +00:00
if " variables " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: variables " )
if not isinstance ( self . data [ methods [ " variables " ] ] , dict ) :
raise Failed ( f " { self . Type } Error: variables must be a dictionary (key: value pairs) " )
logger . trace ( self . data [ methods [ " variables " ] ] )
2022-09-20 21:29:57 +00:00
new_variables = self . data [ methods [ " variables " ] ]
2022-07-14 19:37:10 +00:00
name = self . data [ methods [ " name " ] ] if " name " in methods else None
2022-09-20 21:29:57 +00:00
new_attributes = self . metadata . apply_template ( name , self . mapping_name , self . data , self . data [ methods [ " template " ] ] , new_variables )
2022-02-08 22:57:36 +00:00
for attr in new_attributes :
if attr . lower ( ) not in methods :
self . data [ attr ] = new_attributes [ attr ]
methods [ attr . lower ( ) ] = attr
2022-07-18 15:44:16 +00:00
logger . separator ( f " Validating { self . mapping_name } Attributes " , space = False , border = False )
2023-03-30 19:51:04 +00:00
self . builder_language = self . metadata . language
if " language " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: language " )
if not self . data [ methods [ " language " ] ] :
raise Failed ( f " { self . Type } Error: language attribute is blank " )
logger . debug ( f " Value: { self . data [ methods [ ' language ' ] ] } " )
if str ( self . data [ methods [ " language " ] ] ) . lower ( ) not in self . config . GitHub . translation_keys :
logger . warning ( f " Config Error: Language: { str ( self . data [ methods [ ' language ' ] ] ) . lower ( ) } Not Found using { self . builder_language } . Options: { ' , ' . join ( self . config . GitHub . translation_keys ) } " )
else :
self . builder_language = str ( self . data [ methods [ " language " ] ] ) . lower ( )
self . name = None
2023-04-01 19:02:32 +00:00
if " name " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: name " )
if not self . data [ methods [ " name " ] ] :
raise Failed ( f " { self . Type } Error: name attribute is blank " )
logger . debug ( f " Value: { self . data [ methods [ ' name ' ] ] } " )
self . name = str ( self . data [ methods [ " name " ] ] )
english = None
translations = None
self . limit = 0
if " limit " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: limit " )
if not self . data [ methods [ " limit " ] ] :
raise Failed ( f " { self . Type } Error: limit attribute is blank " )
self . limit = util . parse ( self . Type , " limit " , self . data [ methods [ " limit " ] ] , datatype = " int " , minimum = 1 )
en_key = None
trans_key = None
if " key_name " in methods :
2023-03-30 19:51:04 +00:00
english = self . config . GitHub . translation_yaml ( " en " )
translations = self . config . GitHub . translation_yaml ( self . builder_language )
logger . debug ( " " )
2023-04-01 19:02:32 +00:00
logger . debug ( " Validating Method: key_name " )
if not self . data [ methods [ " key_name " ] ] :
raise Failed ( f " { self . Type } Error: key_name attribute is blank " )
en_key = str ( self . data [ methods [ " key_name " ] ] )
trans_key = en_key
if self . builder_language != " en " :
key_name_key = None
for k , v in english [ " key_names " ] . items ( ) :
if en_key == v :
key_name_key = k
break
if key_name_key and key_name_key in translations [ " key_names " ] :
trans_key = translations [ " key_names " ] [ key_name_key ]
logger . debug ( f " Value: { trans_key } " )
2023-04-18 17:50:39 +00:00
self . key_name = trans_key
2023-04-01 19:02:32 +00:00
en_name = None
en_summary = None
trans_name = None
trans_summary = None
if " translation_key " in methods :
if not english :
english = self . config . GitHub . translation_yaml ( " en " )
if not translations :
translations = self . config . GitHub . translation_yaml ( self . builder_language )
logger . debug ( " " )
2023-03-30 19:51:04 +00:00
logger . debug ( " Validating Method: translation_key " )
if not self . data [ methods [ " translation_key " ] ] :
raise Failed ( f " { self . Type } Error: translation_key attribute is blank " )
logger . debug ( f " Value: { self . data [ methods [ ' translation_key ' ] ] } " )
translation_key = str ( self . data [ methods [ " translation_key " ] ] )
if translation_key not in english [ " collections " ] :
raise Failed ( f " { self . Type } Error: translation_key: { translation_key } is invalid " )
2023-04-01 19:02:32 +00:00
en_name = english [ " collections " ] [ translation_key ] [ " name " ]
en_summary = english [ " collections " ] [ translation_key ] [ " summary " ]
if translation_key in translations [ " collections " ] :
if " name " in translations [ " collections " ] [ translation_key ] :
trans_name = translations [ " collections " ] [ translation_key ] [ " name " ]
if " summary " in translations [ " collections " ] [ translation_key ] :
trans_summary = translations [ " collections " ] [ translation_key ] [ " summary " ]
2023-04-10 01:20:05 +00:00
if " translation_prefix " in methods and self . data [ methods [ " translation_prefix " ] ] :
2023-03-30 19:51:04 +00:00
logger . debug ( " " )
2023-04-01 19:02:32 +00:00
logger . debug ( " Validating Method: translation_prefix " )
logger . debug ( f " Value: { self . data [ methods [ ' translation_prefix ' ] ] } " )
en_name = f " { self . data [ methods [ ' translation_prefix ' ] ] } { en_name } "
trans_name = f " { self . data [ methods [ ' translation_prefix ' ] ] } { trans_name } "
if self . name or en_name or trans_name :
if not english :
english = self . config . GitHub . translation_yaml ( " en " )
if not translations :
translations = self . config . GitHub . translation_yaml ( self . builder_language )
2023-03-30 19:51:04 +00:00
lib_type = self . library . type . lower ( ) if self . library else " item "
en_vars = { k : v [ lib_type ] for k , v in english [ " variables " ] . items ( ) if lib_type in v and v [ lib_type ] }
2023-04-01 19:02:32 +00:00
trans_vars = { k : v [ lib_type ] for k , v in translations [ " variables " ] . items ( ) if lib_type in v and v [ lib_type ] }
2023-03-30 19:51:04 +00:00
for k , v in en_vars . items ( ) :
2023-04-01 19:02:32 +00:00
if k not in trans_vars :
trans_vars [ k ] = v
2023-03-30 19:51:04 +00:00
2023-04-01 19:02:32 +00:00
def apply_vars ( input_str , var_set , var_key , var_limit ) :
2023-03-30 19:51:04 +00:00
input_str = str ( input_str )
2023-04-01 19:02:32 +00:00
if " <<library_type>> " in input_str :
input_str = input_str . replace ( " <<library_type>> " , " <<library_translation>> " )
if " <<library_typeU>> " in input_str :
input_str = input_str . replace ( " <<library_typeU>> " , " <<library_translationU>> " )
2023-03-30 19:51:04 +00:00
for ik , iv in var_set . items ( ) :
if f " << { ik } >> " in input_str :
2023-03-31 22:19:06 +00:00
input_str = input_str . replace ( f " << { ik } >> " , str ( iv ) )
2023-03-30 19:51:04 +00:00
if f " << { ik } U>> " in input_str :
input_str = input_str . replace ( f " << { ik } U>> " , str ( iv ) . capitalize ( ) )
2023-04-01 19:02:32 +00:00
if var_key and " <<key_name>> " in input_str :
input_str = input_str . replace ( " <<key_name>> " , str ( var_key ) )
if var_limit and " <<limit>> " in input_str :
input_str = input_str . replace ( " <<limit>> " , str ( var_limit ) )
2023-03-30 19:51:04 +00:00
return input_str
2023-04-01 19:02:32 +00:00
if self . name :
self . name = apply_vars ( self . name , trans_vars , trans_key , self . limit )
if en_name :
en_name = apply_vars ( en_name , en_vars , en_key , self . limit )
if trans_name :
trans_name = apply_vars ( trans_name , trans_vars , trans_key , self . limit )
if en_summary :
en_summary = apply_vars ( en_summary , en_vars , en_key , self . limit )
if trans_summary :
trans_summary = apply_vars ( trans_summary , trans_vars , trans_key , self . limit )
delete_cols = [ ]
2024-05-02 13:23:55 +00:00
if ( self . name and self . name != en_name ) or ( not self . name and trans_name and en_name != trans_name ) :
2023-04-01 19:02:32 +00:00
delete_cols . append ( en_name )
2023-04-09 00:10:55 +00:00
if self . name and self . name != trans_name and en_name != trans_name :
2023-04-01 19:02:32 +00:00
delete_cols . append ( trans_name )
if delete_cols :
2023-03-30 19:51:04 +00:00
if " delete_collections_named " not in methods :
2023-04-01 19:02:32 +00:00
self . data [ " delete_collections_named " ] = delete_cols
2023-03-30 19:51:04 +00:00
methods [ " delete_collections_named " ] = " delete_collections_named "
2023-04-01 19:02:32 +00:00
elif not self . data [ methods [ " delete_collections_named " ] ] :
self . data [ methods [ " delete_collections_named " ] ] = delete_cols
elif not isinstance ( self . data [ methods [ " delete_collections_named " ] ] , list ) :
2023-04-09 00:10:55 +00:00
if self . data [ methods [ " delete_collections_named " ] ] not in delete_cols :
delete_cols . append ( self . data [ methods [ " delete_collections_named " ] ] )
2023-04-01 19:02:32 +00:00
self . data [ methods [ " delete_collections_named " ] ] = delete_cols
else :
2023-04-09 00:10:55 +00:00
self . data [ methods [ " delete_collections_named " ] ] . extend ( [ d for d in delete_cols if d not in self . data [ methods [ " delete_collections_named " ] ] ] )
2023-03-30 19:51:04 +00:00
2023-04-01 19:02:32 +00:00
if not self . name :
self . name = trans_name if trans_name else en_name
2024-05-02 13:23:55 +00:00
logger . info ( f " Final Name: { self . name } " )
2023-04-01 19:02:32 +00:00
if en_summary or trans_summary :
self . summaries [ " translation " ] = trans_summary if trans_summary else en_summary
2023-03-30 19:51:04 +00:00
if not self . name :
2022-07-15 21:03:04 +00:00
self . name = self . mapping_name
2022-07-14 19:37:10 +00:00
2023-05-18 14:51:51 +00:00
if self . library and self . name not in self . library . collections :
self . library . collections . append ( self . name )
2022-12-21 16:30:11 +00:00
if self . playlist :
if " libraries " not in methods :
raise Failed ( " Playlist Error: libraries attribute is required " )
logger . debug ( " " )
logger . debug ( " Validating Method: libraries " )
if not self . data [ methods [ " libraries " ] ] :
raise Failed ( f " { self . Type } Error: libraries attribute is blank " )
logger . debug ( f " Value: { self . data [ methods [ ' libraries ' ] ] } " )
for pl_library in util . get_list ( self . data [ methods [ " libraries " ] ] ) :
if str ( pl_library ) not in config . library_map :
raise Failed ( f " Playlist Error: Library: { pl_library } not defined " )
self . libraries . append ( config . library_map [ pl_library ] )
self . library = self . libraries [ 0 ]
2022-12-20 22:04:02 +00:00
try :
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name , force_search = True )
except Failed :
self . obj = None
self . only_run_on_create = False
2023-02-21 20:31:09 +00:00
if " only_run_on_create " in methods and not self . playlist :
2022-12-20 22:04:02 +00:00
logger . debug ( " " )
logger . debug ( " Validating Method: only_run_on_create " )
logger . debug ( f " Value: { data [ methods [ ' only_run_on_create ' ] ] } " )
self . only_run_on_create = util . parse ( self . Type , " only_run_on_create " , self . data , datatype = " bool " , methods = methods , default = False )
if self . obj and self . only_run_on_create :
raise NotScheduled ( " Skipped because only_run_on_create is True and the collection already exists " )
2023-02-23 03:34:12 +00:00
if " allowed_library_types " in methods and " run_definition " not in methods :
logger . warning ( f " { self . Type } Warning: allowed_library_types will run as run_definition " )
methods [ " run_definition " ] = methods [ " allowed_library_types " ]
if " run_definition " in methods :
2022-05-12 15:04:52 +00:00
logger . debug ( " " )
2023-02-23 03:34:12 +00:00
logger . debug ( " Validating Method: run_definition " )
if self . data [ methods [ " run_definition " ] ] is None :
raise NotScheduled ( " Skipped because run_definition has no value " )
logger . debug ( f " Value: { self . data [ methods [ ' run_definition ' ] ] } " )
valid_options = [ " true " , " false " ] + plex . library_types
for library_type in util . get_list ( self . data [ methods [ " run_definition " ] ] , lower = True ) :
if library_type not in valid_options :
raise Failed ( f " { self . Type } Error: { library_type } is invalid. Options: true, false, { ' , ' . join ( plex . library_types ) } " )
2022-05-17 07:25:11 +00:00
elif library_type == " false " :
2023-02-23 03:34:12 +00:00
raise NotScheduled ( f " Skipped because run_definition is false " )
elif library_type != " true " and self . library and library_type != self . library . Plex . type :
raise NotScheduled ( f " Skipped because run_definition library_type: { library_type } doesn ' t match " )
2022-05-12 15:04:52 +00:00
2022-07-26 19:58:53 +00:00
if self . playlist : self . builder_level = " item "
elif self . library . is_show : self . builder_level = " show "
elif self . library . is_music : self . builder_level = " artist "
else : self . builder_level = " movie "
level = None
2023-09-29 17:12:01 +00:00
for level_attr in [ " builder_level " , " collection_level " , " overlay_level " ] :
2022-07-26 19:58:53 +00:00
if level_attr in methods :
level = self . data [ methods [ level_attr ] ]
if level_attr != " builder_level " :
logger . warning ( f " Collection Warning: { level_attr } attribute will run as builder_level " )
break
2022-08-23 15:30:53 +00:00
if level and not self . playlist and not self . library . is_movie :
2022-07-26 18:30:40 +00:00
logger . debug ( " " )
2022-07-26 19:58:53 +00:00
logger . debug ( " Validating Method: builder_level " )
2022-07-26 18:30:40 +00:00
if level is None :
2022-07-26 19:58:53 +00:00
logger . error ( f " { self . Type } Error: builder_level attribute is blank " )
2022-07-26 18:30:40 +00:00
else :
logger . debug ( f " Value: { level } " )
level = level . lower ( )
2022-07-26 19:58:53 +00:00
if ( self . library . is_show and level in plex . builder_level_show_options ) or ( self . library . is_music and level in plex . builder_level_music_options ) :
self . builder_level = level
2022-07-26 18:30:40 +00:00
elif ( self . library . is_show and level != " show " ) or ( self . library . is_music and level != " artist " ) :
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) "
2022-07-26 19:58:53 +00:00
raise Failed ( f " { self . Type } Error: { self . data [ methods [ ' builder_level ' ] ] } builder_level invalid { options } " )
self . parts_collection = self . builder_level in plex . builder_level_options
2022-07-26 18:30:40 +00:00
2023-04-05 15:58:46 +00:00
self . posters = { }
self . backgrounds = { }
2024-04-22 14:20:12 +00:00
if not self . overlay and " kometa_poster " in methods :
2023-04-05 15:58:46 +00:00
logger . debug ( " " )
2024-04-22 14:20:12 +00:00
logger . debug ( " Validating Method: kometa_poster " )
if self . data [ methods [ " kometa_poster " ] ] is None :
logger . error ( f " { self . Type } Error: kometa_poster attribute is blank " )
logger . debug ( f " Value: { data [ methods [ ' kometa_poster ' ] ] } " )
2023-04-05 15:58:46 +00:00
try :
2024-04-22 14:20:12 +00:00
self . posters [ " kometa_poster " ] = KometaImage ( self . config , self . data [ methods [ " kometa_poster " ] ] , " kometa_poster " , playlist = self . playlist )
2023-04-05 15:58:46 +00:00
except Failed as e :
logger . error ( e )
2022-04-18 18:16:39 +00:00
if self . overlay :
if " overlay " in methods :
2022-05-15 03:06:32 +00:00
overlay_data = data [ methods [ " overlay " ] ]
2022-04-18 18:16:39 +00:00
else :
2022-05-15 03:06:32 +00:00
overlay_data = 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-05-15 03:06:32 +00:00
suppress = [ ]
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 " ] ] :
2022-05-15 03:06:32 +00:00
suppress = util . get_list ( data [ methods [ " suppress_overlays " ] ] )
2022-04-19 05:58:38 +00:00
else :
2022-05-15 03:06:32 +00:00
logger . error ( f " Overlay Error: suppress_overlays attribute is blank " )
2022-10-28 23:32:48 +00:00
self . overlay = Overlay ( config , library , metadata , str ( self . mapping_name ) , overlay_data , suppress , self . builder_level )
2022-04-19 05:58:38 +00:00
2022-05-07 15:37:36 +00:00
self . sync_to_users = None
2022-12-22 16:38:09 +00:00
self . exclude_users = None
2022-05-07 15:37:36 +00:00
self . valid_users = [ ]
2022-02-08 22:57:36 +00:00
if self . playlist :
2022-05-07 15:37:36 +00:00
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 ] ] } " )
2022-05-11 06:40:50 +00:00
self . sync_to_users = self . data [ methods [ s_attr ] ]
2022-05-07 15:37:36 +00:00
else :
2022-05-11 06:40:50 +00:00
self . sync_to_users = config . general [ " playlist_sync_to_users " ]
2022-12-22 16:38:09 +00:00
logger . warning ( f " Playlist Warning: sync_to_users attribute not found defaulting to playlist_sync_to_users: { self . sync_to_users } " )
if " exclude_users " in methods or " exclude_user " in methods :
s_attr = f " exclude_user { ' s ' if ' exclude_users ' in methods else ' ' } "
logger . debug ( " " )
logger . debug ( f " Validating Method: { s_attr } " )
logger . debug ( f " Value: { self . data [ methods [ s_attr ] ] } " )
self . exclude_users = self . data [ methods [ s_attr ] ]
elif config . general [ " playlist_exclude_users " ] :
self . exclude_users = config . general [ " playlist_exclude_users " ]
logger . warning ( f " Playlist Warning: exclude_users attribute not found defaulting to playlist_exclude_users: { self . exclude_users } " )
2022-05-07 15:37:36 +00:00
2024-02-28 20:40:17 +00:00
plex_users = self . library . users + [ self . library . account . username ]
2022-12-22 16:38:09 +00:00
2022-12-22 20:40:10 +00:00
self . exclude_users = util . get_list ( self . exclude_users ) if self . exclude_users else [ ]
2022-12-22 16:38:09 +00:00
for user in self . exclude_users :
if user not in plex_users :
raise Failed ( f " Playlist Error: User: { user } not found in plex \n Options: { plex_users } " )
2022-05-07 15:37:36 +00:00
if self . sync_to_users :
if str ( self . sync_to_users ) == " all " :
2022-12-22 16:38:09 +00:00
self . valid_users = [ p for p in plex_users if p not in self . exclude_users ]
2022-05-07 15:37:36 +00:00
else :
2023-05-10 13:32:18 +00:00
user_list = self . sync_to_users if isinstance ( self . sync_to_users , list ) else util . get_list ( self . sync_to_users )
for user in user_list :
2022-05-12 15:04:52 +00:00
if user not in plex_users :
2022-05-07 15:37:36 +00:00
raise Failed ( f " Playlist Error: User: { user } not found in plex \n Options: { plex_users } " )
2022-12-22 16:38:09 +00:00
if user not in self . exclude_users :
self . valid_users . append ( user )
2022-05-07 15:37:36 +00:00
if " delete_playlist " in methods :
logger . debug ( " " )
2022-05-27 15:09:46 +00:00
logger . debug ( " Validating Method: delete_playlist " )
logger . debug ( f " Value: { data [ methods [ ' delete_playlist ' ] ] } " )
if util . parse ( self . Type , " delete_playlist " , self . data , datatype = " bool " , methods = methods , default = False ) :
2024-02-28 20:40:17 +00:00
for getter in [ self . library . get_playlist , self . library . get_playlist_from_users ] :
2024-02-16 03:51:58 +00:00
try :
self . obj = getter ( self . name )
break
except Failed as e :
error = e
else :
logger . error ( error )
2022-05-28 16:01:51 +00:00
raise Deleted ( 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 ,
2022-05-10 18:50:19 +00:00
" save_report " : self . library . save_report ,
2021-08-16 05:41:04 +00:00
" missing_only_released " : self . library . missing_only_released ,
2022-12-02 15:01:52 +00:00
" only_filter_missing " : False if self . overlay else 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 = [ ]
2022-10-24 06:17:49 +00:00
self . has_tmdb_filters = False
2023-01-28 02:59:43 +00:00
self . has_imdb_filters = False
2022-11-07 17:05:21 +00:00
self . found_items = [ ]
2022-05-10 18:50:19 +00:00
self . filtered_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-26 18:15:05 +00:00
self . schedule = " "
2022-02-09 02:31:29 +00:00
self . beginning_count = 0
2022-05-17 07:25:11 +00:00
self . default_percent = 50
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
2022-06-27 20:46:46 +00:00
self . non_existing = False
2021-10-04 17:51:32 +00:00
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-11-11 16:59:39 +00:00
self . ignore_blank_results = False
if " ignore_blank_results " in methods and not self . playlist :
logger . debug ( " " )
logger . debug ( " Validating Method: ignore_blank_results " )
logger . debug ( f " Value: { data [ methods [ ' ignore_blank_results ' ] ] } " )
2022-12-20 22:04:02 +00:00
self . ignore_blank_results = util . parse ( self . Type , " ignore_blank_results " , self . data , datatype = " bool " , methods = methods , default = False )
2022-11-11 16:59:39 +00:00
2022-11-01 18:20:00 +00:00
self . smart_filter_details = " "
2022-11-19 20:17:43 +00:00
self . smart_label_url = None
2022-11-01 18:20:00 +00:00
self . smart_label = { " sort_by " : " random " , " all " : { " label " : [ self . name ] } }
self . smart_label_collection = False
if " smart_label " in methods and not self . playlist and not self . overlay and not self . library . is_music :
logger . debug ( " " )
logger . debug ( " Validating Method: smart_label " )
self . smart_label_collection = True
if not self . data [ methods [ " smart_label " ] ] :
logger . warning ( f " { self . Type } Error: smart_label attribute is blank defaulting to random " )
else :
logger . debug ( f " Value: { self . data [ methods [ ' smart_label ' ] ] } " )
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 ) \
or ( self . library . is_show and str ( self . data [ methods [ " smart_label " ] ] ) . lower ( ) in plex . show_sorts ) :
self . smart_label [ " sort_by " ] = str ( self . data [ methods [ " smart_label " ] ] ) . lower ( )
else :
logger . warning ( f " { self . Type } Error: smart_label attribute: { self . data [ methods [ ' smart_label ' ] ] } is invalid defaulting to random " )
if self . smart_label_collection and self . library . smart_label_check ( self . name ) :
2022-11-11 16:59:39 +00:00
try :
2022-11-19 20:17:43 +00:00
_ , self . smart_filter_details , self . smart_label_url = self . build_filter ( " smart_label " , self . smart_label , default_sort = " random " )
2022-11-11 16:59:39 +00:00
except FilterFailed as e :
if self . ignore_blank_results :
raise
else :
raise Failed ( str ( e ) )
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-06-27 20:46:46 +00:00
except NonExisting as e :
self . non_existing = str ( e )
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 :
2022-11-08 20:59:23 +00:00
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name , force_search = True )
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
2023-10-16 20:04:46 +00:00
if " delete_collections_named " in methods and not self . overlay and not self . playlist :
logger . debug ( " " )
logger . debug ( " Validating Method: delete_collections_named " )
logger . debug ( f " Value: { data [ methods [ ' delete_collections_named ' ] ] } " )
for del_col in util . parse ( self . Type , " delete_collections_named " , self . data , datatype = " strlist " , methods = methods ) :
try :
del_obj = self . library . get_collection ( del_col , force_search = True )
self . library . delete ( del_obj )
logger . info ( f " Collection: { del_obj . title } deleted " )
except Failed as e :
if str ( e ) . startswith ( " Plex Error: Failed to delete " ) :
logger . error ( e )
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
2024-01-07 16:48:07 +00:00
self . sync = self . library . sync_mode == " sync " and self . type != " overlay "
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 "
2023-02-13 23:13:54 +00:00
self . tmdb_person_offset = 0
2023-02-07 16:50:18 +00:00
if " tmdb_person_offset " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: tmdb_person_offset " )
logger . debug ( f " Value: { data [ methods [ ' tmdb_person_offset ' ] ] } " )
self . tmdb_person_offset = util . parse ( self . Type , " tmdb_person_offset " , self . data , datatype = " int " , methods = methods , default = 0 , minimum = 0 )
2023-04-18 15:37:05 +00:00
self . tmdb_birthday = None
if " tmdb_birthday " in methods :
logger . debug ( " " )
logger . debug ( " Validating Method: tmdb_birthday " )
logger . debug ( f " Value: { data [ methods [ ' tmdb_birthday ' ] ] } " )
if not self . data [ methods [ " tmdb_birthday " ] ] :
raise Failed ( f " { self . Type } Error: tmdb_birthday attribute is blank " )
parsed_birthday = util . parse ( self . Type , " tmdb_birthday " , self . data , datatype = " dict " , methods = methods )
parsed_methods = { m . lower ( ) : m for m in parsed_birthday }
self . tmdb_birthday = {
" before " : util . parse ( self . Type , " before " , parsed_birthday , datatype = " int " , methods = parsed_methods , minimum = 0 , default = 0 ) ,
2023-04-18 17:50:39 +00:00
" after " : util . parse ( self . Type , " after " , parsed_birthday , datatype = " int " , methods = parsed_methods , minimum = 0 , default = 0 ) ,
" this_month " : util . parse ( self . Type , " this_month " , parsed_birthday , datatype = " bool " , methods = parsed_methods , default = False )
2023-04-18 15:37:05 +00:00
}
first_person = None
self . tmdb_person_birthday = None
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 :
2023-04-18 15:37:05 +00:00
if not first_person :
first_person = tmdb_person
2022-04-05 18:46:00 +00:00
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
2023-04-18 15:37:05 +00:00
if person . birthday and not self . tmdb_person_birthday :
self . tmdb_person_birthday = person . birthday
2022-04-05 18:46:00 +00:00
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 :
2023-02-07 16:50:18 +00:00
result_index = len ( results ) - 1 if self . tmdb_person_offset > = len ( results ) else self . tmdb_person_offset
2022-07-15 13:56:54 +00:00
valid_names . append ( tmdb_person )
2023-02-07 16:50:18 +00:00
if results [ result_index ] . biography :
self . summaries [ " tmdb_person " ] = results [ result_index ] . biography
if results [ result_index ] . profile_url :
self . posters [ " tmdb_person " ] = results [ result_index ] . profile_url
2023-04-18 15:37:05 +00:00
if results [ result_index ] . birthday and not self . tmdb_person_birthday :
self . tmdb_person_birthday = results [ result_index ] . birthday
2022-04-05 18:46:00 +00:00
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
2023-04-18 15:37:05 +00:00
if self . tmdb_birthday :
if " tmdb_person " not in methods :
raise NotScheduled ( " Skipped because tmdb_person is required when using tmdb_birthday " )
if not self . tmdb_person_birthday :
raise NotScheduled ( f " Skipped because No Birthday was found for { first_person } " )
now = datetime ( self . current_time . year , self . current_time . month , self . current_time . day )
try :
delta = datetime ( now . year , self . tmdb_person_birthday . month , self . tmdb_person_birthday . day )
except ValueError :
delta = datetime ( now . year , self . tmdb_person_birthday . month , 28 )
before_delta = delta
after_delta = delta
if delta < now :
try :
before_delta = datetime ( now . year + 1 , self . tmdb_person_birthday . month , self . tmdb_person_birthday . day )
except ValueError :
before_delta = datetime ( now . year + 1 , self . tmdb_person_birthday . month , 28 )
elif delta > now :
try :
after_delta = datetime ( now . year - 1 , self . tmdb_person_birthday . month , self . tmdb_person_birthday . day )
except ValueError :
after_delta = datetime ( now . year - 1 , self . tmdb_person_birthday . month , 28 )
days_after = ( now - after_delta ) . days
days_before = ( before_delta - now ) . days
2023-04-18 17:50:39 +00:00
msg = " "
if self . tmdb_birthday [ " this_month " ] :
if now . month != self . tmdb_person_birthday . month :
msg = f " Skipped because Birthday Month: { self . tmdb_person_birthday . month } is not { now . month } "
elif days_before > self . tmdb_birthday [ " before " ] and days_after > self . tmdb_birthday [ " after " ] :
msg = f " Skipped because days until { self . tmdb_person_birthday . month } / { self . tmdb_person_birthday . day } : { days_before } > { self . tmdb_birthday [ ' before ' ] } and days after { self . tmdb_person_birthday . month } / { self . tmdb_person_birthday . day } : { days_after } > { self . tmdb_birthday [ ' after ' ] } "
if msg :
2023-04-18 15:37:05 +00:00
suffix = " "
if self . details [ " delete_not_scheduled " ] :
try :
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name , force_search = True )
logger . info ( self . delete ( ) )
self . deleted = True
suffix = f " and was deleted "
except Failed :
suffix = f " and could not be found to delete "
2023-04-18 17:50:39 +00:00
raise NotScheduled ( f " { msg } { suffix } " )
2023-04-18 15:37:05 +00:00
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-11-11 16:59:39 +00:00
try :
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 " )
except FilterFailed as e :
if self . ignore_blank_results :
raise
else :
raise Failed ( str ( e ) )
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-06-09 13:40:47 +00:00
if self . parts_collection and " smart_url " in methods :
2022-07-26 19:58:53 +00:00
raise Failed ( f " { self . Type } Error: smart_url is not compatible with builder_level: { self . builder_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-05-27 15:09:46 +00:00
elif " collection_order " not in methods and not self . playlist and not self . blank_collection 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
2024-01-05 21:06:39 +00:00
logger . info ( " " )
2022-05-27 15:09:46 +00:00
logger . warning ( f " { self . Type } Warning: collection_order not found using library default_collection_order: { test_sort } " )
2022-12-23 08:14:51 +00:00
self . custom_sort = " custom " if self . playlist else None
2022-01-07 16:09:12 +00:00
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 } " )
2022-12-23 08:14:51 +00:00
if test_sort in plex . collection_order_options + [ " custom.asc " , " custom.desc " ] :
self . details [ " collection_order " ] = test_sort . split ( " . " ) [ 0 ]
if test_sort . startswith ( " custom " ) and self . build_collection :
self . custom_sort = test_sort
2022-01-02 04:23:47 +00:00
else :
2022-09-22 03:20:21 +00:00
sort_type = self . builder_level
if sort_type == " item " :
if self . library . is_show :
sort_type = " show "
elif self . library . is_music :
sort_type = " artist "
else :
sort_type = " movie "
_ , _ , sorts = plex . sort_types [ sort_type ]
if not isinstance ( test_sort , list ) :
test_sort = [ test_sort ]
self . custom_sort = [ ]
for ts in test_sort :
if ts not in sorts :
raise Failed ( f " { self . Type } Error: collection_order: { ts } is invalid. Options: { ' , ' . join ( sorts ) } " )
self . custom_sort . append ( ts )
2022-12-23 15:28:52 +00:00
if test_sort not in plex . collection_order_options + [ " custom.asc " , " custom.desc " ] and not self . custom_sort :
2024-04-22 14:20: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.asc/custom.desc (Custom Order Collection) \n \t Other sorting options can be found at https://github.com/Kometa-Team/Kometa/wiki/Smart-Builders#sort-options " )
2022-01-02 04:23:47 +00:00
2022-11-19 20:17:43 +00:00
if self . smart :
2022-12-23 08:14:51 +00:00
self . custom_sort = None
2022-01-02 04:23:47 +00:00
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 :
2024-02-02 20:41:44 +00:00
if method_data is None and method_name in all_builders + plex . searches and method_final not in none_builders :
2021-12-29 15:51:22 +00:00
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 " )
2022-07-26 19:58:53 +00:00
elif self . library . is_music and method_name in album_details and self . builder_level != " album " :
2021-12-29 15:51:22 +00:00
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-07-26 19:58:53 +00:00
elif not self . playlist and self . builder_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 :
2022-07-26 19:58:53 +00:00
raise Failed ( f " { self . Type } Error: { method_final } attribute not allowed with Collection Level: { self . builder_level . capitalize ( ) } " )
2021-12-29 15:51:22 +00:00
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 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 )
2024-01-15 21:38:54 +00:00
elif method_name in mojo . builders :
self . _mojo ( method_name , method_data )
2021-12-29 15:51:22 +00:00
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
2023-02-27 20:10:06 +00:00
if " append_label " in methods and not self . playlist and not self . overlay :
logger . debug ( " " )
logger . debug ( " Validating Method: append_label " )
logger . debug ( f " Value: { data [ methods [ ' append_label ' ] ] } " )
append_labels = util . get_list ( data [ methods [ " append_label " ] ] )
if " label.sync " in self . details :
self . details [ " label.sync " ] . extend ( append_labels )
elif " label " in self . details :
self . details [ " label " ] . extend ( append_labels )
else :
self . details [ " label " ] = append_labels
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 " )
2022-12-23 08:14:51 +00:00
if not isinstance ( self . custom_sort , list ) and self . custom_sort 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
2022-11-01 18:20:00 +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-07-04 06:49:52 +00:00
if self . smart_url :
self . sync = False
2022-10-21 21:09:36 +00:00
self . do_report = not self . config . no_report and ( self . details [ " save_report " ] )
self . do_missing = not self . config . no_missing and ( self . details [ " show_missing " ] or self . do_report
2022-04-13 04:30:59 +00:00
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 :
2022-12-20 22:04:02 +00:00
if self . obj and ( ( self . smart and not self . obj . smart ) or ( not self . smart and self . obj . smart ) ) :
logger . info ( " " )
logger . error ( f " { self . Type } Error: Converting { self . obj . title } to a { ' smart ' if self . smart else ' normal ' } collection " )
self . library . delete ( self . obj )
2022-11-28 22:00:54 +00:00
self . obj = None
2022-11-19 20:17:43 +00:00
if self . smart :
check_url = self . smart_url if self . smart_url else self . smart_label_url
2023-10-10 19:57:10 +00:00
if self . obj :
if check_url != self . library . smart_filter ( self . obj ) :
self . library . update_smart_collection ( self . obj , check_url )
2023-12-26 22:07:38 +00:00
logger . info ( f " Metadata: Smart Collection updated to { check_url } " )
2023-10-11 20:03:12 +00:00
self . beginning_count = len ( self . library . fetchItems ( check_url ) ) if check_url else 0
2021-10-04 17:51:32 +00:00
if self . obj :
self . exists = True
2022-02-21 20:56:38 +00:00
if self . sync or self . playlist :
2023-10-11 20:03:12 +00:00
self . remove_item_map = { i . ratingKey : i for i in self . library . get_collection_items ( self . obj , self . smart_label_collection ) }
if not self . smart :
self . beginning_count = len ( self . remove_item_map ) if self . playlist else self . obj . childCount
2021-05-19 20:42:05 +00:00
else :
2021-07-12 02:28:52 +00:00
self . obj = None
2023-06-05 18:29:59 +00:00
if self . sync :
logger . warning ( f " { self . Type } Error: Sync Mode can only be append when using build_collection: false " )
self . sync = False
2021-05-19 20:42:05 +00:00
self . run_again = False
2022-06-27 20:46:46 +00:00
if self . non_existing is not False and self . obj :
raise NotScheduled ( self . non_existing )
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 " :
2023-04-18 17:50:39 +00:00
self . summaries [ method_name ] = str ( method_data ) . replace ( " <<key_name>> " , self . key_name ) if self . key_name else method_data
2021-07-21 20:59:27 +00:00
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 " :
2022-12-19 21:36:51 +00:00
summary , _ = self . config . TVDb . get_list_description ( method_data )
if summary :
self . summaries [ method_name ] = summary
2021-07-21 20:59:27 +00:00
elif method_name == " trakt_description " :
2023-09-01 14:11:00 +00:00
try :
self . summaries [ method_name ] = self . config . Trakt . list_description ( self . config . Trakt . validate_list ( method_data ) [ 0 ] )
except Failed as e :
logger . error ( f " Trakt Error: List description not found: { e } " )
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 " :
2022-05-31 15:37:55 +00:00
try :
2024-04-08 01:50:32 +00:00
if not method_data . startswith ( " https://theposterdb.com/api/assets/ " ) :
image_response = self . config . get ( method_data , headers = util . header ( ) )
if image_response . status_code > = 400 or image_response . headers [ " Content-Type " ] not in util . image_content_types :
raise ConnectionError
2022-05-21 23:20:31 +00:00
self . posters [ method_name ] = method_data
2022-05-31 15:37:55 +00:00
except ConnectionError :
logger . warning ( f " { self . Type } Warning: No Poster Found at { method_data } " )
2022-12-19 21:36:51 +00:00
elif method_name == " tmdb_list_poster " :
self . posters [ method_name ] = self . config . TMDb . get_list ( util . regex_first_int ( method_data , " TMDb List ID " ) ) . poster_url
elif method_name == " tvdb_list_poster " :
_ , poster = self . config . TVDb . get_list_description ( method_data )
if poster :
self . posters [ method_name ] = poster
2021-07-21 20:59:27 +00:00
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 " :
2022-05-31 15:37:55 +00:00
try :
image_response = self . config . get ( method_data , headers = util . header ( ) )
2023-03-21 07:12:50 +00:00
if image_response . status_code > = 400 or image_response . headers [ " Content-Type " ] not in util . image_content_types :
2022-05-31 15:37:55 +00:00
raise ConnectionError
2022-05-21 23:20:31 +00:00
self . backgrounds [ method_name ] = method_data
2022-05-31 15:37:55 +00:00
except ConnectionError :
logger . warning ( f " { self . Type } Warning: No Background Found at { method_data } " )
2021-07-21 20:59:27 +00:00
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-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 )
2022-05-17 13:35:37 +00:00
elif method_name == " default_percent " :
self . default_percent = util . parse ( self . Type , method_name , method_data , datatype = " int " , minimum = 1 , maximum = 100 )
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-08-31 13:49:52 +00:00
if method_name == " item_genre " :
if " item_genre " in methods and " item_genre.sync " in methods :
raise Failed ( f " { self . Type } Error: Cannot use item_genre and item_genre.sync together " )
if " item_genre.remove " in methods and " item_genre.sync " in methods :
raise Failed ( f " { self . Type } Error: Cannot use item_genre.remove and item_genre.sync together " )
self . item_details [ method_final ] = util . get_list ( method_data ) if method_data else [ ]
2022-09-20 02:46:52 +00:00
elif method_name == " item_edition " :
2023-04-28 03:43:26 +00:00
self . item_details [ method_final ] = str ( method_data ) if method_data else " " # noqa
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 " "
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 :
2023-04-28 03:43:26 +00:00
self . item_details [ method_name ] = str ( method_data ) . lower ( ) # noqa
2021-07-21 20:59:27 +00:00
def _radarr ( self , method_name , method_data ) :
2024-01-19 00:04:03 +00:00
if method_name in [ " radarr_add_missing " , " radarr_add_existing " , " radarr_upgrade_existing " , " radarr_monitor_existing " , " radarr_search " , " radarr_monitor " , " radarr_ignore_cache " ] :
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 ) :
2024-01-19 00:04:03 +00:00
if method_name in [ " sonarr_add_missing " , " sonarr_add_existing " , " sonarr_upgrade_existing " , " sonarr_monitor_existing " , " sonarr_season " , " sonarr_search " , " sonarr_cutoff_search " , " sonarr_ignore_cache " ] :
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 }
2023-11-08 22:00:47 +00:00
new_dictionary = {
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 ) ,
2023-11-08 22:00:47 +00:00
}
score_dict = { }
for search_method , search_data in dict_data . items ( ) :
search_attr , modifier = os . path . splitext ( str ( search_method ) . lower ( ) )
2024-05-16 13:29:20 +00:00
if search_attr == " score " and modifier in [ " .gt " , " .gte " , " .lt " , " .lte " ] :
score = util . parse ( self . Type , search_method , dict_data , methods = dict_methods , datatype = " int " , default = - 1 , minimum = 0 , maximum = 10 , parent = method_name )
2023-11-08 22:00:47 +00:00
if score > - 1 :
score_dict [ modifier ] = score
elif search_attr not in [ " username " , " list_name " , " sort_by " ] :
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute not supported " )
new_dictionary [ " score " ] = score_dict
self . builders . append ( ( method_name , self . config . AniList . validate_userlist ( new_dictionary ) ) )
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 ( ) :
2023-12-07 19:49:32 +00:00
lower_method = str ( search_method ) . lower ( )
search_attr , modifier = os . path . splitext ( lower_method )
if lower_method not in anilist . searches :
2022-01-12 16:12:38 +00:00
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 " ] :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 " ] :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 " ] :
2023-12-07 19:49:32 +00:00
new_dictionary [ search_attr ] = util . parse ( self . Type , search_attr , search_data , datatype = " date " , parent = method_name , date_return = " % 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 )
2023-12-07 19:49:32 +00:00
elif lower_method not in [ " sort_by " , " limit " ] :
2022-01-12 16:12:38 +00:00
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
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 " :
2023-12-28 19:55:46 +00:00
try :
for imdb_dict in self . config . IMDb . validate_imdb_lists ( self . Type , method_data , self . language ) :
self . builders . append ( ( method_name , imdb_dict ) )
except Failed as e :
logger . error ( e )
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 " )
2023-01-03 22:07:05 +00:00
elif value in imdb . movie_charts or value in imdb . show_charts :
2021-12-10 08:13:26 +00:00
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 ] } " )
2022-08-29 19:07:01 +00:00
elif method_name == " imdb_watchlist " :
for imdb_user in self . config . IMDb . validate_imdb_watchlists ( self . Type , method_data , self . language ) :
self . builders . append ( ( method_name , imdb_user ) )
2023-12-12 21:24:37 +00:00
elif method_name == " imdb_award " :
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
event_id = util . parse ( self . Type , " event_id " , dict_data , parent = method_name , methods = dict_methods , regex = ( r " (ev \ d+) " , " ev0000003 " ) )
2024-01-01 23:19:29 +00:00
git_event , year_options = self . config . IMDb . get_event_years ( event_id )
2023-12-12 21:24:37 +00:00
if not year_options :
raise Failed ( f " { self . Type } Error: imdb_award event_id attribute: No event found at { imdb . base_url } /event/ { event_id } " )
2024-01-01 23:19:29 +00:00
if " event_year " not in dict_methods :
raise Failed ( f " { self . Type } Error: imdb_award event_year attribute not found " )
og_year = dict_data [ dict_methods [ " event_year " ] ]
if not og_year :
raise Failed ( f " { self . Type } Error: imdb_award event_year attribute is blank " )
2024-01-05 21:06:39 +00:00
if og_year in [ " all " , " latest " ] :
event_year = og_year
2024-01-01 23:19:29 +00:00
elif not isinstance ( og_year , list ) and " - " in str ( og_year ) and len ( str ( og_year ) ) > 7 :
try :
min_year , max_year = og_year . split ( " - " )
min_year = int ( min_year )
max_year = int ( max_year ) if max_year != " current " else None
event_year = [ ]
for option in year_options :
check = int ( option . split ( " - " ) [ 0 ] if " - " in option else option )
if check > = min_year and ( max_year is None or check < = max_year ) :
event_year . append ( option )
except ValueError :
raise Failed ( f " { self . Type } Error: imdb_award event_year attribute invalid: { og_year } " )
else :
event_year = util . parse ( self . Type , " event_year " , og_year , parent = method_name , datatype = " strlist " , options = year_options )
2024-01-03 19:06:05 +00:00
if ( event_year == " all " or len ( event_year ) > 1 ) and not git_event :
2024-01-02 13:51:40 +00:00
raise Failed ( f " { self . Type } Error: Only specific events work when using multiple years. Event Options: [ { ' , ' . join ( [ k for k in self . config . IMDb . events_validation ] ) } ] " )
2024-01-01 23:45:01 +00:00
award_filters = [ ]
if " award_filter " in dict_methods :
if not dict_data [ dict_methods [ " award_filter " ] ] :
raise Failed ( f " { self . Type } Error: imdb_award award_filter attribute is blank " )
award_filters = util . parse ( self . Type , " award_filter " , dict_data [ dict_methods [ " award_filter " ] ] , datatype = " lowerlist " )
category_filters = [ ]
if " category_filter " in dict_methods :
if not dict_data [ dict_methods [ " category_filter " ] ] :
raise Failed ( f " { self . Type } Error: imdb_award category_filter attribute is blank " )
category_filters = util . parse ( self . Type , " category_filter " , dict_data [ dict_methods [ " category_filter " ] ] , datatype = " lowerlist " )
2023-12-12 21:24:37 +00:00
final_category = [ ]
final_awards = [ ]
if award_filters or category_filters :
2024-01-05 21:06:39 +00:00
award_names , category_names = self . config . IMDb . get_award_names ( event_id , year_options [ 0 ] if event_year == " latest " else event_year )
2023-12-12 21:24:37 +00:00
lower_award = { a . lower ( ) : a for a in award_names if a }
for award_filter in award_filters :
if award_filter in lower_award :
final_awards . append ( lower_award [ award_filter ] )
else :
raise Failed ( f " { self . Type } Error: imdb_award award_filter attribute invalid: { award_filter } must be in in [ { ' , ' . join ( [ v for _ , v in lower_award . items ( ) ] ) } ] " )
lower_category = { c . lower ( ) : c for c in category_names if c }
for category_filter in category_filters :
if category_filter in lower_category :
final_category . append ( lower_category [ category_filter ] )
else :
raise Failed ( f " { self . Type } Error: imdb_award category_filter attribute invalid: { category_filter } must be in in [ { ' , ' . join ( [ v for _ , v in lower_category . items ( ) ] ) } ] " )
self . builders . append ( ( method_name , {
" event_id " : event_id , " event_year " : event_year , " award_filter " : final_awards if final_awards else None , " category_filter " : final_category if final_category else None ,
" winning " : util . parse ( self . Type , " winning " , dict_data , parent = method_name , methods = dict_methods , datatype = " bool " , default = False )
} ) )
2023-12-07 19:49:32 +00:00
elif method_name == " imdb_search " :
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
new_dictionary = { " limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , minimum = 0 , default = 100 , parent = method_name ) }
for search_method , search_data in dict_data . items ( ) :
lower_method = str ( search_method ) . lower ( )
search_attr , modifier = os . path . splitext ( lower_method )
if search_data is None :
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute is blank " )
elif lower_method not in imdb . imdb_search_attributes :
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute not supported " )
elif search_attr == " sort_by " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , parent = method_name , options = imdb . sort_options )
elif search_attr == " title " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , parent = method_name )
elif search_attr == " type " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name , options = imdb . title_type_options )
2024-04-08 01:50:10 +00:00
elif search_attr == " topic " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name , options = imdb . topic_options )
2023-12-07 19:49:32 +00:00
elif search_attr == " release " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " date " , parent = method_name , date_return = " % Y- % m- %d " )
elif search_attr == " rating " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " float " , parent = method_name , minimum = 0.1 , maximum = 10 )
elif search_attr in [ " votes " , " imdb_top " , " imdb_bottom " , " popularity " , " runtime " ] :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " int " , parent = method_name , minimum = 0 )
elif search_attr == " genre " :
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name , options = imdb . genre_options )
elif search_attr == " event " :
events = [ ]
for event in util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name ) :
if event in imdb . event_options :
events . append ( event )
else :
res = re . search ( r ' (ev \ d+) ' , event )
if res :
events . append ( res . group ( 1 ) )
else :
2024-01-03 21:57:07 +00:00
raise Failed ( f " { method_name } { search_method } attribute: { search_data } must match pattern ev \\ d+ e.g. ev0000292 or be one of { ' , ' . join ( [ e for e in imdb . event_options ] ) } " )
2023-12-07 19:49:32 +00:00
if events :
new_dictionary [ lower_method ] = events
elif search_attr == " company " :
companies = [ ]
for company in util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name ) :
if company in imdb . company_options :
companies . append ( company )
else :
res = re . search ( r ' (co \ d+) ' , company )
if res :
companies . append ( res . group ( 1 ) )
else :
2024-01-03 21:57:07 +00:00
raise Failed ( f " { method_name } { search_method } attribute: { search_data } must match pattern co \\ d+ e.g. co0098836 or be one of { ' , ' . join ( [ e for e in imdb . company_options ] ) } " )
2023-12-07 19:49:32 +00:00
if companies :
new_dictionary [ lower_method ] = companies
elif search_attr == " content_rating " :
final_list = [ ]
for content in util . get_list ( search_data ) :
if content :
final_dict = { " region " : " US " , " rating " : None }
if not isinstance ( content , dict ) :
final_dict [ " rating " ] = str ( content )
else :
if " rating " not in content or not content [ " rating " ] :
raise Failed ( f " { method_name } { search_method } attribute: rating attribute is required " )
final_dict [ " rating " ] = str ( content [ " rating " ] )
if " region " not in content or not content [ " region " ] :
logger . warning ( f " { method_name } { search_method } attribute: region attribute not found defaulting to ' US ' " )
elif len ( str ( content [ " region " ] ) ) != 2 :
logger . warning ( f " { method_name } { search_method } attribute: region attribute: { str ( content [ ' region ' ] ) } must be only 2 characters defaulting to ' US ' " )
else :
final_dict [ " region " ] = str ( content [ " region " ] ) . upper ( )
final_list . append ( final_dict )
if final_list :
new_dictionary [ lower_method ] = final_list
elif search_attr == " country " :
countries = [ ]
for country in util . parse ( self . Type , search_method , search_data , datatype = " upperlist " , parent = method_name ) :
if country :
if len ( str ( country ) ) != 2 :
raise Failed ( f " { method_name } { search_method } attribute: { country } must be only 2 characters i.e. ' US ' " )
countries . append ( str ( country ) )
if countries :
new_dictionary [ lower_method ] = countries
2024-04-08 01:50:10 +00:00
elif search_attr in [ " keyword " , " language " , " alternate_version " , " crazy_credit " , " location " , " goof " , " plot " , " quote " , " soundtrack " , " trivia " ] :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name )
elif search_attr == " cast " :
casts = [ ]
for cast in util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name ) :
res = re . search ( r ' (nm \ d+) ' , cast )
if res :
casts . append ( res . group ( 1 ) )
else :
2024-01-03 21:57:07 +00:00
raise Failed ( f " { method_name } { search_method } attribute: { search_data } must match pattern nm \\ d+ e.g. nm00988366 " )
2023-12-07 19:49:32 +00:00
if casts :
new_dictionary [ lower_method ] = casts
elif search_attr == " series " :
series = [ ]
for show in util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name ) :
res = re . search ( r ' (tt \ d+) ' , show )
if res :
series . append ( res . group ( 1 ) )
else :
2024-01-03 21:57:07 +00:00
raise Failed ( f " { method_name } { search_method } attribute: { search_data } must match pattern tt \\ d+ e.g. tt00988366 " )
2023-12-07 19:49:32 +00:00
if series :
new_dictionary [ lower_method ] = series
elif search_attr == " list " :
lists = [ ]
for new_list in util . parse ( self . Type , search_method , search_data , datatype = " lowerlist " , parent = method_name ) :
res = re . search ( r ' (ls \ d+) ' , new_list )
if res :
lists . append ( res . group ( 1 ) )
else :
2024-01-03 21:57:07 +00:00
raise Failed ( f " { method_name } { search_method } attribute: { search_data } must match pattern ls \\ d+ e.g. ls000024621 " )
2023-12-07 19:49:32 +00:00
if lists :
new_dictionary [ lower_method ] = lists
elif search_attr == " adult " :
if util . parse ( self . Type , search_method , search_data , datatype = " bool " , parent = method_name ) :
new_dictionary [ lower_method ] = True
2023-12-08 19:15:41 +00:00
elif search_attr != " limit " :
2023-12-07 19:49:32 +00:00
raise Failed ( f " { self . Type } Error: { method_name } { search_method } attribute not supported " )
if len ( new_dictionary ) > 1 :
self . builders . append ( ( method_name , new_dictionary ) )
else :
raise Failed ( f " { self . Type } Error: { method_name } had no valid fields " )
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 ) ,
2022-12-13 23:58:44 +00:00
" limit " : util . parse ( self . Type , " limit " , dict_data , datatype = " int " , methods = dict_methods , default = 100 , parent = method_name , maximum = 500 ) ,
" starting_only " : util . parse ( self . Type , " starting_only " , dict_data , datatype = " bool " , methods = dict_methods , default = False , parent = method_name )
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 :
2022-12-13 17:15:50 +00:00
genre_str = str ( util . parse ( self . Type , " genre " , dict_data , methods = dict_methods , parent = method_name ) )
2023-01-24 19:07:42 +00:00
out_text , out_ints = util . parse_and_or ( self . Type , ' Genre ' , genre_str , self . config . MyAnimeList . genres )
final_text + = f " \n Genre: { out_text } "
final_attributes [ " genres " ] = out_ints
2022-05-03 18:44:19 +00:00
if " genre.not " in dict_methods :
2022-12-13 17:15:50 +00:00
genre_str = str ( util . parse ( self . Type , " genre.not " , dict_data , methods = dict_methods , parent = method_name ) )
2023-01-24 19:07:42 +00:00
out_text , out_ints = util . parse_and_or ( self . Type , ' Genre ' , genre_str , self . config . MyAnimeList . genres )
final_text + = f " \n Not Genre: { out_text } "
final_attributes [ " genres_exclude " ] = out_ints
2022-05-03 18:44:19 +00:00
if " studio " in dict_methods :
2022-12-13 17:15:50 +00:00
studio_str = str ( util . parse ( self . Type , " studio " , dict_data , methods = dict_methods , parent = method_name ) )
2023-01-24 19:07:42 +00:00
out_text , out_ints = util . parse_and_or ( self . Type , ' Studio ' , studio_str , self . config . MyAnimeList . studios )
final_text + = f " \n Studio: { out_text } "
final_attributes [ " producers " ] = out_ints
2022-05-03 18:44:19 +00:00
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 " ] :
2022-05-11 15:55:07 +00:00
raise Failed ( f " { self . Type } Error: mal_search score.lte/score.lt attribute must be greater than score.gte/score.gt " )
2022-05-03 18:44:19 +00:00
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
2023-04-17 13:42:36 +00:00
final_text + = f " \n Safe for Work: { final_attributes [ ' sfw ' ] } "
2022-05-03 18:44:19 +00:00
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 " ] :
2022-12-07 19:44:15 +00:00
logger . warning ( f " Config Warning: { method_name } will run as a mal_search " )
item_list = util . parse ( self . Type , method_name [ 4 : ] , method_data , datatype = " commalist " )
all_items = self . config . MyAnimeList . genres if method_name == " mal_genre " else self . config . MyAnimeList . studios
final_items = [ str ( all_items [ i ] ) for i in item_list if i in all_items ]
final_text = f " MyAnimeList Search \n { method_name [ 4 : ] . capitalize ( ) } : { ' or ' . join ( [ str ( all_items [ i ] ) for i in final_items ] ) } "
self . builders . append ( ( " mal_search " , ( { " genres " if method_name == " mal_genre " else " producers " : " , " . join ( final_items ) } , final_text , 0 ) ) )
2021-07-23 18:45:49 +00:00
2024-01-15 21:38:54 +00:00
def _mojo ( self , method_name , method_data ) :
for dict_data in util . parse ( self . Type , method_name , method_data , datatype = " listdict " ) :
dict_methods = { dm . lower ( ) : dm for dm in dict_data }
final = { }
if method_name == " mojo_record " :
final [ " chart " ] = util . parse ( self . Type , " chart " , dict_data , methods = dict_methods , parent = method_name , options = mojo . top_options )
elif method_name == " mojo_world " :
if " year " not in dict_methods :
raise Failed ( f " { self . Type } Error: { method_name } year attribute not found " )
og_year = dict_data [ dict_methods [ " year " ] ]
if not og_year :
raise Failed ( f " { self . Type } Error: { method_name } year attribute is blank " )
if og_year == " current " :
final [ " year " ] = str ( self . current_year ) # noqa
elif str ( og_year ) . startswith ( " current- " ) :
try :
final [ " year " ] = str ( self . current_year - int ( og_year . split ( " - " ) [ 1 ] ) ) # noqa
if final [ " year " ] not in mojo . year_options :
raise Failed ( f " { self . Type } Error: { method_name } year attribute final value must be 1977 or greater: { og_year } " )
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } year attribute invalid: { og_year } " )
else :
final [ " year " ] = util . parse ( self . Type , " year " , dict_data , methods = dict_methods , parent = method_name , options = mojo . year_options )
elif method_name == " mojo_all_time " :
final [ " chart " ] = util . parse ( self . Type , " chart " , dict_data , methods = dict_methods , parent = method_name , options = mojo . chart_options )
final [ " content_rating_filter " ] = util . parse ( self . Type , " content_rating_filter " , dict_data , methods = dict_methods , parent = method_name , options = mojo . content_rating_options ) if " content_rating_filter " in dict_methods else None
elif method_name == " mojo_never " :
final [ " chart " ] = util . parse ( self . Type , " chart " , dict_data , methods = dict_methods , parent = method_name , default = " domestic " , options = self . config . BoxOfficeMojo . never_options )
final [ " never " ] = str ( util . parse ( self . Type , " never " , dict_data , methods = dict_methods , parent = method_name , default = " 1 " , options = mojo . never_in_options ) ) if " never " in dict_methods else " 1 "
elif method_name in [ " mojo_domestic " , " mojo_international " ] :
dome = method_name == " mojo_domestic "
final [ " range " ] = util . parse ( self . Type , " range " , dict_data , methods = dict_methods , parent = method_name , options = mojo . dome_range_options if dome else mojo . intl_range_options )
if not dome :
final [ " chart " ] = util . parse ( self . Type , " chart " , dict_data , methods = dict_methods , parent = method_name , default = " international " , options = self . config . BoxOfficeMojo . intl_options )
chart_date = self . current_time
if final [ " range " ] != " daily " :
_m = " range_data " if final [ " range " ] == " yearly " and " year " not in dict_methods and " range_data " in dict_methods else " year "
if _m not in dict_methods :
raise Failed ( f " { self . Type } Error: { method_name } { _m } attribute not found " )
og_year = dict_data [ dict_methods [ _m ] ]
if not og_year :
raise Failed ( f " { self . Type } Error: { method_name } { _m } attribute is blank " )
if str ( og_year ) . startswith ( " current- " ) :
try :
chart_date = self . current_time - relativedelta ( years = int ( og_year . split ( " - " ) [ 1 ] ) )
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } { _m } attribute invalid: { og_year } " )
else :
_y = util . parse ( self . Type , _m , dict_data , methods = dict_methods , parent = method_name , default = " current " , options = mojo . year_options )
if _y != " current " :
chart_date = self . current_time - relativedelta ( years = self . current_time . year - _y )
if final [ " range " ] != " yearly " :
if " range_data " not in dict_methods :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute not found " )
og_data = dict_data [ dict_methods [ " range_data " ] ]
if not og_data :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute is blank " )
if final [ " range " ] == " holiday " :
final [ " range_data " ] = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , options = mojo . holiday_options )
elif final [ " range " ] == " daily " :
if og_data == " current " :
final [ " range_data " ] = datetime . strftime ( self . current_time , " % Y- % m- %d " ) # noqa
elif str ( og_data ) . startswith ( " current- " ) :
try :
final [ " range_data " ] = datetime . strftime ( self . current_time - timedelta ( days = int ( og_data . split ( " - " ) [ 1 ] ) ) , " % Y- % m- %d " ) # noqa
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute invalid: { og_data } " )
else :
final [ " range_data " ] = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , default = " current " , datatype = " date " , date_return = " % Y- % m- %d " )
if final [ " range_data " ] == " current " :
final [ " range_data " ] = datetime . strftime ( self . current_time , " % Y- % m- %d " ) # noqa
elif final [ " range " ] in [ " weekend " , " weekly " ] :
if str ( og_data ) . startswith ( " current- " ) :
try :
final_date = chart_date - timedelta ( weeks = int ( og_data . split ( " - " ) [ 1 ] ) )
final_iso = final_date . isocalendar ( )
final [ " range_data " ] = final_iso . week
final [ " year " ] = final_iso . year
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute invalid: { og_data } " )
else :
_v = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , default = " current " , options = [ " current " ] + [ str ( i ) for i in range ( 1 , 54 ) ] )
current_iso = chart_date . isocalendar ( )
final [ " range_data " ] = current_iso . week if _v == " current " else _v
final [ " year " ] = current_iso . year
elif final [ " range " ] == " monthly " :
if str ( og_data ) . startswith ( " current- " ) :
try :
final_date = chart_date - relativedelta ( months = int ( og_data . split ( " - " ) [ 1 ] ) )
final [ " range_data " ] = final_date . month
final [ " year " ] = final_date . year
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute invalid: { og_data } " )
else :
_v = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , default = " current " , options = [ " current " ] + util . lower_months )
final [ " range_data " ] = chart_date . month if _v == " current " else util . lower_months [ _v ]
elif final [ " range " ] == " quarterly " :
if str ( og_data ) . startswith ( " current- " ) :
try :
final_date = chart_date - relativedelta ( months = int ( og_data . split ( " - " ) [ 1 ] ) * 3 )
final [ " range_data " ] = mojo . quarters [ final_date . month ]
final [ " year " ] = final_date . year
except ValueError :
raise Failed ( f " { self . Type } Error: { method_name } range_data attribute invalid: { og_data } " )
else :
_v = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , default = " current " , options = mojo . quarter_options )
final [ " range_data " ] = mojo . quarters [ chart_date . month ] if _v == " current " else _v
elif final [ " range " ] == " season " :
_v = util . parse ( self . Type , " range_data " , dict_data , methods = dict_methods , parent = method_name , default = " current " , options = mojo . season_options )
final [ " range_data " ] = mojo . seasons [ chart_date . month ] if _v == " current " else _v
else :
final [ " range_data " ] = chart_date . year
if " year " not in final :
final [ " year " ] = chart_date . year
if final [ " year " ] < 1977 :
raise Failed ( f " { self . Type } Error: { method_name } attribute final date value must be on year 1977 or greater: { final [ ' year ' ] } " )
final [ " limit " ] = util . parse ( self . Type , " limit " , dict_data , methods = dict_methods , parent = method_name , default = 0 , datatype = " int " , maximum = 1000 ) if " limit " in dict_methods else 0
self . builders . append ( ( method_name , final ) )
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-07-26 19:58:53 +00:00
self . builders . append ( ( method_name , self . builder_level ) )
2022-09-08 20:11:11 +00:00
elif method_name == " plex_watchlist " :
if method_data not in plex . watchlist_sorts :
logger . warning ( f " { self . Type } Warning: Watchlist sort: { method_data } invalid defaulting to added.asc " )
self . builders . append ( ( method_name , method_data if method_data in plex . watchlist_sorts else " added.asc " ) )
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
if method_name == " plex_search " :
2022-11-11 16:59:39 +00:00
try :
self . builders . append ( ( method_name , self . build_filter ( " plex_search " , dict_data ) ) )
except FilterFailed as e :
if self . ignore_blank_results :
raise
else :
raise Failed ( str ( e ) )
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 )
2022-05-31 13:29:37 +00:00
self . builders . append ( ( method_name , { " exclude_prefix " : prefix_list , " exclude " : exact_list } ) )
2021-07-23 18:45:49 +00:00
else :
2022-11-11 16:59:39 +00:00
try :
self . builders . append ( ( " plex_search " , self . build_filter ( " plex_search " , { " any " : { method_name : method_data } } ) ) )
except FilterFailed as e :
if self . ignore_blank_results :
raise
else :
raise Failed ( str ( e ) )
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 ) :
2024-05-01 16:17:46 +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 }
2023-04-28 03:01:33 +00:00
final_dict = {
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_minimum " : util . parse ( self . Type , " list_minimum " , dict_data , datatype = " int " , methods = dict_methods , default = 0 , parent = method_name )
2023-04-28 03:01:33 +00:00
}
2023-04-28 03:43:26 +00:00
buff = final_dict [ " list_size " ] * 3
2023-04-28 03:01:33 +00:00
if self . library . Tautulli . has_section :
2023-04-28 03:43:26 +00:00
buff = 0
2023-04-28 03:01:33 +00:00
elif " list_buffer " in dict_methods :
2023-04-28 03:43:26 +00:00
buff = util . parse ( self . Type , " list_buffer " , dict_data , datatype = " int " , methods = dict_methods , default = buff , parent = method_name )
final_dict [ " list_buffer " ] = buff
2023-04-28 03:01:33 +00:00
self . builders . append ( ( method_name , final_dict ) )
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 ( ) :
2023-12-07 19:49:32 +00:00
lower_method = str ( discover_method ) . lower ( )
discover_attr , modifier = os . path . splitext ( lower_method )
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 " )
2023-12-07 19:49:32 +00:00
elif discover_method . lower ( ) not in tmdb . discover_all :
2022-01-12 16:12:38 +00:00
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 " :
2024-03-19 20:31:25 +00:00
new_dictionary [ discover_attr ] = util . parse ( self . Type , discover_method , discover_data . upper ( ) , 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
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , 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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 :
2024-03-19 20:31:25 +00:00
new_dictionary [ lower_method ] = discover_data . upper ( )
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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , 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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " bool " , parent = method_name )
2021-11-29 00:27:15 +00:00
elif discover_attr == " vote_average " :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 " :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " int " , parent = method_name , minimum = 0 , maximum = 5 )
2021-11-26 08:24:36 +00:00
elif discover_attr == " with_type " :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " int " , parent = method_name , minimum = 0 , maximum = 6 )
2022-03-21 17:40:32 +00:00
elif discover_attr in tmdb . discover_dates :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , discover_data , datatype = " date " , parent = method_name , date_return = " % m/ %d / % Y " )
2021-11-29 00:27:15 +00:00
elif discover_attr in tmdb . discover_years :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_method ] = util . parse ( self . Type , discover_method , 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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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 :
2023-12-07 19:49:32 +00:00
new_dictionary [ lower_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
2022-12-19 21:36:51 +00:00
if item . poster_url :
self . posters [ method_name ] = item . poster_url
2021-07-23 18:45:49 +00:00
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 " ) :
2023-09-01 14:11:00 +00:00
try :
self . summaries [ method_name ] = self . config . Trakt . list_description ( trakt_lists [ 0 ] )
except Failed as e :
logger . error ( f " Trakt Error: List description not found: { e } " )
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 ) :
2024-01-24 18:41:53 +00:00
trakt_dicts . append ( { " userlist " : method_name [ 6 : ] , " user " : trakt_user } )
2022-03-27 06:26:08 +00:00
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 } " )
2024-02-28 20:40:17 +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 " ) )
2024-04-03 13:13:28 +00:00
if item . summary :
self . summaries [ method_name ] = item . summary
2022-05-05 22:05:16 +00:00
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 " ) :
2022-12-19 21:36:51 +00:00
description , poster = self . config . TVDb . get_list_description ( values [ 0 ] )
if description :
self . summaries [ method_name ] = description
if poster :
self . posters [ method_name ] = poster
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-10-03 19:32:50 +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 }
current_filters = [ ]
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 ( ) :
filter_attr , modifier , filter_final = self . library . split ( filter_method )
message = None
if filter_final not in all_filters :
message = f " { self . Type } Error: { filter_final } is not a valid filter attribute "
elif self . builder_level in filters and filter_attr not in filters [ self . builder_level ] :
message = f " { self . Type } Error: { filter_final } is not a valid { self . builder_level } filter attribute "
elif filter_final is None :
message = f " { self . Type } Error: { filter_final } filter attribute is blank "
2021-07-23 18:45:49 +00:00
else :
2023-02-01 19:51:22 +00:00
try :
final_data = self . validate_attribute ( filter_attr , modifier , f " { filter_final } filter " , filter_data , validate )
except FilterFailed as e :
raise Failed ( e )
2022-10-24 06:17:49 +00:00
if self . builder_level in [ " show " , " season " , " artist " , " album " ] and filter_attr in sub_filters :
2022-10-03 19:32:50 +00:00
current_filters . append ( ( " episodes " if self . builder_level in [ " show " , " season " ] else " tracks " , { filter_final : final_data , " percentage " : self . default_percent } ) )
else :
current_filters . append ( ( filter_final , final_data ) )
if message :
if validate :
raise Failed ( message )
else :
logger . error ( message )
if current_filters :
self . filters . append ( current_filters )
2023-04-17 01:39:36 +00:00
self . has_tmdb_filters = any ( [ str ( k ) . split ( " . " ) [ 0 ] in tmdb_filters for f in self . filters for k , v in f ] )
self . has_imdb_filters = any ( [ str ( k ) . split ( " . " ) [ 0 ] in imdb_filters for f in self . filters for k , v in f ] )
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-09-08 20:11:11 +00:00
ids = self . library . get_rating_keys ( method , value , self . playlist )
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 " 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 )
2024-01-15 21:38:54 +00:00
elif " mojo " in method :
ids = self . config . BoxOfficeMojo . get_imdb_ids ( method , value )
2022-01-15 22:40:59 +00:00
elif " mdblist " in method :
2024-05-01 16:17:46 +00:00
ids = self . config . MDBList . get_tmdb_ids ( method , value , self . library . is_movie if not self . playlist else None )
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-10-17 06:48:08 +00:00
logger . debug ( f " { total_ids } IDs Found " )
logger . trace ( f " IDs: { ids } " )
2022-02-08 22:57:36 +00:00
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
2022-07-26 19:58:53 +00:00
if not found and ( self . builder_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-07-26 19:58:53 +00:00
if tmdb_type == " episode " and ( self . builder_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
2023-02-27 19:54:52 +00:00
show_item = pl_library . fetch_item ( pl_library . show_map [ tvdb_id ] [ 0 ] )
2022-02-08 22:57:36 +00:00
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 )
2022-09-25 18:43:58 +00:00
elif tmdb_type in [ " show " , " episode " ] and self . do_missing :
if tmdb_type == " episode " :
tmdb_id , _ , _ = _id . split ( " _ " )
else :
tmdb_id = _id
tvdb_id = self . config . Convert . tmdb_to_tvdb ( tmdb_id , fail = True )
2022-02-08 22:57:36 +00:00
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 )
2022-07-26 19:58:53 +00:00
elif id_type == " tvdb_season " and ( self . builder_level == " season " or self . playlist ) :
2022-02-08 22:57:36 +00:00
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
2023-02-27 19:54:52 +00:00
show_item = pl_library . fetch_item ( pl_library . show_map [ tvdb_id ] [ 0 ] )
2022-02-08 22:57:36 +00:00
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 )
2022-07-26 19:58:53 +00:00
elif id_type == " tvdb_episode " and ( self . builder_level == " episode " or self . playlist ) :
2022-02-08 22:57:36 +00:00
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
2023-02-27 19:54:52 +00:00
show_item = pl_library . fetch_item ( 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 )
2022-10-06 19:50:37 +00:00
elif id_type in [ " tvdb " , " tmdb_show " , " tvdb_season " , " tvdb_episode " ] :
tvdb_season = None
2022-09-26 13:59:54 +00:00
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 )
continue
elif id_type == " tvdb_season " :
2022-10-06 19:50:37 +00:00
tvdb_id , tvdb_season = input_id . split ( " _ " )
2022-09-26 13:59:54 +00:00
tvdb_id = int ( tvdb_id )
2022-10-06 19:50:37 +00:00
tvdb_season = int ( tvdb_season )
2022-09-26 13:59:54 +00:00
elif id_type == " tvdb_episode " :
tvdb_id , _ , _ = input_id . split ( " _ " )
tvdb_id = int ( tvdb_id )
else :
tvdb_id = int ( input_id )
if tvdb_id not in self . ignore_ids :
2022-10-06 19:50:37 +00:00
found_keys = None
2022-09-26 13:59:54 +00:00
for pl_library in self . libraries :
if tvdb_id in pl_library . show_map :
2022-10-06 19:50:37 +00:00
found_keys = pl_library . show_map [ tvdb_id ]
2022-09-26 13:59:54 +00:00
break
2022-10-06 19:50:37 +00:00
if not found_keys and tvdb_id not in self . missing_shows :
2022-09-26 13:59:54 +00:00
self . missing_shows . append ( tvdb_id )
2022-10-06 19:50:37 +00:00
if found_keys :
if self . parts_collection :
rating_keys = [ ]
for rk in found_keys :
try :
2023-02-27 19:54:52 +00:00
item = self . library . fetch_item ( rk )
2022-10-06 19:50:37 +00:00
if self . builder_level == " episode " and isinstance ( item , Show ) :
if tvdb_season is not None :
item = item . season ( season = tvdb_season )
rating_keys . extend ( [ k . ratingKey for k in item . episodes ( ) ] )
elif self . builder_level == " season " and isinstance ( item , Show ) :
rating_keys . extend ( [ k . ratingKey for k in item . seasons ( ) ] )
except Failed as e :
logger . error ( e )
else :
rating_keys = found_keys
2022-02-08 22:57:36 +00:00
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 :
2023-02-27 19:54:52 +00:00
item = self . library . fetch_item ( rk )
2022-02-08 22:57:36 +00:00
if self . playlist and isinstance ( item , ( Show , Season ) ) :
items . extend ( item . episodes ( ) )
2022-07-28 04:51:01 +00:00
elif self . builder_level == " movie " and not isinstance ( item , Movie ) :
logger . info ( f " Item: { item } is not an Movie " )
elif self . builder_level == " show " and not isinstance ( item , Show ) :
logger . info ( f " Item: { item } is not an Show " )
elif self . builder_level == " episode " and not isinstance ( item , Episode ) :
logger . info ( f " Item: { item } is not an Episode " )
elif self . builder_level == " season " and not isinstance ( item , Season ) :
logger . info ( f " Item: { item } is not a Season " )
elif self . builder_level == " artist " and not isinstance ( item , Artist ) :
logger . info ( f " Item: { item } is not an Artist " )
elif self . builder_level == " album " and not isinstance ( item , Album ) :
logger . info ( f " Item: { item } is not an Album " )
elif self . builder_level == " track " and not isinstance ( item , Track ) :
logger . info ( f " Item: { item } is not a Track " )
2022-02-08 22:57:36 +00:00
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 ) )
2022-10-24 06:17:49 +00:00
if self . filters and self . details [ " show_filtered " ] is True :
2021-12-15 15:50:20 +00:00
logger . info ( " " )
logger . info ( " Filtering Builders: " )
2022-05-10 18:50:19 +00:00
filtered_items = [ ]
2021-12-15 15:50:20 +00:00
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
2022-11-07 17:05:21 +00:00
if item not in self . found_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 } " ) :
2022-11-07 17:05:21 +00:00
self . found_items . append ( item )
2021-12-15 15:50:20 +00:00
else :
2022-05-10 18:50:19 +00:00
filtered_items . append ( item )
2021-12-15 15:50:20 +00:00
self . filtered_keys [ item . ratingKey ] = current_title
if self . details [ " show_filtered " ] is True :
logger . info ( f " { name } { self . Type } | X | { current_title } " )
2022-10-21 21:09:36 +00:00
if self . do_report and filtered_items :
2022-05-10 18:50:19 +00:00
self . library . add_filtered ( self . name , [ ( i . title , self . library . get_id_from_maps ( i . ratingKey ) ) for i in filtered_items ] , self . library . is_movie )
2021-08-05 14:59:45 +00:00
2022-05-31 13:29:37 +00:00
def build_filter ( self , method , plex_filter , display = False , default_sort = 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
2022-07-26 19:58:53 +00:00
if self . builder_level == " item " :
2022-09-13 18:47:24 +00:00
if " type " in filter_alias :
if plex_filter [ filter_alias [ " type " ] ] is None :
raise Failed ( f " { self . Type } Error: type attribute is blank " )
if plex_filter [ filter_alias [ " type " ] ] not in plex . sort_types :
raise Failed ( f " { self . Type } Error: type: { plex_filter [ filter_alias [ ' type ' ] ] } is invalid. Options: { ' , ' . join ( plex . sort_types ) } " )
sort_type = plex_filter [ filter_alias [ " type " ] ]
elif self . library . is_show :
2022-05-31 13:29:37 +00:00
sort_type = " show "
elif self . library . is_music :
sort_type = " artist "
else :
sort_type = " movie "
2021-05-29 02:33:55 +00:00
else :
2022-07-26 19:58:53 +00:00
sort_type = self . builder_level
2022-05-31 13:29:37 +00:00
2021-05-29 02:33:55 +00:00
ms = method . split ( " _ " )
2022-05-31 13:29:37 +00:00
filter_details = f " { ms [ 0 ] . capitalize ( ) } { sort_type . capitalize ( ) } { 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-09-22 03:20:21 +00:00
sort = [ ]
2021-05-29 02:33:55 +00:00
if " sort_by " in filter_alias :
2022-09-22 03:20:21 +00:00
test_sorts = plex_filter [ filter_alias [ " sort_by " ] ]
if test_sorts is None :
2021-12-13 07:30:19 +00:00
raise Failed ( f " { self . Type } Error: sort_by attribute is blank " )
2022-09-22 03:20:21 +00:00
if not isinstance ( test_sorts , list ) :
test_sorts = [ test_sorts ]
for test_sort in test_sorts :
if test_sort not in sorts :
raise Failed ( f " { self . Type } Error: sort_by: { test_sort } is invalid. Options: { ' , ' . join ( sorts ) } " )
sort . append ( test_sort )
if not sort :
sort . append ( default_sort if default_sort else type_default_sort )
2021-05-29 02:33:55 +00:00
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 "
else :
2022-08-24 13:00:34 +00:00
try :
if int ( plex_filter [ filter_alias [ " limit " ] ] ) < 1 :
raise ValueError
else :
limit = int ( plex_filter [ filter_alias [ " limit " ] ] )
filter_details + = f " Limit: { limit } \n "
except ValueError :
raise Failed ( f " { self . Type } Error: limit attribute must be an integer greater than 0 " )
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-06-22 07:19:15 +00:00
elif _data is not False and _data != 0 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-06-21 14:07:44 +00:00
if validation is not False and validation != 0 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 "
2022-06-09 13:40:47 +00:00
search_mod = validation [ - 1 ]
if search_mod == " o " :
validation = f " { validation [ : - 1 ] } mon "
results , display_add = build_url_arg ( f " - { validation } " , mod = last_mod , arg_s = f " { validation } { plex . date_sub_mods [ search_mod ] } " , mod_s = last_text )
2022-01-23 19:40:16 +00:00
elif attr == " duration " and modifier in [ " .gt " , " .gte " , " .lt " , " .lte " ] :
results , display_add = build_url_arg ( validation * 60000 )
2022-06-26 19:08:17 +00:00
elif modifier == " .rated " :
2022-06-27 18:33:54 +00:00
results , display_add = build_url_arg ( - 1 , mod = " ! " if validation else " " , arg_s = " Rated " , mod_s = " is " if validation else " is not " )
2022-01-23 19:40:16 +00:00
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 "
2022-09-22 03:20:21 +00:00
filter_url = f " ?type= { type_key } & { f ' limit= { limit } & ' if limit else ' ' } sort= { ' % 2C ' . join ( [ sorts [ s ] for s in sort ] ) } & { final_filter } "
2021-05-29 02:33:55 +00:00
else :
2022-11-11 16:59:39 +00:00
raise FilterFailed ( f " { self . Type } Error: No Plex Filter Created " )
2021-05-29 02:33:55 +00:00
2022-08-04 13:34:02 +00:00
if display :
logger . debug ( f " Smart URL: { filter_url } " )
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
2022-06-16 03:20:40 +00:00
if attribute in tag_attributes and modifier in [ " .regex " ] :
2022-06-01 20:01:40 +00:00
_ , names = self . library . get_search_choices ( attribute , title = not plex_search , name_pairs = True )
valid_list = [ ]
used = [ ]
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 ) :
used . append ( name )
2024-03-23 09:27:56 +00:00
valid_list . append ( ( name , key ) if plex_search else name )
2022-06-01 20:01:40 +00:00
if not valid_list :
error = f " Plex Error: { attribute } : No matches found with regex pattern { data } "
if self . details [ " show_options " ] :
error + = f " \n Options: { names } "
if validate :
raise Failed ( error )
else :
logger . error ( error )
return valid_list
elif modifier == " .regex " :
2022-04-20 16:03:08 +00:00
return util . validate_regex ( data , self . Type , validate = validate )
2022-09-21 14:54:38 +00:00
elif attribute in string_attributes 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 ) )
2023-01-29 00:07:04 +00:00
elif attribute in year_attributes and modifier in [ " " , " .not " , " .gt " , " .gte " , " .lt " , " .lte " ] :
if modifier in [ " " , " .not " ] :
final_years = [ ]
values = util . get_list ( data )
for value in values :
if str ( value ) . startswith ( " current_year " ) :
year_values = str ( value ) . split ( " - " )
try :
final_years . append ( datetime . now ( ) . year - ( 0 if len ( year_values ) == 1 else int ( year_values [ 1 ] . strip ( ) ) ) )
except ValueError :
raise Failed ( f " { self . Type } Error: { final } attribute modifier invalid ' { year_values [ 1 ] } ' " )
else :
final_years . append ( util . parse ( self . Type , final , value , datatype = " int " ) )
return smart_pair ( final_years )
else :
if str ( data ) . startswith ( " current_year " ) :
year_values = str ( data ) . split ( " - " )
try :
return datetime . now ( ) . year - ( 0 if len ( year_values ) == 1 else int ( year_values [ 1 ] . strip ( ) ) )
except ValueError :
raise Failed ( f " { self . Type } Error: { final } attribute modifier invalid ' { year_values [ 1 ] } ' " )
return util . parse ( self . Type , final , data , datatype = " int " , minimum = 0 )
2023-01-27 15:16:00 +00:00
elif ( attribute in number_attributes and modifier in [ " " , " .not " , " .gt " , " .gte " , " .lt " , " .lte " ] ) \
or ( attribute in tag_attributes and modifier in [ " .count_gt " , " .count_gte " , " .count_lt " , " .count_lte " ] ) :
return util . parse ( self . Type , final , data , datatype = " int " , minimum = 0 )
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 )
2024-04-03 14:49:18 +00:00
elif attribute in [ " tmdb_genre " , " tvdb_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-10-24 06:17:49 +00:00
return util . parse ( self . Type , final , data , datatype = " commalist " , options = [ v for k , v in tmdb . discover_types . items ( ) ] )
2022-01-28 21:20:09 +00:00
elif attribute == " tmdb_status " :
2022-10-24 06:17:49 +00:00
return util . parse ( self . Type , final , data , datatype = " commalist " , options = [ v for k , v in tmdb . discover_status . items ( ) ] )
2023-01-27 15:16:00 +00:00
elif attribute == " imdb_keyword " :
new_dictionary = { " minimum_votes " : 0 , " minimum_relevant " : 0 , " minimum_percentage " : 0 }
if isinstance ( data , dict ) and " keyword " not in data :
raise Failed ( f " { self . Type } Error: imdb_keyword requires the keyword attribute " )
elif isinstance ( data , dict ) :
dict_methods = { dm . lower ( ) : dm for dm in data }
2023-01-27 18:33:22 +00:00
new_dictionary [ " keywords " ] = util . parse ( self . Type , " keyword " , data , methods = dict_methods , parent = attribute , datatype = " lowerlist " )
2023-01-27 15:16:00 +00:00
new_dictionary [ " minimum_votes " ] = util . parse ( self . Type , " minimum_votes " , data , methods = dict_methods , parent = attribute , datatype = " int " , minimum = 0 )
new_dictionary [ " minimum_relevant " ] = util . parse ( self . Type , " minimum_relevant " , data , methods = dict_methods , parent = attribute , datatype = " int " , minimum = 0 )
new_dictionary [ " minimum_percentage " ] = util . parse ( self . Type , " minimum_percentage " , data , methods = dict_methods , parent = attribute , datatype = " int " , minimum = 0 , maximum = 100 )
else :
2023-01-27 18:33:22 +00:00
new_dictionary [ " keywords " ] = util . parse ( self . Type , final , data , datatype = " lowerlist " )
2023-01-27 15:16:00 +00:00
return new_dictionary
2022-06-16 03:20:40 +00:00
elif attribute in 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 = [ ]
2022-08-04 20:47:54 +00:00
for fvalue in final_values :
if str ( fvalue ) in search_choices or str ( fvalue ) . lower ( ) in search_choices :
valid_value = search_choices [ str ( fvalue ) if str ( fvalue ) in search_choices else str ( fvalue ) . lower ( ) ]
valid_list . append ( ( fvalue , valid_value ) if plex_search else valid_value )
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 " ] :
2022-08-04 20:47:54 +00:00
actor_id = self . library . get_actor_id ( fvalue )
2022-04-06 02:14:32 +00:00
if actor_id :
2022-04-20 16:03:08 +00:00
if plex_search :
2022-08-04 20:47:54 +00:00
valid_list . append ( ( fvalue , actor_id ) )
2022-04-06 02:14:32 +00:00
else :
valid_list . append ( actor_id )
if not actor_id :
2022-08-04 20:47:54 +00:00
error = f " Plex Error: { attribute } : { fvalue } not found "
2022-04-06 02:14:32 +00:00
if self . details [ " show_options " ] :
error + = f " \n Options: { names } "
if validate :
2022-11-15 21:30:46 +00:00
raise FilterFailed ( error )
2022-11-11 16:59:39 +00:00
elif not self . ignore_blank_results :
2022-04-06 02:14:32 +00:00
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 " ] :
2023-12-07 19:49:32 +00:00
try :
return util . validate_date ( datetime . now ( ) if data == " today " else data , return_as = " % Y- % m- %d " )
except Failed as e :
raise Failed ( f " { self . Type } Error: { final } : { e } " )
2022-06-09 13:40:47 +00:00
elif attribute in date_attributes and modifier in [ " " , " .not " ] :
2022-06-09 15:27:42 +00:00
search_mod = " d "
2022-06-09 13:40:47 +00:00
if plex_search and data and str ( data ) [ - 1 ] in [ " s " , " m " , " h " , " d " , " w " , " o " , " y " ] :
search_mod = str ( data ) [ - 1 ]
data = str ( data ) [ : - 1 ]
search_data = util . parse ( self . Type , final , data , datatype = " int " , minimum = 0 )
return f " { search_data } { search_mod } " if plex_search else search_data
2022-06-16 03:20:40 +00:00
elif attribute in float_attributes and modifier in [ " " , " .not " , " .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 )
2022-06-26 19:08:17 +00:00
elif attribute in boolean_attributes or ( attribute in float_attributes and modifier in [ " .rated " ] ) :
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 :
2022-05-17 07:25:11 +00:00
percentage = self . default_percent
2022-04-27 23:14:52 +00:00
if " percentage " in data :
if data [ " percentage " ] is None :
2022-05-17 07:25:11 +00:00
logger . warning ( f " { self . Type } Warning: percentage filter attribute is blank using { self . default_percent } as default " )
2022-04-27 23:14:52 +00:00
else :
maybe = util . check_num ( data [ " percentage " ] )
if maybe < 0 or maybe > 100 :
2022-05-17 07:25:11 +00:00
logger . warning ( f " { self . Type } Warning: percentage filter attribute must be a number 0-100 using { self . default_percent } as default " )
2022-04-27 23:14:52 +00:00
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 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-11-07 17:05:21 +00:00
total = self . limit if self . limit and len ( self . found_items ) > self . limit else len ( self . found_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
2022-05-10 18:50:19 +00:00
items_added = [ ]
2022-11-07 17:05:21 +00:00
for i , item in enumerate ( self . found_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-11-07 17:05:21 +00:00
self . found_items = self . found_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 :
2022-05-10 18:50:19 +00:00
items_added . append ( item )
2021-11-03 14:36:11 +00:00
amount_added + = 1
2021-12-26 15:48:52 +00:00
if self . details [ " changes_webhooks " ] :
2022-05-10 18:50:19 +00:00
self . notification_additions . append ( util . item_set ( item , self . library . get_id_from_maps ( item . ratingKey ) ) )
if self . playlist and items_added and not self . obj :
self . obj = self . library . create_playlist ( self . name , items_added )
2021-12-17 14:24:46 +00:00
logger . info ( " " )
logger . info ( f " Playlist: { self . name } created " )
2022-05-10 18:50:19 +00:00
elif self . playlist and items_added :
self . obj . addItems ( items_added )
2023-11-13 21:19:59 +00:00
elif items_added :
self . library . alter_collection ( items_added , name , smart_label_collection = self . smart_label_collection )
2022-10-21 21:09:36 +00:00
if self . do_report and items_added :
2022-05-10 18:50:19 +00:00
self . library . add_additions ( self . name , [ ( i . title , self . library . get_id_from_maps ( i . ratingKey ) ) for i in items_added ] , self . library . is_movie )
2022-02-13 16:33:57 +00:00
logger . exorcise ( )
2021-05-24 03:38:46 +00:00
logger . info ( " " )
2022-08-09 05:35:21 +00:00
item_label = f " { self . builder_level . capitalize ( ) } { ' s ' if total > 1 else ' ' } "
logger . info ( f " { total } { item_label } Processed { amount_added } { item_label } Added " )
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
2022-05-10 18:50:19 +00:00
items_removed = [ ]
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 ) } " )
2022-05-10 18:50:19 +00:00
items_removed . append ( item )
2021-12-17 14:24:46 +00:00
amount_removed + = 1
2021-12-26 15:48:52 +00:00
if self . details [ " changes_webhooks " ] :
2022-05-10 18:50:19 +00:00
self . notification_removals . append ( util . item_set ( item , self . library . get_id_from_maps ( item . ratingKey ) ) )
if self . playlist and items_removed :
2023-10-16 20:04:46 +00:00
self . library . _reload ( self . obj )
2022-05-10 18:50:19 +00:00
self . obj . removeItems ( items_removed )
2023-11-13 21:19:59 +00:00
elif items_removed :
self . library . alter_collection ( items_removed , self . name , smart_label_collection = self . smart_label_collection , add = False )
2022-10-21 21:09:36 +00:00
if self . do_report and items_removed :
2022-05-10 18:50:19 +00:00
self . library . add_removed ( self . name , [ ( i . title , self . library . get_id_from_maps ( i . ratingKey ) ) for i in items_removed ] , self . library . is_movie )
2021-10-20 19:58:25 +00:00
logger . info ( " " )
2022-07-26 19:58:53 +00:00
logger . info ( f " { amount_removed } { self . builder_level . capitalize ( ) } { ' s ' if amount_removed == 1 else ' ' } Removed " )
2021-11-03 14:36:11 +00:00
return amount_removed
2021-10-20 19:58:25 +00:00
2022-10-24 06:17:49 +00:00
def check_tmdb_filters ( self , tmdb_item , filters_in , is_movie ) :
for filter_method , filter_data in filters_in :
filter_attr , modifier , filter_final = self . library . split ( filter_method )
if self . config . TMDb . item_filter ( tmdb_item , filter_attr , modifier , filter_final , filter_data , is_movie , self . current_time ) is False :
2022-10-03 19:32:50 +00:00
return False
return True
2024-04-03 12:45:43 +00:00
def check_tvdb_filters ( self , tvdb_item , filters_in ) :
for filter_method , filter_data in filters_in :
filter_attr , modifier , filter_final = self . library . split ( filter_method )
if self . config . TVDb . item_filter ( tvdb_item , filter_attr , modifier , filter_final , filter_data ) is False :
return False
return True
2023-01-27 15:16:00 +00:00
def check_imdb_filters ( self , imdb_info , filters_in ) :
for filter_method , filter_data in filters_in :
filter_attr , modifier , filter_final = self . library . split ( filter_method )
if self . config . IMDb . item_filter ( imdb_info , filter_attr , modifier , filter_final , filter_data ) is False :
return False
return True
2022-10-24 06:17:49 +00:00
def check_missing_filters ( self , item_id , is_movie , tmdb_item = None , check_released = False ) :
2023-01-27 15:16:00 +00:00
imdb_info = None
if self . has_tmdb_filters or self . has_imdb_filters or check_released :
2021-08-07 06:01:21 +00:00
try :
2022-10-24 06:17:49 +00:00
if tmdb_item is None :
2022-02-22 17:20:50 +00:00
if is_movie :
2022-10-24 06:17:49 +00:00
tmdb_item = self . config . TMDb . get_movie ( item_id , ignore_cache = True )
2022-02-22 17:20:50 +00:00
else :
2022-10-24 06:17:49 +00:00
tmdb_item = self . config . TMDb . get_show ( self . config . Convert . tvdb_to_tmdb ( item_id , fail = True ) , ignore_cache = True )
2021-08-07 06:01:21 +00:00
except Failed :
return False
2023-01-27 15:16:00 +00:00
if self . has_imdb_filters and tmdb_item and tmdb_item . imdb_id :
try :
2023-07-10 15:34:02 +00:00
imdb_info = self . config . IMDb . keywords ( tmdb_item . imdb_id , self . language )
2023-01-27 15:16:00 +00:00
except Failed as e :
logger . error ( e )
return False
2022-10-24 21:01:58 +00:00
if check_released :
date_to_check = tmdb_item . release_date if is_movie else tmdb_item . first_air_date
if not date_to_check or date_to_check > self . current_time :
return False
final_return = True
2023-01-27 15:16:00 +00:00
if self . has_tmdb_filters or self . has_imdb_filters :
2022-10-24 21:01:58 +00:00
final_return = False
2022-10-24 06:17:49 +00:00
for filter_list in self . filters :
2023-01-27 15:16:00 +00:00
tmdb_f = [ ]
imdb_f = [ ]
for k , v in filter_list :
if k . split ( " . " ) [ 0 ] in tmdb_filters :
tmdb_f . append ( ( k , v ) )
elif k . split ( " . " ) [ 0 ] in imdb_filters :
imdb_f . append ( ( k , v ) )
2022-10-24 06:17:49 +00:00
or_result = True
2023-01-27 15:16:00 +00:00
if tmdb_f :
if not tmdb_item or self . check_tmdb_filters ( tmdb_item , tmdb_f , is_movie ) is False :
or_result = False
if imdb_f :
if not imdb_info and self . check_imdb_filters ( imdb_info , imdb_f ) is False :
or_result = False
2022-10-24 06:17:49 +00:00
if or_result :
final_return = True
2022-10-03 19:32:50 +00:00
return final_return
2021-08-07 06:01:21 +00:00
2021-12-29 15:51:22 +00:00
def check_filters ( self , item , display ) :
2022-10-03 19:32:50 +00:00
final_return = True
2022-10-24 06:17:49 +00:00
if self . 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 )
2022-10-03 19:32:50 +00:00
final_return = False
2022-10-24 06:17:49 +00:00
tmdb_item = None
2024-04-03 12:45:43 +00:00
tvdb_item = None
2023-01-27 15:16:00 +00:00
imdb_info = None
2022-10-03 19:32:50 +00:00
for filter_list in self . filters :
2022-12-28 17:30:16 +00:00
tmdb_f = [ ]
2024-04-03 12:45:43 +00:00
tvdb_f = [ ]
2023-01-27 15:16:00 +00:00
imdb_f = [ ]
2022-12-28 17:30:16 +00:00
plex_f = [ ]
for k , v in filter_list :
if k . split ( " . " ) [ 0 ] in tmdb_filters :
tmdb_f . append ( ( k , v ) )
2024-04-03 12:45:43 +00:00
elif k . split ( " . " ) [ 0 ] in tvdb_filters :
tvdb_f . append ( ( k , v ) )
2023-01-27 15:16:00 +00:00
elif k . split ( " . " ) [ 0 ] in imdb_filters :
imdb_f . append ( ( k , v ) )
2022-12-28 17:30:16 +00:00
else :
plex_f . append ( ( k , v ) )
2022-10-24 06:17:49 +00:00
or_result = True
if tmdb_f :
if not tmdb_item 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 } " )
or_result = False
else :
try :
if item . ratingKey in self . library . movie_rating_key_map :
tmdb_item = self . config . TMDb . get_movie ( self . library . movie_rating_key_map [ item . ratingKey ] , ignore_cache = True )
else :
tmdb_item = self . config . TMDb . get_show ( self . config . Convert . tvdb_to_tmdb ( self . library . show_rating_key_map [ item . ratingKey ] , fail = True ) , ignore_cache = True )
2023-01-27 15:16:00 +00:00
except Failed as e :
logger . error ( e )
2022-10-24 06:17:49 +00:00
or_result = False
if not tmdb_item or self . check_tmdb_filters ( tmdb_item , tmdb_f , item . ratingKey in self . library . movie_rating_key_map ) is False :
or_result = False
2024-04-03 12:45:43 +00:00
if tvdb_f :
if not tvdb_item and isinstance ( item , Show ) :
if item . ratingKey not in self . library . show_rating_key_map :
logger . warning ( f " Filter Error: No TVDb ID found for { item . title } " )
or_result = False
else :
try :
tvdb_item = self . config . TVDb . get_tvdb_obj ( self . library . show_rating_key_map [ item . ratingKey ] )
except Failed as e :
logger . error ( e )
or_result = False
if not tvdb_item or self . check_tvdb_filters ( tvdb_item , tvdb_f ) is False :
or_result = False
2023-01-27 15:16:00 +00:00
if imdb_f :
if not imdb_info and isinstance ( item , ( Movie , Show ) ) :
if item . ratingKey not in self . library . imdb_rating_key_map :
logger . warning ( f " Filter Error: No IMDb ID found for { item . title } " )
or_result = False
else :
try :
2023-07-10 15:34:02 +00:00
imdb_info = self . config . IMDb . keywords ( self . library . imdb_rating_key_map [ item . ratingKey ] , self . language )
2023-01-27 15:16:00 +00:00
except Failed as e :
logger . error ( e )
or_result = False
if not imdb_info or self . check_imdb_filters ( imdb_info , imdb_f ) is False :
or_result = False
2022-10-24 06:17:49 +00:00
if plex_f and self . library . check_filters ( item , plex_f , self . current_time ) is False :
or_result = False
if or_result :
2022-10-03 19:32:50 +00:00
final_return = True
return final_return
def display_filters ( self ) :
2022-10-24 06:17:49 +00:00
if self . filters :
2022-10-03 19:32:50 +00:00
for filter_list in self . filters :
logger . info ( " " )
for filter_key , filter_value in filter_list :
logger . info ( f " Collection Filter { filter_key } : { filter_value } " )
2021-05-26 14:58:04 +00:00
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 = [ ]
2022-05-10 18:50:19 +00:00
filtered_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
2022-10-24 06:17:49 +00:00
if self . check_missing_filters ( missing_id , True , tmdb_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 :
2022-05-10 18:50:19 +00:00
filtered_movies_with_names . append ( ( current_title , missing_id ) )
2021-08-06 17:46:13 +00:00
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 :
2022-10-21 21:09:36 +00:00
if self . do_report :
2021-08-12 13:37:30 +00:00
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 )
2023-02-05 19:10:08 +00:00
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
2021-08-12 13:37:30 +00:00
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 )
2023-02-05 19:10:08 +00:00
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
2021-08-12 13:37:30 +00:00
if self . run_again :
self . run_again_movies . extend ( missing_tmdb_ids )
2022-10-21 21:09:36 +00:00
if len ( filtered_movies_with_names ) > 0 and self . do_report :
2022-05-10 18:50:19 +00:00
self . library . add_filtered ( self . name , filtered_movies_with_names , True )
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 = [ ]
2022-05-10 18:50:19 +00:00
filtered_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
2022-10-24 06:17:49 +00:00
if self . check_missing_filters ( 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 :
2022-05-10 18:50:19 +00:00
filtered_shows_with_names . append ( ( title , missing_id ) )
2021-08-06 17:46:13 +00:00
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 :
2022-10-21 21:09:36 +00:00
if self . do_report :
2021-08-12 13:37:30 +00:00
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 )
2023-02-05 19:10:08 +00:00
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
2021-08-12 13:37:30 +00:00
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 )
2023-02-05 19:10:08 +00:00
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
2021-08-12 13:37:30 +00:00
if self . run_again :
self . run_again_shows . extend ( missing_tvdb_ids )
2022-10-21 21:09:36 +00:00
if len ( filtered_shows_with_names ) > 0 and self . do_report :
2022-05-10 18:50:19 +00:00
self . library . add_filtered ( self . name , filtered_shows_with_names , False )
if len ( self . missing_parts ) > 0 and self . library . is_show :
if self . details [ " show_missing " ] is True :
for missing in self . missing_parts :
logger . info ( f " { self . name } { self . Type } | ? | { missing } " )
2022-10-21 21:09:36 +00:00
if self . do_report :
2022-05-10 18:50:19 +00:00
self . library . add_missing ( self . name , self . missing_parts , False )
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 ( " " )
2022-11-07 17:05:21 +00:00
self . items = self . found_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 ( " " )
2023-12-26 22:07:38 +00:00
logger . separator ( f " Updating Metadata 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-09-17 21:10:57 +00:00
add_genres = self . item_details [ " item_genre " ] if " item_genre " in self . item_details else None
remove_genres = self . item_details [ " item_genre.remove " ] if " item_genre.remove " in self . item_details else None
sync_genres = self . item_details [ " item_genre.sync " ] if " item_genre.sync " in self . item_details else None
2022-08-31 13:49:52 +00:00
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-07-26 19:58:53 +00:00
for non_item in self . library . search ( label = self . item_details [ " non_item_remove_label " ] , libtype = self . builder_level ) :
2022-05-10 13:56:29 +00:00
if non_item . ratingKey not in rk_compare :
self . library . edit_tags ( " label " , non_item , remove_tags = self . item_details [ " non_item_remove_label " ] )
2022-01-25 01:46:48 +00:00
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 :
2023-02-27 18:37:23 +00:00
item = self . library . reload ( item )
2022-06-26 16:31:22 +00:00
current_labels = [ la . tag for la in self . library . item_labels ( item ) ]
2022-12-16 19:28:08 +00:00
if " item_assets " in self . item_details and self . asset_directory and " Overlay " not in current_labels :
self . library . find_and_upload_assets ( item , current_labels , asset_directory = self . asset_directory )
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 )
2022-08-31 13:49:52 +00:00
self . library . edit_tags ( " genre " , item , add_tags = add_genres , remove_tags = remove_genres , sync_tags = sync_genres )
2022-09-20 02:46:52 +00:00
if " item_edition " in self . item_details and item . editionTitle != self . item_details [ " item_edition " ] :
self . library . query_data ( item . editEditionTitle , self . item_details [ " item_edition " ] )
logger . info ( f " { item . title [ : 25 ] : <25 } | Edition | { self . item_details [ ' item_edition ' ] } " )
2023-07-22 05:46:49 +00:00
path = None
2023-08-10 21:01:40 +00:00
if " item_radarr_tag " in self . item_details or self . radarr_details [ " add_existing " ] or " item_sonarr_tag " in self . item_details or self . sonarr_details [ " add_existing " ] :
if item . locations :
if self . library . is_movie :
path = os . path . dirname ( str ( item . locations [ 0 ] ) )
elif self . library . is_show :
path = str ( item . locations [ 0 ] )
if not path :
logger . error ( f " Plex Error: No location found for { item . title } : { item . locations } " )
2023-07-26 12:23:52 +00:00
if path and 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 ) )
2023-07-26 12:23:52 +00:00
if path and 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-05-09 20:40:39 +00:00
if advance_edits :
2023-12-26 22:07:38 +00:00
logger . debug ( f " Metadata Update: { advance_edits } " )
2022-05-09 20:40:39 +00:00
if self . library . edit_advance ( item , advance_edits ) :
2023-12-26 22:07:38 +00:00
logger . info ( f " { item . title } Advanced Metadata Update Successful " )
2022-05-09 20:40:39 +00:00
else :
2023-12-26 22:07:38 +00:00
logger . error ( f " { item . title } Advanced Metadata Update Failed " )
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 :
2023-04-09 06:46:27 +00:00
try :
if " item_radarr_tag " in self . item_details :
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 " ] )
if self . radarr_details [ " add_existing " ] :
added = self . library . Radarr . add_tmdb ( tmdb_paths , * * self . radarr_details )
self . added_to_radarr . extend ( [ { " title " : movie . title , " id " : movie . tmdbId } for movie in added ] )
except Failed as e :
logger . error ( e )
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
2021-07-03 04:18:09 +00:00
2021-11-29 14:11:23 +00:00
if self . library . Sonarr and tvdb_paths :
2023-04-09 06:46:27 +00:00
try :
if " item_sonarr_tag " in self . item_details :
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 " ] )
if self . sonarr_details [ " add_existing " ] :
added = self . library . Sonarr . add_tvdb ( tvdb_paths , * * self . sonarr_details )
self . added_to_sonarr . extend ( [ { " title " : show . title , " id " : show . tvdbId } for show in added ] )
except Failed as e :
logger . error ( e )
except ArrException as e :
logger . stacktrace ( )
logger . error ( f " Arr Error: { e } " )
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 :
2022-08-09 05:49:02 +00:00
self . library . create_smart_collection ( self . name , self . smart_type_key , self . smart_url , self . ignore_blank_results )
2022-08-04 20:47:54 +00:00
logger . debug ( f " Smart Collection Created: { 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 :
2022-08-09 05:49:02 +00:00
self . library . create_smart_collection ( self . name , smart_type , self . smart_url , self . ignore_blank_results )
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 " )
2022-11-08 20:59:23 +00:00
self . obj = self . library . get_playlist ( self . name ) if self . playlist else self . library . get_collection ( self . name , force_search = True )
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 ( " " )
2023-12-26 22:07:38 +00:00
logger . separator ( f " Updating Metadata of { self . name } { self . Type } " , space = False , border = False )
2021-09-15 03:16:59 +00:00
logger . info ( " " )
2022-05-04 16:27:17 +00:00
if " summary " in self . summaries : summary = ( " summary " , self . summaries [ " summary " ] )
2023-03-30 19:51:04 +00:00
elif " translation " in self . summaries : summary = ( " translation " , self . summaries [ " translation " ] )
2022-05-04 16:27:17 +00:00
elif " tmdb_description " in self . summaries : summary = ( " tmdb_description " , self . summaries [ " tmdb_description " ] )
2022-12-19 21:36:51 +00:00
elif " tvdb_description " in self . summaries : summary = ( " tvdb_description " , self . summaries [ " tvdb_description " ] )
2022-05-04 16:27:17 +00:00
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 " ] )
2022-12-19 21:36:51 +00:00
elif " tvdb_list_details " in self . summaries : summary = ( " tvdb_list_details " , self . summaries [ " tvdb_list_details " ] )
2022-05-04 16:27:17 +00:00
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 " ] )
2024-01-03 16:56:55 +00:00
else : summary = ( None , None )
2021-03-01 04:16:08 +00:00
2022-03-25 20:13:51 +00:00
if self . playlist :
2024-01-03 16:56:55 +00:00
if summary [ 1 ] :
2022-06-28 20:34:20 +00:00
if str ( summary [ 1 ] ) != str ( self . obj . summary ) :
try :
2024-01-10 18:56:26 +00:00
self . obj . editSummary ( str ( summary [ 1 ] ) )
2022-06-28 20:34:20 +00:00
logger . info ( f " Summary ( { summary [ 0 ] } ) | { summary [ 1 ] : <25 } " )
2023-12-26 22:07:38 +00:00
logger . info ( " Metadata: Update Completed " )
2022-06-28 20:34:20 +00:00
updated_details . append ( " Metadata " )
except NotFound :
2023-12-26 22:07:38 +00:00
logger . error ( " Metadata: Failed to Update Please delete the collection and run again " )
2022-06-28 20:34:20 +00:00
logger . info ( " " )
2023-02-22 16:59:27 +00:00
else :
2023-10-16 20:04:46 +00:00
self . library . _reload ( self . obj )
2023-02-21 20:32:08 +00:00
#self.obj.batchEdits()
2022-03-25 20:13:51 +00:00
batch_display = " Collection Metadata Edits "
2024-01-03 16:56:55 +00:00
if summary [ 1 ] and str ( summary [ 1 ] ) != str ( self . obj . summary ) :
2022-05-04 16:27:17 +00:00
self . obj . editSummary ( summary [ 1 ] )
batch_display + = f " \n Summary ( { summary [ 0 ] } ) | { summary [ 1 ] : <25 } "
2022-03-25 20:13:51 +00:00
2023-03-31 14:42:06 +00:00
if " sort_title " in self . details :
new_sort_title = str ( self . details [ " sort_title " ] )
if " <<title>> " in new_sort_title :
title = self . name
for op in [ " The " , " A " , " An " ] :
if title . startswith ( f " { op } " ) :
2023-04-01 19:02:32 +00:00
title = f " { title [ len ( op ) : ] . strip ( ) } , { op . strip ( ) } "
2023-03-31 14:42:06 +00:00
break
2023-04-01 19:02:32 +00:00
new_sort_title = new_sort_title . replace ( " <<title>> " , title )
2023-03-31 14:42:06 +00:00
if new_sort_title != str ( self . obj . titleSort ) :
2023-04-01 19:02:32 +00:00
self . obj . editSortTitle ( new_sort_title )
batch_display + = f " \n Sort Title | { new_sort_title } "
2022-03-25 20:13:51 +00:00
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 ' ] } "
2022-06-08 13:29:56 +00:00
add_tags = self . details [ " label " ] if " label " in self . details else [ ]
2022-03-25 20:13:51 +00:00
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-06-08 13:29:56 +00:00
if sync_tags :
2024-04-22 14:20:12 +00:00
sync_tags . append ( " Kometa " )
2022-06-08 13:29:56 +00:00
else :
2024-04-22 14:20:12 +00:00
add_tags . append ( " Kometa " )
2022-07-06 18:38:26 +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 )
2022-05-06 13:32:37 +00:00
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 :
2023-02-21 20:32:08 +00:00
#self.obj.saveEdits()
2023-12-26 22:07:38 +00:00
logger . info ( " Metadata: Update Completed " )
2022-05-04 16:27:17 +00:00
updated_details . append ( " Metadata " )
2022-03-25 20:13:51 +00:00
except NotFound :
2023-12-26 22:07:38 +00:00
logger . error ( " Metadata: Failed to Update Please delete the collection and run again " )
2022-03-25 20:13:51 +00:00
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-07-15 13:57:27 +00:00
if ( self . blank_collection and self . created ) or int ( self . obj . collectionMode ) not in plex . collection_mode_keys \
2022-05-04 16:27:17 +00:00
or plex . collection_mode_keys [ int ( self . obj . collectionMode ) ] != self . details [ " collection_mode " ] :
2022-07-15 13:57:27 +00:00
if self . blank_collection and self . created :
2022-07-18 18:14:32 +00:00
self . library . collection_mode_query ( self . obj , " hide " )
logger . info ( f " Collection Mode | hide " )
2022-07-26 18:30:40 +00:00
self . library . collection_mode_query ( self . obj , " default " )
logger . info ( f " Collection Mode | default " )
2022-05-04 16:27:17 +00:00
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 :
2022-10-26 06:56:12 +00:00
try :
self . library . edit_query ( self . obj , { " collectionFilterBasedOnUser " : 0 if self . details [ " collection_filtering " ] == " admin " else 1 } , advanced = True )
advance_update = True
except NotFound :
logger . error ( " Collection Error: collection_filtering requires a more recent version of Plex Media Server " )
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
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 :
2022-05-10 13:25:35 +00:00
asset_poster , asset_background , asset_location , _ = self . library . find_item_assets ( name_mapping , asset_directory = self . asset_directory )
if asset_poster :
self . posters [ " asset_directory " ] = asset_poster
if asset_background :
self . backgrounds [ " asset_directory " ] = asset_background
2022-04-25 22:55:59 +00:00
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 )
2023-03-04 20:20:52 +00:00
if self . mapping_name in self . library . collection_images or self . name in self . library . collection_images :
2023-03-21 07:12:50 +00:00
style_data = self . library . collection_images [ self . mapping_name if self . mapping_name in self . library . collection_images else self . name ]
if style_data and " url_poster " in style_data and style_data [ " url_poster " ] :
self . posters [ " style_data " ] = style_data [ " url_poster " ]
elif style_data and " tpdb_poster " in style_data and style_data [ " tpdb_poster " ] :
self . posters [ " style_data " ] = f " https://theposterdb.com/api/assets/ { style_data [ ' tpdb_poster ' ] } "
if style_data and " url_background " in style_data and style_data [ " url_background " ] :
self . backgrounds [ " style_data " ] = style_data [ " url_background " ]
elif style_data and " tpdb_background " in style_data and style_data [ " tpdb_background " ] :
self . backgrounds [ " style_data " ] = f " https://theposterdb.com/api/assets/ { style_data [ ' tpdb_background ' ] } "
2021-02-21 08:13:07 +00:00
2022-05-10 13:25:35 +00:00
self . collection_poster = util . pick_image ( self . obj . title , self . posters , self . library . prioritize_assets , self . library . download_url_assets , asset_location )
self . collection_background = util . pick_image ( self . obj . title , self . backgrounds , self . library . prioritize_assets , self . library . download_url_assets , asset_location , is_poster = False )
2021-03-01 20:59:10 +00:00
2023-04-05 15:58:46 +00:00
clean_temp = False
2024-04-22 14:20:12 +00:00
if isinstance ( self . collection_poster , KometaImage ) :
2023-04-05 15:58:46 +00:00
clean_temp = True
item_vars = { " title " : self . name , " titleU " : self . name . upper ( ) , " titleL " : self . name . lower ( ) }
self . collection_poster = self . collection_poster . save ( item_vars )
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
2023-04-05 15:58:46 +00:00
if clean_temp :
code_base = os . path . dirname ( os . path . dirname ( os . path . abspath ( __file__ ) ) )
posters_dir = os . path . join ( code_base , " defaults " , " posters " )
for filename in os . listdir ( posters_dir ) :
if " temp " in filename :
os . remove ( os . path . join ( posters_dir , filename ) )
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 ( " " )
2022-12-23 08:14:51 +00:00
if not isinstance ( self . custom_sort , list ) :
2022-11-07 17:05:21 +00:00
items = self . found_items
2022-12-23 08:14:51 +00:00
if self . custom_sort == " custom.desc " :
items = items [ : : - 1 ]
2021-12-21 18:48:27 +00:00
else :
2022-03-27 20:59:14 +00:00
plex_search = { " sort_by " : self . custom_sort }
2022-07-26 19:58:53 +00:00
if self . builder_level in [ " season " , " episode " ] :
plex_search [ " type " ] = f " { self . builder_level } s "
2023-04-17 13:42:36 +00:00
plex_search [ " any " ] = { f " { self . builder_level } _collection " : [ self . name ] } # noqa
2022-03-27 20:59:14 +00:00
else :
2023-04-17 13:42:36 +00:00
plex_search [ " any " ] = { " collection " : [ self . name ] }
2022-11-11 16:59:39 +00:00
try :
search_data = self . build_filter ( " plex_search " , plex_search )
except FilterFailed as e :
if self . ignore_blank_results :
raise
else :
raise Failed ( str ( e ) )
2023-06-06 14:57:40 +00:00
items = self . library . fetchItems ( search_data [ 2 ] )
2021-07-29 13:36:30 +00:00
previous = None
2022-11-30 21:12:52 +00:00
sort_edit = False
2022-03-28 19:30:46 +00:00
for i , item in enumerate ( items , 0 ) :
2022-11-12 16:42:53 +00:00
try :
if len ( self . items ) < = i or item . ratingKey != self . items [ i ] . ratingKey :
text = f " after { util . item_title ( previous ) } " if previous else " to the beginning "
self . library . moveItem ( self . obj , item , previous )
logger . info ( f " Moving { util . item_title ( item ) } { text } " )
2022-11-30 21:12:52 +00:00
sort_edit = True
2022-11-12 16:42:53 +00:00
previous = item
except Failed :
2022-11-13 07:22:16 +00:00
logger . error ( f " Failed to Move { util . item_title ( item ) } " )
2022-11-30 21:12:52 +00:00
sort_edit = True
if not sort_edit :
logger . info ( " No Sorting Required " )
2021-12-17 14:24:46 +00:00
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 :
2023-10-16 20:04:46 +00:00
self . library . _reload ( self . obj )
2022-05-09 06:58:09 +00:00
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 ) :
2023-02-14 22:29:05 +00:00
title = self . obj . title if self . obj else self . name
2022-05-28 16:01:51 +00:00
if self . playlist :
2023-02-14 22:29:05 +00:00
output = f " Deleting { self . Type } { title } "
2022-05-28 16:01:51 +00:00
elif self . obj :
output = f " { self . Type } { self . obj . title } deleted "
2022-11-01 18:20:00 +00:00
if self . smart_label_collection :
for item in self . library . search ( label = self . name , libtype = self . builder_level ) :
self . library . edit_tags ( " label " , item , remove_tags = self . name )
2022-05-28 16:01:51 +00:00
else :
output = " "
2024-02-16 03:51:58 +00:00
if self . playlist :
2022-05-28 16:01:51 +00:00
for user in self . valid_users :
2024-02-28 20:40:17 +00:00
try :
if user == self . library . account . username :
2024-02-16 03:51:58 +00:00
_ = self . library . get_playlist ( title ) # Verify if this playlist exists in Admin to avoid log confusion
self . library . delete ( self . obj )
2024-02-28 20:40:17 +00:00
else :
2024-02-16 03:51:58 +00:00
self . library . delete_user_playlist ( title , user )
2024-02-28 20:40:17 +00:00
output + = f " \n Playlist deleted on User { user } "
except Failed :
output + = f " \n Playlist not found on User { user } "
2024-02-16 03:51:58 +00:00
elif self . obj :
self . library . delete ( self . obj )
2021-12-26 15:48:52 +00:00
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 )
2022-11-28 22:00:54 +00:00
except Failed :
2021-12-23 17:02:07 +00:00
pass
2024-02-16 03:51:58 +00:00
if user != self . library . account . username :
2024-02-28 20:40:17 +00:00
self . obj . copyToUser ( user ) . editSummary ( summary = self . obj . summary ) . reload ( )
2024-02-16 03:51:58 +00:00
logger . info ( f " Playlist: { self . name } synced to { user } " )
def exclude_admin_from_playlist ( self ) :
if self . obj and ( self . exclude_users is not None and self . library . account . username in self . exclude_users ) :
logger . info ( " " )
logger . separator ( f " Excluding Admin from Playlist " , space = False , border = False )
logger . info ( " " )
try :
self . library . delete ( self . obj )
2024-02-28 20:40:17 +00:00
logger . info ( f " Playlist: { self . name } deleted on User { self . library . account . username } " )
2024-02-16 03:51:58 +00:00
except Failed :
2024-02-28 20:40:17 +00:00
logger . info ( f " Playlist: { self . name } not found on User { self . library . account . username } " )
2021-12-23 17:02:07 +00:00
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 ) :
2023-10-16 20:04:46 +00:00
self . library . _reload ( self . obj )
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 ,
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 ) :
2022-11-08 20:59:23 +00:00
self . obj = self . library . get_collection ( self . name , force_search = True )
2021-05-12 14:25:48 +00:00
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 :
2023-02-27 19:54:52 +00:00
current = self . library . fetch_item ( int ( rating_key ) )
except Failed as e :
logger . error ( e )
2021-03-01 20:59:10 +00:00
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 ( )
2022-07-26 19:58:53 +00:00
logger . info ( f " { len ( rating_keys ) } { self . builder_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