diff --git a/CHANGELOG b/CHANGELOG index 31e55701..8eef055e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -24,6 +24,8 @@ Added `has_edition` as a [Boolean Filter](https://metamanager.wiki/en/latest/met Added `has_stinger` and `stinger_rating` as [Filters](https://metamanager.wiki/en/latest/metadata/filters.html) based on http://www.mediastinger.com When editing episode metadata the key can now be either episode number, episode title, or episodeoriginally released date. The Collectionless builder now can work with other builders. +Added `country` as an option for Shows when using the builders `plex_search` and `smart_filter`. +Added [Config Secrets](https://metamanager.wiki/en/latest/home/environmental.html#config-secrets) and the ability to load Environment Variables using a `.env` File inside your config folder. # New Defaults Features Removed Translations from the defaults directory and in to their own [repo](https://github.com/meisnate12/PMM-Translations) which is managed at [translations.metamanager.wiki](https://translations.metamanager.wiki/projects/plex-meta-manager/defaults/). diff --git a/VERSION b/VERSION index 954bb72e..41a42d32 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.18.3-develop171 +1.18.3-develop172 diff --git a/defaults/movie/seasonal.yml b/defaults/movie/seasonal.yml index cb2d0cff..9e290a15 100644 --- a/defaults/movie/seasonal.yml +++ b/defaults/movie/seasonal.yml @@ -34,8 +34,8 @@ templates: - imdb_list - trakt_list - mdblist_list - - summary_<> - - name_<> + - summary_format + - name_format - key_name - translation_key - limit diff --git a/docs/home/environmental.md b/docs/home/environmental.md index ef82cea5..100f4070 100644 --- a/docs/home/environmental.md +++ b/docs/home/environmental.md @@ -6,6 +6,8 @@ If you run into a race condition where you have set an Environment Variable with These docs are assuming you have a basic understanding of Docker concepts. One place to get familiar with Docker would be the [official tutorial](https://www.docker.com/101-tutorial/). +Environment Variables can also be placed inside a `.env` file inside your config folder. + | Attribute | Shell Command | Environment Variable | |:------------------------------------------------------|:----------------------------------------------|:-------------------------| | [Config](#config) | `-c` or `--config` | `PMM_CONFIG` | @@ -38,6 +40,7 @@ These docs are assuming you have a basic understanding of Docker concepts. One | [ENV Plex Token](#env-plex-url--token) | `-pt` or `--plex-token` | `PMM_PLEX_TOKEN` | | [Divider Character](#divider-character--screen-width) | `-d` or `--divider` | `PMM_DIVIDER` | | [Screen Width](#divider-character--screen-width) | `-w` or `--width` | `PMM_WIDTH` | +| [Other Secrets](#config-secrets) | `--pmm-***` | `PMM_***` | Further explanation and examples of each command can be found below. @@ -1070,4 +1073,46 @@ python plex_meta_manager.py --divider * --width 200 ``` docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex-meta-manager --divider * --width 200 ``` -```` \ No newline at end of file +```` + +### Config Secrets + +All Run Commands that start with `--pmm-***` and Environment Variables that start with `PMM_***` will be loaded in as Config Secrets. + +These Config Secrets can be loaded into the config by placing `<<***>>` in any field in the config, where `***` is whatever name you want to call the variable. + + + + + + + + + + + + + + + + + +
ShellEnvironment
Flags--pmm-***PMM_***
Example--pmm-mysecret 123456789PMM_MYSECRET=123456789
+ +````{tab} Local Environment +``` +python plex_meta_manager.py --pmm-mysecret 123456789 +``` +```` +````{tab} Docker Environment +``` +docker run -it -v "X:\Media\Plex Meta Manager\config:/config:rw" meisnate12/plex-meta-manager --pmm-mysecret 123456789 +``` +```` + +#### Example Config Usage + +```yaml +tmdb: + apikey: <> +``` diff --git a/modules/config.py b/modules/config.py index a50028e8..21549c6e 100644 --- a/modules/config.py +++ b/modules/config.py @@ -112,7 +112,7 @@ library_operations = { } class ConfigFile: - def __init__(self, default_dir, attrs): + def __init__(self, default_dir, attrs, secrets): logger.info("Locating config...") config_file = attrs["config_file"] if config_file and os.path.exists(config_file): self.config_path = os.path.abspath(config_file) @@ -124,6 +124,7 @@ class ConfigFile: self._mediastingers = None self.default_dir = default_dir + self.secrets = secrets self.read_only = attrs["read_only"] if "read_only" in attrs else False self.version = attrs["version"] if "version" in attrs else None self.branch = attrs["branch"] if "branch" in attrs else None @@ -283,6 +284,25 @@ class ConfigFile: if "trakt" in self.data: self.data["trakt"] = self.data.pop("trakt") if "mal" in self.data: self.data["mal"] = self.data.pop("mal") + def check_next(next_data): + if isinstance(next_data, dict): + for d in next_data: + out = check_next(next_data[d]) + if out: + next_data[d] = out + elif isinstance(next_data, list): + for d in next_data: + check_next(d) + else: + for secret, secret_value in self.secrets.items(): + if f"<<{secret}>>" in str(next_data): + return str(next_data).replace(f"<<{secret}>>", secret_value) + elif f"<<{secret.upper()}>>" in str(next_data): + return str(next_data).replace(f"<<{secret.upper()}>>", secret_value) + return next_data + if self.secrets: + check_next(self.data) + def check_for_attribute(data, attribute, parent=None, test_list=None, default=None, do_print=True, default_is_none=False, req_default=False, var_type="str", throw=False, save=True, int_min=0): endline = "" if parent is not None: diff --git a/plex_meta_manager.py b/plex_meta_manager.py index daafd15f..54aa2d31 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -10,6 +10,7 @@ if sys.version_info[0] != 3 or sys.version_info[1] < 7: try: import plexapi, psutil, requests, schedule + from dotenv import load_dotenv from PIL import ImageFile from plexapi import server from plexapi.exceptions import NotFound @@ -49,13 +50,18 @@ parser.add_argument("-pu", "--plex-url", dest="plex_url", help="Plex URL for Ple parser.add_argument("-pt", "--plex-token", dest="plex_token", help="Plex Token for Plex ENV Tokens", default="", type=str) parser.add_argument("-d", "--divider", dest="divider", help="Character that divides the sections (Default: '=')", default="=", type=str) parser.add_argument("-w", "--width", dest="width", help="Screen Width (Default: 100)", default=100, type=int) -args = parser.parse_args() +args, unknown = parser.parse_known_args() +default_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config") +load_dotenv(os.path.join(default_dir, ".env")) + +static_envs = [] test_value = None def get_arg(env_str, default, arg_bool=False, arg_int=False): global test_value env_vars = [env_str] if not isinstance(env_str, list) else env_str final_value = None + static_envs.extend(env_vars) for env_var in env_vars: env_value = os.environ.get(env_var) if env_var == "BRANCH_NAME": @@ -83,7 +89,7 @@ def get_arg(env_str, default, arg_bool=False, arg_int=False): try: from git import Repo, InvalidGitRepositoryError try: - git_branch = Repo(path=".").head.ref.name + git_branch = Repo(path=".").head.ref.name # noqa except InvalidGitRepositoryError: git_branch = None except ImportError: @@ -123,6 +129,18 @@ log_requests = get_arg("PMM_LOG_REQUESTS", args.log_requests, arg_bool=True) plex_url = get_arg("PMM_PLEX_URL", args.plex_url) plex_token = get_arg("PMM_PLEX_TOKEN", args.plex_token) +secret_args = {} +i = 0 +while i < len(unknown): + if str(unknown[i]).lower().startswith("--pmm-"): + secret_args[str(unknown[i]).lower()[6:]] = str(unknown[i + 1]) + i += 1 + i += 1 + +for env_name, env_data in os.environ.items(): + if str(env_name).upper().startswith("PMM_") and str(env_name).upper() not in static_envs: + secret_args[str(env_name).lower()[4:]] = env_data + if collections: collection_only = True @@ -130,7 +148,6 @@ if screen_width < 90 or screen_width > 300: print(f"Argument Error: width argument invalid: {screen_width} must be an integer between 90 and 300 using the default 100") screen_width = 100 -default_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config") if config_file and os.path.exists(config_file): default_dir = os.path.join(os.path.dirname(os.path.abspath(config_file))) elif config_file and not os.path.exists(config_file): @@ -269,7 +286,7 @@ def start(attrs): config = None stats = {"created": 0, "modified": 0, "deleted": 0, "added": 0, "unchanged": 0, "removed": 0, "radarr": 0, "sonarr": 0, "names": []} try: - config = ConfigFile(default_dir, attrs) + config = ConfigFile(default_dir, attrs, secret_args) except Exception as e: logger.stacktrace() logger.critical(e) diff --git a/requirements.txt b/requirements.txt index 506f008f..21403fe0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,13 +1,14 @@ -PlexAPI==4.13.4 -tmdbapis==1.1.0 arrapi==1.4.2 +GitPython==3.1.31 lxml==4.9.2 -requests==2.28.2 -ruamel.yaml==0.17.21 -schedule==1.1.0 -retrying==1.3.4 +num2words==0.5.12 pathvalidate==2.5.2 pillow==9.5.0 -num2words==0.5.12 +PlexAPI==4.13.4 psutil==5.9.4 -GitPython==3.1.31 \ No newline at end of file +python-dotenv==1.0.0 +requests==2.28.2 +retrying==1.3.4 +ruamel.yaml==0.17.21 +schedule==1.1.0 +tmdbapis==1.1.0 \ No newline at end of file