From ec85f6e172cc004138d875f1980dd8d06dffa7a2 Mon Sep 17 00:00:00 2001 From: JayZed Date: Sat, 16 Mar 2024 22:10:50 -0400 Subject: [PATCH 001/129] Improved multiple exceptions catching and fixed some other providers issues * Backup files should be listed with newest ones first Just like Sonarr and Radarr and everyone else. * Add check_parser_binary() validation method This is mainly to prevent the user from selecting mediainfo as the subtitles parser if it has not yet been installed on the user's system somewhere in the PATH. * import JSONDecodeError from requests.exceptions instead of json Because sometimes it will return the simplejson one instead and that one won't be caught by the except clause. * import JSONDecodeError from requests.exceptions instead of json Because sometimes it will return the simplejson one instead and that one won't be caught by the except clause. Also fixed User-Agent assignment. --- bazarr/app/config.py | 11 ++++++++++- bazarr/radarr/info.py | 8 ++++---- bazarr/sonarr/info.py | 8 ++++---- bazarr/utilities/backup.py | 2 +- custom_libs/subliminal_patch/providers/hdbits.py | 2 +- custom_libs/subliminal_patch/providers/ktuvit.py | 15 ++++++++------- custom_libs/subliminal_patch/providers/subdivx.py | 2 +- 7 files changed, 29 insertions(+), 19 deletions(-) diff --git a/bazarr/app/config.py b/bazarr/app/config.py index 5e9d74bbc..cda51d0ef 100644 --- a/bazarr/app/config.py +++ b/bazarr/app/config.py @@ -7,6 +7,7 @@ import logging import re from urllib.parse import quote_plus +from utilities.binaries import BinaryNotFound, get_binary from literals import EXIT_VALIDATION_ERROR from utilities.central import stop_bazarr from subliminal.cache import region @@ -54,6 +55,14 @@ class Validator(OriginalValidator): ) +def check_parser_binary(value): + try: + get_binary(value) + except BinaryNotFound as e: + raise ValidationError(f"Executable '{value}' not found in search path. Please install before making this selection.") + return True + + validators = [ # general section Validator('general.flask_secret_key', must_exist=True, default=hexlify(os.urandom(16)).decode(), @@ -119,7 +128,7 @@ validators = [ Validator('general.dont_notify_manual_actions', must_exist=True, default=False, is_type_of=bool), Validator('general.hi_extension', must_exist=True, default='hi', is_type_of=str, is_in=['hi', 'cc', 'sdh']), Validator('general.embedded_subtitles_parser', must_exist=True, default='ffprobe', is_type_of=str, - is_in=['ffprobe', 'mediainfo']), + is_in=['ffprobe', 'mediainfo'], condition=check_parser_binary), Validator('general.default_und_audio_lang', must_exist=True, default='', is_type_of=str), Validator('general.default_und_embedded_subtitles_lang', must_exist=True, default='', is_type_of=str), Validator('general.parse_embedded_audio_track', must_exist=True, default=False, is_type_of=bool), diff --git a/bazarr/radarr/info.py b/bazarr/radarr/info.py index c6a3181f3..aec8d65d6 100644 --- a/bazarr/radarr/info.py +++ b/bazarr/radarr/info.py @@ -3,7 +3,7 @@ import logging import requests import datetime -import json +from requests.exceptions import JSONDecodeError from dogpile.cache import make_region @@ -34,13 +34,13 @@ class GetRadarrInfo: if 'version' in radarr_json: radarr_version = radarr_json['version'] else: - raise json.decoder.JSONDecodeError - except json.decoder.JSONDecodeError: + raise JSONDecodeError + except JSONDecodeError: try: rv = f"{url_radarr()}/api/v3/system/status?apikey={settings.radarr.apikey}" radarr_version = requests.get(rv, timeout=int(settings.radarr.http_timeout), verify=False, headers=headers).json()['version'] - except json.decoder.JSONDecodeError: + except JSONDecodeError: logging.debug('BAZARR cannot get Radarr version') radarr_version = 'unknown' except Exception: diff --git a/bazarr/sonarr/info.py b/bazarr/sonarr/info.py index e7fcb8f89..5bccf27a2 100644 --- a/bazarr/sonarr/info.py +++ b/bazarr/sonarr/info.py @@ -3,7 +3,7 @@ import logging import requests import datetime -import json +from requests.exceptions import JSONDecodeError from dogpile.cache import make_region @@ -34,13 +34,13 @@ class GetSonarrInfo: if 'version' in sonarr_json: sonarr_version = sonarr_json['version'] else: - raise json.decoder.JSONDecodeError - except json.decoder.JSONDecodeError: + raise JSONDecodeError + except JSONDecodeError: try: sv = f"{url_sonarr()}/api/v3/system/status?apikey={settings.sonarr.apikey}" sonarr_version = requests.get(sv, timeout=int(settings.sonarr.http_timeout), verify=False, headers=headers).json()['version'] - except json.decoder.JSONDecodeError: + except JSONDecodeError: logging.debug('BAZARR cannot get Sonarr version') sonarr_version = 'unknown' except Exception: diff --git a/bazarr/utilities/backup.py b/bazarr/utilities/backup.py index 136a959b1..a28eadef5 100644 --- a/bazarr/utilities/backup.py +++ b/bazarr/utilities/backup.py @@ -33,7 +33,7 @@ def get_restore_path(): def get_backup_files(fullpath=True): backup_file_pattern = os.path.join(get_backup_path(), 'bazarr_backup_v*.zip') file_list = glob(backup_file_pattern) - file_list.sort(key=os.path.getmtime) + file_list.sort(key=os.path.getmtime, reverse=True) if fullpath: return file_list else: diff --git a/custom_libs/subliminal_patch/providers/hdbits.py b/custom_libs/subliminal_patch/providers/hdbits.py index 10793b9ea..19196aecd 100644 --- a/custom_libs/subliminal_patch/providers/hdbits.py +++ b/custom_libs/subliminal_patch/providers/hdbits.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- import functools -from json import JSONDecodeError +from requests.exceptions import JSONDecodeError import logging import re import time diff --git a/custom_libs/subliminal_patch/providers/ktuvit.py b/custom_libs/subliminal_patch/providers/ktuvit.py index 9bb746547..699349601 100644 --- a/custom_libs/subliminal_patch/providers/ktuvit.py +++ b/custom_libs/subliminal_patch/providers/ktuvit.py @@ -3,6 +3,7 @@ import io import logging import os import json +from requests.exceptions import JSONDecodeError from subzero.language import Language from guessit import guessit @@ -144,7 +145,7 @@ class KtuvitProvider(Provider): self.session.headers["Pragma"] = "no-cache" self.session.headers["Cache-Control"] = "no-cache" self.session.headers["Content-Type"] = "application/json" - self.session.headers["User-Agent"]: os.environ.get( + self.session.headers["User-Agent"] = os.environ.get( "SZ_USER_AGENT", "Sub-Zero/2" ) @@ -161,13 +162,13 @@ class KtuvitProvider(Provider): is_success = self.parse_d_response( r, "IsSuccess", False, "Authentication to the provider" ) - except json.decoder.JSONDecodeError: + except JSONDecodeError: logger.info("Failed to Login to Ktuvit") if not is_success: error_message = '' try: error_message = self.parse_d_response(r, "ErrorMessage", "[None]") - except json.decode.JSONDecoderError: + except JSONDecodeError: raise AuthenticationError( "Error Logging in to Ktuvit Provider: " + str(r.content) ) @@ -473,8 +474,8 @@ class KtuvitProvider(Provider): try: response_content = response.json() - except json.decoder.JSONDecodeError as ex: - raise json.decoder.JSONDecodeError( + except JSONDecodeError as ex: + raise JSONDecodeError( "Unable to parse JSON returned while getting " + message, ex.doc, ex.pos ) else: @@ -486,11 +487,11 @@ class KtuvitProvider(Provider): value = response_content.get(field, default_value) if not value and value != default_value: - raise json.decoder.JSONDecodeError( + raise JSONDecodeError( "Missing " + message, str(response_content), 0 ) else: - raise json.decoder.JSONDecodeError( + raise JSONDecodeError( "Incomplete JSON returned while getting " + message, str(response_content), 0 diff --git a/custom_libs/subliminal_patch/providers/subdivx.py b/custom_libs/subliminal_patch/providers/subdivx.py index 4b250a2a2..ae8625f47 100644 --- a/custom_libs/subliminal_patch/providers/subdivx.py +++ b/custom_libs/subliminal_patch/providers/subdivx.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import -from json import JSONDecodeError +from requests.exceptions import JSONDecodeError import logging import random import re From 1852337ec7b41b2ea02ea339d2ee0e6c316797f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:09:22 -0400 Subject: [PATCH 002/129] no log: Bump actions/setup-python from 4 to 5 (#2434) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/test_bazarr_execution.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f8c15659..0d050ccc0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" diff --git a/.github/workflows/test_bazarr_execution.yml b/.github/workflows/test_bazarr_execution.yml index da31638c0..b7ded4c96 100644 --- a/.github/workflows/test_bazarr_execution.yml +++ b/.github/workflows/test_bazarr_execution.yml @@ -35,7 +35,7 @@ jobs: working-directory: ${{ env.UI_DIRECTORY }} - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: "3.8" From 369622834b6661f215425ebd77f8641082d42e07 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 19:10:42 -0400 Subject: [PATCH 003/129] no log: Bump @types/node from 20.11.26 to 20.11.29 in /frontend (#2435) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.11.26 to 20.11.29. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 78e8bdf83..4282c78a2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,7 +34,7 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", - "@types/node": "^20.11.26", + "@types/node": "^20.11.29", "@types/react": "^18.2.65", "@types/react-dom": "^18.2.21", "@types/react-table": "^7.7.19", @@ -3910,9 +3910,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.26.tgz", - "integrity": "sha512-YwOMmyhNnAWijOBQweOJnQPl068Oqd4K3OFbTc6AHJwzweUwwWG3GIFY74OKks2PJUDkQPeddOQES9mLn1CTEQ==", + "version": "20.11.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.29.tgz", + "integrity": "sha512-P99thMkD/1YkCvAtOd6/zGedKNA0p2fj4ZpjCzcNiSCBWgm3cNRTBfa/qjFnsKkkojxu4vVLtWpesnZ9+ap+gA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" diff --git a/frontend/package.json b/frontend/package.json index 05f5e07e0..14bea1d1e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -38,7 +38,7 @@ "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", - "@types/node": "^20.11.26", + "@types/node": "^20.11.29", "@types/react": "^18.2.65", "@types/react-dom": "^18.2.21", "@types/react-table": "^7.7.19", From b4071f0af6d73976680bbbcb2655259ca000200e Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 18 Mar 2024 20:28:19 -0400 Subject: [PATCH 004/129] Fixed betaseries provider when series doesn't exist. #2431 --- .../subliminal_patch/providers/betaseries.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/betaseries.py b/custom_libs/subliminal_patch/providers/betaseries.py index 4cd66401c..dfc92ccdd 100644 --- a/custom_libs/subliminal_patch/providers/betaseries.py +++ b/custom_libs/subliminal_patch/providers/betaseries.py @@ -83,6 +83,14 @@ class BetaSeriesProvider(Provider): logger.debug('Searching subtitles %r', params) res = self.session.get( server_url + 'episodes/display', params=params, timeout=10) + try: + if res.status_code == 400 and res.json()['errors'][0]['code'] == 4001: + # this is to catch no series found + return [] + elif res.status_code == 400 and res.json()['errors'][0]['code'] == 1001: + raise AuthenticationError("Invalid token provided") + except Exception: + pass res.raise_for_status() result = res.json() matches.add('tvdb_id') @@ -96,8 +104,14 @@ class BetaSeriesProvider(Provider): logger.debug('Searching subtitles %r', params) res = self.session.get( server_url + 'shows/episodes', params=params, timeout=10) - if res.status_code == 400: - raise AuthenticationError("Invalid token provided") + try: + if res.status_code == 400 and res.json()['errors'][0]['code'] == 4001: + # this is to catch no series found + return [] + elif res.status_code == 400 and res.json()['errors'][0]['code'] == 1001: + raise AuthenticationError("Invalid token provided") + except Exception: + pass res.raise_for_status() result = res.json() matches.add('series_tvdb_id') From 8b6204d24ff93a853215e30af4682518f3fd3029 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 20 Mar 2024 13:19:49 -0400 Subject: [PATCH 005/129] no log: Update schedule.yaml --- .github/workflows/schedule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/schedule.yaml b/.github/workflows/schedule.yaml index 69eddf032..f69f016ea 100644 --- a/.github/workflows/schedule.yaml +++ b/.github/workflows/schedule.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Execute - uses: benc-uk/workflow-dispatch@v121 + uses: benc-uk/workflow-dispatch@v123 with: workflow: "release_beta_to_dev" token: ${{ secrets.WF_GITHUB_TOKEN }} From 5fc93b48fba29e56bf788e86c2c8c94818c36212 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 20 Mar 2024 13:22:21 -0400 Subject: [PATCH 006/129] no log: Update schedule.yaml --- .github/workflows/schedule.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/schedule.yaml b/.github/workflows/schedule.yaml index f69f016ea..93a1a1ecd 100644 --- a/.github/workflows/schedule.yaml +++ b/.github/workflows/schedule.yaml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Execute - uses: benc-uk/workflow-dispatch@v123 + uses: benc-uk/workflow-dispatch@v1.2.3 with: workflow: "release_beta_to_dev" token: ${{ secrets.WF_GITHUB_TOKEN }} From 22a0f3be2147991baeff3e8c0b7bfd1c127055f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:15:27 -0400 Subject: [PATCH 007/129] no log: Bump actions/setup-node from 3 to 4 (#2437) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 3 to 4. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .github/workflows/release_beta_to_dev.yaml | 2 +- .github/workflows/release_dev_to_master.yaml | 2 +- .github/workflows/test_bazarr_execution.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0d050ccc0..79b1fc149 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: restore-keys: ${{ runner.os }}-modules- - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" diff --git a/.github/workflows/release_beta_to_dev.yaml b/.github/workflows/release_beta_to_dev.yaml index 994f511bc..f7155984c 100644 --- a/.github/workflows/release_beta_to_dev.yaml +++ b/.github/workflows/release_beta_to_dev.yaml @@ -36,7 +36,7 @@ jobs: restore-keys: ${{ runner.os }}-modules- - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" diff --git a/.github/workflows/release_dev_to_master.yaml b/.github/workflows/release_dev_to_master.yaml index 37d0e7eba..dddf603b0 100644 --- a/.github/workflows/release_dev_to_master.yaml +++ b/.github/workflows/release_dev_to_master.yaml @@ -38,7 +38,7 @@ jobs: restore-keys: ${{ runner.os }}-modules- - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" diff --git a/.github/workflows/test_bazarr_execution.yml b/.github/workflows/test_bazarr_execution.yml index b7ded4c96..034eb782a 100644 --- a/.github/workflows/test_bazarr_execution.yml +++ b/.github/workflows/test_bazarr_execution.yml @@ -22,7 +22,7 @@ jobs: ref: development - name: Setup NodeJS - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: "lts/*" From 538e28fce657ca43a4c3551965dc7e3d2e53ff4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Mar 2024 21:17:49 -0400 Subject: [PATCH 008/129] no log: Bump @types/react from 18.2.65 to 18.2.70 in /frontend (#2438) Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.2.65 to 18.2.70. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react) --- updated-dependencies: - dependency-name: "@types/react" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4282c78a2..4dc38ed3b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -35,7 +35,7 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", "@types/node": "^20.11.29", - "@types/react": "^18.2.65", + "@types/react": "^18.2.70", "@types/react-dom": "^18.2.21", "@types/react-table": "^7.7.19", "@vitejs/plugin-react": "^4.2.1", @@ -3930,9 +3930,9 @@ "devOptional": true }, "node_modules/@types/react": { - "version": "18.2.65", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.65.tgz", - "integrity": "sha512-98TsY0aW4jqx/3RqsUXwMDZSWR1Z4CUlJNue8ueS2/wcxZOsz4xmW1X8ieaWVRHcmmQM3R8xVA4XWB3dJnWwDQ==", + "version": "18.2.70", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz", + "integrity": "sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==", "devOptional": true, "dependencies": { "@types/prop-types": "*", diff --git a/frontend/package.json b/frontend/package.json index 14bea1d1e..a48c455e4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -39,7 +39,7 @@ "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", "@types/node": "^20.11.29", - "@types/react": "^18.2.65", + "@types/react": "^18.2.70", "@types/react-dom": "^18.2.21", "@types/react-table": "^7.7.19", "@vitejs/plugin-react": "^4.2.1", From a41dc546aa2e87cc9993eff4815b5dbbe39ca14c Mon Sep 17 00:00:00 2001 From: Caden Gobat <36030084+cgobat@users.noreply.github.com> Date: Wed, 27 Mar 2024 04:24:01 -0600 Subject: [PATCH 009/129] no log: Correct some misspellings and grammar (#2440) --- frontend/src/pages/Settings/Subtitles/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/pages/Settings/Subtitles/index.tsx b/frontend/src/pages/Settings/Subtitles/index.tsx index f6e0cae37..f2dec69ac 100644 --- a/frontend/src/pages/Settings/Subtitles/index.tsx +++ b/frontend/src/pages/Settings/Subtitles/index.tsx @@ -409,8 +409,7 @@ const SettingsSubtitlesView: FunctionComponent = () => { settingKey="settings-subsync-use_subsync" > - Enable automatic subtitles synchronization after downloading a - subtitle. + Enable automatic synchronization after downloading subtitles. Date: Sat, 30 Mar 2024 09:09:08 -0400 Subject: [PATCH 010/129] Fixed login page getting called even if authentication is disabled. #2441 --- bazarr/app/ui.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bazarr/app/ui.py b/bazarr/app/ui.py index 5ca597be7..925b25912 100644 --- a/bazarr/app/ui.py +++ b/bazarr/app/ui.py @@ -4,7 +4,8 @@ import os import requests import mimetypes -from flask import request, abort, render_template, Response, session, send_file, stream_with_context, Blueprint +from flask import (request, abort, render_template, Response, session, send_file, stream_with_context, Blueprint, + redirect) from functools import wraps from urllib.parse import unquote @@ -65,6 +66,10 @@ def check_login(actual_method): @ui_bp.route('/', defaults={'path': ''}) @ui_bp.route('/') def catch_all(path): + if path.startswith('login') and settings.auth.type not in ['basic', 'form']: + # login page has been accessed when no authentication is enabled + return redirect('/', code=302) + auth = True if settings.auth.type == 'basic': auth = request.authorization From ad16acb88f0d9e4eb0b4d7ba88add7380fe5cd42 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 1 Apr 2024 10:01:09 -0400 Subject: [PATCH 011/129] Fixed improper redirection from login page when base_url isn't empty. --- bazarr/app/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazarr/app/ui.py b/bazarr/app/ui.py index 925b25912..540c26d68 100644 --- a/bazarr/app/ui.py +++ b/bazarr/app/ui.py @@ -68,7 +68,7 @@ def check_login(actual_method): def catch_all(path): if path.startswith('login') and settings.auth.type not in ['basic', 'form']: # login page has been accessed when no authentication is enabled - return redirect('/', code=302) + return redirect(base_url or "/", code=302) auth = True if settings.auth.type == 'basic': From 221d5aa2db29a9da05e37f93e7754d4b3b47ba5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Apr 2024 16:19:01 -0400 Subject: [PATCH 012/129] no log: Bump recharts from 2.12.2 to 2.12.3 in /frontend (#2444) Bumps [recharts](https://github.com/recharts/recharts) from 2.12.2 to 2.12.3. - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/3.x/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v2.12.2...v2.12.3) --- updated-dependencies: - dependency-name: recharts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4dc38ed3b..681516923 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -54,7 +54,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.2", + "recharts": "^2.12.3", "sass": "^1.71.1", "typescript": "^5.4.2", "vite": "^5.1.6", @@ -9319,9 +9319,9 @@ } }, "node_modules/recharts": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.2.tgz", - "integrity": "sha512-9bpxjXSF5g81YsKkTSlaX7mM4b6oYI1mIYck6YkUcWuL3tomADccI51/6thY4LmvhYuRTwpfrOvE80Zc3oBRfQ==", + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.3.tgz", + "integrity": "sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg==", "dev": true, "dependencies": { "clsx": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index a48c455e4..6ad8f41f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -58,7 +58,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.2", + "recharts": "^2.12.3", "sass": "^1.71.1", "typescript": "^5.4.2", "vite": "^5.1.6", From 3e9cfea3c5d7bc856d2956ab7d095c90eeca98a8 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 6 Apr 2024 10:03:09 -0400 Subject: [PATCH 013/129] Fixed restart loop when TCP port already in use. --- bazarr/app/server.py | 15 +++++++++++---- bazarr/literals.py | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/bazarr/app/server.py b/bazarr/app/server.py index d56e1205b..250fa6c0a 100644 --- a/bazarr/app/server.py +++ b/bazarr/app/server.py @@ -4,7 +4,7 @@ import signal import warnings import logging import errno -from literals import EXIT_INTERRUPT, EXIT_NORMAL +from literals import EXIT_INTERRUPT, EXIT_NORMAL, EXIT_PORT_ALREADY_IN_USE_ERROR from utilities.central import restart_bazarr, stop_bazarr from waitress.server import create_server @@ -56,10 +56,17 @@ class Server: logging.exception("BAZARR cannot bind to specified IP, trying with default (0.0.0.0)") self.address = '0.0.0.0' self.connected = False + super(Server, self).__init__() elif error.errno == errno.EADDRINUSE: - logging.exception("BAZARR cannot bind to specified TCP port, trying with default (6767)") - self.port = '6767' - self.connected = False + if self.port != '6767': + logging.exception("BAZARR cannot bind to specified TCP port, trying with default (6767)") + self.port = '6767' + self.connected = False + super(Server, self).__init__() + else: + logging.exception("BAZARR cannot bind to default TCP port (6767) because it's already in use, " + "exiting...") + self.shutdown(EXIT_PORT_ALREADY_IN_USE_ERROR) else: logging.exception("BAZARR cannot start because of unhandled exception.") self.shutdown() diff --git a/bazarr/literals.py b/bazarr/literals.py index 9a5e7eb9a..a76fa2cee 100644 --- a/bazarr/literals.py +++ b/bazarr/literals.py @@ -28,3 +28,4 @@ EXIT_VALIDATION_ERROR = -101 EXIT_CONFIG_CREATE_ERROR = -102 EXIT_PYTHON_UPGRADE_NEEDED = -103 EXIT_REQUIREMENTS_ERROR = -104 +EXIT_PORT_ALREADY_IN_USE_ERROR = -105 From d91b2764966facd5ed4243007aab0634cfdbb911 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 20:58:37 -0400 Subject: [PATCH 014/129] no log: Bump @fortawesome/free-regular-svg-icons in /frontend (#2446) Bumps [@fortawesome/free-regular-svg-icons](https://github.com/FortAwesome/Font-Awesome) from 6.5.1 to 6.5.2. - [Release notes](https://github.com/FortAwesome/Font-Awesome/releases) - [Changelog](https://github.com/FortAwesome/Font-Awesome/blob/6.x/CHANGELOG.md) - [Commits](https://github.com/FortAwesome/Font-Awesome/compare/6.5.1...6.5.2) --- updated-dependencies: - dependency-name: "@fortawesome/free-regular-svg-icons" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 20 +++++++++++++++----- frontend/package.json | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 681516923..0b76684d8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,7 +26,7 @@ "@fontsource/roboto": "^5.0.12", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-regular-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", @@ -2912,18 +2912,28 @@ } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", - "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", + "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, + "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 6ad8f41f8..356823207 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "@fontsource/roboto": "^5.0.12", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.1", + "@fortawesome/free-regular-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", From cdf40950b23e502db387490f27e2326c019c5c2c Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 8 Apr 2024 21:50:51 -0400 Subject: [PATCH 015/129] no log: revert Bump @fortawesome/free-regular-svg-icons in /frontend (#2446)" (#2447) This reverts commit d91b2764966facd5ed4243007aab0634cfdbb911. --- frontend/package-lock.json | 20 +++++--------------- frontend/package.json | 2 +- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 0b76684d8..681516923 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,7 +26,7 @@ "@fontsource/roboto": "^5.0.12", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", @@ -2912,28 +2912,18 @@ } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", - "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", + "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.2" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, - "node_modules/@fortawesome/free-regular-svg-icons/node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", - "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", - "dev": true, - "hasInstallScript": true, - "engines": { - "node": ">=6" - } - }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 356823207..6ad8f41f8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -30,7 +30,7 @@ "@fontsource/roboto": "^5.0.12", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", From 6fc4b4152691e29e49031e29e2adde97f222c571 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Tue, 9 Apr 2024 06:29:51 -0400 Subject: [PATCH 016/129] Upgraded some frontend dependencies --- frontend/package-lock.json | 2087 ++++++++++++++++++------------------ frontend/package.json | 32 +- 2 files changed, 1071 insertions(+), 1048 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 681516923..5587d61aa 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -15,31 +15,31 @@ "@mantine/hooks": "^6.0.21", "@mantine/modals": "^6.0.21", "@mantine/notifications": "^6.0.21", - "axios": "^1.6.7", + "axios": "^1.6.8", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-router-dom": "^6.22.3", - "socket.io-client": "^4.7.4" + "socket.io-client": "^4.7.5" }, "devDependencies": { "@fontsource/roboto": "^5.0.12", - "@fortawesome/fontawesome-svg-core": "^6.5.1", - "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.1", - "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", + "@testing-library/react": "^14.3.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", - "@types/node": "^20.11.29", - "@types/react": "^18.2.70", - "@types/react-dom": "^18.2.21", - "@types/react-table": "^7.7.19", + "@types/node": "^20.12.6", + "@types/react": "^18.2.75", + "@types/react-dom": "^18.2.24", + "@types/react-table": "^7.7.20", "@vitejs/plugin-react": "^4.2.1", - "@vitest/coverage-v8": "^1.3.1", + "@vitest/coverage-v8": "^1.4.0", "@vitest/ui": "^1.2.2", "clsx": "^2.1.0", "eslint": "^8.57.0", @@ -54,10 +54,10 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.3", - "sass": "^1.71.1", - "typescript": "^5.4.2", - "vite": "^5.1.6", + "recharts": "^2.12.4", + "sass": "^1.74.1", + "typescript": "^5.4.4", + "vite": "^5.2.8", "vite-plugin-checker": "^0.6.4", "vitest": "^1.2.2", "yaml": "^2.4.1" @@ -92,105 +92,41 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", + "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", + "@babel/helpers": "^7.24.4", + "@babel/parser": "^7.24.4", "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", + "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -213,9 +149,9 @@ "dev": true }, "node_modules/@babel/eslint-parser": { - "version": "7.23.10", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.23.10.tgz", - "integrity": "sha512-3wSYDPZVnhseRnxRJH6ZVTNknBz76AEnyC+AYYhasjP3Yy23qz0ERR7Fcd2SHmYuSFJ2kY9gaaDd3vyqU09eSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.24.1.tgz", + "integrity": "sha512-d5guuzMlPeDfZIbpQ8+g1NaCNuAGBBGNECh0HVqz1sjOeVLh2CEaifuOysCH18URW6R7pqXINvf5PaR/dC6jLQ==", "dev": true, "dependencies": { "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", @@ -240,14 +176,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz", + "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -295,9 +231,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.0.tgz", - "integrity": "sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.4.tgz", + "integrity": "sha512-lG75yeuUSVu0pIcbhiYMXBXANHrpUPaOfu7ryAzskCgKUHuAxRQI5ssrtmF0X9UXldPlvT0XM/A4F44OXRt6iQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", @@ -305,7 +241,7 @@ "@babel/helper-function-name": "^7.23.0", "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", "semver": "^6.3.1" @@ -397,11 +333,11 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -465,13 +401,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", + "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", + "@babel/helper-member-expression-to-functions": "^7.23.0", "@babel/helper-optimise-call-expression": "^7.22.5" }, "engines": { @@ -518,9 +454,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", "engines": { "node": ">=6.9.0" } @@ -557,13 +493,13 @@ } }, "node_modules/@babel/helpers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", - "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz", + "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==", "dev": true, "dependencies": { "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", + "@babel/traverse": "^7.24.1", "@babel/types": "^7.24.0" }, "engines": { @@ -571,13 +507,14 @@ } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", + "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -648,9 +585,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", + "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -659,13 +596,29 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz", + "integrity": "sha512-qpl6vOOEEzTLLcsuqYYo8yDtrTocmu2xkGvgNebvPjT9DTtfFYGmgDqY+rBYXNlqL4s9qLDn6xkrJv4RxAPiTA==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", + "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -675,14 +628,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", + "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" + "@babel/plugin-transform-optional-chaining": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -692,13 +645,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", + "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -725,14 +678,14 @@ } }, "node_modules/@babel/plugin-proposal-decorators": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.0.tgz", - "integrity": "sha512-LiT1RqZWeij7X+wGxCoYh3/3b8nVOX6/7BZ9wiQgAIyjoeQWdROaodJCgT+dwtbjHaz0r7bEbHJzjSbVfcOyjQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.24.1.tgz", + "integrity": "sha512-zPEvzFijn+hRvJuX2Vu3KbEBN39LN3f7tW3MQO2LsIs57B26KU+kUc82BdAktS1VCM6libzh45eKGI65lg0cpA==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.0", + "@babel/helper-create-class-features-plugin": "^7.24.1", "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-decorators": "^7.24.0" + "@babel/plugin-syntax-decorators": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -862,9 +815,9 @@ } }, "node_modules/@babel/plugin-syntax-decorators": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.0.tgz", - "integrity": "sha512-MXW3pQCu9gUiVGzqkGqsgiINDVYXoAnrY8FYF/rmb+OfufNF0zHMpHPN4ulRrinxYT8Vk/aZJxYqOKsDECjKAw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.24.1.tgz", + "integrity": "sha512-05RJdO/cCrtVWuAaSn1tS3bH8jbsJa/Y1uD186u6J4C/1mnHFxseeuWpsqr9anvo7TUulev7tm7GDwRV+VuhDw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.24.0" @@ -901,12 +854,12 @@ } }, "node_modules/@babel/plugin-syntax-flow": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz", - "integrity": "sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", + "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -916,12 +869,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", + "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -931,12 +884,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", + "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -970,12 +923,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", - "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1087,12 +1040,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1118,12 +1071,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", + "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1133,13 +1086,13 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", + "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-remap-async-to-generator": "^7.22.20", "@babel/plugin-syntax-async-generators": "^7.8.4" }, @@ -1151,13 +1104,13 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", + "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-module-imports": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-remap-async-to-generator": "^7.22.20" }, "engines": { @@ -1168,12 +1121,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", + "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1183,12 +1136,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.4.tgz", + "integrity": "sha512-nIFUZIpGKDf9O9ttyRXpHFpKC+X3Y5mtshZONuEUYBomAKoM4y029Jr+uB1bHGPhNmK8YXHevDtKDOLmtRrp6g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1198,13 +1151,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", + "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1214,13 +1167,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.4.tgz", + "integrity": "sha512-B8q7Pz870Hz/q9UgP8InNpY01CSLDSCyqX7zcRuv3FcPl87A2G17lASroHWaCtbdIcbYzOZ7kWmXFKbijMSmFg==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1231,17 +1184,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", + "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", "@babel/helper-split-export-declaration": "^7.22.6", "globals": "^11.1.0" }, @@ -1253,13 +1206,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", + "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1269,12 +1222,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", + "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1284,13 +1237,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", + "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1300,12 +1253,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", + "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1315,12 +1268,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", + "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { @@ -1331,13 +1284,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", + "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", "dev": true, "dependencies": { "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1347,12 +1300,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", + "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { @@ -1363,13 +1316,13 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz", - "integrity": "sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz", + "integrity": "sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.23.3" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-flow": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1379,12 +1332,12 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", + "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { @@ -1395,14 +1348,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", + "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1412,12 +1365,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", + "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -1428,12 +1381,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", + "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1443,12 +1396,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", + "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -1459,12 +1412,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", + "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1474,13 +1427,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", + "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1490,13 +1443,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", + "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-simple-access": "^7.22.5" }, "engines": { @@ -1507,14 +1460,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", + "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-validator-identifier": "^7.22.20" }, "engines": { @@ -1525,13 +1478,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", + "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", "dev": true, "dependencies": { "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1557,12 +1510,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", + "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1572,12 +1525,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", + "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -1588,12 +1541,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", + "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -1604,16 +1557,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz", - "integrity": "sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", + "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@babel/plugin-transform-parameters": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1623,13 +1575,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", + "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1639,12 +1591,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", + "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -1655,12 +1607,12 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", + "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, @@ -1672,12 +1624,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", + "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1687,13 +1639,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", + "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1703,14 +1655,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", + "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -1721,12 +1673,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", + "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1736,12 +1688,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz", - "integrity": "sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", + "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1785,12 +1737,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.1.tgz", + "integrity": "sha512-kDJgnPujTmAZ/9q2CN4m2/lRsUUPDvsG3+tSHWUJIzMGTt5U/b/fwWd3RO3n+5mjLrsBrVa5eKFRVSQbi3dF1w==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1800,12 +1752,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.24.1.tgz", + "integrity": "sha512-1v202n7aUq4uXAieRTKcwPzNyphlCuqHHDcdSNc+vdhoTEZcFMh+L5yZuCmGaIO7bs1nJUNfHB89TZyoL48xNA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1815,13 +1767,13 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz", - "integrity": "sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", + "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1831,12 +1783,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", + "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1847,12 +1799,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", + "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1862,16 +1814,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.0.tgz", - "integrity": "sha512-zc0GA5IitLKJrSfXlXmp8KDqLrnGECK7YRfQBmEKg1NmBOQ7e+KuclBEKJgzifQeUYLdNiAw4B4bjyvzWVLiSA==", + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.3.tgz", + "integrity": "sha512-J0BuRPNlNqlMTRJ72eVptpt9VcInbxO6iP3jaxr+1NPhC0UkKL+6oeX6VXMEYdADnuqmMmsBspt4d5w8Y/TCbQ==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-module-imports": "^7.24.3", "@babel/helper-plugin-utils": "^7.24.0", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -1882,12 +1834,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", + "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1897,12 +1849,12 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", + "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" }, "engines": { @@ -1913,12 +1865,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", + "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1928,12 +1880,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", + "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1943,12 +1895,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", + "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1958,15 +1910,15 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.4.tgz", + "integrity": "sha512-79t3CQ8+oBGk/80SQ8MN3Bs3obf83zJ0YZjDmDaEZN8MqhMI760apl5z6a20kFeMXBwJX99VpKT8CKxEBp5H1g==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-typescript": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -1976,12 +1928,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", + "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -1991,13 +1943,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", + "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2007,13 +1959,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", + "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2023,13 +1975,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", + "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", "dev": true, "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.0" }, "engines": { "node": ">=6.9.0" @@ -2039,26 +1991,27 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.0.tgz", - "integrity": "sha512-ZxPEzV9IgvGn73iK0E6VB9/95Nd7aMFpbE0l8KQFDG70cOV9IxRP7Y2FUPmlK0v6ImlLqYX50iuZ3ZTVhOF2lA==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.4.tgz", + "integrity": "sha512-7Kl6cSmYkak0FK/FXjSEnLJ1N9T/WA2RkMhu17gZ/dsxKJUuTYNIylahPTzqpLyJN4WhDif8X0XK1R8Wsguo/A==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", + "@babel/compat-data": "^7.24.4", "@babel/helper-compilation-targets": "^7.23.6", "@babel/helper-plugin-utils": "^7.24.0", "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.4", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-assertions": "^7.24.1", + "@babel/plugin-syntax-import-attributes": "^7.24.1", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2070,58 +2023,58 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", + "@babel/plugin-transform-arrow-functions": "^7.24.1", + "@babel/plugin-transform-async-generator-functions": "^7.24.3", + "@babel/plugin-transform-async-to-generator": "^7.24.1", + "@babel/plugin-transform-block-scoped-functions": "^7.24.1", + "@babel/plugin-transform-block-scoping": "^7.24.4", + "@babel/plugin-transform-class-properties": "^7.24.1", + "@babel/plugin-transform-class-static-block": "^7.24.4", + "@babel/plugin-transform-classes": "^7.24.1", + "@babel/plugin-transform-computed-properties": "^7.24.1", + "@babel/plugin-transform-destructuring": "^7.24.1", + "@babel/plugin-transform-dotall-regex": "^7.24.1", + "@babel/plugin-transform-duplicate-keys": "^7.24.1", + "@babel/plugin-transform-dynamic-import": "^7.24.1", + "@babel/plugin-transform-exponentiation-operator": "^7.24.1", + "@babel/plugin-transform-export-namespace-from": "^7.24.1", + "@babel/plugin-transform-for-of": "^7.24.1", + "@babel/plugin-transform-function-name": "^7.24.1", + "@babel/plugin-transform-json-strings": "^7.24.1", + "@babel/plugin-transform-literals": "^7.24.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", + "@babel/plugin-transform-member-expression-literals": "^7.24.1", + "@babel/plugin-transform-modules-amd": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-modules-systemjs": "^7.24.1", + "@babel/plugin-transform-modules-umd": "^7.24.1", "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.24.0", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/plugin-transform-new-target": "^7.24.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", + "@babel/plugin-transform-numeric-separator": "^7.24.1", + "@babel/plugin-transform-object-rest-spread": "^7.24.1", + "@babel/plugin-transform-object-super": "^7.24.1", + "@babel/plugin-transform-optional-catch-binding": "^7.24.1", + "@babel/plugin-transform-optional-chaining": "^7.24.1", + "@babel/plugin-transform-parameters": "^7.24.1", + "@babel/plugin-transform-private-methods": "^7.24.1", + "@babel/plugin-transform-private-property-in-object": "^7.24.1", + "@babel/plugin-transform-property-literals": "^7.24.1", + "@babel/plugin-transform-regenerator": "^7.24.1", + "@babel/plugin-transform-reserved-words": "^7.24.1", + "@babel/plugin-transform-shorthand-properties": "^7.24.1", + "@babel/plugin-transform-spread": "^7.24.1", + "@babel/plugin-transform-sticky-regex": "^7.24.1", + "@babel/plugin-transform-template-literals": "^7.24.1", + "@babel/plugin-transform-typeof-symbol": "^7.24.1", + "@babel/plugin-transform-unicode-escapes": "^7.24.1", + "@babel/plugin-transform-unicode-property-regex": "^7.24.1", + "@babel/plugin-transform-unicode-regex": "^7.24.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.31.0", "semver": "^6.3.1" }, @@ -2147,17 +2100,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.23.3.tgz", - "integrity": "sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", + "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-react-display-name": "^7.23.3", - "@babel/plugin-transform-react-jsx": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-transform-react-display-name": "^7.24.1", + "@babel/plugin-transform-react-jsx": "^7.23.4", "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.23.3" + "@babel/plugin-transform-react-pure-annotations": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -2167,16 +2120,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", + "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-option": "^7.23.5", + "@babel/plugin-syntax-jsx": "^7.24.1", + "@babel/plugin-transform-modules-commonjs": "^7.24.1", + "@babel/plugin-transform-typescript": "^7.24.1" }, "engines": { "node": ">=6.9.0" @@ -2192,9 +2145,9 @@ "dev": true }, "node_modules/@babel/runtime": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", - "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2217,18 +2170,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz", + "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", + "@babel/parser": "^7.24.1", "@babel/types": "^7.24.0", "debug": "^4.3.1", "globals": "^11.1.0" @@ -2325,9 +2278,9 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", - "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", "peer": true, "dependencies": { "@emotion/hash": "^0.9.1", @@ -2371,9 +2324,9 @@ "peer": true }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", "cpu": [ "ppc64" ], @@ -2387,9 +2340,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "cpu": [ "arm" ], @@ -2403,9 +2356,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "cpu": [ "arm64" ], @@ -2419,9 +2372,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", "cpu": [ "x64" ], @@ -2435,9 +2388,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", "cpu": [ "arm64" ], @@ -2451,9 +2404,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", "cpu": [ "x64" ], @@ -2467,9 +2420,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", "cpu": [ "arm64" ], @@ -2483,9 +2436,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", "cpu": [ "x64" ], @@ -2499,9 +2452,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", "cpu": [ "arm" ], @@ -2515,9 +2468,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", "cpu": [ "arm64" ], @@ -2531,9 +2484,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", "cpu": [ "ia32" ], @@ -2547,9 +2500,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "cpu": [ "loong64" ], @@ -2563,9 +2516,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", "cpu": [ "mips64el" ], @@ -2579,9 +2532,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "cpu": [ "ppc64" ], @@ -2595,9 +2548,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "cpu": [ "riscv64" ], @@ -2611,9 +2564,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "cpu": [ "s390x" ], @@ -2627,9 +2580,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", "cpu": [ "x64" ], @@ -2643,9 +2596,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", "cpu": [ "x64" ], @@ -2659,9 +2612,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", "cpu": [ "x64" ], @@ -2675,9 +2628,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "cpu": [ "x64" ], @@ -2691,9 +2644,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", "cpu": [ "arm64" ], @@ -2707,9 +2660,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", "cpu": [ "ia32" ], @@ -2723,9 +2676,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", "cpu": [ "x64" ], @@ -2876,9 +2829,9 @@ "dev": true }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", - "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", "dev": true, "hasInstallScript": true, "engines": { @@ -2886,52 +2839,52 @@ } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", - "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz", + "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-brands-svg-icons": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.1.tgz", - "integrity": "sha512-093l7DAkx0aEtBq66Sf19MgoZewv1zeY9/4C7vSKPO4qMwEsW/2VYTUTpBtLwfb9T2R73tXaRDPmE4UqLCYHfg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz", + "integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz", - "integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz", + "integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz", - "integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz", + "integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==", "dev": true, "hasInstallScript": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.1" + "@fortawesome/fontawesome-common-types": "6.5.2" }, "engines": { "node": ">=6" @@ -2978,9 +2931,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@istanbuljs/schema": { @@ -3410,9 +3363,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", - "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.1.tgz", + "integrity": "sha512-fH8/o8nSUek8ceQnT7K4EQbSiV7jgkHq81m9lWZFIXjJ7lJzpWXbQFpT/Zh6OZYnpFykvzC3fbEvEAFZu03dPA==", "cpu": [ "arm" ], @@ -3423,9 +3376,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", - "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.1.tgz", + "integrity": "sha512-Y/9OHLjzkunF+KGEoJr3heiD5X9OLa8sbT1lm0NYeKyaM3oMhhQFvPB0bNZYJwlq93j8Z6wSxh9+cyKQaxS7PQ==", "cpu": [ "arm64" ], @@ -3436,9 +3389,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", - "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.1.tgz", + "integrity": "sha512-+kecg3FY84WadgcuSVm6llrABOdQAEbNdnpi5X3UwWiFVhZIZvKgGrF7kmLguvxHNQy+UuRV66cLVl3S+Rkt+Q==", "cpu": [ "arm64" ], @@ -3449,9 +3402,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", - "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.1.tgz", + "integrity": "sha512-2pYRzEjVqq2TB/UNv47BV/8vQiXkFGVmPFwJb+1E0IFFZbIX8/jo1olxqqMbo6xCXf8kabANhp5bzCij2tFLUA==", "cpu": [ "x64" ], @@ -3462,9 +3415,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", - "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.1.tgz", + "integrity": "sha512-mS6wQ6Do6/wmrF9aTFVpIJ3/IDXhg1EZcQFYHZLHqw6AzMBjTHWnCG35HxSqUNphh0EHqSM6wRTT8HsL1C0x5g==", "cpu": [ "arm" ], @@ -3475,9 +3428,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", - "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.1.tgz", + "integrity": "sha512-p9rGKYkHdFMzhckOTFubfxgyIO1vw//7IIjBBRVzyZebWlzRLeNhqxuSaZ7kCEKVkm/kuC9fVRW9HkC/zNRG2w==", "cpu": [ "arm64" ], @@ -3488,9 +3441,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", - "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.1.tgz", + "integrity": "sha512-nDY6Yz5xS/Y4M2i9JLQd3Rofh5OR8Bn8qe3Mv/qCVpHFlwtZSBYSPaU4mrGazWkXrdQ98GB//H0BirGR/SKFSw==", "cpu": [ "arm64" ], @@ -3500,10 +3453,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.1.tgz", + "integrity": "sha512-im7HE4VBL+aDswvcmfx88Mp1soqL9OBsdDBU8NqDEYtkri0qV0THhQsvZtZeNNlLeCUQ16PZyv7cqutjDF35qw==", + "cpu": [ + "ppc64le" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", - "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.1.tgz", + "integrity": "sha512-RWdiHuAxWmzPJgaHJdpvUUlDz8sdQz4P2uv367T2JocdDa98iRw2UjIJ4QxSyt077mXZT2X6pKfT2iYtVEvOFw==", "cpu": [ "riscv64" ], @@ -3513,10 +3479,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.1.tgz", + "integrity": "sha512-VMgaGQ5zRX6ZqV/fas65/sUGc9cPmsntq2FiGmayW9KMNfWVG/j0BAqImvU4KTeOOgYSf1F+k6at1UfNONuNjA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", - "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.1.tgz", + "integrity": "sha512-9Q7DGjZN+hTdJomaQ3Iub4m6VPu1r94bmK2z3UeWP3dGUecRC54tmVu9vKHTm1bOt3ASoYtEz6JSRLFzrysKlA==", "cpu": [ "x64" ], @@ -3527,9 +3506,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", - "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.1.tgz", + "integrity": "sha512-JNEG/Ti55413SsreTguSx0LOVKX902OfXIKVg+TCXO6Gjans/k9O6ww9q3oLGjNDaTLxM+IHFMeXy/0RXL5R/g==", "cpu": [ "x64" ], @@ -3540,9 +3519,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", - "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.1.tgz", + "integrity": "sha512-ryS22I9y0mumlLNwDFYZRDFLwWh3aKaC72CWjFcFvxK0U6v/mOkM5Up1bTbCRAhv3kEIwW2ajROegCIQViUCeA==", "cpu": [ "arm64" ], @@ -3553,9 +3532,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", - "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.1.tgz", + "integrity": "sha512-TdloItiGk+T0mTxKx7Hp279xy30LspMso+GzQvV2maYePMAWdmrzqSNZhUpPj3CGw12aGj57I026PgLCTu8CGg==", "cpu": [ "ia32" ], @@ -3566,9 +3545,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", - "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.1.tgz", + "integrity": "sha512-wQGI+LY/Py20zdUPq+XCem7JcPOyzIJBm3dli+56DJsQOHbnXZFEwgmnC6el1TPAfC8lBT3m+z69RmLykNUbew==", "cpu": [ "x64" ], @@ -3579,9 +3558,9 @@ ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz", - "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.1.tgz", + "integrity": "sha512-S3Kq8e7LqxkA9s7HKLqXGTGck1uwis5vAXan3FnU5yw1Ec5hsSGnq4s/UCaSqABPOnOTg7zASLyst7+ohgWexg==", "dev": true }, "node_modules/@sinclair/typebox": { @@ -3596,22 +3575,23 @@ "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, "node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", + "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", + "aria-query": "5.3.0", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/@testing-library/jest-dom": { @@ -3679,9 +3659,9 @@ "dev": true }, "node_modules/@testing-library/react": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.1.tgz", - "integrity": "sha512-sGdjws32ai5TLerhvzThYFbpnF9XtL65Cjf+gB0Dhr29BGqK+mAeN7SURSdu+eqgET4ANcWoC7FQpkaiGvBr+A==", + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.0.tgz", + "integrity": "sha512-AYJGvNFMbCa5vt1UtDCa/dcaABrXq8gph6VN+cffIx0UeA0qiGqS+sT60+sb+Gjc8tGXdECWYQgaF0khf8b+Lg==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", @@ -3696,6 +3676,34 @@ "react-dom": "^18.0.0" } }, + "node_modules/@testing-library/react/node_modules/@testing-library/dom": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.1.3", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@testing-library/react/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dev": true, + "dependencies": { + "deep-equal": "^2.0.5" + } + }, "node_modules/@testing-library/user-event": { "version": "14.5.2", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", @@ -3910,9 +3918,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.29", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.29.tgz", - "integrity": "sha512-P99thMkD/1YkCvAtOd6/zGedKNA0p2fj4ZpjCzcNiSCBWgm3cNRTBfa/qjFnsKkkojxu4vVLtWpesnZ9+ap+gA==", + "version": "20.12.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.6.tgz", + "integrity": "sha512-3KurE8taB8GCvZBPngVbp0lk5CKi8M9f9k1rsADh0Evdz5SzJ+Q+Hx9uHoFGsLnLnd1xmkDQr2hVhlA0Mn0lKQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -3924,46 +3932,39 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", "devOptional": true }, "node_modules/@types/react": { - "version": "18.2.70", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.70.tgz", - "integrity": "sha512-hjlM2hho2vqklPhopNkXkdkeq6Lv8WSZTpr7956zY+3WS5cfYUewtCzsJLsbW5dEv3lfSeQ4W14ZFeKC437JRQ==", + "version": "18.2.75", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.75.tgz", + "integrity": "sha512-+DNnF7yc5y0bHkBTiLKqXFe+L4B3nvOphiMY3tuA5X10esmjqk7smyBZzbGTy2vsiy/Bnzj8yFIBL8xhRacoOg==", "devOptional": true, "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.21", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.21.tgz", - "integrity": "sha512-gnvBA/21SA4xxqNXEwNiVcP0xSGHh/gi1VhWv9Bl46a0ItbTT5nFY+G9VSQpaG/8N/qdJpJ+vftQ4zflTtnjLw==", + "version": "18.2.24", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.24.tgz", + "integrity": "sha512-cN6upcKd8zkGy4HU9F1+/s98Hrp6D4MOcippK4PoE8OZRngohHZpbJn1GsaDLz87MqvHNoT13nHvNqM9ocRHZg==", "dev": true, "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-table": { - "version": "7.7.19", - "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.19.tgz", - "integrity": "sha512-47jMa1Pai7ily6BXJCW33IL5ghqmCWs2VM9s+h1D4mCaK5P4uNkZOW3RMMg8MCXBvAJ0v9+sPqKjhid0PaJPQA==", + "version": "7.7.20", + "resolved": "https://registry.npmjs.org/@types/react-table/-/react-table-7.7.20.tgz", + "integrity": "sha512-ahMp4pmjVlnExxNwxyaDrFgmKxSbPwU23sGQw2gJK4EhCvnvmib2s/O/+y1dfV57dXOwpr2plfyBol+vEHbi2w==", "dev": true, "dependencies": { "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "devOptional": true - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -4345,9 +4346,9 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz", - "integrity": "sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", + "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -4355,12 +4356,13 @@ "debug": "^4.3.4", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^4.0.1", + "istanbul-lib-source-maps": "^5.0.4", "istanbul-reports": "^3.1.6", "magic-string": "^0.30.5", "magicast": "^0.3.3", "picocolors": "^1.0.0", "std-env": "^3.5.0", + "strip-literal": "^2.0.0", "test-exclude": "^6.0.0", "v8-to-istanbul": "^9.2.0" }, @@ -4368,17 +4370,17 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.3.1" + "vitest": "1.4.0" } }, "node_modules/@vitest/expect": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", - "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", + "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", "dev": true, "dependencies": { - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", "chai": "^4.3.10" }, "funding": { @@ -4386,12 +4388,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", - "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", + "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", "dev": true, "dependencies": { - "@vitest/utils": "1.3.1", + "@vitest/utils": "1.4.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -4427,9 +4429,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", - "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", + "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -4473,9 +4475,9 @@ "dev": true }, "node_modules/@vitest/spy": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", - "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", + "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -4485,12 +4487,12 @@ } }, "node_modules/@vitest/ui": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.3.1.tgz", - "integrity": "sha512-2UrFLJ62c/eJGPHcclstMKlAR7E1WB1ITe1isuowEPJJHi3HfqofvsUqQ1cGrEF7kitG1DJuwURUA3HLDtQkXA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-1.4.0.tgz", + "integrity": "sha512-XC6CMhN1gzYcGbpn6/Oanj4Au2EXwQEX6vpcOeLlZv8dy7g11Ukx8zwtYQbwxs9duK2s9j2o5rbQiCP5DPAcmw==", "dev": true, "dependencies": { - "@vitest/utils": "1.3.1", + "@vitest/utils": "1.4.0", "fast-glob": "^3.3.2", "fflate": "^0.8.1", "flatted": "^3.2.9", @@ -4502,13 +4504,13 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.3.1" + "vitest": "1.4.0" } }, "node_modules/@vitest/utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", - "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -4583,9 +4585,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -4669,9 +4671,9 @@ "dev": true }, "node_modules/aria-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", - "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", "dependencies": { "tslib": "^2.0.0" }, @@ -4680,12 +4682,12 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/array-buffer-byte-length": { @@ -4705,15 +4707,16 @@ } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -4732,35 +4735,17 @@ "node": ">=8" } }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array.prototype.findlast": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.4.tgz", - "integrity": "sha512-BMtLxpV+8BD+6ZPFIWmnUBpQoy+A+ujcg4rhp2iwCRJYA7PEh2MS4NL3lz8EiDlLrJPp2hg9qWihr5pd//jcGw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" }, "engines": { @@ -4771,15 +4756,16 @@ } }, "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", + "es-abstract": "^1.23.2", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" }, "engines": { @@ -4887,15 +4873,6 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -4934,11 +4911,11 @@ } }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -4981,57 +4958,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", + "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.1", + "core-js-compat": "^3.36.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", + "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" + "@babel/helper-define-polyfill-provider": "^0.6.1" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -5081,12 +5026,15 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { @@ -5194,9 +5142,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001597", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001597.tgz", - "integrity": "sha512-7LjJvmQU6Sj7bL0j5b5WY/3n7utXUJvAe1lxhsHDbLmwX9mdL86Yjtr+5SRCyf8qME4M7pU2hswj0FpyBVCv9w==", + "version": "1.0.30001607", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz", + "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==", "dev": true, "funding": [ { @@ -5375,12 +5323,12 @@ "peer": true }, "node_modules/core-js-compat": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", + "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", "dev": true, "dependencies": { - "browserslist": "^4.22.3" + "browserslist": "^4.23.0" }, "funding": { "type": "opencollective", @@ -5587,6 +5535,57 @@ "node": ">=18" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -5775,9 +5774,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.702", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.702.tgz", - "integrity": "sha512-LYLXyEUsZ3nNSwiOWjI88N1PJUAMU2QphQSgGLVkFnb3FxZxNui2Vzi2PaKPgPWbsWbZstZnh6BMf/VQJamjiQ==", + "version": "1.4.730", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.730.tgz", + "integrity": "sha512-oJRPo82XEqtQAobHpJIR3zW5YO3sSRRkPz2an4yxi1UvqhsGm54vR/wzTFV74a3soDOJ8CKW7ajOOX5ESzddwg==", "dev": true }, "node_modules/emoji-regex": { @@ -5847,17 +5846,21 @@ } }, "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.1", "arraybuffer.prototype.slice": "^1.0.3", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", "es-define-property": "^1.0.0", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", @@ -5868,10 +5871,11 @@ "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.1", + "hasown": "^2.0.2", "internal-slot": "^1.0.7", "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.3", @@ -5882,17 +5886,17 @@ "object-keys": "^1.1.1", "object.assign": "^4.1.5", "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", + "safe-array-concat": "^1.1.2", "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.2", "typed-array-byte-length": "^1.0.1", "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -5901,12 +5905,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==", - "dev": true - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -5949,26 +5947,37 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", - "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "version": "1.0.18", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", + "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", "dev": true, "dependencies": { - "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.4", + "es-abstract": "^1.23.0", "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", + "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.0" + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -6015,9 +6024,9 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", "dev": true, "hasInstallScript": true, "bin": { @@ -6027,29 +6036,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, "node_modules/escalade": { @@ -6341,19 +6350,10 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, "node_modules/eslint-plugin-react": { - "version": "7.34.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.0.tgz", - "integrity": "sha512-MeVXdReleBTdkz/bvcQMSnCXGi+c9kvy51IpinjnJgutl3YTHWsDdke7Z1ufZpGfDG8xduBDKyjtB9JH1eBKIQ==", + "version": "7.34.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", + "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", @@ -6754,9 +6754,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -7421,6 +7421,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -7723,28 +7738,19 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "istanbul-lib-coverage": "^3.0.0" }, "engines": { "node": ">=10" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", @@ -8235,9 +8241,9 @@ } }, "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", + "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -8580,28 +8586,29 @@ } }, "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8611,40 +8618,45 @@ } }, "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", + "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", "dev": true, "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8825,8 +8837,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8861,9 +8872,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "dev": true, "funding": [ { @@ -8882,7 +8893,7 @@ "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" @@ -9135,11 +9146,11 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", - "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", + "version": "2.5.9", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.9.tgz", + "integrity": "sha512-bvHCLBrFfM2OgcrpPY2YW84sPdS2o2HKWJUf1xGyGLnSoEnOTOBpahIarjRuYtN0ryahCeP242yf+5TrBX/pZA==", "dependencies": { - "react-remove-scroll-bar": "^2.3.4", + "react-remove-scroll-bar": "^2.3.6", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", @@ -9159,9 +9170,9 @@ } }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.5.tgz", - "integrity": "sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" @@ -9210,9 +9221,9 @@ } }, "node_modules/react-smooth": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.0.tgz", - "integrity": "sha512-2NMXOBY1uVUQx1jBeENGA497HK20y6CPGYL1ZnJLeoQ8rrc3UfmOM82sRxtzpcoCkUMy4CS0RGylfuVhuFjBgg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", "dev": true, "dependencies": { "fast-equals": "^5.0.1", @@ -9319,9 +9330,9 @@ } }, "node_modules/recharts": { - "version": "2.12.3", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.3.tgz", - "integrity": "sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg==", + "version": "2.12.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.4.tgz", + "integrity": "sha512-dM4skmk4fDKEDjL9MNunxv6zcTxePGVEzRnLDXALRpfJ85JoQ0P0APJ/CoJlmnQI0gPjBlOkjzrwrfQrRST3KA==", "dev": true, "dependencies": { "clsx": "^2.0.0", @@ -9364,16 +9375,16 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", - "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0", - "get-intrinsic": "^1.2.3", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", "which-builtin-type": "^1.1.3" }, @@ -9532,9 +9543,9 @@ } }, "node_modules/rollup": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", - "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.1.tgz", + "integrity": "sha512-4LnHSdd3QK2pa1J6dFbfm1HN0D7vSK/ZuZTsdyUAlA6Rr1yTouUTL13HaDOGJVgby461AhrNGBS7sCGXXtT+SA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -9547,19 +9558,21 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.13.0", - "@rollup/rollup-android-arm64": "4.13.0", - "@rollup/rollup-darwin-arm64": "4.13.0", - "@rollup/rollup-darwin-x64": "4.13.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", - "@rollup/rollup-linux-arm64-gnu": "4.13.0", - "@rollup/rollup-linux-arm64-musl": "4.13.0", - "@rollup/rollup-linux-riscv64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-gnu": "4.13.0", - "@rollup/rollup-linux-x64-musl": "4.13.0", - "@rollup/rollup-win32-arm64-msvc": "4.13.0", - "@rollup/rollup-win32-ia32-msvc": "4.13.0", - "@rollup/rollup-win32-x64-msvc": "4.13.0", + "@rollup/rollup-android-arm-eabi": "4.14.1", + "@rollup/rollup-android-arm64": "4.14.1", + "@rollup/rollup-darwin-arm64": "4.14.1", + "@rollup/rollup-darwin-x64": "4.14.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.1", + "@rollup/rollup-linux-arm64-gnu": "4.14.1", + "@rollup/rollup-linux-arm64-musl": "4.14.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.1", + "@rollup/rollup-linux-riscv64-gnu": "4.14.1", + "@rollup/rollup-linux-s390x-gnu": "4.14.1", + "@rollup/rollup-linux-x64-gnu": "4.14.1", + "@rollup/rollup-linux-x64-musl": "4.14.1", + "@rollup/rollup-win32-arm64-msvc": "4.14.1", + "@rollup/rollup-win32-ia32-msvc": "4.14.1", + "@rollup/rollup-win32-x64-msvc": "4.14.1", "fsevents": "~2.3.2" } }, @@ -9634,9 +9647,9 @@ "dev": true }, "node_modules/sass": { - "version": "1.71.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", - "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", + "version": "1.74.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz", + "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -9786,9 +9799,9 @@ } }, "node_modules/socket.io-client": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.4.tgz", - "integrity": "sha512-wh+OkeF0rAVCrABWQBaEjLfb7DVPotMbu0cgWgyR0v6eA4EoVnAwcIeIbcdTE3GT/H3kbdLl7OoH2+asoDRIIg==", + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.2", @@ -9821,9 +9834,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -9881,34 +9894,41 @@ "dev": true }, "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -9918,28 +9938,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10000,21 +10023,21 @@ } }, "node_modules/strip-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", - "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", "dev": true, "dependencies": { - "js-tokens": "^8.0.2" + "js-tokens": "^9.0.0" }, "funding": { "url": "https://github.com/sponsors/antfu" } }, "node_modules/strip-literal/node_modules/js-tokens": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", - "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", "dev": true }, "node_modules/stylis": { @@ -10090,9 +10113,9 @@ "dev": true }, "node_modules/tinypool": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", - "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", + "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", "dev": true, "engines": { "node": ">=14.0.0" @@ -10300,9 +10323,9 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { "call-bind": "^1.0.7", @@ -10320,9 +10343,9 @@ } }, "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10333,9 +10356,9 @@ } }, "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", "dev": true }, "node_modules/unbox-primitive": { @@ -10467,9 +10490,9 @@ } }, "node_modules/use-callback-ref": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz", - "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", "dependencies": { "tslib": "^2.0.0" }, @@ -10565,9 +10588,9 @@ "dev": true }, "node_modules/victory-vendor": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", - "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", "dev": true, "dependencies": { "@types/d3-array": "^3.0.3", @@ -10587,14 +10610,14 @@ } }, "node_modules/vite": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", - "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", + "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.35", - "rollup": "^4.2.0" + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -10642,9 +10665,9 @@ } }, "node_modules/vite-node": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", - "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", + "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -10760,16 +10783,16 @@ "dev": true }, "node_modules/vitest": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", - "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", + "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", "dev": true, "dependencies": { - "@vitest/expect": "1.3.1", - "@vitest/runner": "1.3.1", - "@vitest/snapshot": "1.3.1", - "@vitest/spy": "1.3.1", - "@vitest/utils": "1.3.1", + "@vitest/expect": "1.4.0", + "@vitest/runner": "1.4.0", + "@vitest/snapshot": "1.4.0", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -10783,7 +10806,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.2", "vite": "^5.0.0", - "vite-node": "1.3.1", + "vite-node": "1.4.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -10798,8 +10821,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.3.1", - "@vitest/ui": "1.3.1", + "@vitest/browser": "1.4.0", + "@vitest/ui": "1.4.0", "happy-dom": "*", "jsdom": "*" }, diff --git a/frontend/package.json b/frontend/package.json index 6ad8f41f8..cdf441278 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,31 +19,31 @@ "@mantine/hooks": "^6.0.21", "@mantine/modals": "^6.0.21", "@mantine/notifications": "^6.0.21", - "axios": "^1.6.7", + "axios": "^1.6.8", "react": "^18.2.0", "react-dom": "^18.2.0", "react-query": "^3.39.3", "react-router-dom": "^6.22.3", - "socket.io-client": "^4.7.4" + "socket.io-client": "^4.7.5" }, "devDependencies": { "@fontsource/roboto": "^5.0.12", - "@fortawesome/fontawesome-svg-core": "^6.5.1", - "@fortawesome/free-brands-svg-icons": "^6.5.1", - "@fortawesome/free-regular-svg-icons": "^6.5.1", - "@fortawesome/free-solid-svg-icons": "^6.5.1", + "@fortawesome/fontawesome-svg-core": "^6.5.2", + "@fortawesome/free-brands-svg-icons": "^6.5.2", + "@fortawesome/free-regular-svg-icons": "^6.5.2", + "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", + "@testing-library/react": "^14.3.0", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", - "@types/node": "^20.11.29", - "@types/react": "^18.2.70", - "@types/react-dom": "^18.2.21", - "@types/react-table": "^7.7.19", + "@types/node": "^20.12.6", + "@types/react": "^18.2.75", + "@types/react-dom": "^18.2.24", + "@types/react-table": "^7.7.20", "@vitejs/plugin-react": "^4.2.1", - "@vitest/coverage-v8": "^1.3.1", + "@vitest/coverage-v8": "^1.4.0", "@vitest/ui": "^1.2.2", "clsx": "^2.1.0", "eslint": "^8.57.0", @@ -58,10 +58,10 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.3", - "sass": "^1.71.1", - "typescript": "^5.4.2", - "vite": "^5.1.6", + "recharts": "^2.12.4", + "sass": "^1.74.1", + "typescript": "^5.4.4", + "vite": "^5.2.8", "vite-plugin-checker": "^0.6.4", "vitest": "^1.2.2", "yaml": "^2.4.1" From 3c30492e71d716dbced8bbdbc7cd004b064a65b4 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 10 Apr 2024 23:11:00 -0400 Subject: [PATCH 017/129] Improved best subtitles logging when score is below minimum score. --- custom_libs/subliminal_patch/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index 3c7e00479..5e861f348 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -539,6 +539,7 @@ class SZProviderPool(ProviderPool): use_hearing_impaired = hearing_impaired in ("prefer", "force HI") is_episode = isinstance(video, Episode) + max_score = sum(val for key, val in compute_score._scores['episode' if is_episode else 'movie'].items() if key != "hash") # sort subtitles by score unsorted_subtitles = [] @@ -570,7 +571,9 @@ class SZProviderPool(ProviderPool): for subtitle, score, score_without_hash, matches, orig_matches in scored_subtitles: # check score if score < min_score: - logger.info('%r: Score %d is below min_score (%d)', subtitle, score, min_score) + min_score_in_percent = round(min_score * 100 / max_score, 2) if min_score > 0 else 0 + logger.info('%r: Score %d is below min_score: %d out of %d (or %r%%)', + subtitle, score, min_score, max_score, min_score_in_percent) break # stop when all languages are downloaded From 77ebd036f210ec30529ea5419d7a91ac3c0ece13 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Sun, 14 Apr 2024 21:19:13 +0900 Subject: [PATCH 018/129] Added animetosho provider --- README.md | 1 + bazarr/app/config.py | 11 + bazarr/app/get_providers.py | 3 + bazarr/subtitles/refiners/__init__.py | 2 + bazarr/subtitles/refiners/anidb.py | 140 ++++ custom_libs/subliminal/video.py | 6 +- custom_libs/subliminal_patch/extensions.py | 1 - .../subliminal_patch/providers/animetosho.py | 164 ++++ custom_libs/subliminal_patch/video.py | 4 + .../pages/Settings/Providers/components.tsx | 59 +- .../src/pages/Settings/Providers/index.tsx | 12 +- frontend/src/pages/Settings/Providers/list.ts | 35 + tests/conftest.py | 2 +- .../data/animetosho_episode_response.json | 756 ++++++++++++++++++ .../data/animetosho_series_response.json | 232 ++++++ tests/subliminal_patch/test_animetosho.py | 59 ++ 16 files changed, 1465 insertions(+), 22 deletions(-) create mode 100644 bazarr/subtitles/refiners/anidb.py create mode 100644 custom_libs/subliminal_patch/providers/animetosho.py create mode 100644 tests/subliminal_patch/data/animetosho_episode_response.json create mode 100644 tests/subliminal_patch/data/animetosho_series_response.json create mode 100644 tests/subliminal_patch/test_animetosho.py diff --git a/README.md b/README.md index c244d7b47..0114bbff5 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ If you need something that is not already part of Bazarr, feel free to create a ## Supported subtitles providers: - Addic7ed +- Animetosho (requires AniDb HTTP API client described [here](https://wiki.anidb.net/HTTP_API_Definition)) - Assrt - BetaSeries - BSplayer diff --git a/bazarr/app/config.py b/bazarr/app/config.py index cda51d0ef..49ae4cf32 100644 --- a/bazarr/app/config.py +++ b/bazarr/app/config.py @@ -109,6 +109,7 @@ validators = [ Validator('general.adaptive_searching_delta', must_exist=True, default='1w', is_type_of=str, is_in=['3d', '1w', '2w', '3w', '4w']), Validator('general.enabled_providers', must_exist=True, default=[], is_type_of=list), + Validator('general.enabled_integrations', must_exist=True, default=[], is_type_of=list), Validator('general.multithreading', must_exist=True, default=True, is_type_of=bool), Validator('general.chmod_enabled', must_exist=True, default=False, is_type_of=bool), Validator('general.chmod', must_exist=True, default='0640', is_type_of=str), @@ -234,6 +235,11 @@ validators = [ Validator('addic7ed.user_agent', must_exist=True, default='', is_type_of=str), Validator('addic7ed.vip', must_exist=True, default=False, is_type_of=bool), + # animetosho section + Validator('animetosho.search_threshold', must_exist=True, default=6, is_type_of=int, gte=1, lte=15), + Validator('animetosho.anidb_api_client', must_exist=True, default='', is_type_of=str, cast=str), + Validator('animetosho.anidb_api_client_ver', must_exist=True, default=1, is_type_of=int, gte=1, lte=9), + # avistaz section Validator('avistaz.cookies', must_exist=True, default='', is_type_of=str), Validator('avistaz.user_agent', must_exist=True, default='', is_type_of=str), @@ -369,6 +375,10 @@ validators = [ Validator('postgresql.database', must_exist=True, default='', is_type_of=str), Validator('postgresql.username', must_exist=True, default='', is_type_of=str, cast=str), Validator('postgresql.password', must_exist=True, default='', is_type_of=str, cast=str), + + # anidb section + Validator('anidb.api_client', must_exist=True, default='', is_type_of=str), + Validator('anidb.api_client_ver', must_exist=True, default=1, is_type_of=int), ] @@ -442,6 +452,7 @@ array_keys = ['excluded_tags', 'subzero_mods', 'excluded_series_types', 'enabled_providers', + 'enabled_integrations', 'path_mappings', 'path_mappings_movie', 'language_equals', diff --git a/bazarr/app/get_providers.py b/bazarr/app/get_providers.py index bb78f29d9..7df6b8742 100644 --- a/bazarr/app/get_providers.py +++ b/bazarr/app/get_providers.py @@ -324,6 +324,9 @@ def get_providers_auth(): 'timeout': settings.whisperai.timeout, 'ffmpeg_path': _FFMPEG_BINARY, 'loglevel': settings.whisperai.loglevel, + }, + "animetosho": { + 'search_threshold': settings.animetosho.search_threshold, } } diff --git a/bazarr/subtitles/refiners/__init__.py b/bazarr/subtitles/refiners/__init__.py index 750fa4f18..ff1e715a0 100644 --- a/bazarr/subtitles/refiners/__init__.py +++ b/bazarr/subtitles/refiners/__init__.py @@ -3,9 +3,11 @@ from .ffprobe import refine_from_ffprobe from .database import refine_from_db from .arr_history import refine_from_arr_history +from .anidb import refine_from_anidb registered = { "database": refine_from_db, "ffprobe": refine_from_ffprobe, "arr_history": refine_from_arr_history, + "anidb": refine_from_anidb, } diff --git a/bazarr/subtitles/refiners/anidb.py b/bazarr/subtitles/refiners/anidb.py new file mode 100644 index 000000000..2174e0f7c --- /dev/null +++ b/bazarr/subtitles/refiners/anidb.py @@ -0,0 +1,140 @@ +# coding=utf-8 +# fmt: off + +import logging +import requests +from collections import namedtuple +from datetime import timedelta +from requests.exceptions import HTTPError + +from app.config import settings +from subliminal import Episode, region + +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + import xml.etree.ElementTree as etree + +refined_providers = {'animetosho'} + +api_url = 'http://api.anidb.net:9001/httpapi' + + +class AniDBClient(object): + def __init__(self, api_client_key=None, api_client_ver=1, session=None): + self.session = session or requests.Session() + self.api_client_key = api_client_key + self.api_client_ver = api_client_ver + + AnimeInfo = namedtuple('AnimeInfo', ['anime', 'episode_offset']) + + @region.cache_on_arguments(expiration_time=timedelta(days=1).total_seconds()) + def get_series_mappings(self): + r = self.session.get( + 'https://raw.githubusercontent.com/Anime-Lists/anime-lists/master/anime-list.xml', + timeout=10 + ) + + r.raise_for_status() + + return r.content + + @region.cache_on_arguments(expiration_time=timedelta(days=1).total_seconds()) + def get_series_id(self, mappings, tvdb_series_season, tvdb_series_id, episode): + # Enrich the collection of anime with the episode offset + animes = [ + self.AnimeInfo(anime, int(anime.attrib.get('episodeoffset', 0))) + for anime in mappings.findall( + f".//anime[@tvdbid='{tvdb_series_id}'][@defaulttvdbseason='{tvdb_series_season}']" + ) + ] + + if not animes: + return None + + # Sort the anime by offset in ascending order + animes.sort(key=lambda a: a.episode_offset) + + # Different from Tvdb, Anidb have different ids for the Parts of a season + anidb_id = None + offset = 0 + + for index, anime_info in enumerate(animes): + anime, episode_offset = anime_info + anidb_id = int(anime.attrib.get('anidbid')) + if episode > episode_offset: + anidb_id = anidb_id + offset = episode_offset + + return anidb_id, episode - offset + + @region.cache_on_arguments(expiration_time=timedelta(days=1).total_seconds()) + def get_series_episodes_ids(self, tvdb_series_id, season, episode): + mappings = etree.fromstring(self.get_series_mappings()) + + series_id, episode_no = self.get_series_id(mappings, season, tvdb_series_id, episode) + + if not series_id: + return None, None + + episodes = etree.fromstring(self.get_episodes(series_id)) + + return series_id, int(episodes.find(f".//episode[epno='{episode_no}']").attrib.get('id')) + + @region.cache_on_arguments(expiration_time=timedelta(days=1).total_seconds()) + def get_episodes(self, series_id): + r = self.session.get( + api_url, + params={ + 'request': 'anime', + 'client': self.api_client_key, + 'clientver': self.api_client_ver, + 'protover': 1, + 'aid': series_id + }, + timeout=10) + r.raise_for_status() + + xml_root = etree.fromstring(r.content) + + response_code = xml_root.attrib.get('code') + if response_code == '500': + raise HTTPError('AniDB API Abuse detected. Banned status.') + elif response_code == '302': + raise HTTPError('AniDB API Client error. Client is disabled or does not exists.') + + episode_elements = xml_root.find('episodes') + + if not episode_elements: + raise ValueError + + return etree.tostring(episode_elements, encoding='utf8', method='xml') + + +def refine_from_anidb(path, video): + if refined_providers.intersection(settings.general.enabled_providers) and video.series_anidb_id is None: + refine_anidb_ids(video) + + +def refine_anidb_ids(video): + if not isinstance(video, Episode) and not video.series_tvdb_id: + logging.debug(f'Video is not an Anime TV series, skipping refinement for {video}') + + return video + + anidb_client = AniDBClient(settings.anidb.api_client, settings.anidb.api_client_ver) + + season = video.season if video.season else 0 + + anidb_series_id, anidb_episode_id = anidb_client.get_series_episodes_ids(video.series_tvdb_id, season, video.episode) + + if not anidb_episode_id: + logging.error(f'Could not find anime series {video.series}') + + return video + + video.series_anidb_id = anidb_series_id + video.series_anidb_episode_id = anidb_episode_id diff --git a/custom_libs/subliminal/video.py b/custom_libs/subliminal/video.py index 1264d0b84..12aeba5f5 100644 --- a/custom_libs/subliminal/video.py +++ b/custom_libs/subliminal/video.py @@ -129,7 +129,8 @@ class Episode(Video): """ def __init__(self, name, series, season, episode, title=None, year=None, original_series=True, tvdb_id=None, - series_tvdb_id=None, series_imdb_id=None, alternative_series=None, **kwargs): + series_tvdb_id=None, series_imdb_id=None, alternative_series=None, series_anidb_id=None, + series_anidb_episode_id=None, **kwargs): super(Episode, self).__init__(name, **kwargs) #: Series of the episode @@ -162,6 +163,9 @@ class Episode(Video): #: Alternative names of the series self.alternative_series = alternative_series or [] + self.series_anidb_episode_id = series_anidb_episode_id + self.series_anidb_id = series_anidb_id + @classmethod def fromguess(cls, name, guess): if guess['type'] != 'episode': diff --git a/custom_libs/subliminal_patch/extensions.py b/custom_libs/subliminal_patch/extensions.py index eeb2ee4cb..800a1d547 100644 --- a/custom_libs/subliminal_patch/extensions.py +++ b/custom_libs/subliminal_patch/extensions.py @@ -64,4 +64,3 @@ subliminal.refiner_manager.register('drone = subliminal_patch.refiners.drone:ref subliminal.refiner_manager.register('filebot = subliminal_patch.refiners.filebot:refine') subliminal.refiner_manager.register('file_info_file = subliminal_patch.refiners.file_info_file:refine') subliminal.refiner_manager.register('symlinks = subliminal_patch.refiners.symlinks:refine') - diff --git a/custom_libs/subliminal_patch/providers/animetosho.py b/custom_libs/subliminal_patch/providers/animetosho.py new file mode 100644 index 000000000..d9c078503 --- /dev/null +++ b/custom_libs/subliminal_patch/providers/animetosho.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import + +import logging +import lzma + +from guessit import guessit +from requests import Session +from subzero.language import Language + + +from subliminal.exceptions import ConfigurationError, ProviderError +from subliminal_patch.providers import Provider +from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin +from subliminal_patch.subtitle import Subtitle, guess_matches +from subliminal.video import Episode + +try: + from lxml import etree +except ImportError: + try: + import xml.etree.cElementTree as etree + except ImportError: + import xml.etree.ElementTree as etree + +logger = logging.getLogger(__name__) + +# TODO: Test and Support Other Languages +supported_languages = [ + "eng", # English + "ita", # Italian +] + + +class AnimeToshoSubtitle(Subtitle): + """AnimeTosho.org Subtitle.""" + provider_name = 'animetosho' + + def __init__(self, language, download_link, meta): + super(AnimeToshoSubtitle, self).__init__(language, page_link=download_link) + self.meta = meta + self.download_link = download_link + + @property + def id(self): + return self.download_link + + def get_matches(self, video): + matches = set() + matches |= guess_matches(video, guessit(self.meta['filename'])) + + # Add these data are explicit extracted from the API and they always have to match otherwise they wouldn't + # arrive at this point and would stop on list_subtitles. + matches.update(['title', 'series', 'tvdb_id', 'season', 'episode']) + + return matches + + +class AnimeToshoProvider(Provider, ProviderSubtitleArchiveMixin): + """AnimeTosho.org Provider.""" + subtitle_class = AnimeToshoSubtitle + languages = {Language('por', 'BR')} | {Language(sl) for sl in supported_languages} + video_types = Episode + + def __init__(self, search_threshold=None): + self.session = None + + if not all([search_threshold]): + raise ConfigurationError("Search threshold, Api Client and Version must be specified!") + + self.search_threshold = search_threshold + + def initialize(self): + self.session = Session() + + def terminate(self): + self.session.close() + + def list_subtitles(self, video, languages): + if not video.series_anidb_episode_id: + raise ProviderError("Video does not have an AnimeTosho Episode ID!") + + return [s for s in self._get_series(video.series_anidb_episode_id) if s.language in languages] + + def download_subtitle(self, subtitle): + logger.info('Downloading subtitle %r', subtitle) + + r = self.session.get(subtitle.page_link, timeout=10) + r.raise_for_status() + + # Check if the bytes content starts with the xz magic number of the xz archives + if not self._is_xz_file(r.content): + raise ProviderError('Unidentified archive type') + + subtitle.content = lzma.decompress(r.content) + + return subtitle + + @staticmethod + def _is_xz_file(content): + return content.startswith(b'\xFD\x37\x7A\x58\x5A\x00') + + def _get_series(self, episode_id): + storage_download_url = 'https://animetosho.org/storage/attach/' + feed_api_url = 'https://feed.animetosho.org/json' + + subtitles = [] + + entries = self._get_series_entries(episode_id) + + for entry in entries: + r = self.session.get( + feed_api_url, + params={ + 'show': 'torrent', + 'id': entry['id'], + }, + timeout=10 + ) + r.raise_for_status() + + for file in r.json()['files']: + if 'attachments' not in file: + continue + + subtitle_files = list(filter(lambda f: f['type'] == 'subtitle', file['attachments'])) + + for subtitle_file in subtitle_files: + hex_id = format(subtitle_file['id'], '08x') + + subtitle = self.subtitle_class( + Language.fromalpha3b(subtitle_file['info']['lang']), + storage_download_url + '{}/{}.xz'.format(hex_id, subtitle_file['id']), + meta=file, + ) + + logger.debug('Found subtitle %r', subtitle) + + subtitles.append(subtitle) + + return subtitles + + def _get_series_entries(self, episode_id): + api_url = 'https://feed.animetosho.org/json' + + r = self.session.get( + api_url, + params={ + 'eid': episode_id, + }, + timeout=10 + ) + + r.raise_for_status() + + j = r.json() + + # Ignore records that are not yet ready or has been abandoned by AnimeTosho. + entries = list(filter(lambda t: t['status'] == 'complete', j))[:self.search_threshold] + + # Return the latest entries that have been added as it is used to cutoff via the user configuration threshold + entries.sort(key=lambda t: t['timestamp'], reverse=True) + + return entries diff --git a/custom_libs/subliminal_patch/video.py b/custom_libs/subliminal_patch/video.py index 0d613bb94..f5df0c92e 100644 --- a/custom_libs/subliminal_patch/video.py +++ b/custom_libs/subliminal_patch/video.py @@ -33,6 +33,8 @@ class Video(Video_): edition=None, other=None, info_url=None, + series_anidb_id=None, + series_anidb_episode_id=None, **kwargs ): super(Video, self).__init__( @@ -57,3 +59,5 @@ class Video(Video_): self.original_path = name self.other = other self.info_url = info_url + self.series_anidb_series_id = series_anidb_id, + self.series_anidb_episode_id = series_anidb_episode_id, diff --git a/frontend/src/pages/Settings/Providers/components.tsx b/frontend/src/pages/Settings/Providers/components.tsx index 87abe7571..91076d7de 100644 --- a/frontend/src/pages/Settings/Providers/components.tsx +++ b/frontend/src/pages/Settings/Providers/components.tsx @@ -39,14 +39,24 @@ import { } from "../utilities/FormValues"; import { SettingsProvider, useSettings } from "../utilities/SettingsProvider"; import { useSettingValue } from "../utilities/hooks"; -import { ProviderInfo, ProviderList } from "./list"; +import { ProviderInfo } from "./list"; -const ProviderKey = "settings-general-enabled_providers"; +type SettingsKey = + | "settings-general-enabled_providers" + | "settings-general-enabled_integrations"; -export const ProviderView: FunctionComponent = () => { +interface ProviderViewProps { + availableOptions: Readonly; + settingsKey: SettingsKey; +} + +export const ProviderView: FunctionComponent = ({ + availableOptions, + settingsKey, +}) => { const settings = useSettings(); const staged = useStagedValues(); - const providers = useSettingValue(ProviderKey); + const providers = useSettingValue(settingsKey); const { update } = useFormActions(); @@ -61,17 +71,27 @@ export const ProviderView: FunctionComponent = () => { staged, settings, onChange: update, + availableOptions: availableOptions, + settingsKey: settingsKey, }); } }, - [modals, providers, settings, staged, update], + [ + modals, + providers, + settings, + staged, + update, + availableOptions, + settingsKey, + ], ); const cards = useMemo(() => { if (providers) { return providers .flatMap((v) => { - const item = ProviderList.find((inn) => inn.key === v); + const item = availableOptions.find((inn) => inn.key === v); if (item) { return item; } else { @@ -89,7 +109,7 @@ export const ProviderView: FunctionComponent = () => { } else { return []; } - }, [providers, select]); + }, [providers, select, availableOptions]); return ( @@ -106,6 +126,8 @@ interface ProviderToolProps { staged: LooseObject; settings: Settings; onChange: (v: LooseObject) => void; + availableOptions: Readonly; + settingsKey: Readonly; } const SelectItem = forwardRef< @@ -126,6 +148,8 @@ const ProviderTool: FunctionComponent = ({ staged, settings, onChange, + availableOptions, + settingsKey, }) => { const modals = useModals(); @@ -147,11 +171,11 @@ const ProviderTool: FunctionComponent = ({ if (idx !== -1) { const newProviders = [...enabledProviders]; newProviders.splice(idx, 1); - onChangeRef.current({ [ProviderKey]: newProviders }); + onChangeRef.current({ [settingsKey]: newProviders }); modals.closeAll(); } } - }, [payload, enabledProviders, modals]); + }, [payload, enabledProviders, modals, settingsKey]); const submit = useCallback( (values: FormValues) => { @@ -161,8 +185,7 @@ const ProviderTool: FunctionComponent = ({ // Add this provider if not exist if (enabledProviders.find((v) => v === info.key) === undefined) { - const newProviders = [...enabledProviders, info.key]; - changes[ProviderKey] = newProviders; + changes[settingsKey] = [...enabledProviders, info.key]; } // Apply submit hooks @@ -172,7 +195,7 @@ const ProviderTool: FunctionComponent = ({ modals.closeAll(); } }, - [info, enabledProviders, modals], + [info, enabledProviders, modals, settingsKey], ); const canSave = info !== null; @@ -188,18 +211,18 @@ const ProviderTool: FunctionComponent = ({ } }, []); - const availableOptions = useMemo( + const options = useMemo( () => - ProviderList.filter( + availableOptions.filter( (v) => enabledProviders?.find((p) => p === v.key && p !== info?.key) === undefined, ), - [info?.key, enabledProviders], + [info?.key, enabledProviders, availableOptions], ); - const options = useSelectorOptions( - availableOptions, + const selectorOptions = useSelectorOptions( + options, (v) => v.name ?? capitalize(v.key), ); @@ -289,7 +312,7 @@ const ProviderTool: FunctionComponent = ({ placeholder="Click to Select a Provider" itemComponent={SelectItem} disabled={payload !== null} - {...options} + {...selectorOptions} value={info} onChange={onSelect} > diff --git a/frontend/src/pages/Settings/Providers/index.tsx b/frontend/src/pages/Settings/Providers/index.tsx index bd8b648ff..ce855c4ee 100644 --- a/frontend/src/pages/Settings/Providers/index.tsx +++ b/frontend/src/pages/Settings/Providers/index.tsx @@ -11,12 +11,16 @@ import { Text, } from "../components"; import { ProviderView } from "./components"; +import { IntegrationList, ProviderList } from "./list"; const SettingsProvidersView: FunctionComponent = () => { return (
- +
{ Link to subscribe
+
+ +
); }; diff --git a/frontend/src/pages/Settings/Providers/list.ts b/frontend/src/pages/Settings/Providers/list.ts index 967526187..eff9ea4e9 100644 --- a/frontend/src/pages/Settings/Providers/list.ts +++ b/frontend/src/pages/Settings/Providers/list.ts @@ -64,6 +64,20 @@ export const ProviderList: Readonly = [ }, ], }, + { + key: "animetosho", + name: "Anime Tosho", + description: + "Anime Tosho is a free, completely automated service which mirrors most torrents posted on TokyoTosho's anime category, Nyaa.si's English translated anime category and AniDex's anime category.", + inputs: [ + { + type: "text", + key: "search_threshold", + defaultValue: 6, + name: "Search Threshold. Increase if you often cannot find subtitles for your Anime. Note that increasing the value will decrease the performance of the search for each Episode.", + }, + ], + }, { key: "argenteam_dump", name: "Argenteam Dump", @@ -538,3 +552,24 @@ export const ProviderList: Readonly = [ description: "Chinese Subtitles Provider. Anti-captcha required.", }, ]; + +export const IntegrationList: Readonly = [ + { + key: "anidb", + name: "AniDB", + description: + "AniDB is non-profit database of anime information that is freely open to the public.", + inputs: [ + { + type: "text", + key: "api_client", + name: "API Client", + }, + { + type: "text", + key: "api_client_ver", + name: "API Client Version", + }, + ], + }, +]; diff --git a/tests/conftest.py b/tests/conftest.py index 46d8fc4a0..c7fbdea07 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,7 +7,7 @@ import pkg_resources sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../libs/")) sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../bazarr/")) - +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../custom_libs/")) def pytest_report_header(config): conflicting_packages = _get_conflicting("libs") diff --git a/tests/subliminal_patch/data/animetosho_episode_response.json b/tests/subliminal_patch/data/animetosho_episode_response.json new file mode 100644 index 000000000..3f7a898d3 --- /dev/null +++ b/tests/subliminal_patch/data/animetosho_episode_response.json @@ -0,0 +1,756 @@ +[ + { + "id": 608526, + "title": "[EMBER] Ore dake Level Up na Ken S01E12 [1080p] [HEVC WEBRip] (Solo Leveling)", + "link": "https://animetosho.org/view/ember-ore-dake-level-up-na-ken-s01e12.n1796835", + "timestamp": 1711853493, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796835, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/f9670720a5142ec31aa8e3715745f1392e3ffd5b/%5BEMBER%5D%20Solo%20Leveling%20-%2012.torrent", + "torrent_name": "[EMBER] Solo Leveling - 12.mkv", + "info_hash": "f9670720a5142ec31aa8e3715745f1392e3ffd5b", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:7FTQOIFFCQXMGGVI4NYVORPRHEXD77K3&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BEMBER%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20S01E12%20%5B1080p%5D%20%5BHEVC%20WEBRip%5D%20%28Solo%20Leveling%29", + "seeders": 316, + "leechers": 24, + "torrent_downloaded_count": 1164, + "tracker_updated": 1711872554, + "nzb_url": "https://animetosho.org/storage/nzbs/0009490e/%5BEMBER%5D%20Solo%20Leveling%20-%2012.nzb", + "total_size": 369572311, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608516, + "title": "[Anime Time] Solo Leveling - 12 [1080p][HEVC 10bit x265][AAC][Multi Sub] [Weekly] Ore dake Level Up na Ken", + "link": "https://animetosho.org/view/anime-time-solo-leveling-12-1080p-hevc-10bit.n1796824", + "timestamp": 1711851382, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796824, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/db16234b7aba70d2d901bde30fb0aa899589da47/%5BAnime%20Time%5D%20Solo%20Leveling%20-%2012%20%5B1080p%5D%5BHEVC%2010bit%20x265%5D%5BAAC%5D%5BMulti%20Sub%5D.torrent", + "torrent_name": "[Anime Time] Solo Leveling - 12 [1080p][HEVC 10bit x265][AAC][Multi Sub].mkv", + "info_hash": "db16234b7aba70d2d901bde30fb0aa899589da47", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:3MLCGS32XJYNFWIBXXRQ7MFKRGKYTWSH&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=%5BAnime%20Time%5D%20Solo%20Leveling%20-%2012%20%5B1080p%5D%5BHEVC%2010bit%20x265%5D%5BAAC%5D%5BMulti%20Sub%5D%20%5BWeekly%5D%20Ore%20dake%20Level%20Up%20na%20Ken", + "seeders": 85, + "leechers": 11, + "torrent_downloaded_count": 436, + "tracker_updated": 1711892085, + "nzb_url": "https://animetosho.org/storage/nzbs/00094904/%5BAnime%20Time%5D%20Solo%20Leveling%20-%2012%20%5B1080p%5D%5BHEVC%2010bit%20x265%5D%5BAAC%5D%5BMulti%20Sub%5D.nzb", + "total_size": 588022366, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "http://animetime.cc/" + }, + { + "id": 608491, + "title": "Solo Leveling - 12 (WEB 4K | 2160p AV1 AAC).mkv", + "link": "https://animetosho.org/view/solo-leveling-12-web-4k-2160p-av1-aac-mkv.d595164", + "timestamp": 1711845852, + "status": "complete", + "tosho_id": null, + "nyaa_id": null, + "nyaa_subdom": null, + "anidex_id": 595164, + "torrent_url": "https://animetosho.org/storage/torrent/cfc631026a1837ef70ff6993aa9c1b1304e0d75b/Solo%20Leveling%20-%2012%20%28WEB%204K%20AV1%20AAC%29.torrent", + "torrent_name": "Solo Leveling - 12 (WEB 4K AV1 AAC).mkv", + "info_hash": "cfc631026a1837ef70ff6993aa9c1b1304e0d75b", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:Z7DDCATKDA3664H7NGJ2VHA3CMCOBV23&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Ftracker.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker-udp.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&dn=Solo%20Leveling%20-%2012%20%28WEB%204K%20%7C%202160p%20AV1%20AAC%29.mkv", + "seeders": 11, + "leechers": 0, + "torrent_downloaded_count": 118, + "tracker_updated": 1712027991, + "nzb_url": "https://animetosho.org/storage/nzbs/000948eb/Solo%20Leveling%20-%2012%20%28WEB%204K%20AV1%20AAC%29.nzb", + "total_size": 586875995, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608439, + "title": "[SanKyuu] Ore dake Level Up na Ken (Solo Leveling) - 12 [WEB 1080p][AV1][AAC E-AC3][Multi-Sub]", + "link": "https://animetosho.org/view/sankyuu-ore-dake-level-up-na-ken-solo.n1796722", + "timestamp": 1711836670, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796722, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/fb8193f9da38b06a444e2a50966c3fbefdb7dcde/%5BSanKyuu%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20%28Solo%20Leveling%29%20-%2012%20%5BWEB%201080p%20AV1%20AAC%20E-AC3%5D%20%5B325A372A%5D.torrent", + "torrent_name": "[SanKyuu] Ore dake Level Up na Ken (Solo Leveling) - 12 [WEB 1080p AV1 AAC E-AC3] [325A372A].mkv", + "info_hash": "fb8193f9da38b06a444e2a50966c3fbefdb7dcde", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:7OAZH6O2HCYGURCOFJIJM3B7X363PXG6&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=http%3A%2F%2Ftracker.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker-udp.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&dn=%5BSanKyuu%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20%28Solo%20Leveling%29%20-%2012%20%5BWEB%201080p%5D%5BAV1%5D%5BAAC%20E-AC3%5D%5BMulti-Sub%5D", + "seeders": 51, + "leechers": 2, + "torrent_downloaded_count": 522, + "tracker_updated": 1711939563, + "nzb_url": "https://animetosho.org/storage/nzbs/000948b7/%5BSanKyuu%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20%28Solo%20Leveling%29%20-%2012%20%5BWEB%201080p%20AV1%20AAC%20E-AC3%5D%20%5B325A372A%5D.nzb", + "total_size": 477160034, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608407, + "title": "[NeoLX] Solo Leveling - S01E12 [END][1080p x264 10bits AAC][Multiple Subtitles].mkv", + "link": "https://animetosho.org/view/neolx-solo-leveling-s01e12-end-1080p-x264-10bits.1859288", + "timestamp": 1711831500, + "status": "complete", + "tosho_id": 1859288, + "nyaa_id": null, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/4a053ad27c1ca38db68b77f1a81860948bec93cb/%5BNeoLX%5D%20Solo%20Leveling%20-%20S01E12%20%5BEND%5D%5B1080p%20x264%2010bits%20AAC%5D%5BMultiple%20Subtitles%5D.torrent", + "torrent_name": "[NeoLX] Solo Leveling - S01E12 [END][1080p x264 10bits AAC][Multiple Subtitles].mkv", + "info_hash": "4a053ad27c1ca38db68b77f1a81860948bec93cb", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:JICTVUT4DSRY3NULO7Y2QGDASSF6ZE6L&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=http%3A%2F%2Ftracker.anirena.com%3A80%2Fannounce&tr=http%3A%2F%2Ftracker.acgnx.se%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.tiny-vps.com%3A6969%2Fannounce&dn=%5BNeoLX%5D%20Solo%20Leveling%20-%20S01E12%20%5BEND%5D%5B1080p%20x264%2010bits%20AAC%5D%5BMultiple%20Subtitles%5D.mkv", + "seeders": 22, + "leechers": 0, + "torrent_downloaded_count": 444, + "tracker_updated": 1712027991, + "nzb_url": "https://animetosho.org/storage/nzbs/00094897/%5BNeoLX%5D%20Solo%20Leveling%20-%20S01E12%20%5BEND%5D%5B1080p%20x264%2010bits%20AAC%5D%5BMultiple%20Subtitles%5D.nzb", + "total_size": 1775949490, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608385, + "title": "[CameEsp] Solo Leveling - 12 [1080p][ESP-ENG][mkv].mkv", + "link": "https://animetosho.org/view/cameesp-solo-leveling-12-1080p-esp-eng-mkv-mkv.1859281", + "timestamp": 1711829220, + "status": "skipped", + "tosho_id": 1859281, + "nyaa_id": 1796635, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/7e683310c0a03a8d94ad4a1aaac0b7f97d98e70e/%5BCameEsp%5D%20Solo%20Leveling%20-%2012%20%5B1080p%5D%5BESP-ENG%5D%5Bmkv%5D.torrent", + "torrent_name": "", + "info_hash": "7e683310c0a03a8d94ad4a1aaac0b7f97d98e70e", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:PZUDGEGAUA5I3FFNJINKVQFX7F6ZRZYO&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=http%3A%2F%2Ftracker.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&dn=%5BCameEsp%5D%20Solo%20Leveling%20-%2012%20%5B1080p%5D%5BESP-ENG%5D%5Bmkv%5D.mkv", + "seeders": 178, + "leechers": 21, + "torrent_downloaded_count": 541, + "tracker_updated": null, + "nzb_url": null, + "total_size": 1448914325, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://linkr.bio/CAME_CameEsp" + }, + { + "id": 608386, + "title": "[CameEsp] Solo Leveling - 12 [720p][ESP-ENG][mkv].mkv", + "link": "https://animetosho.org/view/cameesp-solo-leveling-12-720p-esp-eng-mkv-mkv.1859282", + "timestamp": 1711829220, + "status": "skipped", + "tosho_id": 1859282, + "nyaa_id": 1796636, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/3b35bdf9880489b38f7a318cca8fe02ff31e430c/%5BCameEsp%5D%20Solo%20Leveling%20-%2012%20%5B720p%5D%5BESP-ENG%5D%5Bmkv%5D.torrent", + "torrent_name": "", + "info_hash": "3b35bdf9880489b38f7a318cca8fe02ff31e430c", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:HM2336MIASE3HD32GGGMVD7AF7ZR4QYM&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=http%3A%2F%2Ftracker.anirena.com%3A80%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&dn=%5BCameEsp%5D%20Solo%20Leveling%20-%2012%20%5B720p%5D%5BESP-ENG%5D%5Bmkv%5D.mkv", + "seeders": 35, + "leechers": 10, + "torrent_downloaded_count": 160, + "tracker_updated": null, + "nzb_url": null, + "total_size": 737663624, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://linkr.bio/CAME_CameEsp" + }, + { + "id": 608370, + "title": "[Valenciano] Ore dake Level Up na Ken - 12 [1080p][AV1 10bit][AAC][Multi-Sub] (Weekly).mkv", + "link": "https://animetosho.org/view/valenciano-ore-dake-level-up-na-ken-12.n1796530", + "timestamp": 1711826614, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796530, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/f89a9ffa23f5fc3321f3e2c476dbccbf0435fc98/%5BValenciano%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BAV1%2010bit%5D%5BAAC%5D%5BMulti-Sub%5D%20%28Weekly%29.torrent", + "torrent_name": "[Valenciano] Ore dake Level Up na Ken - 12 [1080p][AV1 10bit][AAC][Multi-Sub] (Weekly).mkv", + "info_hash": "f89a9ffa23f5fc3321f3e2c476dbccbf0435fc98", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:7CNJ76RD6X6DGIPT4LCHNW6MX4CDL7EY&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BValenciano%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BAV1%2010bit%5D%5BAAC%5D%5BMulti-Sub%5D%20%28Weekly%29.mkv", + "seeders": 22, + "leechers": 2, + "torrent_downloaded_count": 240, + "tracker_updated": 1711940503, + "nzb_url": "https://animetosho.org/storage/nzbs/00094872/%5BValenciano%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BAV1%2010bit%5D%5BAAC%5D%5BMulti-Sub%5D%20%28Weekly%29.nzb", + "total_size": 427930434, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/83dRFDFDp7" + }, + { + "id": 608368, + "title": "[Erai-raws] Ore dake Level Up na Ken - 12 [1080p][HEVC][Multiple Subtitle] [ENG][POR-BR][SPA-LA][SPA][ARA][FRE][GER][ITA][RUS]", + "link": "https://animetosho.org/view/erai-raws-ore-dake-level-up-na-ken.n1796524", + "timestamp": 1711825169, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796524, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/8009ee0e9b20732f44df9bf5eaef926df0814d68/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BHEVC%5D%5BMultiple%20Subtitle%5D%5BB46F650F%5D.torrent", + "torrent_name": "[Erai-raws] Ore dake Level Up na Ken - 12 [1080p][HEVC][Multiple Subtitle][B46F650F].mkv", + "info_hash": "8009ee0e9b20732f44df9bf5eaef926df0814d68", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:QAE64DU3EBZS6RG7TP26V34SNXYICTLI&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=http%3A%2F%2Fopen.acgnxtracker.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BHEVC%5D%5BMultiple%20Subtitle%5D%20%5BENG%5D%5BPOR-BR%5D%5BSPA-LA%5D%5BSPA%5D%5BARA%5D%5BFRE%5D%5BGER%5D%5BITA%5D%5BRUS%5D", + "seeders": 298, + "leechers": 12, + "torrent_downloaded_count": 2398, + "tracker_updated": 1711933623, + "nzb_url": "https://animetosho.org/storage/nzbs/00094870/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BHEVC%5D%5BMultiple%20Subtitle%5D%5BB46F650F%5D.nzb", + "total_size": 1073028408, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399105, + "article_url": null, + "article_title": null, + "website_url": "https://www.erai-raws.info/anime-list/ore-dake-level-up-na-ken/" + }, + { + "id": 608349, + "title": "[DKB] Solo Leveling - S01E12 [1080p][END][HEVC x265 10bit][Multi-Subs][weekly]", + "link": "https://animetosho.org/view/dkb-solo-leveling-s01e12-1080p-end-hevc-x265.n1796502", + "timestamp": 1711823055, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796502, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/d89e1a3b415c0b569481b06dbbe58d936f2e6f0a/%5BDKB%5D%20Solo%20Leveling%20-%20S01E12%20%5B1080p%5D%5BEND%5D%5BHEVC%20x265%2010bit%5D%5BMulti-Subs%5D.torrent", + "torrent_name": "[DKB] Solo Leveling - S01E12 [1080p][END][HEVC x265 10bit][Multi-Subs].mkv", + "info_hash": "d89e1a3b415c0b569481b06dbbe58d936f2e6f0a", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:3CPBUO2BLQFVNFEBWBW3XZMNSNXS43YK&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337%2Fannounce&dn=%5BDKB%5D%20Solo%20Leveling%20-%20S01E12%20%5B1080p%5D%5BEND%5D%5BHEVC%20x265%2010bit%5D%5BMulti-Subs%5D%5Bweekly%5D", + "seeders": 153, + "leechers": 9, + "torrent_downloaded_count": 1010, + "tracker_updated": 1711922054, + "nzb_url": "https://animetosho.org/storage/nzbs/0009485d/%5BDKB%5D%20Solo%20Leveling%20-%20S01E12%20%5B1080p%5D%5BEND%5D%5BHEVC%20x265%2010bit%5D%5BMulti-Subs%5D.nzb", + "total_size": 665460303, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399329, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/ZBQCQ7u" + }, + { + "id": 608347, + "title": "[ASW] Solo Leveling - 12 [1080p HEVC x265 10Bit][AAC]", + "link": "https://animetosho.org/view/asw-solo-leveling-12-1080p-hevc-x265-10bit-aac.n1796496", + "timestamp": 1711822239, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796496, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/78d41db42271123d520f971b71a67d970cfdb64c/%5BASW%5D%20Solo%20Leveling%20-%2012%20%5B1080p%20HEVC%5D%5BE1A01A3E%5D.torrent", + "torrent_name": "[ASW] Solo Leveling - 12 [1080p HEVC][E1A01A3E].mkv", + "info_hash": "78d41db42271123d520f971b71a67d970cfdb64c", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:PDKB3NBCOEJD2UQPS4NXDJT5S4GP3NSM&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BASW%5D%20Solo%20Leveling%20-%2012%20%5B1080p%20HEVC%20x265%2010Bit%5D%5BAAC%5D", + "seeders": 689, + "leechers": 212, + "torrent_downloaded_count": 1960, + "tracker_updated": null, + "nzb_url": "https://animetosho.org/storage/nzbs/0009485b/%5BASW%5D%20Solo%20Leveling%20-%2012%20%5B1080p%20HEVC%5D%5BE1A01A3E%5D.nzb", + "total_size": 455351405, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399037, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/6XnYdWP" + }, + { + "id": 608346, + "title": "[Tenrai-Sensei] Solo Leveling - S01E12 - Arise [Web][1080p][HEVC 10bit x265] Ore dake Level Up na Ken", + "link": "https://animetosho.org/view/tenrai-sensei-solo-leveling-s01e12-arise-web-1080p.n1796495", + "timestamp": 1711822029, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796495, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/8b22acf6c18e9ac56f0af00a6061918712d8d992/Solo%20Leveling%20-%20S01E12%20-%20Arise.torrent", + "torrent_name": "Solo Leveling - S01E12 - Arise.mkv", + "info_hash": "8b22acf6c18e9ac56f0af00a6061918712d8d992", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:RMRKZ5WBR2NMK3YK6AFGAYMRQ4JNRWMS&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Fopentracker.i2p.rocks%3A6969%2Fannounce&dn=%5BTenrai-Sensei%5D%20Solo%20Leveling%20-%20S01E12%20-%20Arise%20%5BWeb%5D%5B1080p%5D%5BHEVC%2010bit%20x265%5D%20Ore%20dake%20Level%20Up%20na%20Ken", + "seeders": 1000000000, + "leechers": 1000000000, + "torrent_downloaded_count": 3028, + "tracker_updated": null, + "nzb_url": "https://animetosho.org/storage/nzbs/0009485a/Solo%20Leveling%20-%20S01E12%20-%20Arise.nzb", + "total_size": 574082022, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399753, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/WucPeE5ume" + }, + { + "id": 608339, + "title": "[Judas] Ore dake Level Up na Ken (Solo Leveling) - S01E12 [1080p][HEVC x265 10bit][Multi-Subs] (Weekly)", + "link": "https://animetosho.org/view/judas-ore-dake-level-up-na-ken-solo.n1796483", + "timestamp": 1711821551, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796483, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/aa0f1690f5bff99891883dbbde61e4c59920aa5e/%5BJudas%5D%20Solo%20Leveling%20-%20S01E12.torrent", + "torrent_name": "[Judas] Solo Leveling - S01E12.mkv", + "info_hash": "aa0f1690f5bff99891883dbbde61e4c59920aa5e", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:VIHRNEHVX74ZREMIHW554YPEYWMSBKS6&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BJudas%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20%28Solo%20Leveling%29%20-%20S01E12%20%5B1080p%5D%5BHEVC%20x265%2010bit%5D%5BMulti-Subs%5D%20%28Weekly%29", + "seeders": 422, + "leechers": 20, + "torrent_downloaded_count": 3434, + "tracker_updated": 1711874024, + "nzb_url": "https://animetosho.org/storage/nzbs/00094853/%5BJudas%5D%20Solo%20Leveling%20-%20S01E12.nzb", + "total_size": 485334036, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399248, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/vbJ7RTn" + }, + { + "id": 608338, + "title": "[Raze] Solo Leveling (Ore dake Level Up na Ken) - 12 x265 10bit 1080p 143.8561fps.mkv", + "link": "https://animetosho.org/view/raze-solo-leveling-ore-dake-level-up-na.n1796482", + "timestamp": 1711821474, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796482, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/bcebcb468c1a2557404afa18f4948cb4fded5c17/%5BRaze%5D%20Solo%20Leveling%20%28Ore%20dake%20Level%20Up%20na%20Ken%29%20-%2012%20x265%2010bit%201080p%20143.8561fps.torrent", + "torrent_name": "[Raze] Solo Leveling (Ore dake Level Up na Ken) - 12 x265 10bit 1080p 143.8561fps.mkv", + "info_hash": "bcebcb468c1a2557404afa18f4948cb4fded5c17", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:XTV4WRUMDISVOQCK7IMPJFEMWT662XAX&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BRaze%5D%20Solo%20Leveling%20%28Ore%20dake%20Level%20Up%20na%20Ken%29%20-%2012%20x265%2010bit%201080p%20143.8561fps.mkv", + "seeders": 15, + "leechers": 1, + "torrent_downloaded_count": 216, + "tracker_updated": 1711932770, + "nzb_url": "https://animetosho.org/storage/nzbs/00094852/%5BRaze%5D%20Solo%20Leveling%20%28Ore%20dake%20Level%20Up%20na%20Ken%29%20-%2012%20x265%2010bit%201080p%20143.8561fps.nzb", + "total_size": 987255128, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/72ZKssY" + }, + { + "id": 608324, + "title": "Ore dake Level Up na Ken - S01E12 - 720p WEB x264 -NanDesuKa (CR).mkv", + "link": "https://animetosho.org/view/ore-dake-level-up-na-ken-s01e12-720p.n1796469", + "timestamp": 1711817371, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796469, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/514d25bbb699f0cd50ba82663f4d9972e42aec7e/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20720p%20WEB%20x264%20-NanDesuKa%20%28CR%29.torrent", + "torrent_name": "Ore dake Level Up na Ken - S01E12 - 720p WEB x264 -NanDesuKa (CR).mkv", + "info_hash": "514d25bbb699f0cd50ba82663f4d9972e42aec7e", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:KFGSLO5WTHYM2UF2QJTD6TMZOLSCV3D6&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20720p%20WEB%20x264%20-NanDesuKa%20%28CR%29.mkv", + "seeders": 24, + "leechers": 1, + "torrent_downloaded_count": 147, + "tracker_updated": 1711941873, + "nzb_url": "https://animetosho.org/storage/nzbs/00094844/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20720p%20WEB%20x264%20-NanDesuKa%20%28CR%29.nzb", + "total_size": 733563952, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608323, + "title": "Ore dake Level Up na Ken - S01E12 - 1080p WEB x264 -NanDesuKa (CR).mkv", + "link": "https://animetosho.org/view/ore-dake-level-up-na-ken-s01e12-1080p.n1796468", + "timestamp": 1711817278, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796468, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/2368c8599cb9dfcd15c4133c195a026c0c061452/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%201080p%20WEB%20x264%20-NanDesuKa%20%28CR%29.torrent", + "torrent_name": "Ore dake Level Up na Ken - S01E12 - 1080p WEB x264 -NanDesuKa (CR).mkv", + "info_hash": "2368c8599cb9dfcd15c4133c195a026c0c061452", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:ENUMQWM4XHP42FOECM6BSWQCNQGAMFCS&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%201080p%20WEB%20x264%20-NanDesuKa%20%28CR%29.mkv", + "seeders": 43, + "leechers": 1, + "torrent_downloaded_count": 325, + "tracker_updated": 1711950739, + "nzb_url": "https://animetosho.org/storage/nzbs/00094843/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%201080p%20WEB%20x264%20-NanDesuKa%20%28CR%29.nzb", + "total_size": 1444814636, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608322, + "title": "Ore dake Level Up na Ken - S01E12 - 480p WEB x264 -NanDesuKa (CR).mkv", + "link": "https://animetosho.org/view/ore-dake-level-up-na-ken-s01e12-480p.n1796467", + "timestamp": 1711817275, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796467, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/65c34046223f78409badf5138035708875dae046/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20480p%20WEB%20x264%20-NanDesuKa%20%28CR%29.torrent", + "torrent_name": "Ore dake Level Up na Ken - S01E12 - 480p WEB x264 -NanDesuKa (CR).mkv", + "info_hash": "65c34046223f78409badf5138035708875dae046", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:MXBUARRCH54EBG5N6UJYANLQRB25VYCG&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.internetwarriors.net%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20480p%20WEB%20x264%20-NanDesuKa%20%28CR%29.mkv", + "seeders": 2, + "leechers": 1, + "torrent_downloaded_count": 47, + "tracker_updated": 1711939543, + "nzb_url": "https://animetosho.org/storage/nzbs/00094842/Ore%20dake%20Level%20Up%20na%20Ken%20-%20S01E12%20-%20480p%20WEB%20x264%20-NanDesuKa%20%28CR%29.nzb", + "total_size": 379888841, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null + }, + { + "id": 608321, + "title": "[ToonsHub] Solo Leveling E12 Arise 2160p B-Global WEB-DL x264 (Multi-Subs)", + "link": "https://animetosho.org/view/toonshub-solo-leveling-e12-arise-2160p-b-global.n1796464", + "timestamp": 1711816495, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796464, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/953e7663ac91c946ee46efd1f00c544a81ac5210/Solo%20Leveling%20E12%20Arise%202160p%20B-Global%20WEB-DL%20x264%20%5BJapanese%5D%20%28AAC%202.0%29%20MSubs_ToonsHub_.torrent", + "torrent_name": "Solo Leveling E12 Arise 2160p B-Global WEB-DL x264 [Japanese] (AAC 2.0) MSubs_ToonsHub_.mkv", + "info_hash": "953e7663ac91c946ee46efd1f00c544a81ac5210", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:SU7HMY5MSHEUN3SG57I7ADCUJKA2YUQQ&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=%5BToonsHub%5D%20Solo%20Leveling%20E12%20Arise%202160p%20B-Global%20WEB-DL%20x264%20%28Multi-Subs%29", + "seeders": 283, + "leechers": 6, + "torrent_downloaded_count": 2103, + "tracker_updated": 1711940653, + "nzb_url": "https://animetosho.org/storage/nzbs/00094841/Solo%20Leveling%20E12%20Arise%202160p%20B-Global%20WEB-DL%20x264%20%5BJapanese%5D%20%28AAC%202.0%29%20MSubs_ToonsHub_.nzb", + "total_size": 1725930387, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/2mPFKykW4j" + }, + { + "id": 608320, + "title": "Solo Leveling S01E12 Arise 1080p CR WEB-DL AAC2.0 H 264-VARYG (Ore dake Level Up na Ken, Multi-Subs)", + "link": "https://animetosho.org/view/solo-leveling-s01e12-arise-1080p-cr-web-dl.n1796463", + "timestamp": 1711816461, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796463, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/e8c25942e297bfd38515d785ca82e0dac7a09688/Solo.Leveling.S01E12.Arise.1080p.CR.WEB-DL.AAC2.0.H.264-VARYG.torrent", + "torrent_name": "Solo.Leveling.S01E12.Arise.1080p.CR.WEB-DL.AAC2.0.H.264-VARYG.mkv", + "info_hash": "e8c25942e297bfd38515d785ca82e0dac7a09688", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:5DBFSQXCS675HBIV26C4VAXA3LD2BFUI&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=Solo%20Leveling%20S01E12%20Arise%201080p%20CR%20WEB-DL%20AAC2.0%20H%20264-VARYG%20%28Ore%20dake%20Level%20Up%20na%20Ken%2C%20Multi-Subs%29", + "seeders": 476, + "leechers": 78, + "torrent_downloaded_count": 2233, + "tracker_updated": 1711948863, + "nzb_url": "https://animetosho.org/storage/nzbs/00094840/Solo.Leveling.S01E12.Arise.1080p.CR.WEB-DL.AAC2.0.H.264-VARYG.nzb", + "total_size": 1447070929, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://myanimelist.net/anime/52299/" + }, + { + "id": 608319, + "title": "[ToonsHub] Solo Leveling E12 Arise 1080p B-Global WEB-DL x264 (Multi-Subs)", + "link": "https://animetosho.org/view/toonshub-solo-leveling-e12-arise-1080p-b-global.n1796462", + "timestamp": 1711816458, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796462, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/adf123922973e4c0f7979ab6d3874d2e2e0e5207/Solo%20Leveling%20E12%20Arise%201080p%20B-Global%20WEB-DL%20x264%20%5BJapanese%5D%20%28AAC%202.0%29%20MSubs_ToonsHub_.torrent", + "torrent_name": "Solo Leveling E12 Arise 1080p B-Global WEB-DL x264 [Japanese] (AAC 2.0) MSubs_ToonsHub_.mkv", + "info_hash": "adf123922973e4c0f7979ab6d3874d2e2e0e5207", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:VXYSHERJOPSMB54XTK3NHB2NFYXA4UQH&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=http%3A%2F%2Fanidex.moe%3A6969%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&dn=%5BToonsHub%5D%20Solo%20Leveling%20E12%20Arise%201080p%20B-Global%20WEB-DL%20x264%20%28Multi-Subs%29", + "seeders": 112, + "leechers": 2, + "torrent_downloaded_count": 955, + "tracker_updated": 1711941514, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483f/Solo%20Leveling%20E12%20Arise%201080p%20B-Global%20WEB-DL%20x264%20%5BJapanese%5D%20%28AAC%202.0%29%20MSubs_ToonsHub_.nzb", + "total_size": 424813220, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": "https://discord.gg/2mPFKykW4j" + }, + { + "id": 608318, + "title": "[Erai-raws] Ore dake Level Up na Ken - 12 [480p][Multiple Subtitle] [ENG][POR-BR][SPA-LA][SPA][ARA][FRE][GER][ITA][RUS]", + "link": "https://animetosho.org/view/erai-raws-ore-dake-level-up-na-ken.n1796461", + "timestamp": 1711816354, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796461, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/5b4a6ec6c71d1033f336bdf027fc3f56dbb6544b/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B480p%5D%5BMultiple%20Subtitle%5D%5B348538A9%5D.torrent", + "torrent_name": "[Erai-raws] Ore dake Level Up na Ken - 12 [480p][Multiple Subtitle][348538A9].mkv", + "info_hash": "5b4a6ec6c71d1033f336bdf027fc3f56dbb6544b", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:LNFG5RWHDUIDH4ZWXXYCP7B7K3N3MVCL&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=http%3A%2F%2Fopen.acgnxtracker.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B480p%5D%5BMultiple%20Subtitle%5D%20%5BENG%5D%5BPOR-BR%5D%5BSPA-LA%5D%5BSPA%5D%5BARA%5D%5BFRE%5D%5BGER%5D%5BITA%5D%5BRUS%5D", + "seeders": 57, + "leechers": 1, + "torrent_downloaded_count": 593, + "tracker_updated": 1711935570, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483e/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B480p%5D%5BMultiple%20Subtitle%5D%5B348538A9%5D.nzb", + "total_size": 390242548, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399156, + "article_url": null, + "article_title": null, + "website_url": "https://www.erai-raws.info/anime-list/ore-dake-level-up-na-ken/" + }, + { + "id": 608317, + "title": "[Erai-raws] Ore dake Level Up na Ken - 12 [720p][Multiple Subtitle] [ENG][POR-BR][SPA-LA][SPA][ARA][FRE][GER][ITA][RUS]", + "link": "https://animetosho.org/view/erai-raws-ore-dake-level-up-na-ken.n1796459", + "timestamp": 1711816335, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796459, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/371911c245694044bb84dfa1cb162c5922e46090/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B720p%5D%5BMultiple%20Subtitle%5D%5BFF79133E%5D.torrent", + "torrent_name": "[Erai-raws] Ore dake Level Up na Ken - 12 [720p][Multiple Subtitle][FF79133E].mkv", + "info_hash": "371911c245694044bb84dfa1cb162c5922e46090", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:G4MRDQSFNFAEJO4E36Q4WFRMLEROIYEQ&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=http%3A%2F%2Fopen.acgnxtracker.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B720p%5D%5BMultiple%20Subtitle%5D%20%5BENG%5D%5BPOR-BR%5D%5BSPA-LA%5D%5BSPA%5D%5BARA%5D%5BFRE%5D%5BGER%5D%5BITA%5D%5BRUS%5D", + "seeders": 244, + "leechers": 8, + "torrent_downloaded_count": 1819, + "tracker_updated": 1711891219, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483d/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B720p%5D%5BMultiple%20Subtitle%5D%5BFF79133E%5D.nzb", + "total_size": 743917601, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399115, + "article_url": null, + "article_title": null, + "website_url": "https://www.erai-raws.info/anime-list/ore-dake-level-up-na-ken/" + }, + { + "id": 608316, + "title": "[Erai-raws] Ore dake Level Up na Ken - 12 [1080p][Multiple Subtitle] [ENG][POR-BR][SPA-LA][SPA][ARA][FRE][GER][ITA][RUS]", + "link": "https://animetosho.org/view/erai-raws-ore-dake-level-up-na-ken.n1796456", + "timestamp": 1711816309, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796456, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/927f32f78956475287b80b7a3b8069d775b3bb74/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BMultiple%20Subtitle%5D%5BC8116259%5D.torrent", + "torrent_name": "[Erai-raws] Ore dake Level Up na Ken - 12 [1080p][Multiple Subtitle][C8116259].mkv", + "info_hash": "927f32f78956475287b80b7a3b8069d775b3bb74", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:SJ7TF54JKZDVFB5YBN5DXADJ2523HO3U&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=http%3A%2F%2Fopen.acgnxtracker.com%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BMultiple%20Subtitle%5D%20%5BENG%5D%5BPOR-BR%5D%5BSPA-LA%5D%5BSPA%5D%5BARA%5D%5BFRE%5D%5BGER%5D%5BITA%5D%5BRUS%5D", + "seeders": 1054, + "leechers": 30, + "torrent_downloaded_count": 7259, + "tracker_updated": 1711904421, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483c/%5BErai-raws%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20-%2012%20%5B1080p%5D%5BMultiple%20Subtitle%5D%5BC8116259%5D.nzb", + "total_size": 1455168235, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399072, + "article_url": null, + "article_title": null, + "website_url": "https://www.erai-raws.info/anime-list/ore-dake-level-up-na-ken/" + }, + { + "id": 608315, + "title": "[SubsPlease] Solo Leveling - 12 (1080p) [5B47BF7E].mkv", + "link": "https://animetosho.org/view/subsplease-solo-leveling-12-1080p-5b47bf7e-mkv.1859208", + "timestamp": 1711816291, + "status": "complete", + "tosho_id": 1859208, + "nyaa_id": 1796455, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/ac03972f83b13773c7d6c0387969d3873c98fe6e/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%281080p%29%20%5B5B47BF7E%5D.torrent", + "torrent_name": "[SubsPlease] Solo Leveling - 12 (1080p) [5B47BF7E].mkv", + "info_hash": "ac03972f83b13773c7d6c0387969d3873c98fe6e", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:VQBZOL4DWE3XHR6WYA4HS2OTQ46JR7TO&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710%2Fannounce&dn=%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%281080p%29%20%5B5B47BF7E%5D.mkv", + "seeders": 4800, + "leechers": 126, + "torrent_downloaded_count": 32750, + "tracker_updated": 1711902497, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483b/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%281080p%29%20%5B5B47BF7E%5D.nzb", + "total_size": 1448782755, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3398902, + "article_url": null, + "article_title": null, + "website_url": "https://subsplease.org" + }, + { + "id": 608314, + "title": "[SubsPlease] Solo Leveling - 12 (720p) [83538700].mkv", + "link": "https://animetosho.org/view/subsplease-solo-leveling-12-720p-83538700-mkv.1859207", + "timestamp": 1711816270, + "status": "complete", + "tosho_id": 1859207, + "nyaa_id": 1796454, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/5a65737737ea4a6c24c2ef17fb87906f250f2b2b/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28720p%29%20%5B83538700%5D.torrent", + "torrent_name": "[SubsPlease] Solo Leveling - 12 (720p) [83538700].mkv", + "info_hash": "5a65737737ea4a6c24c2ef17fb87906f250f2b2b", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:LJSXG5ZX5JFGYJGC54L7XB4QN4SQ6KZL&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710%2Fannounce&dn=%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28720p%29%20%5B83538700%5D.mkv", + "seeders": 1155, + "leechers": 51, + "torrent_downloaded_count": 8614, + "tracker_updated": 1711883436, + "nzb_url": "https://animetosho.org/storage/nzbs/0009483a/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28720p%29%20%5B83538700%5D.nzb", + "total_size": 737532022, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3398956, + "article_url": null, + "article_title": null, + "website_url": "https://subsplease.org" + }, + { + "id": 608313, + "title": "[SubsPlease] Solo Leveling - 12 (480p) [9FBA731B].mkv", + "link": "https://animetosho.org/view/subsplease-solo-leveling-12-480p-9fba731b-mkv.1859206", + "timestamp": 1711816267, + "status": "complete", + "tosho_id": 1859206, + "nyaa_id": 1796453, + "nyaa_subdom": null, + "anidex_id": null, + "torrent_url": "https://animetosho.org/storage/torrent/071cfac21f433b07e81e43f33f9a869098af4f64/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28480p%29%20%5B9FBA731B%5D.torrent", + "torrent_name": "[SubsPlease] Solo Leveling - 12 (480p) [9FBA731B].mkv", + "info_hash": "071cfac21f433b07e81e43f33f9a869098af4f64", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:A4OPVQQ7IM5QP2A6IPZT7GUGSCMK6T3E&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Ftracker.coppersurfer.tk%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2F9.rarbg.to%3A2710%2Fannounce&tr=udp%3A%2F%2F9.rarbg.me%3A2710%2Fannounce&dn=%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28480p%29%20%5B9FBA731B%5D.mkv", + "seeders": 385, + "leechers": 143, + "torrent_downloaded_count": 2467, + "tracker_updated": null, + "nzb_url": "https://animetosho.org/storage/nzbs/00094839/%5BSubsPlease%5D%20Solo%20Leveling%20-%2012%20%28480p%29%20%5B9FBA731B%5D.nzb", + "total_size": 383857166, + "num_files": 1, + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": 3399016, + "article_url": null, + "article_title": null, + "website_url": "https://subsplease.org" + } +] \ No newline at end of file diff --git a/tests/subliminal_patch/data/animetosho_series_response.json b/tests/subliminal_patch/data/animetosho_series_response.json new file mode 100644 index 000000000..290a8f7a8 --- /dev/null +++ b/tests/subliminal_patch/data/animetosho_series_response.json @@ -0,0 +1,232 @@ +{ + "title": "[EMBER] Ore dake Level Up na Ken S01E12 [1080p] [HEVC WEBRip] (Solo Leveling)", + "timestamp": 1711853493, + "status": "complete", + "tosho_id": null, + "nyaa_id": 1796835, + "nyaa_subdom": null, + "anidex_id": null, + "comment": "", + "is_dupe": false, + "deleted": false, + "torrent_url": "https://animetosho.org/storage/torrent/f9670720a5142ec31aa8e3715745f1392e3ffd5b/%5BEMBER%5D%20Solo%20Leveling%20-%2012.torrent", + "torrent_name": "[EMBER] Solo Leveling - 12.mkv", + "info_hash": "f9670720a5142ec31aa8e3715745f1392e3ffd5b", + "info_hash_v2": null, + "magnet_uri": "magnet:?xt=urn:btih:7FTQOIFFCQXMGGVI4NYVORPRHEXD77K3&tr=http%3A%2F%2Fnyaa.tracker.wf%3A7777%2Fannounce&tr=udp%3A%2F%2Fopen.stealth.si%3A80%2Fannounce&tr=udp%3A%2F%2Ftracker.opentrackr.org%3A1337%2Fannounce&tr=udp%3A%2F%2Fexodus.desync.com%3A6969%2Fannounce&tr=udp%3A%2F%2Ftracker.torrent.eu.org%3A451%2Fannounce&dn=%5BEMBER%5D%20Ore%20dake%20Level%20Up%20na%20Ken%20S01E12%20%5B1080p%5D%20%5BHEVC%20WEBRip%5D%20%28Solo%20Leveling%29", + "nzb_url": "https://animetosho.org/storage/nzbs/0009490e/%5BEMBER%5D%20Solo%20Leveling%20-%2012.nzb", + "anidb_aid": 17495, + "anidb_eid": 277518, + "anidb_fid": null, + "article_url": null, + "article_title": null, + "website_url": null, + "total_size": 369572311, + "num_files": 1, + "primary_file_id": 1154190, + "files": [ + { + "id": 1154190, + "is_archive": false, + "filename": "[EMBER] Solo Leveling - 12.mkv", + "size": 369572311, + "processed": true, + "crc32": "cc8c36bc", + "md5": "07306f34a7cc9d25c2ec8a3b7d0b573a", + "sha1": "d79665531066b24439b0ef421074e6445d8b25a6", + "sha256": "0df3ec5424233bdb75ff33077b196e515455152a76ab5d4eab7a63cdcbb8c917", + "ed2k": "c0359507e7da148750279ff229cb9fbf", + "bt2": "b4526e074a41981e73cdcd13b9142a4a774bd15ff641b5cf114ace527268d213", + "vidframe_timestamps": [ + 15000, + 354000, + 693000, + 1032000, + 1371000 + ], + "file_info_type": "mediainfo", + "file_info": "General\nUnique ID : 196382376391031659169980002506916611524 (0x93BDDEB35938CEE294C45B12E5A5C5C4)\nComplete name : [EMBER] Solo Leveling - 12.mkv\nFormat : Matroska\nFormat version : Version 4\nFile size : 352 MiB\nDuration : 23 min 40 s\nOverall bit rate : 2 082 kb/s\nFrame rate : 23.976 FPS\nMovie name : S01E12-Arise\nEncoded date : 2024-03-31 02:15:23 UTC\nWriting application : mkvmerge v69.0.0 ('Day And Age') 64-bit\nWriting library : libebml v1.4.2 + libmatroska v1.6.4\nCover : Yes\nAttachments : cover.jpg / Roboto-Medium.ttf / Roboto-MediumItalic.ttf / arial.ttf / arialbd.ttf / comic.ttf / comicbd.ttf / times.ttf / timesbd.ttf / trebuc.ttf / trebucbd.ttf / verdana.ttf / verdanab.ttf / CONSOLA.TTF / CONSOLAB.TTF\n\nVideo\nID : 1\nFormat : HEVC\nFormat/Info : High Efficiency Video Coding\nFormat profile : Main 10@L4@High\nCodec ID : V_MPEGH/ISO/HEVC\nDuration : 23 min 40 s\nBit rate : 1 914 kb/s\nWidth : 1 920 pixels\nHeight : 1 080 pixels\nDisplay aspect ratio : 16:9\nFrame rate mode : Constant\nFrame rate : 23.976 (24000/1001) FPS\nColor space : YUV\nChroma subsampling : 4:2:0\nBit depth : 10 bits\nBits/(Pixel*Frame) : 0.038\nStream size : 324 MiB (92%)\nTitle : Presented By EMBER\nWriting library : x265 2.4+14-bc0e9bd7c08f5ddc:[Windows][GCC 6.3.0][64 bit] 10bit: KG7x [x265.ru]\nEncoding settings : cpuid=1173503 / frame-threads=3 / numa-pools=8 / wpp / no-pmode / no-pme / no-psnr / no-ssim / log-level=2 / input-csp=1 / input-res=1920x1080 / interlace=0 / total-frames=0 / level-idc=40 / high-tier=1 / uhd-bd=0 / ref=4 / no-allow-non-conformance / no-repeat-headers / annexb / no-aud / no-hrd / info / hash=0 / no-temporal-layers / open-gop / min-keyint=23 / keyint=250 / bframes=8 / b-adapt=2 / b-pyramid / bframe-bias=0 / rc-lookahead=70 / lookahead-slices=4 / scenecut=40 / no-intra-refresh / ctu=64 / min-cu-size=8 / rect / no-amp / max-tu-size=32 / tu-inter-depth=2 / tu-intra-depth=2 / limit-tu=4 / rdoq-level=2 / dynamic-rd=0.00 / no-ssim-rd / signhide / no-tskip / nr-intra=0 / nr-inter=0 / no-constrained-intra / no-strong-intra-smoothing / max-merge=3 / limit-refs=3 / limit-modes / me=3 / subme=3 / merange=57 / temporal-mvp / weightp / no-weightb / no-analyze-src-pics / deblock=1:1 / no-sao / no-sao-non-deblock / rd=4 / no-early-skip / rskip / no-fast-intra / no-tskip-fast / no-cu-lossless / no-b-intra / rdpenalty=0 / psy-rd=1.20 / psy-rdoq=2.00 / no-rd-refine / analysis-mode=0 / no-lossless / cbqpoffs=0 / crqpoffs=0 / rc=crf / crf=23.0 / qcomp=0.60 / qpstep=4 / stats-write=0 / stats-read=0 / vbv-maxrate=30000 / vbv-bufsize=30000 / vbv-init=0.9 / crf-max=0.0 / crf-min=0.0 / ipratio=1.40 / pbratio=1.30 / aq-mode=3 / aq-strength=0.70 / cutree / zone-count=0 / no-strict-cbr / qg-size=32 / no-rc-grain / qpmax=69 / qpmin=0 / sar=0 / overscan=0 / videoformat=5 / range=0 / colorprim=1 / transfer=1 / colormatrix=1 / chromaloc=0 / display-window=0 / max-cll=0,0 / min-luma=0 / max-luma=1023 / log2-max-poc-lsb=8 / vui-timing-info / vui-hrd-info / slices=1 / opt-qp-pps / opt-ref-list-length-pps / no-multi-pass-opt-rps / scenecut-bias=0.05 / no-opt-cu-delta-qp / no-aq-motion / no-hdr / no-hdr-opt / no-dhdr10-opt / refine-level=5 / no-limit-sao / ctu-info=0\nDefault : Yes\nForced : No\nColor range : Limited\nColor primaries : BT.709\nTransfer characteristics : BT.709\nMatrix coefficients : BT.709\n\nAudio\nID : 2\nFormat : AAC LC\nFormat/Info : Advanced Audio Codec Low Complexity\nCodec ID : A_AAC-2\nDuration : 23 min 40 s\nBit rate : 128 kb/s\nChannel(s) : 2 channels\nChannel layout : L R\nSampling rate : 44.1 kHz\nFrame rate : 43.066 FPS (1024 SPF)\nCompression mode : Lossy\nStream size : 21.7 MiB (6%)\nLanguage : Japanese\nDefault : Yes\nForced : No\n\nText\nID : 3\nFormat : ASS\nCodec ID : S_TEXT/ASS\nCodec ID/Info : Advanced Sub Station Alpha\nDuration : 23 min 20 s\nBit rate : 98 b/s\nFrame rate : 0.168 FPS\nCount of elements : 235\nCompression mode : Lossless\nStream size : 16.8 KiB (0%)\nLanguage : English\nDefault : Yes\nForced : No\n\nMenu\n00:00:00.000 : en:Intro\n00:02:00.000 : en:OP\n00:03:30.000 : en:Main EP\n00:21:42.000 : en:ED\n00:23:12.000 : en:Extra", + "attachments": [ + { + "id": 268579, + "filename": "arial.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 892680 + }, + { + "id": 624527, + "filename": "arialbd.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 854584 + }, + { + "id": 1962029, + "filename": "chapters.xml", + "type": "xmlmeta", + "info": { + "meta": "chapters" + }, + "size": 1874 + }, + { + "id": 9973, + "filename": "comic.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 240588 + }, + { + "id": 13424, + "filename": "comicbd.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 224404 + }, + { + "id": 727768, + "filename": "CONSOLA.TTF", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 358460 + }, + { + "id": 183919, + "filename": "CONSOLAB.TTF", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 368720 + }, + { + "id": 1900106, + "filename": "cover.jpg", + "type": "other", + "info": { + "mime": "image/jpeg" + }, + "size": 178243 + }, + { + "id": 1066790, + "filename": "Roboto-Medium.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 351704 + }, + { + "id": 1066791, + "filename": "Roboto-MediumItalic.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 378412 + }, + { + "id": 1962028, + "filename": "tags.xml", + "type": "xmlmeta", + "info": { + "meta": "tags" + }, + "size": 2698 + }, + { + "id": 653220, + "filename": "times.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 975228 + }, + { + "id": 653221, + "filename": "timesbd.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 980120 + }, + { + "id": 1961547, + "filename": "track3.eng.ass", + "type": "subtitle", + "info": { + "number": 3, + "codec": "ASS", + "lang": "eng", + "default": 1, + "enabled": 1, + "forced": 0, + "trackid": 2, + "tracknum": 3 + }, + "size": 25988 + }, + { + "id": 71778, + "filename": "trebuc.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 218432 + }, + { + "id": 16207, + "filename": "trebucbd.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 206776 + }, + { + "id": 1149, + "filename": "verdana.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 240456 + }, + { + "id": 8678, + "filename": "verdanab.ttf", + "type": "other", + "info": { + "mime": "application/x-truetype-font" + }, + "size": 208456 + } + ], + "links": { + "ClickNUpload": "https://clicknupload.red/ydok51qdzjr7", + "DailyUploads": "https://dailyuploads.net/nksc253gopcq", + "DownloadGG": "https://download.gg/file-18642810_87ce8be6abbb74f6", + "GoFile": "https://gofile.io/d/XgPwnN", + "KrakenFiles": "https://krakenfiles.com/view/9lJPP2MvAt/file.html", + "MdiaLoad": "https://down.mdiaload.com/t9w39f21ad9o", + "MultiUp": "https://multiup.io/download/b7958d691eb0b38477acdb63db00cca6/%5BEMBER%5D%20Solo%20Leveling%20-%2012.mkv", + "Uppit": "http://uppit.com/g58333gdg3ei" + } + } + ] +} \ No newline at end of file diff --git a/tests/subliminal_patch/test_animetosho.py b/tests/subliminal_patch/test_animetosho.py new file mode 100644 index 000000000..acb62781c --- /dev/null +++ b/tests/subliminal_patch/test_animetosho.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +import os +import pytest + +from subliminal_patch.core import Episode +from subliminal_patch.providers.animetosho import AnimeToshoProvider +from subzero.language import Language + +@pytest.fixture(scope="session") +def anime_episodes(): + return { + "frieren_s01e01": Episode( + "Frieren - Beyond Journey's End S01E28 1080p WEB x264 AAC -Tsundere-Raws (CR) (Sousou no Frieren).mkv", + "Frieren: Beyond Journey's End", + 1, + 28, + source="Web", + series_anidb_id=17617, + series_anidb_episode_id=271418, + series_tvdb_id=424536, + series_imdb_id="tt22248376", + release_group="Tsundere-Raws", + resolution="1080p", + video_codec="H.264", + ), + "solo_leveling_s01e10": Episode( + "[New-raws] Ore Dake Level Up na Ken - 12 END [1080p] [AMZN].mkv", + "Solo Leveling", + 1, + 12, + source="Web", + series_anidb_id=17495, + series_anidb_episode_id=277518, + series_tvdb_id=389597, + series_imdb_id="tt21209876", + release_group="New-raws", + resolution="1080p", + video_codec="H.264", + ), + } + + +def test_list_subtitles(anime_episodes, requests_mock, data): + language = Language("eng") + item = anime_episodes["solo_leveling_s01e10"] + + with open(os.path.join(data, 'animetosho_episode_response.json'), "rb") as f: + requests_mock.get(' https://feed.animetosho.org/json?eid=277518', content=f.read()) + + with open(os.path.join(data, 'animetosho_series_response.json'), "rb") as f: + response = f.read() + requests_mock.get('https://feed.animetosho.org/json?show=torrent&id=608516', content=response) + requests_mock.get('https://feed.animetosho.org/json?show=torrent&id=608526', content=response) + + with AnimeToshoProvider(2) as provider: + subtitles = provider.list_subtitles(item, languages={language}) + + assert len(subtitles) == 2 From 8037ec033f72485bc8f223ff11a81fbce39a0e03 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 15 Apr 2024 08:17:51 -0400 Subject: [PATCH 019/129] Fixed the upgrade subtitles loop when languages profile is set to normal or HI. We now delete the previous subtitles just before saving the new one. --- bazarr/subtitles/download.py | 12 ++++++++++-- bazarr/subtitles/upgrade.py | 8 ++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/bazarr/subtitles/download.py b/bazarr/subtitles/download.py index c4a8e2da7..5f588fbf6 100644 --- a/bazarr/subtitles/download.py +++ b/bazarr/subtitles/download.py @@ -24,8 +24,9 @@ from .processing import process_subtitle @update_pools -def generate_subtitles(path, languages, audio_language, sceneName, title, media_type, - forced_minimum_score=None, is_upgrade=False, profile_id=None, check_if_still_required=False): +def generate_subtitles(path, languages, audio_language, sceneName, title, media_type, forced_minimum_score=None, + is_upgrade=False, profile_id=None, check_if_still_required=False, + previous_subtitles_to_delete=None): if not languages: return None @@ -87,6 +88,13 @@ def generate_subtitles(path, languages, audio_language, sceneName, title, media_ fld = get_target_folder(path) chmod = int(settings.general.chmod, 8) if not sys.platform.startswith( 'win') and settings.general.chmod_enabled else None + if is_upgrade and previous_subtitles_to_delete: + try: + # delete previously downloaded subtitles in case of an upgrade to prevent edge loop + # issue. + os.remove(previous_subtitles_to_delete) + except (OSError, FileNotFoundError): + pass saved_subtitles = save_subtitles(video.original_path, subtitles, single=settings.general.single_language, tags=None, # fixme diff --git a/bazarr/subtitles/upgrade.py b/bazarr/subtitles/upgrade.py index eb072b500..4593c71bc 100644 --- a/bazarr/subtitles/upgrade.py +++ b/bazarr/subtitles/upgrade.py @@ -110,7 +110,9 @@ def upgrade_subtitles(): episode['seriesTitle'], 'series', forced_minimum_score=int(episode['score']), - is_upgrade=True)) + is_upgrade=True, + previous_subtitles_to_delete=path_mappings.path_replace( + episode['subtitles_path']))) if result: if isinstance(result, list) and len(result): @@ -195,7 +197,9 @@ def upgrade_subtitles(): movie['title'], 'movie', forced_minimum_score=int(movie['score']), - is_upgrade=True)) + is_upgrade=True, + previous_subtitles_to_delete=path_mappings.path_replace_movie( + movie['subtitles_path']))) if result: if isinstance(result, list) and len(result): result = result[0] From b2d807d9d9fb98c7cdf79e5452201589e1bb9198 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Tue, 16 Apr 2024 00:42:15 +0900 Subject: [PATCH 020/129] Fixed manual upload table long names without spacing. #2448 --- frontend/src/components/tables/BaseTable.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/components/tables/BaseTable.tsx b/frontend/src/components/tables/BaseTable.tsx index 6ec49e61a..e41a2d313 100644 --- a/frontend/src/components/tables/BaseTable.tsx +++ b/frontend/src/components/tables/BaseTable.tsx @@ -24,6 +24,7 @@ const useStyles = createStyles((theme) => { display: "block", maxWidth: "100%", overflowX: "auto", + overflowWrap: "anywhere", }, table: { borderCollapse: "collapse", From 7578b8ef146431a74f96129b9bcde2388bb90bbe Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 15 Apr 2024 15:00:10 -0400 Subject: [PATCH 021/129] Updated apprise to version 1.7.6 --- .../INSTALLER | 0 .../LICENSE | 0 .../METADATA | 9 +- .../RECORD | 52 ++-- libs/apprise-1.7.6.dist-info/REQUESTED | 0 .../WHEEL | 0 .../entry_points.txt | 0 .../top_level.txt | 0 libs/apprise/AppriseLocale.py | 3 + libs/apprise/URLBase.py | 121 +++++--- libs/apprise/__init__.py | 2 +- libs/apprise/attachment/AttachBase.py | 4 +- libs/apprise/attachment/AttachHTTP.py | 284 ++++++++++-------- libs/apprise/config/ConfigBase.py | 3 + libs/apprise/conversion.py | 4 +- libs/apprise/decorators/CustomNotifyPlugin.py | 4 + libs/apprise/plugins/NotifyAprs.py | 39 ++- libs/apprise/plugins/NotifyBase.py | 17 +- .../{NotifyFaast.py => NotifyChantify.py} | 137 ++++----- libs/apprise/plugins/NotifyEmail.py | 40 ++- libs/apprise/plugins/NotifyFeishu.py | 231 ++++++++++++++ libs/apprise/plugins/NotifyFreeMobile.py | 204 +++++++++++++ libs/apprise/plugins/NotifyMQTT.py | 2 +- libs/apprise/plugins/NotifyNtfy.py | 2 +- libs/apprise/plugins/NotifyRocketChat.py | 59 +++- libs/apprise/plugins/NotifyTelegram.py | 55 +++- libs/apprise/plugins/NotifyZulip.py | 24 +- libs/version.txt | 2 +- 28 files changed, 981 insertions(+), 317 deletions(-) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/INSTALLER (100%) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/LICENSE (100%) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/METADATA (97%) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/RECORD (86%) create mode 100644 libs/apprise-1.7.6.dist-info/REQUESTED rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/WHEEL (100%) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/entry_points.txt (100%) rename libs/{apprise-1.7.4.dist-info => apprise-1.7.6.dist-info}/top_level.txt (100%) rename libs/apprise/plugins/{NotifyFaast.py => NotifyChantify.py} (57%) create mode 100644 libs/apprise/plugins/NotifyFeishu.py create mode 100644 libs/apprise/plugins/NotifyFreeMobile.py diff --git a/libs/apprise-1.7.4.dist-info/INSTALLER b/libs/apprise-1.7.6.dist-info/INSTALLER similarity index 100% rename from libs/apprise-1.7.4.dist-info/INSTALLER rename to libs/apprise-1.7.6.dist-info/INSTALLER diff --git a/libs/apprise-1.7.4.dist-info/LICENSE b/libs/apprise-1.7.6.dist-info/LICENSE similarity index 100% rename from libs/apprise-1.7.4.dist-info/LICENSE rename to libs/apprise-1.7.6.dist-info/LICENSE diff --git a/libs/apprise-1.7.4.dist-info/METADATA b/libs/apprise-1.7.6.dist-info/METADATA similarity index 97% rename from libs/apprise-1.7.4.dist-info/METADATA rename to libs/apprise-1.7.6.dist-info/METADATA index 1fbaf2a08..ac7cb9aac 100644 --- a/libs/apprise-1.7.4.dist-info/METADATA +++ b/libs/apprise-1.7.6.dist-info/METADATA @@ -1,12 +1,12 @@ Metadata-Version: 2.1 Name: apprise -Version: 1.7.4 +Version: 1.7.6 Summary: Push Notifications that work with just about every platform! Home-page: https://github.com/caronc/apprise Author: Chris Caron Author-email: lead2gold@gmail.com License: BSD -Keywords: Alerts Apprise API Automated Packet Reporting System AWS Boxcar BulkSMS BulkVS Burst SMS Chat CLI ClickSend D7Networks Dapnet DBus DingTalk Discord Email Emby Enigma2 Faast FCM Flock Form Gnome Google Chat Gotify Growl Guilded Home Assistant httpSMS IFTTT Join JSON Kavenegar KODI Kumulos LaMetric Line LunaSea MacOSX Mailgun Mastodon Matrix Mattermost MessageBird Microsoft Misskey MQTT MSG91 MSTeams Nextcloud NextcloudTalk Notica Notifiarr Notifico Ntfy Office365 OneSignal Opsgenie PagerDuty PagerTree ParsePlatform PopcornNotify Prowl PushBullet Pushed Pushjet PushMe Push Notifications Pushover PushSafer Pushy PushDeer Reddit Revolt Rocket.Chat RSyslog Ryver SendGrid ServerChan SES Signal SimplePush Sinch Slack SMSEagle SMS Manager SMTP2Go SNS SparkPost Streamlabs Stride Synology Chat Syslog Techulus Telegram Threema Gateway Twilio Twist Twitter Voipms Vonage Webex WeCom Bot WhatsApp Windows XBMC XML Zulip +Keywords: Alerts Apprise API Automated Packet Reporting System AWS Boxcar BulkSMS BulkVS Burst SMS Chantify Chat CLI ClickSend D7Networks Dapnet DBus DingTalk Discord Email Emby Enigma2 FCM Feishu Flock Form Free Mobile Gnome Google Chat Gotify Growl Guilded Home Assistant httpSMS IFTTT Join JSON Kavenegar KODI Kumulos LaMetric Line LunaSea MacOSX Mailgun Mastodon Matrix Mattermost MessageBird Microsoft Misskey MQTT MSG91 MSTeams Nextcloud NextcloudTalk Notica Notifiarr Notifico Ntfy Office365 OneSignal Opsgenie PagerDuty PagerTree ParsePlatform PopcornNotify Prowl PushBullet Pushed Pushjet PushMe Push Notifications Pushover PushSafer Pushy PushDeer Reddit Revolt Rocket.Chat RSyslog Ryver SendGrid ServerChan SES Signal SimplePush Sinch Slack SMSEagle SMS Manager SMTP2Go SNS SparkPost Streamlabs Stride Synology Chat Syslog Techulus Telegram Threema Gateway Twilio Twist Twitter Voipms Vonage Webex WeCom Bot WhatsApp Windows XBMC XML Zulip Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: System Administrators @@ -20,6 +20,7 @@ Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: License :: OSI Approved :: BSD License @@ -98,11 +99,12 @@ The table below identifies the services this tool supports and some example serv | [AWS SES](https://github.com/caronc/apprise/wiki/Notify_ses) | ses:// | (TCP) 443 | ses://user@domain/AccessKeyID/AccessSecretKey/RegionName
ses://user@domain/AccessKeyID/AccessSecretKey/RegionName/email1/email2/emailN | [Bark](https://github.com/caronc/apprise/wiki/Notify_bark) | bark:// | (TCP) 80 or 443 | bark://hostname
bark://hostname/device_key
bark://hostname/device_key1/device_key2/device_keyN
barks://hostname
barks://hostname/device_key
barks://hostname/device_key1/device_key2/device_keyN | [Boxcar](https://github.com/caronc/apprise/wiki/Notify_boxcar) | boxcar:// | (TCP) 443 | boxcar://hostname
boxcar://hostname/@tag
boxcar://hostname/device_token
boxcar://hostname/device_token1/device_token2/device_tokenN
boxcar://hostname/@tag/@tag2/device_token +| [Chantify](https://github.com/caronc/apprise/wiki/Notify_chantify) | chantify:// | (TCP) 443 | chantify://token | [Discord](https://github.com/caronc/apprise/wiki/Notify_discord) | discord:// | (TCP) 443 | discord://webhook_id/webhook_token
discord://avatar@webhook_id/webhook_token | [Emby](https://github.com/caronc/apprise/wiki/Notify_emby) | emby:// or embys:// | (TCP) 8096 | emby://user@hostname/
emby://user:password@hostname | [Enigma2](https://github.com/caronc/apprise/wiki/Notify_enigma2) | enigma2:// or enigma2s:// | (TCP) 80 or 443 | enigma2://hostname -| [Faast](https://github.com/caronc/apprise/wiki/Notify_faast) | faast:// | (TCP) 443 | faast://authorizationtoken | [FCM](https://github.com/caronc/apprise/wiki/Notify_fcm) | fcm:// | (TCP) 443 | fcm://project@apikey/DEVICE_ID
fcm://project@apikey/#TOPIC
fcm://project@apikey/DEVICE_ID1/#topic1/#topic2/DEVICE_ID2/ +| [Feishu](https://github.com/caronc/apprise/wiki/Notify_feishu) | feishu:// | (TCP) 443 | feishu://token | [Flock](https://github.com/caronc/apprise/wiki/Notify_flock) | flock:// | (TCP) 443 | flock://token
flock://botname@token
flock://app_token/u:userid
flock://app_token/g:channel_id
flock://app_token/u:userid/g:channel_id | [Google Chat](https://github.com/caronc/apprise/wiki/Notify_googlechat) | gchat:// | (TCP) 443 | gchat://workspace/key/token | [Gotify](https://github.com/caronc/apprise/wiki/Notify_gotify) | gotify:// or gotifys:// | (TCP) 80 or 443 | gotify://hostname/token
gotifys://hostname/token?priority=high @@ -184,6 +186,7 @@ The table below identifies the services this tool supports and some example serv | [DAPNET](https://github.com/caronc/apprise/wiki/Notify_dapnet) | dapnet:// | (TCP) 80 | dapnet://user:pass@callsign
dapnet://user:pass@callsign1/callsign2/callsignN | [D7 Networks](https://github.com/caronc/apprise/wiki/Notify_d7networks) | d7sms:// | (TCP) 443 | d7sms://token@PhoneNo
d7sms://token@ToPhoneNo1/ToPhoneNo2/ToPhoneNoN | [DingTalk](https://github.com/caronc/apprise/wiki/Notify_dingtalk) | dingtalk:// | (TCP) 443 | dingtalk://token/
dingtalk://token/ToPhoneNo
dingtalk://token/ToPhoneNo1/ToPhoneNo2/ToPhoneNo1/ +| [Free-Mobile](https://github.com/caronc/apprise/wiki/Notify_freemobile) | freemobile:// | (TCP) 443 | freemobile://user@password/ [httpSMS](https://github.com/caronc/apprise/wiki/Notify_httpsms) | httpsms:// | (TCP) 443 | httpsms://ApiKey@FromPhoneNo
httpsms://ApiKey@FromPhoneNo/ToPhoneNo
httpsms://ApiKey@FromPhoneNo/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/ | [Kavenegar](https://github.com/caronc/apprise/wiki/Notify_kavenegar) | kavenegar:// | (TCP) 443 | kavenegar://ApiKey/ToPhoneNo
kavenegar://FromPhoneNo@ApiKey/ToPhoneNo
kavenegar://ApiKey/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN | [MessageBird](https://github.com/caronc/apprise/wiki/Notify_messagebird) | msgbird:// | (TCP) 443 | msgbird://ApiKey/FromPhoneNo
msgbird://ApiKey/FromPhoneNo/ToPhoneNo
msgbird://ApiKey/FromPhoneNo/ToPhoneNo1/ToPhoneNo2/ToPhoneNoN/ diff --git a/libs/apprise-1.7.4.dist-info/RECORD b/libs/apprise-1.7.6.dist-info/RECORD similarity index 86% rename from libs/apprise-1.7.4.dist-info/RECORD rename to libs/apprise-1.7.6.dist-info/RECORD index ca5dcd003..250648105 100644 --- a/libs/apprise-1.7.4.dist-info/RECORD +++ b/libs/apprise-1.7.6.dist-info/RECORD @@ -1,12 +1,12 @@ ../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233 -apprise-1.7.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -apprise-1.7.4.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 -apprise-1.7.4.dist-info/METADATA,sha256=Lc66iPsSCFv0zmoQX8NFuc_V5CqFYN5Yrx_gqeN8OF8,44502 -apprise-1.7.4.dist-info/RECORD,, -apprise-1.7.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise-1.7.4.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 -apprise-1.7.4.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 -apprise-1.7.4.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 +apprise-1.7.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +apprise-1.7.6.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 +apprise-1.7.6.dist-info/METADATA,sha256=z_gaX2IdNJqw4T9q7AYQri9jcIs-OTGCo3t2EgEY-mw,44823 +apprise-1.7.6.dist-info/RECORD,, +apprise-1.7.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise-1.7.6.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 +apprise-1.7.6.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 +apprise-1.7.6.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 apprise/Apprise.py,sha256=Stm2NhJprWRaMwQfTiIQG_nR1bLpHi_zcdwEcsCpa-A,32865 apprise/Apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203 apprise/AppriseAsset.py,sha256=jRW8Y1EcAvjVA9h_mINmsjO4DM3S0aDl6INIFVMcUCs,11647 @@ -15,13 +15,13 @@ apprise/AppriseAttachment.py,sha256=vhrktSrp8GLr32aK4KqV6BX83IpI1lxZe-pGo1wiSFM, apprise/AppriseAttachment.pyi,sha256=R9-0dVqWpeaFrVpcREwPhGy3qHWztG5jEjYIOsbE5dM,1145 apprise/AppriseConfig.py,sha256=wfuR6Mb3ZLHvjvqWdFp9lVmjjDRWs65unY9qa92RkCg,16909 apprise/AppriseConfig.pyi,sha256=_mUlCnncqAq8sL01WxQTgZjnb2ic9kZXvtqZmVl-fc8,1568 -apprise/AppriseLocale.py,sha256=ISth7xC7M1WhsSNXdGZFouaA4bi07KP35m9RX-ExG48,8852 +apprise/AppriseLocale.py,sha256=4uSr4Nj_rz6ISMMAfRVRk58wZVLKOofJgk2x0_E8NkQ,8994 apprise/AttachmentManager.py,sha256=EwlnjuKn3fv_pioWcmMCkyDTsO178t6vkEOD8AjAPsw,2053 apprise/ConfigurationManager.py,sha256=MUmGajxjgnr6FGN7xb3q0nD0VVgdTdvapBBR7CsI-rc,2058 apprise/NotificationManager.py,sha256=ZJgkiCgcJ7Bz_6bwQ47flrcxvLMbA4Vbw0HG_yTsGdE,2041 -apprise/URLBase.py,sha256=ZWjHz69790EfVNDIBzWzRZzjw-gwC3db_t3_3an6cWI,28388 +apprise/URLBase.py,sha256=xRP0-blocp9UudYh04Hb3fIEmTZWJaTv_tzjrqaB9fg,29423 apprise/URLBase.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520 -apprise/__init__.py,sha256=oBHq9Zbcwz9DTkurqnEhbu9Q79a0TdVAZrWFIhlk__8,3368 +apprise/__init__.py,sha256=ArtvoarAMnBcSfXF7L_hzq5CUJ9TUnHopiC7xafCe3c,3368 apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986 apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758 apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646 @@ -45,22 +45,22 @@ apprise/assets/themes/default/apprise-warning-128x128.png,sha256=pf5c4Ph7jWH7gf3 apprise/assets/themes/default/apprise-warning-256x256.png,sha256=SY-xlaiXaj420iEYKC2_fJxU-yj2SuaQg6xfPNi83bw,43708 apprise/assets/themes/default/apprise-warning-32x32.png,sha256=97R2ywNvcwczhBoWEIgajVtWjgT8fLs4FCCz4wu0dwc,2472 apprise/assets/themes/default/apprise-warning-72x72.png,sha256=L8moEInkO_OLxoOcuvN7rmrGZo64iJeH20o-24MQghE,7913 -apprise/attachment/AttachBase.py,sha256=ik3hRFnr8Z9bXt69P9Ej1VST4gQbnE0C_9WQvEE-72A,13592 +apprise/attachment/AttachBase.py,sha256=T3WreGrTsqqGplXJO36jm-N14X7ymSc9xt7XdTYuXVE,13656 apprise/attachment/AttachBase.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih9GP8,961 apprise/attachment/AttachFile.py,sha256=MbHY_av0GeM_AIBKV02Hq7SHiZ9eCr1yTfvDMUgi2I4,4765 -apprise/attachment/AttachHTTP.py,sha256=dyDy3U47cI28ENhaw1r5nQlGh8FWHZlHI8n9__k8wcY,11995 +apprise/attachment/AttachHTTP.py,sha256=_CMPp4QGLATfGO2-Nw57sxsQyed9z3ywgoB0vpK3KZk,13779 apprise/attachment/__init__.py,sha256=xabgXpvV05X-YRuqIt3uGYMXwYNXjHyF6Dwd8HfZCFE,1658 apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697 apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593 apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447 -apprise/config/ConfigBase.py,sha256=A4p_N9vSxOK37x9kuYeZFzHhAeEt-TCe2oweNi2KGg4,53062 +apprise/config/ConfigBase.py,sha256=d1efIuQFCJr66WgpudV2DWtxY3-tuZAyMAhHXBzJ8p0,53194 apprise/config/ConfigBase.pyi,sha256=cngfobwH6v2vxYbQrObDi5Z-t5wcquWF-wR0kBCr3Eg,54 apprise/config/ConfigFile.py,sha256=u_SDaN3OHMyaAq2X7k_T4_PRKkVsDwleqBz9YIN5lbA,6138 apprise/config/ConfigHTTP.py,sha256=Iy6Ji8_nX3xDjFgJGLrz4ftrMlMiyKiFGzYGJ7rMSMQ,9457 apprise/config/ConfigMemory.py,sha256=epEAgNy-eJVWoQaUOvjivMWxXTofy6wAQ-NbCqYmuyE,2829 apprise/config/__init__.py,sha256=lbsxrUpB1IYM2q7kjYhsXQGgPF-yZXJrKFE361tdIPY,1663 -apprise/conversion.py,sha256=bvTu-3TU2CPEhdroLRtd_XpDzzXqe_wyUql089IpYxs,6197 -apprise/decorators/CustomNotifyPlugin.py,sha256=F49vOM2EVy43Pn3j8z7tgTacweMUxGhw0UX-1n2Y3c8,7836 +apprise/conversion.py,sha256=0VZ0eCZfksN-97Vl0TjVjwnCTgus3XTRioceSFnP-gc,6277 +apprise/decorators/CustomNotifyPlugin.py,sha256=i4D-sgOsBWsxO5auWCN2bgXLLPuADaaLlJ1gUKLj2bU,7972 apprise/decorators/__init__.py,sha256=e_PDAm0kQNzwDPx-NJZLPfLMd2VAABvNZtxx_iDviRM,1487 apprise/decorators/notify.py,sha256=a2WupErNw1_SMAld7jPC273bskiChMpYy95BOog5A9w,5111 apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738 @@ -69,21 +69,22 @@ apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=oUTuHREmLEYN07oqYqRMJ_kU71-o5o37Ns apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921 apprise/manager.py,sha256=R9w8jxQRNy6Z_XDcobkt4JYbrC4jtj2OwRw9Zrib3CA,26857 apprise/plugins/NotifyAppriseAPI.py,sha256=ISBE0brD3eQdyw3XrGXd4Uc4kSYvIuI3SSUVCt-bkdo,16654 -apprise/plugins/NotifyAprs.py,sha256=IS1uxIl391L3i2LOK6x8xmlOG1W58k4o793Oq2W5Wao,24220 +apprise/plugins/NotifyAprs.py,sha256=xdL_aIVgb4ggxRFeCdkZAbgHYZ8DWLw9pRpLZQ0rHoE,25523 apprise/plugins/NotifyBark.py,sha256=bsDvKooRy4k1Gg7tvBjv3DIx7-WZiV_mbTrkTwMtd9Q,15698 -apprise/plugins/NotifyBase.py,sha256=9MB2uv4Rv8BnoXjU52k5Mv4YQppkNPv4Y_iPwauKxKQ,29716 +apprise/plugins/NotifyBase.py,sha256=G3xkF_a2BWqNSxsrnOW7NUgHjOqBCYC5zihCifWemo8,30360 apprise/plugins/NotifyBase.pyi,sha256=aKlZXRYUgG8lz_ZgGkYYJ_GKhuf18youTmMU-FlG7z8,21 apprise/plugins/NotifyBoxcar.py,sha256=vR00-WggHa1nHYWyb-f5P2V-G4f683fU_-GBlIeJvD0,12867 apprise/plugins/NotifyBulkSMS.py,sha256=stPWAFCfhBP617zYK9Dgk6pNJBN_WcyJtODzo0jR1QQ,16005 apprise/plugins/NotifyBulkVS.py,sha256=viLGeyUDiirRRM7CgRqqElHSLYFnMugDtWE6Ytjqfaw,13290 apprise/plugins/NotifyBurstSMS.py,sha256=cN2kRETKIK5LhwpQEA8C68LKv8KEUPmXYe-nTSegGls,15550 +apprise/plugins/NotifyChantify.py,sha256=GJJOAtSnVoIfKbJF_W1DTu7WsvS_zHdjO4T1XTKT87g,6673 apprise/plugins/NotifyClickSend.py,sha256=UfOJqsas6WLjQskojuJE7I_-lrb5QrkMiBZv-po_Q9c,11229 apprise/plugins/NotifyD7Networks.py,sha256=4E6Fh0kQoDlMMwgZJDOXky7c7KrdMMvqprcfm29scWU,15043 apprise/plugins/NotifyDBus.py,sha256=1eVJHIL3XkFjDePMqfcll35Ie1vxggJ1iBsVFAIaF00,14379 apprise/plugins/NotifyDapnet.py,sha256=KuXjBU0ZrIYtoDei85NeLZ-IP810T4w5oFXH9sWiSh0,13624 apprise/plugins/NotifyDingTalk.py,sha256=NJyETgN6QjtRqtxQjfBLFVuFpURyWykRftm6WpQJVbY,12009 apprise/plugins/NotifyDiscord.py,sha256=M_qmTzB7NNL5_agjYDX38KBN1jRzDBp2EMSNwEF_9Tw,26072 -apprise/plugins/NotifyEmail.py,sha256=DhAzLFX4pzzuS07QQFcv0VUOYu2PzQE7TTjlPokJcPY,38883 +apprise/plugins/NotifyEmail.py,sha256=Y_ZOrdK6hTUKHLvogKpV5VqD8byzDyDSvwIVmfdsC2g,39789 apprise/plugins/NotifyEmby.py,sha256=OMVO8XsVl_XCBYNNNQi8ni2lS4voLfU8Puk1xJOAvHs,24039 apprise/plugins/NotifyEnigma2.py,sha256=Hj0Q9YOeljSwbfiuMKLqXTVX_1g_mjNUGEts7wfrwno,11498 apprise/plugins/NotifyFCM/__init__.py,sha256=mBFtIgIJuLIFnMB5ndx5Makjs9orVMc2oLoD7LaVT48,21669 @@ -91,9 +92,10 @@ apprise/plugins/NotifyFCM/color.py,sha256=8iqDtadloQh2TMxkFmIFwenHqKp1pHHn1bwyWO apprise/plugins/NotifyFCM/common.py,sha256=978uBUoNdtopCtylipGiKQdsQ8FTONxkFBp7uJMZHc8,1718 apprise/plugins/NotifyFCM/oauth.py,sha256=Vvbd0-rd5BPIjAneG3rILU153JIzfSZ0kaDov6hm96M,11197 apprise/plugins/NotifyFCM/priority.py,sha256=0WuRW1y1HVnybgjlTeCZPHzt7j8SwWnC7faNcjioAOc,8163 -apprise/plugins/NotifyFaast.py,sha256=_F1633tQhk8gCfaNpZZm808f2G0S6fP0OOEetSiv0h8,6972 +apprise/plugins/NotifyFeishu.py,sha256=IpcABdLZJ1vcQdZHlmASVbNOiOCIrmgKFhz1hbdskY4,7266 apprise/plugins/NotifyFlock.py,sha256=0rUIa9nToGsO8BTUgixh8Z_qdVixJeH479UNYjcE4EM,12748 apprise/plugins/NotifyForm.py,sha256=38nL-2m1cf4gEQFQ4NpvA4j9i5_nNUgelReWFSjyV5U,17905 +apprise/plugins/NotifyFreeMobile.py,sha256=XCkgZLc3KKGlx_9UdeoMJVcHpeQrOml9T93S-DGf4bs,6644 apprise/plugins/NotifyGnome.py,sha256=8MXTa8gZg1wTgNJfLlmq7_fl3WaYK-SX6VR91u308C4,9059 apprise/plugins/NotifyGoogleChat.py,sha256=lnoN17m6lZANaXcElDTP8lcuVWjIZEK8C6_iqJNAnw4,12622 apprise/plugins/NotifyGotify.py,sha256=DNlOIHyuYitO5use9oa_REPm2Fant7y9QSaatrZFNI0,10551 @@ -109,7 +111,7 @@ apprise/plugins/NotifyKumulos.py,sha256=eCEW2ZverZqETOLHVWMC4E8Ll6rEhhEWOSD73RD8 apprise/plugins/NotifyLametric.py,sha256=h8vZoX-Ll5NBZRprBlxTO2H9w0lOiMxglGvUgJtK4_8,37534 apprise/plugins/NotifyLine.py,sha256=OVI0ozMJcq_-dI8dodVX52dzUzgENlAbOik-Kw4l-rI,10676 apprise/plugins/NotifyLunaSea.py,sha256=woN8XdkwAjhgxAXp7Zj4XsWLybNL80l4W3Dx5BvobZg,14459 -apprise/plugins/NotifyMQTT.py,sha256=PFLwESgR8dMZvVFHxmOZ8xfy-YqyX5b2kl_e8Z1lo-0,19537 +apprise/plugins/NotifyMQTT.py,sha256=cnuG4f3bYYNPhEj9qDX8SLmnxLVT9G1b8J5w6-mQGKY,19545 apprise/plugins/NotifyMSG91.py,sha256=P7JPyT1xmucnaEeCZPf_6aJfe1gS_STYYwEM7hJ7QBw,12677 apprise/plugins/NotifyMSTeams.py,sha256=dFH575hoLL3zRddbBKfozlYjxvPJGbj3BKvfJSIkvD0,22976 apprise/plugins/NotifyMacOSX.py,sha256=y2fGpSZXomFiNwKbWImrXQUMVM4JR4uPCnsWpnxQrFA,8271 @@ -124,7 +126,7 @@ apprise/plugins/NotifyNextcloudTalk.py,sha256=dLl_g7Knq5PVcadbzDuQsxbGHTZlC4r-pQ apprise/plugins/NotifyNotica.py,sha256=yHmk8HiNFjzoI4Gewo_nBRrx9liEmhT95k1d10wqhYg,12990 apprise/plugins/NotifyNotifiarr.py,sha256=ADwLJO9eenfLkNa09tXMGSBTM4c3zTY0SEePvyB8WYA,15857 apprise/plugins/NotifyNotifico.py,sha256=Qe9jMN_M3GL4XlYIWkAf-w_Hf65g9Hde4bVuytGhUW4,12035 -apprise/plugins/NotifyNtfy.py,sha256=TkDs6jOc30XQn2O2BJ14-nE_cohPdJiSS8DpYXc9hoE,27953 +apprise/plugins/NotifyNtfy.py,sha256=AtJt2zH35mMQTwRDxKia93NPy6-4rtixplP53zIYV2M,27979 apprise/plugins/NotifyOffice365.py,sha256=8TxsVsdbUghmNj0kceMlmoZzTOKQTgn3priI8JuRuHE,25190 apprise/plugins/NotifyOneSignal.py,sha256=gsw7ckW7xLiJDRUb7eJHNe_4bvdBXmt6_YsB1u_ghjw,18153 apprise/plugins/NotifyOpsgenie.py,sha256=zJWpknjoHq35Iv9w88ucR62odaeIN3nrGFPtYnhDdjA,20515 @@ -144,7 +146,7 @@ apprise/plugins/NotifyPushy.py,sha256=mmWcnu905Fvc8ihYXvZ7lVYErGZH5Q-GbBNS20v5r4 apprise/plugins/NotifyRSyslog.py,sha256=W42LT90X65-pNoU7KdhdX1PBcmsz9RyV376CDa_H3CI,11982 apprise/plugins/NotifyReddit.py,sha256=E78OSyDQfUalBEcg71sdMsNBOwdj7cVBnELrhrZEAXY,25785 apprise/plugins/NotifyRevolt.py,sha256=DRA9Xylwl6leVjVFuJcP4L1cG49CIBtnQdxh4BKnAZ4,14500 -apprise/plugins/NotifyRocketChat.py,sha256=GTEfT-upQ56tJgE0kuc59l4uQGySj_d15wjdcARR9Ko,24624 +apprise/plugins/NotifyRocketChat.py,sha256=Cb_nasX0-G3FoPMYvNk55RJ-tHuXUCTLUn2wTSi4IcI,25738 apprise/plugins/NotifyRyver.py,sha256=yhHPMLGeJtcHwBKSPPk0OBfp59DgTvXio1R59JhrJu4,11823 apprise/plugins/NotifySES.py,sha256=wtRmpAZkS5mQma6sdiaPT6U1xcgoj77CB9mNFvSEAw8,33545 apprise/plugins/NotifySMSEagle.py,sha256=voFNqOewD9OC1eRctD0YdUB_ZSWsb06rjUwBfCcxPYA,24161 @@ -162,7 +164,7 @@ apprise/plugins/NotifyStreamlabs.py,sha256=lx3N8T2ufUWFYIZ-kU_rOv50YyGWBqLSCKk7x apprise/plugins/NotifySynology.py,sha256=_jTqfgWeOuSi_I8geMOraHBVFtDkvm9mempzymrmeAo,11105 apprise/plugins/NotifySyslog.py,sha256=J9Kain2bb-PDNiG5Ydb0q678cYjNE_NjZFqMG9oEXM0,10617 apprise/plugins/NotifyTechulusPush.py,sha256=m43_Qj1scPcgCRX5Dr2Ul7nxMbaiVxNzm_HRuNmfgoA,7253 -apprise/plugins/NotifyTelegram.py,sha256=Bim4mmPcefHNpvbNSy3pmLuCXRw5IVVWUNUB1SkIhDM,35624 +apprise/plugins/NotifyTelegram.py,sha256=XE7PC9LRzcrfE2bpLKyor5lO_7B9LS4Xw1UlUmA4a2A,37187 apprise/plugins/NotifyThreema.py,sha256=C_C3j0fJWgeF2uB7ceJFXOdC6Lt0TFBInFMs5Xlg04M,11885 apprise/plugins/NotifyTwilio.py,sha256=WCo8eTI9OF1rtg3ueHHRDXt4Lp45eZ6h3IdTZVf5HM8,15976 apprise/plugins/NotifyTwist.py,sha256=nZA73CYVe-p0tkVMy5q3vFRyflLM4yjUo9LECvkUwgc,28841 @@ -175,7 +177,7 @@ apprise/plugins/NotifyWhatsApp.py,sha256=PtzW0ue3d2wZ8Pva_LG29jUcpRRP03TFxO5SME_ apprise/plugins/NotifyWindows.py,sha256=QgWJfJF8AE6RWr-L81YYVZNWrnImK9Qr3B991HWanqU,8563 apprise/plugins/NotifyXBMC.py,sha256=5hDuOTP3Kwtp4NEMaokNjWyEKEkQcN_fSx-cUPJvhaU,12096 apprise/plugins/NotifyXML.py,sha256=WJnmdvXseuTRgioVMRqpR8a09cDfTpPTfuFlTnT_TfI,16973 -apprise/plugins/NotifyZulip.py,sha256=mbZoPiQXFbcaJ5UYDbkX4HJPAvRzPEAB-rsOlF9SD4o,13755 +apprise/plugins/NotifyZulip.py,sha256=M8cSL7nZvtBYyTX6045g34tyn2vyybltgD1CoI4Xa7A,13968 apprise/plugins/__init__.py,sha256=jTfLmW47kZC_Wf5eFFta2NoD2J-7_E7JaPrrVMIECkU,18725 apprise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 apprise/utils.py,sha256=SjRU2tb1UsVnTCTXPUyXVz3WpRbDWwAHH-d3ll38EHY,53185 diff --git a/libs/apprise-1.7.6.dist-info/REQUESTED b/libs/apprise-1.7.6.dist-info/REQUESTED new file mode 100644 index 000000000..e69de29bb diff --git a/libs/apprise-1.7.4.dist-info/WHEEL b/libs/apprise-1.7.6.dist-info/WHEEL similarity index 100% rename from libs/apprise-1.7.4.dist-info/WHEEL rename to libs/apprise-1.7.6.dist-info/WHEEL diff --git a/libs/apprise-1.7.4.dist-info/entry_points.txt b/libs/apprise-1.7.6.dist-info/entry_points.txt similarity index 100% rename from libs/apprise-1.7.4.dist-info/entry_points.txt rename to libs/apprise-1.7.6.dist-info/entry_points.txt diff --git a/libs/apprise-1.7.4.dist-info/top_level.txt b/libs/apprise-1.7.6.dist-info/top_level.txt similarity index 100% rename from libs/apprise-1.7.4.dist-info/top_level.txt rename to libs/apprise-1.7.6.dist-info/top_level.txt diff --git a/libs/apprise/AppriseLocale.py b/libs/apprise/AppriseLocale.py index cb9512e5e..e900ce5bc 100644 --- a/libs/apprise/AppriseLocale.py +++ b/libs/apprise/AppriseLocale.py @@ -219,6 +219,9 @@ class AppriseLocale: try: # Acquire our locale lang = locale.getlocale()[0] + # Compatibility for Python >= 3.12 + if lang == 'C': + lang = AppriseLocale._default_language except (ValueError, TypeError) as e: # This occurs when an invalid locale was parsed from the diff --git a/libs/apprise/URLBase.py b/libs/apprise/URLBase.py index 2467a4c1e..90ea85c66 100644 --- a/libs/apprise/URLBase.py +++ b/libs/apprise/URLBase.py @@ -669,6 +669,79 @@ class URLBase: 'verify': 'yes' if self.verify_certificate else 'no', } + @staticmethod + def post_process_parse_url_results(results): + """ + After parsing the URL, this function applies a bit of extra logic to + support extra entries like `pass` becoming `password`, etc + + This function assumes that parse_url() was called previously setting + up the basics to be checked + """ + + # if our URL ends with an 's', then assume our secure flag is set. + results['secure'] = (results['schema'][-1] == 's') + + # QSD Checking (over-rides all) + qsd_exists = True if isinstance(results.get('qsd'), dict) else False + + if qsd_exists and 'verify' in results['qsd']: + # Pulled from URL String + results['verify'] = parse_bool( + results['qsd'].get('verify', True)) + + elif 'verify' in results: + # Pulled from YAML Configuratoin + results['verify'] = parse_bool(results.get('verify', True)) + + else: + # Support SSL Certificate 'verify' keyword. Default to being + # enabled + results['verify'] = True + + # Password overrides + if 'pass' in results: + results['password'] = results['pass'] + del results['pass'] + + if qsd_exists: + if 'password' in results['qsd']: + results['password'] = results['qsd']['password'] + if 'pass' in results['qsd']: + results['password'] = results['qsd']['pass'] + + # User overrides + if 'user' in results['qsd']: + results['user'] = results['qsd']['user'] + + # parse_url() always creates a 'password' and 'user' entry in the + # results returned. Entries are set to None if they weren't + # specified + if results['password'] is None and 'user' in results['qsd']: + # Handle cases where the user= provided in 2 locations, we want + # the original to fall back as a being a password (if one + # wasn't otherwise defined) e.g. + # mailtos://PASSWORD@hostname?user=admin@mail-domain.com + # - in the above, the PASSWORD gets lost in the parse url() + # since a user= over-ride is specified. + presults = parse_url(results['url']) + if presults: + # Store our Password + results['password'] = presults['user'] + + # Store our socket read timeout if specified + if 'rto' in results['qsd']: + results['rto'] = results['qsd']['rto'] + + # Store our socket connect timeout if specified + if 'cto' in results['qsd']: + results['cto'] = results['qsd']['cto'] + + if 'port' in results['qsd']: + results['port'] = results['qsd']['port'] + + return results + @staticmethod def parse_url(url, verify_host=True, plus_to_space=False, strict_port=False): @@ -698,53 +771,7 @@ class URLBase: # We're done; we failed to parse our url return results - # if our URL ends with an 's', then assume our secure flag is set. - results['secure'] = (results['schema'][-1] == 's') - - # Support SSL Certificate 'verify' keyword. Default to being enabled - results['verify'] = True - - if 'verify' in results['qsd']: - results['verify'] = parse_bool( - results['qsd'].get('verify', True)) - - # Password overrides - if 'password' in results['qsd']: - results['password'] = results['qsd']['password'] - if 'pass' in results['qsd']: - results['password'] = results['qsd']['pass'] - - # User overrides - if 'user' in results['qsd']: - results['user'] = results['qsd']['user'] - - # parse_url() always creates a 'password' and 'user' entry in the - # results returned. Entries are set to None if they weren't specified - if results['password'] is None and 'user' in results['qsd']: - # Handle cases where the user= provided in 2 locations, we want - # the original to fall back as a being a password (if one wasn't - # otherwise defined) - # e.g. - # mailtos://PASSWORD@hostname?user=admin@mail-domain.com - # - the PASSWORD gets lost in the parse url() since a user= - # over-ride is specified. - presults = parse_url(results['url']) - if presults: - # Store our Password - results['password'] = presults['user'] - - # Store our socket read timeout if specified - if 'rto' in results['qsd']: - results['rto'] = results['qsd']['rto'] - - # Store our socket connect timeout if specified - if 'cto' in results['qsd']: - results['cto'] = results['qsd']['cto'] - - if 'port' in results['qsd']: - results['port'] = results['qsd']['port'] - - return results + return URLBase.post_process_parse_url_results(results) @staticmethod def http_response_code_lookup(code, response_mask=None): diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py index bb18eaec8..81373c75b 100644 --- a/libs/apprise/__init__.py +++ b/libs/apprise/__init__.py @@ -27,7 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. __title__ = 'Apprise' -__version__ = '1.7.4' +__version__ = '1.7.6' __author__ = 'Chris Caron' __license__ = 'BSD' __copywrite__ = 'Copyright (C) 2024 Chris Caron ' diff --git a/libs/apprise/attachment/AttachBase.py b/libs/apprise/attachment/AttachBase.py index 062e553d7..8cb6bd5cb 100644 --- a/libs/apprise/attachment/AttachBase.py +++ b/libs/apprise/attachment/AttachBase.py @@ -253,7 +253,7 @@ class AttachBase(URLBase): return self.detected_mimetype \ if self.detected_mimetype else self.unknown_mimetype - def exists(self): + def exists(self, retrieve_if_missing=True): """ Simply returns true if the object has downloaded and stored the attachment AND the attachment has not expired. @@ -282,7 +282,7 @@ class AttachBase(URLBase): # The file is not present pass - return self.download() + return False if not retrieve_if_missing else self.download() def invalidate(self): """ diff --git a/libs/apprise/attachment/AttachHTTP.py b/libs/apprise/attachment/AttachHTTP.py index 719ebc625..5a3af9467 100644 --- a/libs/apprise/attachment/AttachHTTP.py +++ b/libs/apprise/attachment/AttachHTTP.py @@ -29,6 +29,7 @@ import re import os import requests +import threading from tempfile import NamedTemporaryFile from .AttachBase import AttachBase from ..common import ContentLocation @@ -56,6 +57,9 @@ class AttachHTTP(AttachBase): # Web based requests are remote/external to our current location location = ContentLocation.HOSTED + # thread safe loading + _lock = threading.Lock() + def __init__(self, headers=None, **kwargs): """ Initialize HTTP Object @@ -96,9 +100,6 @@ class AttachHTTP(AttachBase): # our content is inaccessible return False - # Ensure any existing content set has been invalidated - self.invalidate() - # prepare header headers = { 'User-Agent': self.app_id, @@ -117,134 +118,154 @@ class AttachHTTP(AttachBase): url += self.fullpath - self.logger.debug('HTTP POST URL: %s (cert_verify=%r)' % ( - url, self.verify_certificate, - )) - # Where our request object will temporarily live. r = None # Always call throttle before any remote server i/o is made self.throttle() - try: - # Make our request - with requests.get( - url, - headers=headers, - auth=auth, - params=self.qsd, - verify=self.verify_certificate, - timeout=self.request_timeout, - stream=True) as r: - - # Handle Errors - r.raise_for_status() - - # Get our file-size (if known) - try: - file_size = int(r.headers.get('Content-Length', '0')) - except (TypeError, ValueError): - # Handle edge case where Content-Length is a bad value - file_size = 0 - - # Perform a little Q/A on file limitations and restrictions - if self.max_file_size > 0 and file_size > self.max_file_size: - - # The content retrieved is to large - self.logger.error( - 'HTTP response exceeds allowable maximum file length ' - '({}KB): {}'.format( - int(self.max_file_size / 1024), - self.url(privacy=True))) - - # Return False (signifying a failure) - return False - - # Detect config format based on mime if the format isn't - # already enforced - self.detected_mimetype = r.headers.get('Content-Type') - - d = r.headers.get('Content-Disposition', '') - result = re.search( - "filename=['\"]?(?P[^'\"]+)['\"]?", d, re.I) - if result: - self.detected_name = result.group('name').strip() - - # Create a temporary file to work with - self._temp_file = NamedTemporaryFile() - - # Get our chunk size - chunk_size = self.chunk_size - - # Track all bytes written to disk - bytes_written = 0 - - # If we get here, we can now safely write our content to disk - for chunk in r.iter_content(chunk_size=chunk_size): - # filter out keep-alive chunks - if chunk: - self._temp_file.write(chunk) - bytes_written = self._temp_file.tell() - - # Prevent a case where Content-Length isn't provided - # we don't want to fetch beyond our limits - if self.max_file_size > 0: - if bytes_written > self.max_file_size: - # The content retrieved is to large - self.logger.error( - 'HTTP response exceeds allowable maximum ' - 'file length ({}KB): {}'.format( - int(self.max_file_size / 1024), - self.url(privacy=True))) - - # Invalidate any variables previously set - self.invalidate() - - # Return False (signifying a failure) - return False - - elif bytes_written + chunk_size \ - > self.max_file_size: - # Adjust out next read to accomodate up to our - # limit +1. This will prevent us from readig - # to much into our memory buffer - self.max_file_size - bytes_written + 1 - - # Ensure our content is flushed to disk for post-processing - self._temp_file.flush() - - # Set our minimum requirements for a successful download() call - self.download_path = self._temp_file.name - if not self.detected_name: - self.detected_name = os.path.basename(self.fullpath) - - except requests.RequestException as e: - self.logger.error( - 'A Connection error occurred retrieving HTTP ' - 'configuration from %s.' % self.host) - self.logger.debug('Socket Exception: %s' % str(e)) - - # Invalidate any variables previously set - self.invalidate() - - # Return False (signifying a failure) - return False - - except (IOError, OSError): - # IOError is present for backwards compatibility with Python - # versions older then 3.3. >= 3.3 throw OSError now. - - # Could not open and/or write the temporary file - self.logger.error( - 'Could not write attachment to disk: {}'.format( - self.url(privacy=True))) + with self._lock: + if self.exists(retrieve_if_missing=False): + # Due to locking; it's possible a concurrent thread already + # handled the retrieval in which case we can safely move on + self.logger.trace( + 'HTTP Attachment %s already retrieved', + self._temp_file.name) + return True - # Invalidate any variables previously set + # Ensure any existing content set has been invalidated self.invalidate() - # Return False (signifying a failure) - return False + self.logger.debug( + 'HTTP Attachment Fetch URL: %s (cert_verify=%r)' % ( + url, self.verify_certificate)) + + try: + # Make our request + with requests.get( + url, + headers=headers, + auth=auth, + params=self.qsd, + verify=self.verify_certificate, + timeout=self.request_timeout, + stream=True) as r: + + # Handle Errors + r.raise_for_status() + + # Get our file-size (if known) + try: + file_size = int(r.headers.get('Content-Length', '0')) + except (TypeError, ValueError): + # Handle edge case where Content-Length is a bad value + file_size = 0 + + # Perform a little Q/A on file limitations and restrictions + if self.max_file_size > 0 and \ + file_size > self.max_file_size: + + # The content retrieved is to large + self.logger.error( + 'HTTP response exceeds allowable maximum file ' + 'length ({}KB): {}'.format( + int(self.max_file_size / 1024), + self.url(privacy=True))) + + # Return False (signifying a failure) + return False + + # Detect config format based on mime if the format isn't + # already enforced + self.detected_mimetype = r.headers.get('Content-Type') + + d = r.headers.get('Content-Disposition', '') + result = re.search( + "filename=['\"]?(?P[^'\"]+)['\"]?", d, re.I) + if result: + self.detected_name = result.group('name').strip() + + # Create a temporary file to work with; delete must be set + # to False or it isn't compatible with Microsoft Windows + # instances. In lieu of this, __del__ will clean up the + # file for us. + self._temp_file = NamedTemporaryFile(delete=False) + + # Get our chunk size + chunk_size = self.chunk_size + + # Track all bytes written to disk + bytes_written = 0 + + # If we get here, we can now safely write our content to + # disk + for chunk in r.iter_content(chunk_size=chunk_size): + # filter out keep-alive chunks + if chunk: + self._temp_file.write(chunk) + bytes_written = self._temp_file.tell() + + # Prevent a case where Content-Length isn't + # provided. In this case we don't want to fetch + # beyond our limits + if self.max_file_size > 0: + if bytes_written > self.max_file_size: + # The content retrieved is to large + self.logger.error( + 'HTTP response exceeds allowable ' + 'maximum file length ' + '({}KB): {}'.format( + int(self.max_file_size / 1024), + self.url(privacy=True))) + + # Invalidate any variables previously set + self.invalidate() + + # Return False (signifying a failure) + return False + + elif bytes_written + chunk_size \ + > self.max_file_size: + # Adjust out next read to accomodate up to + # our limit +1. This will prevent us from + # reading to much into our memory buffer + self.max_file_size - bytes_written + 1 + + # Ensure our content is flushed to disk for post-processing + self._temp_file.flush() + + # Set our minimum requirements for a successful download() + # call + self.download_path = self._temp_file.name + if not self.detected_name: + self.detected_name = os.path.basename(self.fullpath) + + except requests.RequestException as e: + self.logger.error( + 'A Connection error occurred retrieving HTTP ' + 'configuration from %s.' % self.host) + self.logger.debug('Socket Exception: %s' % str(e)) + + # Invalidate any variables previously set + self.invalidate() + + # Return False (signifying a failure) + return False + + except (IOError, OSError): + # IOError is present for backwards compatibility with Python + # versions older then 3.3. >= 3.3 throw OSError now. + + # Could not open and/or write the temporary file + self.logger.error( + 'Could not write attachment to disk: {}'.format( + self.url(privacy=True))) + + # Invalidate any variables previously set + self.invalidate() + + # Return False (signifying a failure) + return False # Return our success return True @@ -254,11 +275,30 @@ class AttachHTTP(AttachBase): Close our temporary file """ if self._temp_file: + self.logger.trace( + 'Attachment cleanup of %s', self._temp_file.name) self._temp_file.close() + + try: + # Ensure our file is removed (if it exists) + os.unlink(self._temp_file.name) + + except OSError: + pass + + # Reset our temporary file to prevent from entering + # this block again self._temp_file = None super().invalidate() + def __del__(self): + """ + Tidy memory if open + """ + with self._lock: + self.invalidate() + def url(self, privacy=False, *args, **kwargs): """ Returns the URL built dynamically based on specified arguments. diff --git a/libs/apprise/config/ConfigBase.py b/libs/apprise/config/ConfigBase.py index 731945256..32e1bde34 100644 --- a/libs/apprise/config/ConfigBase.py +++ b/libs/apprise/config/ConfigBase.py @@ -1184,6 +1184,9 @@ class ConfigBase(URLBase): # Prepare our Asset Object _results['asset'] = asset + # Handle post processing of result set + _results = URLBase.post_process_parse_url_results(_results) + # Store our preloaded entries preloaded.append({ 'results': _results, diff --git a/libs/apprise/conversion.py b/libs/apprise/conversion.py index 86e967bc4..4d5632f59 100644 --- a/libs/apprise/conversion.py +++ b/libs/apprise/conversion.py @@ -58,8 +58,8 @@ def markdown_to_html(content): """ Converts specified content from markdown to HTML. """ - - return markdown(content) + return markdown(content, extensions=[ + 'markdown.extensions.nl2br', 'markdown.extensions.tables']) def text_to_html(content): diff --git a/libs/apprise/decorators/CustomNotifyPlugin.py b/libs/apprise/decorators/CustomNotifyPlugin.py index 4c93ef1bd..eb5f17b78 100644 --- a/libs/apprise/decorators/CustomNotifyPlugin.py +++ b/libs/apprise/decorators/CustomNotifyPlugin.py @@ -147,6 +147,10 @@ class CustomNotifyPlugin(NotifyBase): self._default_args = {} + # Some variables do not need to be set + if 'secure' in kwargs: + del kwargs['secure'] + # Apply our updates based on what was parsed dict_full_update(self._default_args, self._base_args) dict_full_update(self._default_args, kwargs) diff --git a/libs/apprise/plugins/NotifyAprs.py b/libs/apprise/plugins/NotifyAprs.py index c56982a70..5d8c3c100 100644 --- a/libs/apprise/plugins/NotifyAprs.py +++ b/libs/apprise/plugins/NotifyAprs.py @@ -203,6 +203,13 @@ class NotifyAprs(NotifyBase): "type": "string", "map_to": "targets", }, + "delay": { + "name": _("Resend Delay"), + "type": "float", + "min": 0.0, + "max": 5.0, + "default": 0.0, + }, "locale": { "name": _("Locale"), "type": "choice:string", @@ -212,7 +219,7 @@ class NotifyAprs(NotifyBase): } ) - def __init__(self, targets=None, locale=None, **kwargs): + def __init__(self, targets=None, locale=None, delay=None, **kwargs): """ Initialize APRS Object """ @@ -272,6 +279,28 @@ class NotifyAprs(NotifyBase): self.logger.warning(msg) raise TypeError(msg) + # Update our delay + if delay is None: + self.delay = NotifyAprs.template_args["delay"]["default"] + + else: + try: + self.delay = float(delay) + if self.delay < NotifyAprs.template_args["delay"]["min"]: + raise ValueError() + + elif self.delay >= NotifyAprs.template_args["delay"]["max"]: + raise ValueError() + + except (TypeError, ValueError): + msg = "Unsupported APRS-IS delay ({}) specified. ".format( + delay) + self.logger.warning(msg) + raise TypeError(msg) + + # Bump up our request_rate + self.request_rate_per_sec += self.delay + # Set the transmitter group self.locale = \ NotifyAprs.template_args["locale"]["default"] \ @@ -674,6 +703,10 @@ class NotifyAprs(NotifyBase): # Store our locale if not default params['locale'] = self.locale + if self.delay != NotifyAprs.template_args["delay"]["default"]: + # Store our locale if not default + params['delay'] = "{:.2f}".format(self.delay) + # Extend our parameters params.update(self.url_parameters(privacy=privacy, *args, **kwargs)) @@ -727,6 +760,10 @@ class NotifyAprs(NotifyBase): # All entries after the hostname are additional targets results["targets"].extend(NotifyAprs.split_path(results["fullpath"])) + # Get Delay (if set) + if 'delay' in results['qsd'] and len(results['qsd']['delay']): + results['delay'] = NotifyAprs.unquote(results['qsd']['delay']) + # Support the 'to' variable so that we can support rooms this way too # The 'to' makes it easier to use yaml configuration if "to" in results["qsd"] and len(results["qsd"]["to"]): diff --git a/libs/apprise/plugins/NotifyBase.py b/libs/apprise/plugins/NotifyBase.py index 6daa8aa1d..c29417c60 100644 --- a/libs/apprise/plugins/NotifyBase.py +++ b/libs/apprise/plugins/NotifyBase.py @@ -457,6 +457,19 @@ class NotifyBase(URLBase): # Handle situations where the title is None title = '' if not title else title + # Truncate flag set with attachments ensures that only 1 + # attachment passes through. In the event there could be many + # services specified, we only want to do this logic once. + # The logic is only applicable if ther was more then 1 attachment + # specified + overflow = self.overflow_mode if overflow is None else overflow + if attach and len(attach) > 1 and overflow == OverflowMode.TRUNCATE: + # Save first attachment + _attach = AppriseAttachment(attach[0], asset=self.asset) + else: + # reference same attachment + _attach = attach + # Apply our overflow (if defined) for chunk in self._apply_overflow( body=body, title=title, overflow=overflow, @@ -465,7 +478,7 @@ class NotifyBase(URLBase): # Send notification yield dict( body=chunk['body'], title=chunk['title'], - notify_type=notify_type, attach=attach, + notify_type=notify_type, attach=_attach, body_format=body_format ) @@ -485,7 +498,7 @@ class NotifyBase(URLBase): }, { title: 'the title goes here', - body: 'the message body goes here', + body: 'the continued message body goes here', }, ] diff --git a/libs/apprise/plugins/NotifyFaast.py b/libs/apprise/plugins/NotifyChantify.py similarity index 57% rename from libs/apprise/plugins/NotifyFaast.py rename to libs/apprise/plugins/NotifyChantify.py index f82c44020..d912bd257 100644 --- a/libs/apprise/plugins/NotifyFaast.py +++ b/libs/apprise/plugins/NotifyChantify.py @@ -26,118 +26,111 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +# Chantify +# 1. Visit https://chanify.net/ + +# The API URL will look something like this: +# https://api.chanify.net/v1/sender/token +# + import requests from .NotifyBase import NotifyBase -from ..common import NotifyImageSize from ..common import NotifyType -from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ from ..utils import validate_regex +from ..AppriseLocale import gettext_lazy as _ -class NotifyFaast(NotifyBase): +class NotifyChantify(NotifyBase): """ - A wrapper for Faast Notifications + A wrapper for Chantify Notifications """ # The default descriptive name associated with the Notification - service_name = 'Faast' + service_name = _('Chantify') # The services URL - service_url = 'http://www.faast.io/' + service_url = 'https://chanify.net/' - # The default protocol (this is secure for faast) - protocol = 'faast' + # The default secure protocol + secure_protocol = 'chantify' # A URL that takes you to the setup/help of the specific protocol - setup_url = 'https://github.com/caronc/apprise/wiki/Notify_faast' - - # Faast uses the http protocol with JSON requests - notify_url = 'https://www.appnotifications.com/account/notifications.json' + setup_url = 'https://github.com/caronc/apprise/wiki/Notify_chantify' - # Allows the user to specify the NotifyImageSize object - image_size = NotifyImageSize.XY_72 + # Notification URL + notify_url = 'https://api.chanify.net/v1/sender/{token}/' # Define object templates templates = ( - '{schema}://{authtoken}', + '{schema}://{token}', ) - # Define our template tokens + # The title is not used + title_maxlen = 0 + + # Define our tokens; these are the minimum tokens required required to + # be passed into this function (as arguments). The syntax appends any + # previously defined in the base package and builds onto them template_tokens = dict(NotifyBase.template_tokens, **{ - 'authtoken': { - 'name': _('Authorization Token'), + 'token': { + 'name': _('Token'), 'type': 'string', 'private': True, 'required': True, + 'regex': (r'^[A-Z0-9_-]+$', 'i'), }, }) # Define our template arguments template_args = dict(NotifyBase.template_args, **{ - 'image': { - 'name': _('Include Image'), - 'type': 'bool', - 'default': True, - 'map_to': 'include_image', + 'token': { + 'alias_of': 'token', }, }) - def __init__(self, authtoken, include_image=True, **kwargs): + def __init__(self, token, **kwargs): """ - Initialize Faast Object + Initialize Chantify Object """ super().__init__(**kwargs) - # Store the Authentication Token - self.authtoken = validate_regex(authtoken) - if not self.authtoken: - msg = 'An invalid Faast Authentication Token ' \ - '({}) was specified.'.format(authtoken) + self.token = validate_regex( + token, *self.template_tokens['token']['regex']) + if not self.token: + msg = 'The Chantify token specified ({}) is invalid.'\ + .format(token) self.logger.warning(msg) raise TypeError(msg) - # Associate an image with our post - self.include_image = include_image - return def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs): """ - Perform Faast Notification + Send our notification """ + # prepare our headers headers = { 'User-Agent': self.app_id, - 'Content-Type': 'multipart/form-data' + 'Content-Type': 'application/x-www-form-urlencoded', } - # prepare JSON Object + # Our Message payload = { - 'user_credentials': self.authtoken, - 'title': title, - 'message': body, + 'text': body } - # Acquire our image if we're configured to do so - image_url = None if not self.include_image \ - else self.image_url(notify_type) - - if image_url: - payload['icon_url'] = image_url - - self.logger.debug('Faast POST URL: %s (cert_verify=%r)' % ( - self.notify_url, self.verify_certificate, - )) - self.logger.debug('Faast Payload: %s' % str(payload)) + self.logger.debug('Chantify GET URL: %s (cert_verify=%r)' % ( + self.notify_url, self.verify_certificate)) + self.logger.debug('Chantify Payload: %s' % str(payload)) # Always call throttle before any remote server i/o is made self.throttle() try: r = requests.post( - self.notify_url, + self.notify_url.format(token=self.token), data=payload, headers=headers, verify=self.verify_certificate, @@ -146,10 +139,10 @@ class NotifyFaast(NotifyBase): if r.status_code != requests.codes.ok: # We had a problem status_str = \ - NotifyFaast.http_response_code_lookup(r.status_code) + NotifyChantify.http_response_code_lookup(r.status_code) self.logger.warning( - 'Failed to send Faast notification:' + 'Failed to send Chantify notification: ' '{}{}error={}.'.format( status_str, ', ' if status_str else '', @@ -161,12 +154,12 @@ class NotifyFaast(NotifyBase): return False else: - self.logger.info('Sent Faast notification.') + self.logger.info('Sent Chantify notification.') except requests.RequestException as e: self.logger.warning( - 'A Connection error occurred sending Faast notification.', - ) + 'A Connection error occurred sending Chantify ' + 'notification.') self.logger.debug('Socket Exception: %s' % str(e)) # Return; we're done @@ -179,18 +172,13 @@ class NotifyFaast(NotifyBase): Returns the URL built dynamically based on specified arguments. """ - # Define any URL parameters - params = { - 'image': 'yes' if self.include_image else 'no', - } - - # Extend our parameters - params.update(self.url_parameters(privacy=privacy, *args, **kwargs)) + # Prepare our parameters + params = self.url_parameters(privacy=privacy, *args, **kwargs) - return '{schema}://{authtoken}/?{params}'.format( - schema=self.protocol, - authtoken=self.pprint(self.authtoken, privacy, safe=''), - params=NotifyFaast.urlencode(params), + return '{schema}://{token}/?{params}'.format( + schema=self.secure_protocol, + token=self.pprint(self.token, privacy, safe=''), + params=NotifyChantify.urlencode(params), ) @staticmethod @@ -200,16 +188,19 @@ class NotifyFaast(NotifyBase): us to re-instantiate this object. """ + + # parse_url already handles getting the `user` and `password` fields + # populated. results = NotifyBase.parse_url(url, verify_host=False) if not results: # We're done early as we couldn't load the results return results - # Store our authtoken using the host - results['authtoken'] = NotifyFaast.unquote(results['host']) + # Allow over-ride + if 'token' in results['qsd'] and len(results['qsd']['token']): + results['token'] = NotifyChantify.unquote(results['qsd']['token']) - # Include image with our post - results['include_image'] = \ - parse_bool(results['qsd'].get('image', True)) + else: + results['token'] = NotifyChantify.unquote(results['host']) return results diff --git a/libs/apprise/plugins/NotifyEmail.py b/libs/apprise/plugins/NotifyEmail.py index e3ecde3f6..80f88bf61 100644 --- a/libs/apprise/plugins/NotifyEmail.py +++ b/libs/apprise/plugins/NotifyEmail.py @@ -45,7 +45,7 @@ from .NotifyBase import NotifyBase from ..URLBase import PrivacyMode from ..common import NotifyFormat, NotifyType from ..conversion import convert_between -from ..utils import is_email, parse_emails +from ..utils import is_email, parse_emails, is_hostname from ..AppriseLocale import gettext_lazy as _ from ..logger import logger @@ -566,12 +566,20 @@ class NotifyEmail(NotifyBase): # Apply any defaults based on certain known configurations self.NotifyEmailDefaults(secure_mode=secure_mode, **kwargs) - if self.user and self.host: - # Prepare the bases of our email - self.from_addr = [self.app_id, '{}@{}'.format( - re.split(r'[\s@]+', self.user)[0], - self.host, - )] + if self.user: + if self.host: + # Prepare the bases of our email + self.from_addr = [self.app_id, '{}@{}'.format( + re.split(r'[\s@]+', self.user)[0], + self.host, + )] + + else: + result = is_email(self.user) + if result: + # Prepare the bases of our email and include domain + self.host = result['domain'] + self.from_addr = [self.app_id, self.user] if from_addr: result = is_email(from_addr) @@ -1037,11 +1045,25 @@ class NotifyEmail(NotifyBase): us to re-instantiate this object. """ - results = NotifyBase.parse_url(url) + results = NotifyBase.parse_url(url, verify_host=False) if not results: # We're done early as we couldn't load the results return results + # Prepare our target lists + results['targets'] = [] + + if not is_hostname(results['host'], ipv4=False, ipv6=False, + underscore=False): + + if is_email(NotifyEmail.unquote(results['host'])): + # Don't lose defined email addresses + results['targets'].append(NotifyEmail.unquote(results['host'])) + + # Detect if we have a valid hostname or not; be sure to reset it's + # value if invalid; we'll attempt to figure this out later on + results['host'] = '' + # The From address is a must; either through the use of templates # from= entry and/or merging the user and hostname together, this # must be calculated or parse_url will fail. @@ -1052,7 +1074,7 @@ class NotifyEmail(NotifyBase): # Get our potential email targets; if none our found we'll just # add one to ourselves - results['targets'] = NotifyEmail.split_path(results['fullpath']) + results['targets'] += NotifyEmail.split_path(results['fullpath']) # Attempt to detect 'to' email address if 'to' in results['qsd'] and len(results['qsd']['to']): diff --git a/libs/apprise/plugins/NotifyFeishu.py b/libs/apprise/plugins/NotifyFeishu.py new file mode 100644 index 000000000..e6988333c --- /dev/null +++ b/libs/apprise/plugins/NotifyFeishu.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# BSD 2-Clause License +# +# Apprise - Push Notification Library. +# Copyright (c) 2024, Chris Caron +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Feishu +# 1. Visit https://open.feishu.cn + +# Custom Bot Setup +# https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot +# + +import requests +from json import dumps + +from .NotifyBase import NotifyBase +from ..common import NotifyType +from ..utils import validate_regex +from ..AppriseLocale import gettext_lazy as _ + + +class NotifyFeishu(NotifyBase): + """ + A wrapper for Feishu Notifications + """ + + # The default descriptive name associated with the Notification + service_name = _('Feishu') + + # The services URL + service_url = 'https://open.feishu.cn/' + + # The default secure protocol + secure_protocol = 'feishu' + + # A URL that takes you to the setup/help of the specific protocol + setup_url = 'https://github.com/caronc/apprise/wiki/Notify_feishu' + + # Notification URL + notify_url = 'https://open.feishu.cn/open-apis/bot/v2/hook/{token}/' + + # Define object templates + templates = ( + '{schema}://{token}', + ) + + # The title is not used + title_maxlen = 0 + + # Limit is documented to be 20K message sizes. This number safely + # allows padding around that size. + body_maxlen = 19985 + + # Define our tokens; these are the minimum tokens required required to + # be passed into this function (as arguments). The syntax appends any + # previously defined in the base package and builds onto them + template_tokens = dict(NotifyBase.template_tokens, **{ + 'token': { + 'name': _('Token'), + 'type': 'string', + 'private': True, + 'required': True, + 'regex': (r'^[A-Z0-9_-]+$', 'i'), + }, + }) + + # Define our template arguments + template_args = dict(NotifyBase.template_args, **{ + 'token': { + 'alias_of': 'token', + }, + }) + + def __init__(self, token, **kwargs): + """ + Initialize Feishu Object + """ + super().__init__(**kwargs) + + self.token = validate_regex( + token, *self.template_tokens['token']['regex']) + if not self.token: + msg = 'The Feishu token specified ({}) is invalid.'\ + .format(token) + self.logger.warning(msg) + raise TypeError(msg) + + return + + def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs): + """ + Send our notification + """ + + # prepare our headers + headers = { + 'User-Agent': self.app_id, + 'Content-Type': "application/json", + } + + # Our Message + payload = { + 'msg_type': 'text', + "content": { + "text": body, + } + } + + self.logger.debug('Feishu GET URL: %s (cert_verify=%r)' % ( + self.notify_url, self.verify_certificate)) + self.logger.debug('Feishu Payload: %s' % str(payload)) + + # Always call throttle before any remote server i/o is made + self.throttle() + + try: + r = requests.post( + self.notify_url.format(token=self.token), + data=dumps(payload).encode('utf-8'), + headers=headers, + verify=self.verify_certificate, + timeout=self.request_timeout, + ) + + # + # Sample Responses + # + # Valid: + # { + # "code": 0, + # "data": {}, + # "msg": "success" + # } + + # Invalid (non 200 response): + # { + # "code": 9499, + # "msg": "Bad Request", + # "data": {} + # } + if r.status_code != requests.codes.ok: + # We had a problem + status_str = \ + NotifyFeishu.http_response_code_lookup(r.status_code) + + self.logger.warning( + 'Failed to send Feishu notification: ' + '{}{}error={}.'.format( + status_str, + ', ' if status_str else '', + r.status_code)) + + self.logger.debug('Response Details:\r\n{}'.format(r.content)) + + # Return; we're done + return False + + else: + self.logger.info('Sent Feishu notification.') + + except requests.RequestException as e: + self.logger.warning( + 'A Connection error occurred sending Feishu ' + 'notification.') + self.logger.debug('Socket Exception: %s' % str(e)) + + # Return; we're done + return False + + return True + + def url(self, privacy=False, *args, **kwargs): + """ + Returns the URL built dynamically based on specified arguments. + """ + + # Prepare our parameters + params = self.url_parameters(privacy=privacy, *args, **kwargs) + + return '{schema}://{token}/?{params}'.format( + schema=self.secure_protocol, + token=self.pprint(self.token, privacy, safe=''), + params=NotifyFeishu.urlencode(params), + ) + + @staticmethod + def parse_url(url): + """ + Parses the URL and returns enough arguments that can allow + us to re-instantiate this object. + + """ + + # parse_url already handles getting the `user` and `password` fields + # populated. + results = NotifyBase.parse_url(url, verify_host=False) + if not results: + # We're done early as we couldn't load the results + return results + + # Allow over-ride + if 'token' in results['qsd'] and len(results['qsd']['token']): + results['token'] = NotifyFeishu.unquote(results['qsd']['token']) + + else: + results['token'] = NotifyFeishu.unquote(results['host']) + + return results diff --git a/libs/apprise/plugins/NotifyFreeMobile.py b/libs/apprise/plugins/NotifyFreeMobile.py new file mode 100644 index 000000000..4aad8db3d --- /dev/null +++ b/libs/apprise/plugins/NotifyFreeMobile.py @@ -0,0 +1,204 @@ +# -*- coding: utf-8 -*- +# BSD 2-Clause License +# +# Apprise - Push Notification Library. +# Copyright (c) 2024, Chris Caron +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# Free Mobile +# 1. Visit https://mobile.free.fr/ + +# the URL will look something like this: +# https://smsapi.free-mobile.fr/sendmsg +# + +import requests +from json import dumps + +from .NotifyBase import NotifyBase +from ..common import NotifyType +from ..AppriseLocale import gettext_lazy as _ + + +class NotifyFreeMobile(NotifyBase): + """ + A wrapper for Free-Mobile Notifications + """ + + # The default descriptive name associated with the Notification + service_name = _('Free-Mobile') + + # The services URL + service_url = 'https://mobile.free.fr/' + + # The default secure protocol + secure_protocol = 'freemobile' + + # A URL that takes you to the setup/help of the specific protocol + setup_url = 'https://github.com/caronc/apprise/wiki/Notify_freemobile' + + # Plain Text Notification URL + notify_url = 'https://smsapi.free-mobile.fr/sendmsg' + + # Define object templates + templates = ( + '{schema}://{user}@{password}', + ) + + # The title is not used + title_maxlen = 0 + + # SMS Messages are restricted in size + body_maxlen = 160 + + # Define our tokens; these are the minimum tokens required required to + # be passed into this function (as arguments). The syntax appends any + # previously defined in the base package and builds onto them + template_tokens = dict(NotifyBase.template_tokens, **{ + 'user': { + 'name': _('Username'), + 'type': 'string', + 'required': True, + }, + 'password': { + 'name': _('Password'), + 'type': 'string', + 'private': True, + 'required': True, + }, + }) + + def __init__(self, **kwargs): + """ + Initialize Free Mobile Object + """ + super().__init__(**kwargs) + + if not (self.user and self.password): + msg = 'A FreeMobile user and password ' \ + 'combination was not provided.' + self.logger.warning(msg) + raise TypeError(msg) + + return + + def url(self, privacy=False, *args, **kwargs): + """ + Returns the URL built dynamically based on specified arguments. + """ + + # Prepare our parameters + params = self.url_parameters(privacy=privacy, *args, **kwargs) + + return '{schema}://{user}@{password}/?{params}'.format( + schema=self.secure_protocol, + user=self.user, + password=self.pprint(self.password, privacy, safe=''), + params=NotifyFreeMobile.urlencode(params), + ) + + def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs): + """ + Send our notification + """ + + # prepare our headers + headers = { + 'User-Agent': self.app_id, + } + + # Prepare our payload + payload = { + 'user': self.user, + 'pass': self.password, + 'msg': body + } + + self.logger.debug('Free Mobile GET URL: %s (cert_verify=%r)' % ( + self.notify_url, self.verify_certificate)) + self.logger.debug('Free Mobile Payload: %s' % str(payload)) + + # Always call throttle before any remote server i/o is made + self.throttle() + + try: + r = requests.post( + self.notify_url, + data=dumps(payload).encode('utf-8'), + headers=headers, + verify=self.verify_certificate, + timeout=self.request_timeout, + ) + if r.status_code != requests.codes.ok: + # We had a problem + status_str = \ + NotifyFreeMobile.http_response_code_lookup(r.status_code) + + self.logger.warning( + 'Failed to send Free Mobile notification: ' + '{}{}error={}.'.format( + status_str, + ', ' if status_str else '', + r.status_code)) + + self.logger.debug('Response Details:\r\n{}'.format(r.content)) + + # Return; we're done + return False + + else: + self.logger.info('Sent Free Mobile notification.') + + except requests.RequestException as e: + self.logger.warning( + 'A Connection error occurred sending Free Mobile ' + 'notification.') + self.logger.debug('Socket Exception: %s' % str(e)) + + # Return; we're done + return False + + return True + + @staticmethod + def parse_url(url): + """ + Parses the URL and returns enough arguments that can allow + us to re-instantiate this object. + + """ + + # parse_url already handles getting the `user` and `password` fields + # populated. + results = NotifyBase.parse_url(url, verify_host=False) + if not results: + # We're done early as we couldn't load the results + return results + + # The hostname can act as the password if specified and a password + # was otherwise not (specified): + if not results.get('password'): + results['password'] = NotifyFreeMobile.unquote(results['host']) + + return results diff --git a/libs/apprise/plugins/NotifyMQTT.py b/libs/apprise/plugins/NotifyMQTT.py index 4e159b662..49380d330 100644 --- a/libs/apprise/plugins/NotifyMQTT.py +++ b/libs/apprise/plugins/NotifyMQTT.py @@ -89,7 +89,7 @@ class NotifyMQTT(NotifyBase): requirements = { # Define our required packaging in order to work - 'packages_required': 'paho-mqtt' + 'packages_required': 'paho-mqtt < 2.0.0' } # The default descriptive name associated with the Notification diff --git a/libs/apprise/plugins/NotifyNtfy.py b/libs/apprise/plugins/NotifyNtfy.py index 138c3fca7..40834eece 100644 --- a/libs/apprise/plugins/NotifyNtfy.py +++ b/libs/apprise/plugins/NotifyNtfy.py @@ -698,7 +698,7 @@ class NotifyNtfy(NotifyBase): """ Returns the number of targets associated with this notification """ - return len(self.topics) + return 1 if not self.topics else len(self.topics) @staticmethod def parse_url(url): diff --git a/libs/apprise/plugins/NotifyRocketChat.py b/libs/apprise/plugins/NotifyRocketChat.py index 8e6c0751c..9011a5e71 100644 --- a/libs/apprise/plugins/NotifyRocketChat.py +++ b/libs/apprise/plugins/NotifyRocketChat.py @@ -59,6 +59,9 @@ class RocketChatAuthMode: # providing a webhook WEBHOOK = "webhook" + # Support token submission + TOKEN = "token" + # Providing a username and password (default) BASIC = "basic" @@ -66,6 +69,7 @@ class RocketChatAuthMode: # Define our authentication modes ROCKETCHAT_AUTH_MODES = ( RocketChatAuthMode.WEBHOOK, + RocketChatAuthMode.TOKEN, RocketChatAuthMode.BASIC, ) @@ -107,6 +111,8 @@ class NotifyRocketChat(NotifyBase): templates = ( '{schema}://{user}:{password}@{host}:{port}/{targets}', '{schema}://{user}:{password}@{host}/{targets}', + '{schema}://{user}:{token}@{host}:{port}/{targets}', + '{schema}://{user}:{token}@{host}/{targets}', '{schema}://{webhook}@{host}', '{schema}://{webhook}@{host}:{port}', '{schema}://{webhook}@{host}/{targets}', @@ -135,6 +141,11 @@ class NotifyRocketChat(NotifyBase): 'type': 'string', 'private': True, }, + 'token': { + 'name': _('API Token'), + 'map_to': 'password', + 'private': True, + }, 'webhook': { 'name': _('Webhook'), 'type': 'string', @@ -230,13 +241,20 @@ class NotifyRocketChat(NotifyBase): if self.webhook is not None: # Just a username was specified, we treat this as a webhook self.mode = RocketChatAuthMode.WEBHOOK + elif self.password and len(self.password) > 32: + self.mode = RocketChatAuthMode.TOKEN else: self.mode = RocketChatAuthMode.BASIC - if self.mode == RocketChatAuthMode.BASIC \ + self.logger.debug( + "Auto-Detected Rocketchat Auth Mode: %s", self.mode) + + if self.mode in (RocketChatAuthMode.BASIC, RocketChatAuthMode.TOKEN) \ and not (self.user and self.password): # Username & Password is required for Rocket Chat to work - msg = 'No Rocket.Chat user/pass combo was specified.' + msg = 'No Rocket.Chat {} was specified.'.format( + 'user/pass combo' if self.mode == RocketChatAuthMode.BASIC else + 'user/apikey') self.logger.warning(msg) raise TypeError(msg) @@ -245,6 +263,13 @@ class NotifyRocketChat(NotifyBase): self.logger.warning(msg) raise TypeError(msg) + if self.mode == RocketChatAuthMode.TOKEN: + # Set our headers for further communication + self.headers.update({ + 'X-User-Id': self.user, + 'X-Auth-Token': self.password, + }) + # Validate recipients and drop bad ones: for recipient in parse_list(targets): result = IS_CHANNEL.match(recipient) @@ -309,12 +334,13 @@ class NotifyRocketChat(NotifyBase): params.update(self.url_parameters(privacy=privacy, *args, **kwargs)) # Determine Authentication - if self.mode == RocketChatAuthMode.BASIC: + if self.mode in (RocketChatAuthMode.BASIC, RocketChatAuthMode.TOKEN): auth = '{user}:{password}@'.format( user=NotifyRocketChat.quote(self.user, safe=''), password=self.pprint( self.password, privacy, mode=PrivacyMode.Secret, safe=''), ) + else: auth = '{user}{webhook}@'.format( user='{}:'.format(NotifyRocketChat.quote(self.user, safe='')) @@ -359,8 +385,11 @@ class NotifyRocketChat(NotifyBase): # Call the _send_ function applicable to whatever mode we're in # - calls _send_webhook_notification if the mode variable is set # - calls _send_basic_notification if the mode variable is not set - return getattr(self, '_send_{}_notification'.format(self.mode))( - body=body, title=title, notify_type=notify_type, **kwargs) + return getattr(self, '_send_{}_notification'.format( + RocketChatAuthMode.WEBHOOK + if self.mode == RocketChatAuthMode.WEBHOOK + else RocketChatAuthMode.BASIC))( + body=body, title=title, notify_type=notify_type, **kwargs) def _send_webhook_notification(self, body, title='', notify_type=NotifyType.INFO, **kwargs): @@ -412,7 +441,7 @@ class NotifyRocketChat(NotifyBase): """ # Track whether we authenticated okay - if not self.login(): + if self.mode == RocketChatAuthMode.BASIC and not self.login(): return False # prepare JSON Object @@ -432,9 +461,7 @@ class NotifyRocketChat(NotifyBase): channel = channels.pop(0) payload['channel'] = channel - if not self._send( - payload, notify_type=notify_type, headers=self.headers, - **kwargs): + if not self._send(payload, notify_type=notify_type, **kwargs): # toggle flag has_error = True @@ -447,15 +474,14 @@ class NotifyRocketChat(NotifyBase): room = rooms.pop(0) payload['roomId'] = room - if not self._send( - payload, notify_type=notify_type, headers=self.headers, - **kwargs): + if not self._send(payload, notify_type=notify_type, **kwargs): # toggle flag has_error = True - # logout - self.logout() + if self.mode == RocketChatAuthMode.BASIC: + # logout + self.logout() return not has_error @@ -476,7 +502,7 @@ class NotifyRocketChat(NotifyBase): return payload def _send(self, payload, notify_type, path='api/v1/chat.postMessage', - headers={}, **kwargs): + **kwargs): """ Perform Notify Rocket.Chat Notification """ @@ -487,6 +513,9 @@ class NotifyRocketChat(NotifyBase): api_url, self.verify_certificate)) self.logger.debug('Rocket.Chat Payload: %s' % str(payload)) + # Copy our existing headers + headers = self.headers.copy() + # Apply minimum headers headers.update({ 'User-Agent': self.app_id, diff --git a/libs/apprise/plugins/NotifyTelegram.py b/libs/apprise/plugins/NotifyTelegram.py index dbea79b1a..cce8af625 100644 --- a/libs/apprise/plugins/NotifyTelegram.py +++ b/libs/apprise/plugins/NotifyTelegram.py @@ -82,6 +82,34 @@ IS_CHAT_ID_RE = re.compile( ) +class TelegramMarkdownVersion: + """ + Telegram Markdown Version + """ + # Classic (Original Telegram Markdown) + ONE = 'MARKDOWN' + + # Supports strikethrough and many other items + TWO = 'MarkdownV2' + + +TELEGRAM_MARKDOWN_VERSION_MAP = { + # v1 + "v1": TelegramMarkdownVersion.ONE, + "1": TelegramMarkdownVersion.ONE, + # v2 + "v2": TelegramMarkdownVersion.TWO, + "2": TelegramMarkdownVersion.TWO, + "default": TelegramMarkdownVersion.TWO, +} + +TELEGRAM_MARKDOWN_VERSIONS = { + # Note: This also acts as a reverse lookup mapping + TelegramMarkdownVersion.ONE: 'v1', + TelegramMarkdownVersion.TWO: 'v2', +} + + class TelegramContentPlacement: """ The Telegram Content Placement @@ -333,6 +361,12 @@ class NotifyTelegram(NotifyBase): 'name': _('Topic Thread ID'), 'type': 'int', }, + 'mdv': { + 'name': _('Markdown Version'), + 'type': 'choice:string', + 'values': ('v1', 'v2'), + 'default': 'v2', + }, 'to': { 'alias_of': 'targets', }, @@ -346,7 +380,7 @@ class NotifyTelegram(NotifyBase): def __init__(self, bot_token, targets, detect_owner=True, include_image=False, silent=None, preview=None, topic=None, - content=None, **kwargs): + content=None, mdv=None, **kwargs): """ Initialize Telegram Object """ @@ -361,6 +395,17 @@ class NotifyTelegram(NotifyBase): self.logger.warning(err) raise TypeError(err) + # Get our Markdown Version + self.markdown_ver = \ + TELEGRAM_MARKDOWN_VERSION_MAP[NotifyTelegram. + template_args['mdv']['default']] \ + if mdv is None else \ + next(( + v for k, v in TELEGRAM_MARKDOWN_VERSION_MAP.items() + if str(mdv).lower().startswith(k)), + TELEGRAM_MARKDOWN_VERSION_MAP[NotifyTelegram. + template_args['mdv']['default']]) + # Define whether or not we should make audible alarms self.silent = self.template_args['silent']['default'] \ if silent is None else bool(silent) @@ -717,8 +762,7 @@ class NotifyTelegram(NotifyBase): # Prepare Message Body if self.notify_format == NotifyFormat.MARKDOWN: - _payload['parse_mode'] = 'MARKDOWN' - + _payload['parse_mode'] = self.markdown_ver _payload['text'] = body else: # HTML @@ -886,6 +930,7 @@ class NotifyTelegram(NotifyBase): 'silent': 'yes' if self.silent else 'no', 'preview': 'yes' if self.preview else 'no', 'content': self.content, + 'mdv': TELEGRAM_MARKDOWN_VERSIONS[self.markdown_ver], } if self.topic: @@ -990,6 +1035,10 @@ class NotifyTelegram(NotifyBase): # Store our bot token results['bot_token'] = bot_token + # Support Markdown Version + if 'mdv' in results['qsd'] and len(results['qsd']['mdv']): + results['mdv'] = results['qsd']['mdv'] + # Support Thread Topic if 'topic' in results['qsd'] and len(results['qsd']['topic']): results['topic'] = results['qsd']['topic'] diff --git a/libs/apprise/plugins/NotifyZulip.py b/libs/apprise/plugins/NotifyZulip.py index 66ffb9d1d..54fe2d062 100644 --- a/libs/apprise/plugins/NotifyZulip.py +++ b/libs/apprise/plugins/NotifyZulip.py @@ -163,6 +163,9 @@ class NotifyZulip(NotifyBase): 'to': { 'alias_of': 'targets', }, + 'token': { + 'alias_of': 'token', + }, }) # The default hostname to append to a defined organization @@ -377,21 +380,24 @@ class NotifyZulip(NotifyBase): # The botname results['botname'] = NotifyZulip.unquote(results['user']) - # The first token is stored in the hostname + # The organization is stored in the hostname results['organization'] = NotifyZulip.unquote(results['host']) - # Now fetch the remaining tokens - try: - results['token'] = \ - NotifyZulip.split_path(results['fullpath'])[0] + # Store our targets + results['targets'] = NotifyZulip.split_path(results['fullpath']) - except IndexError: + if 'token' in results['qsd'] and len(results['qsd']['token']): + # Store our token if specified + results['token'] = NotifyZulip.unquote(results['qsd']['token']) + + elif results['targets']: + # First item is the token + results['token'] = results['targets'].pop(0) + + else: # no token results['token'] = None - # Get unquoted entries - results['targets'] = NotifyZulip.split_path(results['fullpath'])[1:] - # Support the 'to' variable so that we can support rooms this way too # The 'to' makes it easier to use yaml configuration if 'to' in results['qsd'] and len(results['qsd']['to']): diff --git a/libs/version.txt b/libs/version.txt index bfd019444..ccabf65cc 100644 --- a/libs/version.txt +++ b/libs/version.txt @@ -2,7 +2,7 @@ alembic==1.13.1 aniso8601==9.0.1 argparse==1.4.0 -apprise==1.7.4 +apprise==1.7.6 apscheduler<=3.10.4 attrs==23.2.0 blinker==1.7.0 From af893847c618a5c531038a825aa2687c972b2117 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Tue, 16 Apr 2024 10:24:45 +0900 Subject: [PATCH 022/129] Fixed subtitle toolbox overlap --- frontend/src/components/SubtitleToolsMenu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/SubtitleToolsMenu.tsx b/frontend/src/components/SubtitleToolsMenu.tsx index 50509c3c2..e36a1e9e1 100644 --- a/frontend/src/components/SubtitleToolsMenu.tsx +++ b/frontend/src/components/SubtitleToolsMenu.tsx @@ -140,7 +140,7 @@ const SubtitleToolsMenu: FunctionComponent = ({ const disabledTools = selections.length === 0; return ( - + {children} Tools From a8c352854fc70717de54823f4ce185990be5b8f7 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Tue, 16 Apr 2024 20:20:34 -0400 Subject: [PATCH 023/129] Fixed Swagger UI broken since last libraries update (1.4.3-beta) --- bazarr/app/server.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/bazarr/app/server.py b/bazarr/app/server.py index 250fa6c0a..5caefbf67 100644 --- a/bazarr/app/server.py +++ b/bazarr/app/server.py @@ -18,10 +18,7 @@ from .database import close_database from .app import create_app app = create_app() -ui_bp.register_blueprint(api_bp, url_prefix='/api') -# Mute UserWarning with flask-restx and Flask >= 2.2.0. Will be raised as an exception in 2.3.0 -# https://github.com/python-restx/flask-restx/issues/485 -warnings.filterwarnings('ignore', message='The setup method ') +app.register_blueprint(api_bp, url_prefix=base_url.rstrip('/') + '/api') app.register_blueprint(ui_bp, url_prefix=base_url.rstrip('/')) From e4bc792ee0fba64a3a20d817bbf2c3e2ba66b737 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Tue, 16 Apr 2024 20:54:52 -0400 Subject: [PATCH 024/129] Fixed mass editor returning a 413 error with a large series/movies set. --- bazarr/app/app.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bazarr/app/app.py b/bazarr/app/app.py index 982cea34b..918d877e1 100644 --- a/bazarr/app/app.py +++ b/bazarr/app/app.py @@ -1,6 +1,6 @@ # coding=utf-8 -from flask import Flask, redirect +from flask import Flask, redirect, Request from flask_compress import Compress from flask_cors import CORS @@ -13,9 +13,17 @@ from .config import settings, base_url socketio = SocketIO() +class CustomRequest(Request): + def __init__(self, *args, **kwargs): + super(CustomRequest, self).__init__(*args, **kwargs) + # required to increase form-data size before returning a 413 + self.max_form_parts = 10000 + + def create_app(): # Flask Setup app = Flask(__name__) + app.request_class = CustomRequest app.config['COMPRESS_ALGORITHM'] = 'gzip' Compress(app) app.wsgi_app = ReverseProxied(app.wsgi_app) From b7be8007f27361fe05cd73b0537babd14b06a79f Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Wed, 17 Apr 2024 09:57:55 +0900 Subject: [PATCH 025/129] no log: add animetosho provider anidb integration required message (#2457) * chore: add animetosho provider anidb intergration required message * chore: cs --- frontend/src/pages/Settings/Providers/list.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/pages/Settings/Providers/list.ts b/frontend/src/pages/Settings/Providers/list.ts index eff9ea4e9..a3b870d22 100644 --- a/frontend/src/pages/Settings/Providers/list.ts +++ b/frontend/src/pages/Settings/Providers/list.ts @@ -77,6 +77,7 @@ export const ProviderList: Readonly = [ name: "Search Threshold. Increase if you often cannot find subtitles for your Anime. Note that increasing the value will decrease the performance of the search for each Episode.", }, ], + message: "Requires AniDB Integration.", }, { key: "argenteam_dump", From eff4568f7206c68e5b5b882d966185fc22938803 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Wed, 17 Apr 2024 11:54:07 +0900 Subject: [PATCH 026/129] no log: table overflow wrapper specific to upload modal (#2459) * fix: table overflow wrapper specific to upload modal * chore: apply prettier --- frontend/src/components/forms/MovieUploadForm.tsx | 12 +++++++++++- frontend/src/components/forms/SeriesUploadForm.tsx | 12 +++++++++++- frontend/src/components/tables/BaseTable.tsx | 1 - 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/forms/MovieUploadForm.tsx b/frontend/src/components/forms/MovieUploadForm.tsx index fbcb0b04f..b51614770 100644 --- a/frontend/src/components/forms/MovieUploadForm.tsx +++ b/frontend/src/components/forms/MovieUploadForm.tsx @@ -19,6 +19,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, Checkbox, + createStyles, Divider, MantineColor, Stack, @@ -78,12 +79,21 @@ interface Props { onComplete?: () => void; } +const useStyles = createStyles((theme) => { + return { + wrapper: { + overflowWrap: "anywhere", + }, + }; +}); + const MovieUploadForm: FunctionComponent = ({ files, movie, onComplete, }) => { const modals = useModals(); + const { classes } = useStyles(); const profile = useLanguageProfileBy(movie.profileId); @@ -279,7 +289,7 @@ const MovieUploadForm: FunctionComponent = ({ modals.closeSelf(); })} > - + diff --git a/frontend/src/components/forms/SeriesUploadForm.tsx b/frontend/src/components/forms/SeriesUploadForm.tsx index 784baf8a5..5ce9c821a 100644 --- a/frontend/src/components/forms/SeriesUploadForm.tsx +++ b/frontend/src/components/forms/SeriesUploadForm.tsx @@ -23,6 +23,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Button, Checkbox, + createStyles, Divider, MantineColor, Stack, @@ -85,12 +86,21 @@ interface Props { onComplete?: VoidFunction; } +const useStyles = createStyles((theme) => { + return { + wrapper: { + overflowWrap: "anywhere", + }, + }; +}); + const SeriesUploadForm: FunctionComponent = ({ series, files, onComplete, }) => { const modals = useModals(); + const { classes } = useStyles(); const episodes = useEpisodesBySeriesId(series.sonarrSeriesId); const episodeOptions = useSelectorOptions( episodes.data ?? [], @@ -358,7 +368,7 @@ const SeriesUploadForm: FunctionComponent = ({ modals.closeSelf(); })} > - + diff --git a/frontend/src/components/tables/BaseTable.tsx b/frontend/src/components/tables/BaseTable.tsx index e41a2d313..6ec49e61a 100644 --- a/frontend/src/components/tables/BaseTable.tsx +++ b/frontend/src/components/tables/BaseTable.tsx @@ -24,7 +24,6 @@ const useStyles = createStyles((theme) => { display: "block", maxWidth: "100%", overflowX: "auto", - overflowWrap: "anywhere", }, table: { borderCollapse: "collapse", From 0322f22a51f02dcd10e397f0804fbd54dae18898 Mon Sep 17 00:00:00 2001 From: Hlib Date: Thu, 18 Apr 2024 19:38:38 +0300 Subject: [PATCH 027/129] no log: add AvistaZ and CinemaZ to supported providers list in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0114bbff5..2520aa9f9 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ If you need something that is not already part of Bazarr, feel free to create a - Addic7ed - Animetosho (requires AniDb HTTP API client described [here](https://wiki.anidb.net/HTTP_API_Definition)) - Assrt +- AvistaZ, CinemaZ (Get session cookies using method described [here](https://github.com/morpheus65535/bazarr/pull/2375#issuecomment-2057010996)) - BetaSeries - BSplayer - Embedded Subtitles From a39d874d3b44d8c3a4f4f030fd4c2c9ac4c20cf5 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Thu, 18 Apr 2024 22:34:10 -0400 Subject: [PATCH 028/129] Fixed upgrade process to properly upgrade bazarr.py when it's updated. #2456 --- bazarr/app/check_update.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bazarr/app/check_update.py b/bazarr/app/check_update.py index 327294324..c5bfdeb68 100644 --- a/bazarr/app/check_update.py +++ b/bazarr/app/check_update.py @@ -160,8 +160,7 @@ def apply_update(): 'BAZARR was unable to delete the previous build directory during upgrade process.') for file in archive.namelist(): - if file.startswith(zip_root_directory) and file != zip_root_directory and not \ - file.endswith('bazarr.py'): + if file.startswith(zip_root_directory) and file != zip_root_directory: file_path = os.path.join(bazarr_dir, file[len(zip_root_directory):]) parent_dir = os.path.dirname(file_path) os.makedirs(parent_dir, exist_ok=True) From 73224866cbeb2e2f30277118b5af0f0d1d9daf8a Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Sat, 20 Apr 2024 03:18:36 +0900 Subject: [PATCH 029/129] Added additional languages to animetosho provider --- .../subliminal_patch/providers/animetosho.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/animetosho.py b/custom_libs/subliminal_patch/providers/animetosho.py index d9c078503..25b1ea95b 100644 --- a/custom_libs/subliminal_patch/providers/animetosho.py +++ b/custom_libs/subliminal_patch/providers/animetosho.py @@ -25,10 +25,20 @@ except ImportError: logger = logging.getLogger(__name__) -# TODO: Test and Support Other Languages supported_languages = [ + "ara", # Arabic "eng", # English + "fin", # Finnish + "fra", # French + "heb", # Hebrew "ita", # Italian + "jpn", # Japanese + "por", # Portuguese + "pol", # Polish + "spa", # Spanish + "swe", # Swedish + "tha", # Thai + "tur", # Turkish ] @@ -128,8 +138,15 @@ class AnimeToshoProvider(Provider, ProviderSubtitleArchiveMixin): for subtitle_file in subtitle_files: hex_id = format(subtitle_file['id'], '08x') + lang = Language.fromalpha3b(subtitle_file['info']['lang']) + + # For Portuguese and Portuguese Brazilian they both share the same code, the name is the only + # identifier AnimeTosho provides. + if lang.alpha3 == 'por' and subtitle_file['info']['name'].lower().find('brazil'): + lang = Language('por', 'BR') + subtitle = self.subtitle_class( - Language.fromalpha3b(subtitle_file['info']['lang']), + lang, storage_download_url + '{}/{}.xz'.format(hex_id, subtitle_file['id']), meta=file, ) From 6ecfc11012d416e3aa5ec6a3ca26649d481f6af0 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Sat, 20 Apr 2024 21:08:06 +0900 Subject: [PATCH 030/129] no log: add dependabot groups (#2461) --- .github/dependabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 507bf3eb4..a95c835a2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,6 +8,19 @@ updates: prefix: "[bot]" open-pull-requests-limit: 1 target-branch: "development" + groups: + fortawesome: + patterns: + - "@fortawesome*" + mantine: + patterns: + - "@mantine*" + react: + patterns: + - "react" + - "react-dom" + - "@types/react" + - "@types/react-dom" - package-ecosystem: 'github-actions' directory: '/' schedule: From abc4500443830c5b175a51790b0def38b1a719f7 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 20 Apr 2024 10:47:53 -0400 Subject: [PATCH 031/129] Additional fix for restart process. #2456 --- bazarr.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/bazarr.py b/bazarr.py index dedf1aea1..58284ca09 100644 --- a/bazarr.py +++ b/bazarr.py @@ -8,12 +8,14 @@ import sys import time from bazarr.app.get_args import args -from bazarr.literals import * +from bazarr.literals import EXIT_PYTHON_UPGRADE_NEEDED, EXIT_NORMAL, FILE_RESTART, FILE_STOP, ENV_RESTARTFILE, ENV_STOPFILE, EXIT_INTERRUPT + def exit_program(status_code): print(f'Bazarr exited with status code {status_code}.') raise SystemExit(status_code) + def check_python_version(): python_version = platform.python_version_tuple() minimum_py3_tuple = (3, 8, 0) @@ -52,9 +54,10 @@ check_python_version() dir_name = os.path.dirname(__file__) + def start_bazarr(): script = [get_python_path(), "-u", os.path.normcase(os.path.join(dir_name, 'bazarr', 'main.py'))] + sys.argv[1:] - ep = subprocess.Popen(script, stdout=None, stderr=None, stdin=subprocess.DEVNULL) + ep = subprocess.Popen(script, stdout=None, stderr=None, stdin=subprocess.DEVNULL, env=os.environ) print(f"Bazarr starting child process with PID {ep.pid}...") return ep @@ -81,21 +84,21 @@ def get_stop_status_code(input_file): def check_status(): global child_process - if os.path.exists(stopfile): - status_code = get_stop_status_code(stopfile) + if os.path.exists(stop_file): + status_code = get_stop_status_code(stop_file) try: print(f"Deleting stop file...") - os.remove(stopfile) + os.remove(stop_file) except Exception as e: print('Unable to delete stop file.') finally: terminate_child() exit_program(status_code) - if os.path.exists(restartfile): + if os.path.exists(restart_file): try: print(f"Deleting restart file...") - os.remove(restartfile) + os.remove(restart_file) except Exception: print('Unable to delete restart file.') finally: @@ -119,19 +122,19 @@ def interrupt_handler(signum, frame): if __name__ == '__main__': interrupted = False signal.signal(signal.SIGINT, interrupt_handler) - restartfile = os.path.join(args.config_dir, FILE_RESTART) - stopfile = os.path.join(args.config_dir, FILE_STOP) - os.environ[ENV_STOPFILE] = stopfile - os.environ[ENV_RESTARTFILE] = restartfile + restart_file = os.path.join(args.config_dir, FILE_RESTART) + stop_file = os.path.join(args.config_dir, FILE_STOP) + os.environ[ENV_STOPFILE] = stop_file + os.environ[ENV_RESTARTFILE] = restart_file # Cleanup leftover files try: - os.remove(restartfile) + os.remove(restart_file) except FileNotFoundError: pass try: - os.remove(stopfile) + os.remove(stop_file) except FileNotFoundError: pass From 03dc2109f3b5dcb8b4d93321c8df595ea6bce08d Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 20 Apr 2024 10:50:32 -0400 Subject: [PATCH 032/129] no log: pep8 fix --- bazarr.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/bazarr.py b/bazarr.py index 58284ca09..7e75272b0 100644 --- a/bazarr.py +++ b/bazarr.py @@ -60,7 +60,7 @@ def start_bazarr(): ep = subprocess.Popen(script, stdout=None, stderr=None, stdin=subprocess.DEVNULL, env=os.environ) print(f"Bazarr starting child process with PID {ep.pid}...") return ep - + def terminate_child(): print(f"Terminating child process with PID {child_process.pid}") @@ -69,7 +69,7 @@ def terminate_child(): def get_stop_status_code(input_file): try: - with open(input_file,'r') as file: + with open(input_file, 'r') as file: # read status code from file, if it exists line = file.readline() try: @@ -77,7 +77,7 @@ def get_stop_status_code(input_file): except (ValueError, TypeError): status_code = EXIT_NORMAL file.close() - except: + except Exception: status_code = EXIT_NORMAL return status_code @@ -87,9 +87,9 @@ def check_status(): if os.path.exists(stop_file): status_code = get_stop_status_code(stop_file) try: - print(f"Deleting stop file...") + print("Deleting stop file...") os.remove(stop_file) - except Exception as e: + except Exception: print('Unable to delete stop file.') finally: terminate_child() @@ -97,13 +97,13 @@ def check_status(): if os.path.exists(restart_file): try: - print(f"Deleting restart file...") + print("Deleting restart file...") os.remove(restart_file) except Exception: print('Unable to delete restart file.') finally: terminate_child() - print(f"Bazarr is restarting...") + print("Bazarr is restarting...") child_process = start_bazarr() @@ -116,7 +116,7 @@ def interrupt_handler(signum, frame): interrupted = True print('Handling keyboard interrupt...') else: - print(f"Stop doing that! I heard you the first time!") + print("Stop doing that! I heard you the first time!") if __name__ == '__main__': @@ -148,5 +148,5 @@ if __name__ == '__main__': time.sleep(5) except (KeyboardInterrupt, SystemExit, ChildProcessError): # this code should never be reached, if signal handling is working properly - print(f'Bazarr exited main script file via keyboard interrupt.') + print('Bazarr exited main script file via keyboard interrupt.') exit_program(EXIT_INTERRUPT) From 6dbe1433644b1bc35a631e2f3758cfe8ddcc3b0b Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 21 Apr 2024 21:31:16 -0400 Subject: [PATCH 033/129] Added minimal Python 3.12 compatibility. Not yet official support. --- bazarr/app/scheduler.py | 4 ++-- custom_libs/subliminal/core.py | 8 ++++---- custom_libs/subliminal_patch/providers/legendasdivx.py | 2 +- custom_libs/subscene_api/subscene.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bazarr/app/scheduler.py b/bazarr/app/scheduler.py index f0e73637f..f63076aa1 100644 --- a/bazarr/app/scheduler.py +++ b/bazarr/app/scheduler.py @@ -321,8 +321,8 @@ class Scheduler: self.aps_scheduler.modify_job(job.id, next_run_time=datetime.now(tz=self.timezone) + timedelta(seconds=randrange( - job.trigger.interval.total_seconds() * 0.75, - job.trigger.interval.total_seconds()))) + int(job.trigger.interval.total_seconds() * 0.75), + int(job.trigger.interval.total_seconds())))) def __no_task(self): for job in self.aps_scheduler.get_jobs(): diff --git a/custom_libs/subliminal/core.py b/custom_libs/subliminal/core.py index cf26f333a..142d50f22 100644 --- a/custom_libs/subliminal/core.py +++ b/custom_libs/subliminal/core.py @@ -591,7 +591,7 @@ def scan_videos(path, age=None, archives=True): def refine(video, episode_refiners=None, movie_refiners=None, **kwargs): - """Refine a video using :ref:`refiners`. + r"""Refine a video using :ref:`refiners`. .. note:: @@ -619,7 +619,7 @@ def refine(video, episode_refiners=None, movie_refiners=None, **kwargs): def list_subtitles(videos, languages, pool_class=ProviderPool, **kwargs): - """List subtitles. + r"""List subtitles. The `videos` must pass the `languages` check of :func:`check_video`. @@ -660,7 +660,7 @@ def list_subtitles(videos, languages, pool_class=ProviderPool, **kwargs): def download_subtitles(subtitles, pool_class=ProviderPool, **kwargs): - """Download :attr:`~subliminal.subtitle.Subtitle.content` of `subtitles`. + r"""Download :attr:`~subliminal.subtitle.Subtitle.content` of `subtitles`. :param subtitles: subtitles to download. :type subtitles: list of :class:`~subliminal.subtitle.Subtitle` @@ -677,7 +677,7 @@ def download_subtitles(subtitles, pool_class=ProviderPool, **kwargs): def download_best_subtitles(videos, languages, min_score=0, hearing_impaired=False, only_one=False, compute_score=None, pool_class=ProviderPool, **kwargs): - """List and download the best matching subtitles. + r"""List and download the best matching subtitles. The `videos` must pass the `languages` and `undefined` (`only_one`) checks of :func:`check_video`. diff --git a/custom_libs/subliminal_patch/providers/legendasdivx.py b/custom_libs/subliminal_patch/providers/legendasdivx.py index 46a52ffd1..612693bb9 100644 --- a/custom_libs/subliminal_patch/providers/legendasdivx.py +++ b/custom_libs/subliminal_patch/providers/legendasdivx.py @@ -324,7 +324,7 @@ class LegendasdivxProvider(Provider): # for series, if no results found, try again just with series and season (subtitle packs) if isinstance(video, Episode): logger.debug("Legendasdivx.pt :: trying again with just series and season on query.") - querytext = re.sub("(e|E)(\d{2})", "", querytext) + querytext = re.sub(r"(e|E)(\d{2})", "", querytext) # sleep for a 1 second before another request sleep(1) res = self.session.get(_searchurl.format(query=querytext), allow_redirects=False) diff --git a/custom_libs/subscene_api/subscene.py b/custom_libs/subscene_api/subscene.py index e91f6fdef..463e4e26d 100644 --- a/custom_libs/subscene_api/subscene.py +++ b/custom_libs/subscene_api/subscene.py @@ -64,7 +64,7 @@ class NewEndpoint(Exception): # utils def soup_for(url, data=None, session=None, user_agent=DEFAULT_USER_AGENT): - url = re.sub("\s", "+", url) + url = re.sub(r"\s", "+", url) if not session: r = Request(url, data=None, headers=dict(HEADERS, **{"User-Agent": user_agent})) html = urlopen(r).read().decode("utf-8") From a2fee0e1e416bb45574cac9841f96c2e2cfe0303 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Mon, 22 Apr 2024 11:11:32 +0900 Subject: [PATCH 034/129] Fixed Anidb refinement for not anime episodes. #2463 --- bazarr/subtitles/refiners/anidb.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bazarr/subtitles/refiners/anidb.py b/bazarr/subtitles/refiners/anidb.py index 2174e0f7c..15bc46c95 100644 --- a/bazarr/subtitles/refiners/anidb.py +++ b/bazarr/subtitles/refiners/anidb.py @@ -115,16 +115,16 @@ class AniDBClient(object): def refine_from_anidb(path, video): + if not isinstance(video, Episode) or not video.series_tvdb_id: + logging.debug(f'Video is not an Anime TV series, skipping refinement for {video}') + + return + if refined_providers.intersection(settings.general.enabled_providers) and video.series_anidb_id is None: refine_anidb_ids(video) def refine_anidb_ids(video): - if not isinstance(video, Episode) and not video.series_tvdb_id: - logging.debug(f'Video is not an Anime TV series, skipping refinement for {video}') - - return video - anidb_client = AniDBClient(settings.anidb.api_client, settings.anidb.api_client_ver) season = video.season if video.season else 0 From 369b2c73439d15c5e9c43526b239e36329c82329 Mon Sep 17 00:00:00 2001 From: Vitiko Date: Tue, 23 Apr 2024 17:20:36 -0400 Subject: [PATCH 035/129] Embedded Subtitles provider: handle FileNotFoundError --- .../subliminal_patch/providers/embeddedsubtitles.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/embeddedsubtitles.py b/custom_libs/subliminal_patch/providers/embeddedsubtitles.py index 7fbf1bef8..1c1a99f7d 100644 --- a/custom_libs/subliminal_patch/providers/embeddedsubtitles.py +++ b/custom_libs/subliminal_patch/providers/embeddedsubtitles.py @@ -208,8 +208,11 @@ class EmbeddedSubtitlesProvider(Provider): except Exception as error: logger.debug("'%s' raised running modifier", error) - with open(path, "rb") as sub: - subtitle.content = sub.read() + if os.path.exists(path): + with open(path, "rb") as sub: + subtitle.content = sub.read() + else: + logger.error("%s not found in filesystem", path) def _get_subtitle_path(self, subtitle: EmbeddedSubtitle): container = subtitle.container From fd190aad143a01a83e13dcf03b82bb34ddb8d2fc Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 24 Apr 2024 06:49:38 -0400 Subject: [PATCH 036/129] Fixed SyntaxWarning with Python 3.12. #2462 --- custom_libs/libfilebot/main.py | 4 ++-- custom_libs/subliminal/extensions.py | 2 +- custom_libs/subliminal/refiners/__init__.py | 2 +- custom_libs/subliminal/video.py | 4 ++-- custom_libs/subliminal_patch/core.py | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/custom_libs/libfilebot/main.py b/custom_libs/libfilebot/main.py index 3e1333e0b..e93822bb3 100644 --- a/custom_libs/libfilebot/main.py +++ b/custom_libs/libfilebot/main.py @@ -50,7 +50,7 @@ def default_xattr(fn): XATTR_MAP = { "default": ( default_xattr, - lambda result: re.search('(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)', + lambda result: re.search(r'(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)', result).group(2) ), # "darwin": ( @@ -60,7 +60,7 @@ XATTR_MAP = { # ), "darwin": ( lambda fn: ["filebot", "-script", "fn:xattr", fn], - lambda result: re.search('(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)', + lambda result: re.search(r'(?um)(net\.filebot\.filename(?=="|: )[=:" ]+|Attribute.+:\s)([^"\n\r\0]+)', result).group(2) ), "win32": ( diff --git a/custom_libs/subliminal/extensions.py b/custom_libs/subliminal/extensions.py index 327087a02..547abe98a 100644 --- a/custom_libs/subliminal/extensions.py +++ b/custom_libs/subliminal/extensions.py @@ -6,7 +6,7 @@ from stevedore import ExtensionManager class RegistrableExtensionManager(ExtensionManager): - """:class:~stevedore.extensions.ExtensionManager` with support for registration. + r""":class:~stevedore.extensions.ExtensionManager` with support for registration. It allows loading of internal extensions without setup and registering/unregistering additional extensions. diff --git a/custom_libs/subliminal/refiners/__init__.py b/custom_libs/subliminal/refiners/__init__.py index bbb8d3ef8..4f3e45418 100644 --- a/custom_libs/subliminal/refiners/__init__.py +++ b/custom_libs/subliminal/refiners/__init__.py @@ -1,4 +1,4 @@ -""" +r""" Refiners enrich a :class:`~subliminal.video.Video` object by adding information to it. A refiner is a simple function: diff --git a/custom_libs/subliminal/video.py b/custom_libs/subliminal/video.py index 12aeba5f5..2168d91a9 100644 --- a/custom_libs/subliminal/video.py +++ b/custom_libs/subliminal/video.py @@ -115,7 +115,7 @@ class Video(object): class Episode(Video): - """Episode :class:`Video`. + r"""Episode :class:`Video`. :param str series: series of the episode. :param int season: season number of the episode. @@ -202,7 +202,7 @@ class Episode(Video): class Movie(Video): - """Movie :class:`Video`. + r"""Movie :class:`Video`. :param str title: title of the movie. :param int year: year of the movie. diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index 5e861f348..db34b6d95 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -1057,7 +1057,7 @@ def list_supported_video_types(pool_class, **kwargs): def download_subtitles(subtitles, pool_class=ProviderPool, **kwargs): - """Download :attr:`~subliminal.subtitle.Subtitle.content` of `subtitles`. + r"""Download :attr:`~subliminal.subtitle.Subtitle.content` of `subtitles`. :param subtitles: subtitles to download. :type subtitles: list of :class:`~subliminal.subtitle.Subtitle` @@ -1074,7 +1074,7 @@ def download_subtitles(subtitles, pool_class=ProviderPool, **kwargs): def download_best_subtitles(videos, languages, min_score=0, hearing_impaired=False, only_one=False, compute_score=None, pool_class=ProviderPool, throttle_time=0, **kwargs): - """List and download the best matching subtitles. + r"""List and download the best matching subtitles. The `videos` must pass the `languages` and `undefined` (`only_one`) checks of :func:`check_video`. @@ -1245,7 +1245,7 @@ def save_subtitles(file_path, subtitles, single=False, directory=None, chmod=Non def refine(video, episode_refiners=None, movie_refiners=None, **kwargs): - """Refine a video using :ref:`refiners`. + r"""Refine a video using :ref:`refiners`. patch: add traceback logging From 5e0433834e16dfbc1c7184fd2116b2d7a79db631 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Thu, 25 Apr 2024 09:27:04 +0900 Subject: [PATCH 037/129] Fixed animetosho provider empty subtitle name. #2468 --- custom_libs/subliminal_patch/providers/animetosho.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/animetosho.py b/custom_libs/subliminal_patch/providers/animetosho.py index 25b1ea95b..f242c420f 100644 --- a/custom_libs/subliminal_patch/providers/animetosho.py +++ b/custom_libs/subliminal_patch/providers/animetosho.py @@ -141,8 +141,9 @@ class AnimeToshoProvider(Provider, ProviderSubtitleArchiveMixin): lang = Language.fromalpha3b(subtitle_file['info']['lang']) # For Portuguese and Portuguese Brazilian they both share the same code, the name is the only - # identifier AnimeTosho provides. - if lang.alpha3 == 'por' and subtitle_file['info']['name'].lower().find('brazil'): + # identifier AnimeTosho provides. Also, some subtitles does not have name, in this case it could + # be a false negative but there is nothing we can use to guarantee it is PT-BR, we rather skip it. + if lang.alpha3 == 'por' and subtitle_file['info'].get('name', '').lower().find('brazil'): lang = Language('por', 'BR') subtitle = self.subtitle_class( From 0bdfcd0eda004adc40d84f4fe6ada3ea1e41ffb2 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Thu, 25 Apr 2024 09:57:39 +0900 Subject: [PATCH 038/129] no log: Fix anidb enrichment return type (#2472) --- bazarr/subtitles/refiners/anidb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazarr/subtitles/refiners/anidb.py b/bazarr/subtitles/refiners/anidb.py index 15bc46c95..88ee55ecb 100644 --- a/bazarr/subtitles/refiners/anidb.py +++ b/bazarr/subtitles/refiners/anidb.py @@ -53,7 +53,7 @@ class AniDBClient(object): ] if not animes: - return None + return None, None # Sort the anime by offset in ascending order animes.sort(key=lambda a: a.episode_offset) From 86b889d3b6a2c36a97f2dbf83a984311b53048ca Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Thu, 25 Apr 2024 11:34:42 +0900 Subject: [PATCH 039/129] Improved cutoff options label. #2466 --- frontend/src/components/forms/ProfileEditForm.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/forms/ProfileEditForm.tsx b/frontend/src/components/forms/ProfileEditForm.tsx index 874f5b8a6..eecacd73e 100644 --- a/frontend/src/components/forms/ProfileEditForm.tsx +++ b/frontend/src/components/forms/ProfileEditForm.tsx @@ -82,7 +82,12 @@ const ProfileEditForm: FunctionComponent = ({ const itemCutoffOptions = useSelectorOptions( form.values.items, - (v) => v.language, + (v) => { + const suffix = + v.hi === "True" ? ":hi" : v.forced === "True" ? ":forced" : ""; + + return v.language + suffix; + }, (v) => String(v.id), ); From c5a5dc9ddf45ba6512825e667811a90665328f43 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 29 Apr 2024 16:06:34 -0400 Subject: [PATCH 040/129] no log: fixed tasks view when running in dev environment (--no-tasks). --- bazarr/app/scheduler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bazarr/app/scheduler.py b/bazarr/app/scheduler.py index f63076aa1..00b038c49 100644 --- a/bazarr/app/scheduler.py +++ b/bazarr/app/scheduler.py @@ -47,6 +47,10 @@ ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365 def a_long_time_from_now(job): + # job isn't scheduled at all + if job.next_run_time is None: + return True + # currently defined as more than a year from now delta = job.next_run_time - datetime.now(job.next_run_time.tzinfo) return delta.total_seconds() > ONE_YEAR_IN_SECONDS From 5749971d67b7fa7932a8c707f50732a22615a37f Mon Sep 17 00:00:00 2001 From: JayZed Date: Mon, 29 Apr 2024 22:11:47 -0400 Subject: [PATCH 041/129] Improved whisper provider to not throttle when unsupported audio language is encountered. #2474 As we have noted before, bad input data should be no reason to throttle a provider. In this case, if the input language was not supported by whisper, we were raising a ValueError that was never caught and causing an error in the whisper provider for which it was throttled. Instead, we are now detecting this case and logging an error message. However, given that the input language was not one of the 99 currently known to whisper, it's probably a mislabeled audio track. If the user desired output language is English, then we will tell whisper that the input audio is also English and ask it to transcribe it. Whisper does a very good job of transcribing almost anything to English, so it's worth a try. This should address the throttling in issue #2474. --- .../subliminal_patch/providers/whisperai.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/whisperai.py b/custom_libs/subliminal_patch/providers/whisperai.py index dfd733da3..d427f8ad2 100644 --- a/custom_libs/subliminal_patch/providers/whisperai.py +++ b/custom_libs/subliminal_patch/providers/whisperai.py @@ -169,7 +169,7 @@ def whisper_get_language_reverse(alpha3): lan = whisper_get_language(wl, whisper_languages[wl]) if lan.alpha3 == alpha3: return wl - raise ValueError + return None def language_from_alpha3(lang): name = Language(lang).name @@ -317,7 +317,7 @@ class WhisperAIProvider(Provider): if out == None: logger.info(f"Whisper cannot process {subtitle.video.original_path} because of missing/bad audio track") subtitle.content = None - return + return logger.debug(f'Audio stream length (in WAV format) is {len(out):,} bytes') @@ -326,11 +326,23 @@ class WhisperAIProvider(Provider): else: output_language = "eng" + input_language = whisper_get_language_reverse(subtitle.audio_language) + if input_language is None: + if output_language == "eng": + # guess that audio track is mislabelled English and let whisper try to transcribe it + input_language = "en" + subtitle.task = "transcribe" + logger.info(f"Whisper treating unsupported audio track language: '{subtitle.audio_language}' as English") + else: + logger.info(f"Whisper cannot process {subtitle.video.original_path} because of unsupported audio track language: '{subtitle.audio_language}'") + subtitle.content = None + return + logger.info(f'Starting WhisperAI {subtitle.task} to {language_from_alpha3(output_language)} for {subtitle.video.original_path}') startTime = time.time() r = self.session.post(f"{self.endpoint}/asr", - params={'task': subtitle.task, 'language': whisper_get_language_reverse(subtitle.audio_language), 'output': 'srt', 'encode': 'false'}, + params={'task': subtitle.task, 'language': input_language, 'output': 'srt', 'encode': 'false'}, files={'audio_file': out}, timeout=(self.response, self.timeout)) From 1c2538ef3c1ff0314489890cf773e983c99b8315 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 22:13:28 -0400 Subject: [PATCH 042/129] no log: Bump @testing-library/react from 14.3.0 to 15.0.5 in /frontend (#2478) Bumps [@testing-library/react](https://github.com/testing-library/react-testing-library) from 14.3.0 to 15.0.5. - [Release notes](https://github.com/testing-library/react-testing-library/releases) - [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/react-testing-library/compare/v14.3.0...v15.0.5) --- updated-dependencies: - dependency-name: "@testing-library/react" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 137 ++----------------------------------- frontend/package.json | 2 +- 2 files changed, 7 insertions(+), 132 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5587d61aa..37c79ec14 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -30,7 +30,7 @@ "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.3.0", + "@testing-library/react": "^15.0.5", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", @@ -3579,7 +3579,6 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.0.0.tgz", "integrity": "sha512-PmJPnogldqoVFf+EwbHvbBJ98MmqASV8kLrBYgsDNxQcFMeIS7JFL48sfyXvuMtgmWO/wMhh25odr+8VhDmn4g==", "dev": true, - "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -3659,51 +3658,23 @@ "dev": true }, "node_modules/@testing-library/react": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.0.tgz", - "integrity": "sha512-AYJGvNFMbCa5vt1UtDCa/dcaABrXq8gph6VN+cffIx0UeA0qiGqS+sT60+sb+Gjc8tGXdECWYQgaF0khf8b+Lg==", + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-15.0.5.tgz", + "integrity": "sha512-ttodVWYA2i2w4hRa6krKrmS1vKxAEkwDz34y+CwbcrbZUxFzUYN3a5xZyFKo+K6LBseCRCUkwcjATpaNn/UsIA==", "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", + "@testing-library/dom": "^10.0.0", "@types/react-dom": "^18.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" }, "peerDependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" } }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@testing-library/react/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, "node_modules/@testing-library/user-event": { "version": "14.5.2", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", @@ -5626,38 +5597,6 @@ "node": ">=6" } }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -5926,26 +5865,6 @@ "node": ">= 0.4" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-iterator-helpers": { "version": "1.0.18", "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", @@ -7306,22 +7225,6 @@ "loose-envify": "^1.0.0" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -8542,22 +8445,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -9875,18 +9762,6 @@ "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", "dev": true }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/string-natural-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index cdf441278..9ca6672c4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -34,7 +34,7 @@ "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.3.0", + "@testing-library/react": "^15.0.5", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", "@types/lodash": "^4.17.0", From 2782551c9bd5418e6fe0484a4002b4a350869917 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Tue, 30 Apr 2024 19:28:41 +0900 Subject: [PATCH 043/129] Fixed Animetosho provider error for tv shows * chore: Skip anime * wip --- custom_libs/subliminal_patch/providers/animetosho.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/providers/animetosho.py b/custom_libs/subliminal_patch/providers/animetosho.py index f242c420f..550e2bf4b 100644 --- a/custom_libs/subliminal_patch/providers/animetosho.py +++ b/custom_libs/subliminal_patch/providers/animetosho.py @@ -88,7 +88,9 @@ class AnimeToshoProvider(Provider, ProviderSubtitleArchiveMixin): def list_subtitles(self, video, languages): if not video.series_anidb_episode_id: - raise ProviderError("Video does not have an AnimeTosho Episode ID!") + logger.debug('Skipping video %r. It is not an anime or the anidb_episode_id could not be identified', video) + + return [] return [s for s in self._get_series(video.series_anidb_episode_id) if s.language in languages] From ad151ff1393656f71d9c9558d8d86c1d23a1429c Mon Sep 17 00:00:00 2001 From: Wim de With Date: Wed, 1 May 2024 12:13:55 +0200 Subject: [PATCH 044/129] Added timeout to update check API call --- bazarr/app/check_update.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazarr/app/check_update.py b/bazarr/app/check_update.py index c5bfdeb68..4a8f64868 100644 --- a/bazarr/app/check_update.py +++ b/bazarr/app/check_update.py @@ -25,7 +25,7 @@ def check_releases(): url_releases = 'https://api.github.com/repos/morpheus65535/Bazarr/releases?per_page=100' try: logging.debug(f'BAZARR getting releases from Github: {url_releases}') - r = requests.get(url_releases, allow_redirects=True) + r = requests.get(url_releases, allow_redirects=True, timeout=15) r.raise_for_status() except requests.exceptions.HTTPError: logging.exception("Error trying to get releases from Github. Http error.") From bea2f0b781ba4b1f5bd7dea2644854a141b3884d Mon Sep 17 00:00:00 2001 From: JayZed Date: Thu, 2 May 2024 06:32:03 -0400 Subject: [PATCH 045/129] Fixed embedded ASS subtitles writing encoding error For a couple of files, I had UnicodeEncodeErrors raised when writing out a file it had successfully read in. In my case, the output file was truncated to 1 KB. --- custom_libs/subliminal_patch/providers/embeddedsubtitles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/providers/embeddedsubtitles.py b/custom_libs/subliminal_patch/providers/embeddedsubtitles.py index 1c1a99f7d..002f439b7 100644 --- a/custom_libs/subliminal_patch/providers/embeddedsubtitles.py +++ b/custom_libs/subliminal_patch/providers/embeddedsubtitles.py @@ -382,7 +382,7 @@ def _clean_ass_subtitles(path, output_path): logger.debug("Cleaned lines: %d", abs(len(lines) - len(clean_lines))) - with open(output_path, "w") as f: + with open(output_path, "w", encoding="utf-8", errors="ignore") as f: f.writelines(clean_lines) logger.debug("Lines written to output path: %s", output_path) From 2c4ed03817c724463701bf313f7fcb7b48039f81 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Thu, 2 May 2024 22:05:41 -0400 Subject: [PATCH 046/129] Fixed HI subtitles identification when downloading and improved some constants. #2386 --- bazarr/app/signalr_client.py | 8 ++++---- bazarr/app/ui.py | 8 ++++---- bazarr/constants.py | 8 ++++---- bazarr/radarr/filesystem.py | 4 ++-- bazarr/radarr/info.py | 6 +++--- bazarr/radarr/notify.py | 4 ++-- bazarr/radarr/rootfolder.py | 4 ++-- bazarr/radarr/sync/utils.py | 10 +++++----- bazarr/sonarr/filesystem.py | 4 ++-- bazarr/sonarr/info.py | 6 +++--- bazarr/sonarr/notify.py | 4 ++-- bazarr/sonarr/rootfolder.py | 4 ++-- bazarr/sonarr/sync/utils.py | 14 +++++++------- bazarr/subtitles/indexer/utils.py | 8 ++++---- custom_libs/subliminal_patch/core.py | 4 ++++ 15 files changed, 50 insertions(+), 46 deletions(-) diff --git a/bazarr/app/signalr_client.py b/bazarr/app/signalr_client.py index b731e09e8..9f930e215 100644 --- a/bazarr/app/signalr_client.py +++ b/bazarr/app/signalr_client.py @@ -12,7 +12,7 @@ from signalrcore.hub_connection_builder import HubConnectionBuilder from collections import deque from time import sleep -from constants import headers +from constants import HEADERS from app.event_handler import event_stream from sonarr.sync.episodes import sync_episodes, sync_one_episode from sonarr.sync.series import update_series, update_one_series @@ -39,7 +39,7 @@ class SonarrSignalrClientLegacy: self.session = Session() self.session.timeout = 60 self.session.verify = False - self.session.headers = headers + self.session.headers = HEADERS self.connection = None self.connected = False @@ -162,7 +162,7 @@ class SonarrSignalrClient: .with_url(f"{url_sonarr()}/signalr/messages?access_token={self.apikey_sonarr}", options={ "verify_ssl": False, - "headers": headers + "headers": HEADERS }) \ .with_automatic_reconnect({ "type": "raw", @@ -229,7 +229,7 @@ class RadarrSignalrClient: .with_url(f"{url_radarr()}/signalr/messages?access_token={self.apikey_radarr}", options={ "verify_ssl": False, - "headers": headers + "headers": HEADERS }) \ .with_automatic_reconnect({ "type": "raw", diff --git a/bazarr/app/ui.py b/bazarr/app/ui.py index 540c26d68..df43f7b0c 100644 --- a/bazarr/app/ui.py +++ b/bazarr/app/ui.py @@ -9,7 +9,7 @@ from flask import (request, abort, render_template, Response, session, send_file from functools import wraps from urllib.parse import unquote -from constants import headers +from constants import HEADERS from literals import FILE_LOG from sonarr.info import url_api_sonarr from radarr.info import url_api_radarr @@ -118,7 +118,7 @@ def series_images(url): baseUrl = settings.sonarr.base_url url_image = f'{url_api_sonarr()}{url.lstrip(baseUrl)}?apikey={apikey}'.replace('poster-250', 'poster-500') try: - req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=headers) + req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=HEADERS) except Exception: return '', 404 else: @@ -132,7 +132,7 @@ def movies_images(url): baseUrl = settings.radarr.base_url url_image = f'{url_api_radarr()}{url.lstrip(baseUrl)}?apikey={apikey}' try: - req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=headers) + req = requests.get(url_image, stream=True, timeout=15, verify=False, headers=HEADERS) except Exception: return '', 404 else: @@ -173,7 +173,7 @@ def proxy(protocol, url): url = f'{protocol}://{unquote(url)}' params = request.args try: - result = requests.get(url, params, allow_redirects=False, verify=False, timeout=5, headers=headers) + result = requests.get(url, params, allow_redirects=False, verify=False, timeout=5, headers=HEADERS) except Exception as e: return dict(status=False, error=repr(e)) else: diff --git a/bazarr/constants.py b/bazarr/constants.py index 4f8af9614..f49c07cc0 100644 --- a/bazarr/constants.py +++ b/bazarr/constants.py @@ -4,10 +4,10 @@ import os import re # set Bazarr user-agent used to make requests -headers = {"User-Agent": os.environ["SZ_USER_AGENT"]} - -# hearing-impaired detection regex -hi_regex = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](? 1 * 1024 * 1024: + if os.path.getsize(subtitle_path) > MAXIMUM_SUBTITLE_SIZE: logging.debug(f"BAZARR subtitles file is too large to be text based. Skipping this file: " f"{subtitle_path}") continue @@ -119,7 +119,7 @@ def guess_external_subtitles(dest_folder, subtitles, media_type, previously_inde # check if file exist: if os.path.exists(subtitle_path) and os.path.splitext(subtitle_path)[1] in core.SUBTITLE_EXTENSIONS: # to improve performance, skip detection of files larger that 1M - if os.path.getsize(subtitle_path) > 1 * 1024 * 1024: + if os.path.getsize(subtitle_path) > MAXIMUM_SUBTITLE_SIZE: logging.debug(f"BAZARR subtitles file is too large to be text based. Skipping this file: " f"{subtitle_path}") continue @@ -136,6 +136,6 @@ def guess_external_subtitles(dest_folder, subtitles, media_type, previously_inde continue text = text.decode(encoding) - if bool(re.search(hi_regex, text)): + if bool(re.search(core.HI_REGEX, text)): subtitles[subtitle] = Language.rebuild(subtitles[subtitle], forced=False, hi=True) return subtitles diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index db34b6d95..cb21f4581 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -49,6 +49,8 @@ SUBTITLE_EXTENSIONS = ('.srt', '.sub', '.smi', '.txt', '.ssa', '.ass', '.mpl', ' _POOL_LIFETIME = datetime.timedelta(hours=12) +HI_REGEX = re.compile(r'[*¶♫♪].{3,}[*¶♫♪]|[\[\(\{].{3,}[\]\)\}](? Date: Thu, 2 May 2024 22:53:36 -0400 Subject: [PATCH 047/129] no log: pep8 fixes --- bazarr/api/system/logs.py | 1 - bazarr/app/config.py | 2 +- bazarr/app/logger.py | 25 ++++++++++++------------- bazarr/app/scheduler.py | 1 - bazarr/app/server.py | 4 ++-- bazarr/constants.py | 1 - bazarr/init.py | 3 ++- bazarr/main.py | 3 ++- bazarr/subtitles/sync.py | 6 +++--- bazarr/subtitles/tools/subsyncer.py | 4 ++-- bazarr/utilities/central.py | 15 +++++++++++---- 11 files changed, 35 insertions(+), 30 deletions(-) diff --git a/bazarr/api/system/logs.py b/bazarr/api/system/logs.py index 7293f44c2..23daa98b4 100644 --- a/bazarr/api/system/logs.py +++ b/bazarr/api/system/logs.py @@ -7,7 +7,6 @@ from flask_restx import Resource, Namespace, fields, marshal from app.config import settings from app.logger import empty_log -from app.get_args import args from utilities.central import get_log_file_path from ..utils import authenticate diff --git a/bazarr/app/config.py b/bazarr/app/config.py index 49ae4cf32..af3a7ae05 100644 --- a/bazarr/app/config.py +++ b/bazarr/app/config.py @@ -58,7 +58,7 @@ class Validator(OriginalValidator): def check_parser_binary(value): try: get_binary(value) - except BinaryNotFound as e: + except BinaryNotFound: raise ValidationError(f"Executable '{value}' not found in search path. Please install before making this selection.") return True diff --git a/bazarr/app/logger.py b/bazarr/app/logger.py index 8470944d5..a47acf3dc 100644 --- a/bazarr/app/logger.py +++ b/bazarr/app/logger.py @@ -11,7 +11,6 @@ from logging.handlers import TimedRotatingFileHandler from utilities.central import get_log_file_path from pytz_deprecation_shim import PytzUsageWarning -from .get_args import args from .config import settings @@ -62,18 +61,18 @@ class UnwantedWaitressMessageFilter(logging.Filter): if settings.general.debug: # no filtering in debug mode return True - - unwantedMessages = [ - "Exception while serving /api/socket.io/", - ['Session is disconnected', 'Session not found' ], - - "Exception while serving /api/socket.io/", - ["'Session is disconnected'", "'Session not found'" ], - - "Exception while serving /api/socket.io/", - ['"Session is disconnected"', '"Session not found"' ], - - "Exception when servicing %r", + + unwantedMessages = [ + "Exception while serving /api/socket.io/", + ['Session is disconnected', 'Session not found'], + + "Exception while serving /api/socket.io/", + ["'Session is disconnected'", "'Session not found'"], + + "Exception while serving /api/socket.io/", + ['"Session is disconnected"', '"Session not found"'], + + "Exception when servicing %r", [], ] diff --git a/bazarr/app/scheduler.py b/bazarr/app/scheduler.py index 00b038c49..39cbe88b3 100644 --- a/bazarr/app/scheduler.py +++ b/bazarr/app/scheduler.py @@ -10,7 +10,6 @@ from apscheduler.triggers.date import DateTrigger from apscheduler.events import EVENT_JOB_SUBMITTED, EVENT_JOB_EXECUTED, EVENT_JOB_ERROR from datetime import datetime, timedelta from calendar import day_name -from math import floor from random import randrange from tzlocal import get_localzone try: diff --git a/bazarr/app/server.py b/bazarr/app/server.py index 5caefbf67..1def54dab 100644 --- a/bazarr/app/server.py +++ b/bazarr/app/server.py @@ -87,9 +87,9 @@ class Server: pass def close_all(self): - print(f"Closing database...") + print("Closing database...") close_database() - print(f"Closing webserver...") + print("Closing webserver...") self.server.close() def shutdown(self, status=EXIT_NORMAL): diff --git a/bazarr/constants.py b/bazarr/constants.py index f49c07cc0..b96236625 100644 --- a/bazarr/constants.py +++ b/bazarr/constants.py @@ -1,7 +1,6 @@ # coding=utf-8 import os -import re # set Bazarr user-agent used to make requests HEADERS = {"User-Agent": os.environ["SZ_USER_AGENT"]} diff --git a/bazarr/init.py b/bazarr/init.py index 98b7d3129..6dded426c 100644 --- a/bazarr/init.py +++ b/bazarr/init.py @@ -19,7 +19,8 @@ from utilities.backup import restore_from_backup from app.database import init_db -from literals import * +from literals import (EXIT_CONFIG_CREATE_ERROR, ENV_BAZARR_ROOT_DIR, DIR_BACKUP, DIR_CACHE, DIR_CONFIG, DIR_DB, DIR_LOG, + DIR_RESTORE, EXIT_REQUIREMENTS_ERROR) from utilities.central import make_bazarr_dir, restart_bazarr, stop_bazarr # set start time global variable as epoch diff --git a/bazarr/main.py b/bazarr/main.py index 15af61e97..ab25f6ae5 100644 --- a/bazarr/main.py +++ b/bazarr/main.py @@ -1,7 +1,6 @@ # coding=utf-8 import os -import io from threading import Thread @@ -42,6 +41,8 @@ from languages.get_languages import load_language_in_db # noqa E402 from app.signalr_client import sonarr_signalr_client, radarr_signalr_client # noqa E402 from app.server import webserver, app # noqa E402 from app.announcements import get_announcements_to_file # noqa E402 +from utilities.central import stop_bazarr # noqa E402 +from literals import EXIT_NORMAL # noqa E402 if args.create_db_revision: create_db_revision(app) diff --git a/bazarr/subtitles/sync.py b/bazarr/subtitles/sync.py index dffc24fcb..f7a3a69c4 100644 --- a/bazarr/subtitles/sync.py +++ b/bazarr/subtitles/sync.py @@ -33,9 +33,9 @@ def sync_subtitles(video_path, srt_path, srt_lang, forced, percent_score, sonarr 'max_offset_seconds': str(settings.subsync.max_offset_seconds), 'no_fix_framerate': settings.subsync.no_fix_framerate, 'gss': settings.subsync.gss, - 'reference': None, # means choose automatically within video file - 'sonarr_series_id': sonarr_series_id, - 'sonarr_episode_id': sonarr_episode_id, + 'reference': None, # means choose automatically within video file + 'sonarr_series_id': sonarr_series_id, + 'sonarr_episode_id': sonarr_episode_id, 'radarr_id': radarr_id, } subsync.sync(**sync_kwargs) diff --git a/bazarr/subtitles/tools/subsyncer.py b/bazarr/subtitles/tools/subsyncer.py index 72364aa5e..a71424516 100644 --- a/bazarr/subtitles/tools/subsyncer.py +++ b/bazarr/subtitles/tools/subsyncer.py @@ -30,8 +30,8 @@ class SubSyncer: self.vad = 'subs_then_webrtc' self.log_dir_path = os.path.join(args.config_dir, 'log') - def sync(self, video_path, srt_path, srt_lang, - max_offset_seconds, no_fix_framerate, gss, reference=None, + def sync(self, video_path, srt_path, srt_lang, + max_offset_seconds, no_fix_framerate, gss, reference=None, sonarr_series_id=None, sonarr_episode_id=None, radarr_id=None): self.reference = video_path self.srtin = srt_path diff --git a/bazarr/utilities/central.py b/bazarr/utilities/central.py index 3a0ed8378..009c42e1d 100644 --- a/bazarr/utilities/central.py +++ b/bazarr/utilities/central.py @@ -6,30 +6,37 @@ import logging import os from pathlib import Path -from literals import * + +from literals import ENV_BAZARR_ROOT_DIR, DIR_LOG, ENV_STOPFILE, ENV_RESTARTFILE, EXIT_NORMAL, FILE_LOG + def get_bazarr_dir(sub_dir): path = os.path.join(os.environ[ENV_BAZARR_ROOT_DIR], sub_dir) return path + def make_bazarr_dir(sub_dir): path = get_bazarr_dir(sub_dir) if not os.path.exists(path): os.mkdir(path) + def get_log_file_path(): path = os.path.join(get_bazarr_dir(DIR_LOG), FILE_LOG) return path + def get_stop_file_path(): return os.environ[ENV_STOPFILE] - + + def get_restart_file_path(): return os.environ[ENV_RESTARTFILE] + def stop_bazarr(status_code=EXIT_NORMAL, exit_main=True): try: - with open(get_stop_file_path(),'w', encoding='UTF-8') as file: + with open(get_stop_file_path(), 'w', encoding='UTF-8') as file: # write out status code for final exit file.write(f'{status_code}\n') file.close() @@ -39,6 +46,7 @@ def stop_bazarr(status_code=EXIT_NORMAL, exit_main=True): if exit_main: raise SystemExit(status_code) + def restart_bazarr(): try: Path(get_restart_file_path()).touch() @@ -46,4 +54,3 @@ def restart_bazarr(): logging.error(f'BAZARR Cannot create restart file: {repr(e)}') logging.info('Bazarr is being restarted...') raise SystemExit(EXIT_NORMAL) - \ No newline at end of file From 970b0f9d4769d47e325fb01441abbc14996d7262 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Sun, 5 May 2024 02:19:36 +0900 Subject: [PATCH 048/129] Added animetosho release info --- custom_libs/subliminal_patch/providers/animetosho.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/providers/animetosho.py b/custom_libs/subliminal_patch/providers/animetosho.py index 550e2bf4b..1fb791e86 100644 --- a/custom_libs/subliminal_patch/providers/animetosho.py +++ b/custom_libs/subliminal_patch/providers/animetosho.py @@ -46,10 +46,11 @@ class AnimeToshoSubtitle(Subtitle): """AnimeTosho.org Subtitle.""" provider_name = 'animetosho' - def __init__(self, language, download_link, meta): + def __init__(self, language, download_link, meta, release_info): super(AnimeToshoSubtitle, self).__init__(language, page_link=download_link) self.meta = meta self.download_link = download_link + self.release_info = release_info @property def id(self): @@ -152,6 +153,7 @@ class AnimeToshoProvider(Provider, ProviderSubtitleArchiveMixin): lang, storage_download_url + '{}/{}.xz'.format(hex_id, subtitle_file['id']), meta=file, + release_info=entry.get('title'), ) logger.debug('Found subtitle %r', subtitle) From d886515f9cd2c3726e3db3ad15b8451f4d954f77 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 May 2024 19:06:38 -0400 Subject: [PATCH 049/129] no log: Bump recharts from 2.12.4 to 2.12.6 in /frontend (#2487) Bumps [recharts](https://github.com/recharts/recharts) from 2.12.4 to 2.12.6. - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/3.x/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v2.12.4...v2.12.6) --- updated-dependencies: - dependency-name: recharts dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 37c79ec14..af368c0e5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -54,7 +54,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.4", + "recharts": "^2.12.6", "sass": "^1.74.1", "typescript": "^5.4.4", "vite": "^5.2.8", @@ -9217,9 +9217,9 @@ } }, "node_modules/recharts": { - "version": "2.12.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.4.tgz", - "integrity": "sha512-dM4skmk4fDKEDjL9MNunxv6zcTxePGVEzRnLDXALRpfJ85JoQ0P0APJ/CoJlmnQI0gPjBlOkjzrwrfQrRST3KA==", + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.6.tgz", + "integrity": "sha512-D+7j9WI+D0NHauah3fKHuNNcRK8bOypPW7os1DERinogGBGaHI7i6tQKJ0aUF3JXyBZ63dyfKIW2WTOPJDxJ8w==", "dev": true, "dependencies": { "clsx": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index 9ca6672c4..a079d0d10 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -58,7 +58,7 @@ "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", "react-table": "^7.8.0", - "recharts": "^2.12.4", + "recharts": "^2.12.6", "sass": "^1.74.1", "typescript": "^5.4.4", "vite": "^5.2.8", From 5630c441b04478c38483d7f6c228a36b7cf91408 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 6 May 2024 23:42:02 -0400 Subject: [PATCH 050/129] Added a database migration to get past the issues with incomplete table_languages_profiles. ##2485 --- migrations/versions/452dd0f0b578_.py | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 migrations/versions/452dd0f0b578_.py diff --git a/migrations/versions/452dd0f0b578_.py b/migrations/versions/452dd0f0b578_.py new file mode 100644 index 000000000..13e623988 --- /dev/null +++ b/migrations/versions/452dd0f0b578_.py @@ -0,0 +1,46 @@ +"""empty message + +Revision ID: 452dd0f0b578 +Revises: 30f37e2e15e1 +Create Date: 2024-05-06 20:27:15.618027 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '452dd0f0b578' +down_revision = '30f37e2e15e1' +branch_labels = None +depends_on = None + +bind = op.get_context().bind +insp = sa.inspect(bind) + + +def column_exists(table_name, column_name): + columns = insp.get_columns(table_name) + return any(c["name"] == column_name for c in columns) + + +def upgrade(): + if column_exists('table_shows', 'alternativeTitle'): + with op.batch_alter_table('table_shows', schema=None) as batch_op: + batch_op.drop_column('alternativeTitle') + + if not column_exists('table_languages_profiles', 'originalFormat'): + with op.batch_alter_table('table_languages_profiles', schema=None) as batch_op: + batch_op.add_column(sa.Column('originalFormat', sa.Integer(), server_default='0')) + + if not column_exists('table_languages_profiles', 'mustContain'): + with op.batch_alter_table('table_languages_profiles', schema=None) as batch_op: + batch_op.add_column(sa.Column('mustContain', sa.Text(), server_default='[]')) + + if not column_exists('table_languages_profiles', 'mustNotContain'): + with op.batch_alter_table('table_languages_profiles', schema=None) as batch_op: + batch_op.add_column(sa.Column('mustNotContain', sa.Text(), server_default='[]')) + + +def downgrade(): + pass From 397310eff56a6be2093e8a719df00398c7ce703c Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Wed, 8 May 2024 09:32:17 +0900 Subject: [PATCH 051/129] no log: Fix husky installation (#2488) --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index a079d0d10..5d428ff92 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -77,7 +77,7 @@ "test:ui": "vitest --ui", "coverage": "vitest run --coverage", "format": "prettier -w .", - "prepare": "cd .. && husky install frontend/.husky" + "prepare": "cd .. && husky frontend/.husky" }, "browserslist": { "production": [ From 4815313ac6f36154e6e59b0ee3ca87c04a36bb7c Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Wed, 8 May 2024 22:29:31 -0400 Subject: [PATCH 052/129] Fixed db migrations dropping tables content because of ForeignKey constraints. #2489 --- migrations/env.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/migrations/env.py b/migrations/env.py index d706218a2..beddf9710 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -1,6 +1,7 @@ from flask import current_app from alembic import context +from sqlalchemy import text import logging @@ -95,8 +96,22 @@ def run_migrations_online(): ) with context.begin_transaction(): + bind = context.get_bind() + + if bind.engine.name == 'sqlite': + bind.execute(text("PRAGMA foreign_keys=OFF;")) + elif bind.engine.name == 'postgresql': + bind.execute(text("SET CONSTRAINTS ALL DEFERRED;")) + context.run_migrations() + if bind.engine.name == 'sqlite': + bind.execute(text("PRAGMA foreign_keys=ON;")) + elif bind.engine.name == 'postgresql': + bind.execute(text("SET CONSTRAINTS ALL IMMEDIATE;")) + + bind.close() + if context.is_offline_mode(): run_migrations_offline() From 6e3422524c852c6c4e443c3116c3011639c3f96b Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Thu, 9 May 2024 12:35:41 +0900 Subject: [PATCH 053/129] Removed dependency over moment * feat: remove moment dependency * refactor * add tests * small format * rename argument --- frontend/package-lock.json | 10 ---- frontend/package.json | 1 - frontend/src/pages/System/Status/index.tsx | 30 +++++++---- frontend/src/utilities/time.test.ts | 60 ++++++++++++++++++++++ frontend/src/utilities/time.ts | 29 +++++++++++ 5 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 frontend/src/utilities/time.test.ts create mode 100644 frontend/src/utilities/time.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index af368c0e5..42c99c60c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -49,7 +49,6 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", - "moment": "^2.30.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", @@ -8325,15 +8324,6 @@ "ufo": "^1.3.2" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5d428ff92..df5b6918a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,7 +53,6 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", - "moment": "^2.30.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", diff --git a/frontend/src/pages/System/Status/index.tsx b/frontend/src/pages/System/Status/index.tsx index 80757c4c7..49c88ccd4 100644 --- a/frontend/src/pages/System/Status/index.tsx +++ b/frontend/src/pages/System/Status/index.tsx @@ -20,7 +20,6 @@ import { Text, } from "@mantine/core"; import { useDocumentTitle } from "@mantine/hooks"; -import moment from "moment"; import { FunctionComponent, PropsWithChildren, @@ -28,6 +27,13 @@ import { useCallback, useState, } from "react"; +import { + divisorDay, + divisorHour, + divisorMinute, + divisorSecond, + formatTime, +} from "@/utilities/time"; import Table from "./table"; interface InfoProps { @@ -98,15 +104,19 @@ const SystemStatusView: FunctionComponent = () => { const update = useCallback(() => { const startTime = status?.start_time; if (startTime) { - const duration = moment.duration( - moment().utc().unix() - startTime, - "seconds", - ), - days = duration.days(), - hours = duration.hours().toString().padStart(2, "0"), - minutes = duration.minutes().toString().padStart(2, "0"), - seconds = duration.seconds().toString().padStart(2, "0"); - setUptime(days + "d " + hours + ":" + minutes + ":" + seconds); + // Current time in seconds + const currentTime = Math.floor(Date.now() / 1000); + + const uptimeInSeconds = currentTime - startTime; + + const uptime: string = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + setUptime(uptime); } }, [status?.start_time]); diff --git a/frontend/src/utilities/time.test.ts b/frontend/src/utilities/time.test.ts new file mode 100644 index 000000000..a0e936a25 --- /dev/null +++ b/frontend/src/utilities/time.test.ts @@ -0,0 +1,60 @@ +import { + divisorDay, + divisorHour, + divisorMinute, + divisorSecond, + formatTime, +} from "./time"; + +describe("formatTime", () => { + it("should format day hour minute and second", () => { + const uptimeInSeconds = 3661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("0d 01:01:01"); + }); + + it("should format multiple digits of days", () => { + const uptimeInSeconds = 50203661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("581d 25:27:41"); + }); + + it("should format time day hour minute", () => { + const uptimeInSeconds = 3661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + ]); + + expect(formattedTime).toBe("0d 01:01"); + }); + + it("should format zero uptime", () => { + const uptimeInSeconds = 0; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("0d 00:00:00"); + }); +}); diff --git a/frontend/src/utilities/time.ts b/frontend/src/utilities/time.ts new file mode 100644 index 000000000..54f93289f --- /dev/null +++ b/frontend/src/utilities/time.ts @@ -0,0 +1,29 @@ +interface TimeFormat { + unit: string; + divisor: number; +} + +export const divisorDay = 24 * 60 * 60; +export const divisorHour = 60 * 60; +export const divisorMinute = 60; +export const divisorSecond = 1; + +export const formatTime = ( + timeInSeconds: number, + formats: TimeFormat[], +): string => + formats.reduce( + (formattedTime: string, { unit, divisor }: TimeFormat, index: number) => { + const timeValue: number = + index === 0 + ? Math.floor(timeInSeconds / divisor) + : Math.floor(timeInSeconds / divisor) % 60; + return ( + formattedTime + + (index === 0 + ? `${timeValue}${unit} ` + : `${timeValue.toString().padStart(2, "0")}${index < formats.length - 1 ? ":" : ""}`) + ); + }, + "", + ); From 5b5beadf4d49954eb4ac95659ab470915002647d Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Thu, 9 May 2024 12:35:41 +0900 Subject: [PATCH 054/129] Removed dependency over moment library * feat: remove moment dependency * refactor * add tests * small format * rename argument --- frontend/package-lock.json | 10 ---- frontend/package.json | 1 - frontend/src/pages/System/Status/index.tsx | 30 +++++++---- frontend/src/utilities/time.test.ts | 60 ++++++++++++++++++++++ frontend/src/utilities/time.ts | 29 +++++++++++ 5 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 frontend/src/utilities/time.test.ts create mode 100644 frontend/src/utilities/time.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index af368c0e5..42c99c60c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -49,7 +49,6 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", - "moment": "^2.30.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", @@ -8325,15 +8324,6 @@ "ufo": "^1.3.2" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 5d428ff92..df5b6918a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -53,7 +53,6 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", - "moment": "^2.30.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", diff --git a/frontend/src/pages/System/Status/index.tsx b/frontend/src/pages/System/Status/index.tsx index 80757c4c7..49c88ccd4 100644 --- a/frontend/src/pages/System/Status/index.tsx +++ b/frontend/src/pages/System/Status/index.tsx @@ -20,7 +20,6 @@ import { Text, } from "@mantine/core"; import { useDocumentTitle } from "@mantine/hooks"; -import moment from "moment"; import { FunctionComponent, PropsWithChildren, @@ -28,6 +27,13 @@ import { useCallback, useState, } from "react"; +import { + divisorDay, + divisorHour, + divisorMinute, + divisorSecond, + formatTime, +} from "@/utilities/time"; import Table from "./table"; interface InfoProps { @@ -98,15 +104,19 @@ const SystemStatusView: FunctionComponent = () => { const update = useCallback(() => { const startTime = status?.start_time; if (startTime) { - const duration = moment.duration( - moment().utc().unix() - startTime, - "seconds", - ), - days = duration.days(), - hours = duration.hours().toString().padStart(2, "0"), - minutes = duration.minutes().toString().padStart(2, "0"), - seconds = duration.seconds().toString().padStart(2, "0"); - setUptime(days + "d " + hours + ":" + minutes + ":" + seconds); + // Current time in seconds + const currentTime = Math.floor(Date.now() / 1000); + + const uptimeInSeconds = currentTime - startTime; + + const uptime: string = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + setUptime(uptime); } }, [status?.start_time]); diff --git a/frontend/src/utilities/time.test.ts b/frontend/src/utilities/time.test.ts new file mode 100644 index 000000000..a0e936a25 --- /dev/null +++ b/frontend/src/utilities/time.test.ts @@ -0,0 +1,60 @@ +import { + divisorDay, + divisorHour, + divisorMinute, + divisorSecond, + formatTime, +} from "./time"; + +describe("formatTime", () => { + it("should format day hour minute and second", () => { + const uptimeInSeconds = 3661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("0d 01:01:01"); + }); + + it("should format multiple digits of days", () => { + const uptimeInSeconds = 50203661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("581d 25:27:41"); + }); + + it("should format time day hour minute", () => { + const uptimeInSeconds = 3661; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + ]); + + expect(formattedTime).toBe("0d 01:01"); + }); + + it("should format zero uptime", () => { + const uptimeInSeconds = 0; + + const formattedTime = formatTime(uptimeInSeconds, [ + { unit: "d", divisor: divisorDay }, + { unit: "h", divisor: divisorHour }, + { unit: "m", divisor: divisorMinute }, + { unit: "s", divisor: divisorSecond }, + ]); + + expect(formattedTime).toBe("0d 00:00:00"); + }); +}); diff --git a/frontend/src/utilities/time.ts b/frontend/src/utilities/time.ts new file mode 100644 index 000000000..54f93289f --- /dev/null +++ b/frontend/src/utilities/time.ts @@ -0,0 +1,29 @@ +interface TimeFormat { + unit: string; + divisor: number; +} + +export const divisorDay = 24 * 60 * 60; +export const divisorHour = 60 * 60; +export const divisorMinute = 60; +export const divisorSecond = 1; + +export const formatTime = ( + timeInSeconds: number, + formats: TimeFormat[], +): string => + formats.reduce( + (formattedTime: string, { unit, divisor }: TimeFormat, index: number) => { + const timeValue: number = + index === 0 + ? Math.floor(timeInSeconds / divisor) + : Math.floor(timeInSeconds / divisor) % 60; + return ( + formattedTime + + (index === 0 + ? `${timeValue}${unit} ` + : `${timeValue.toString().padStart(2, "0")}${index < formats.length - 1 ? ":" : ""}`) + ); + }, + "", + ); From bb4b01f3fb1f23ce7a5bf4fc5e2fbf89d302da16 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Thu, 9 May 2024 15:19:31 -0400 Subject: [PATCH 055/129] Removed closed subscene provider --- bazarr/app/config.py | 13 - bazarr/app/get_providers.py | 7 +- bazarr/subtitles/manual.py | 16 +- bazarr/subtitles/utils.py | 1 - custom_libs/custom_version.txt | 1 - .../subliminal_patch/converters/subscene.py | 92 ---- .../subliminal_patch/providers/subscene.py | 366 ---------------- .../providers/subscene_cloudscraper.py | 410 ------------------ custom_libs/subscene_api/subscene.py | 299 ------------- 9 files changed, 2 insertions(+), 1203 deletions(-) delete mode 100644 custom_libs/subliminal_patch/converters/subscene.py delete mode 100644 custom_libs/subliminal_patch/providers/subscene.py delete mode 100644 custom_libs/subliminal_patch/providers/subscene_cloudscraper.py delete mode 100644 custom_libs/subscene_api/subscene.py diff --git a/bazarr/app/config.py b/bazarr/app/config.py index af3a7ae05..2af614909 100644 --- a/bazarr/app/config.py +++ b/bazarr/app/config.py @@ -293,10 +293,6 @@ validators = [ Validator('napisy24.username', must_exist=True, default='', is_type_of=str, cast=str), Validator('napisy24.password', must_exist=True, default='', is_type_of=str, cast=str), - # subscene section - Validator('subscene.username', must_exist=True, default='', is_type_of=str, cast=str), - Validator('subscene.password', must_exist=True, default='', is_type_of=str, cast=str), - # betaseries section Validator('betaseries.token', must_exist=True, default='', is_type_of=str, cast=str), @@ -686,15 +682,6 @@ def save_settings(settings_items): reset_providers = True region.delete('oscom_token') - if key == 'settings-subscene-username': - if key != settings.subscene.username: - reset_providers = True - region.delete('subscene_cookies2') - elif key == 'settings-subscene-password': - if key != settings.subscene.password: - reset_providers = True - region.delete('subscene_cookies2') - if key == 'settings-titlovi-username': if key != settings.titlovi.username: reset_providers = True diff --git a/bazarr/app/get_providers.py b/bazarr/app/get_providers.py index 7df6b8742..d7a61326f 100644 --- a/bazarr/app/get_providers.py +++ b/bazarr/app/get_providers.py @@ -125,7 +125,7 @@ def provider_throttle_map(): PROVIDERS_FORCED_OFF = ["addic7ed", "tvsubtitles", "legendasdivx", "napiprojekt", "shooter", - "hosszupuska", "supersubtitles", "titlovi", "assrt", "subscene"] + "hosszupuska", "supersubtitles", "titlovi", "assrt"] throttle_count = {} @@ -259,11 +259,6 @@ def get_providers_auth(): 'also_foreign': False, # fixme 'verify_ssl': settings.podnapisi.verify_ssl }, - 'subscene': { - 'username': settings.subscene.username, - 'password': settings.subscene.password, - 'only_foreign': False, # fixme - }, 'legendasdivx': { 'username': settings.legendasdivx.username, 'password': settings.legendasdivx.password, diff --git a/bazarr/subtitles/manual.py b/bazarr/subtitles/manual.py index 3b0a6ad3e..ba57eb193 100644 --- a/bazarr/subtitles/manual.py +++ b/bazarr/subtitles/manual.py @@ -18,7 +18,7 @@ from app.config import get_scores, settings, get_array_from from utilities.helper import get_target_folder, force_unicode from app.database import get_profiles_list -from .pool import update_pools, _get_pool, _init_pool +from .pool import update_pools, _get_pool from .utils import get_video, _get_lang_obj, _get_scores, _set_forced_providers from .processing import process_subtitle @@ -46,21 +46,7 @@ def manual_search(path, profile_id, providers, sceneName, title, media_type): try: if providers: subtitles = list_all_subtitles([video], language_set, pool) - - if 'subscene' in providers: - s_pool = _init_pool("movie", profile_id, {"subscene"}) - - subscene_language_set = set() - for language in language_set: - if language.forced: - subscene_language_set.add(language) - if len(subscene_language_set): - s_pool.provider_configs.update({"subscene": {"only_foreign": True}}) - subtitles_subscene = list_all_subtitles([video], subscene_language_set, s_pool) - s_pool.provider_configs.update({"subscene": {"only_foreign": False}}) - subtitles[video] += subtitles_subscene[video] else: - subtitles = [] logging.info("BAZARR All providers are throttled") return 'All providers are throttled' except Exception: diff --git a/bazarr/subtitles/utils.py b/bazarr/subtitles/utils.py index 4fa0a8d27..436bc7b52 100644 --- a/bazarr/subtitles/utils.py +++ b/bazarr/subtitles/utils.py @@ -97,7 +97,6 @@ def _set_forced_providers(pool, also_forced=False, forced_required=False): pool.provider_configs.update( { "podnapisi": {'also_foreign': also_forced, "only_foreign": forced_required}, - "subscene": {"only_foreign": forced_required}, "opensubtitles": {'also_foreign': also_forced, "only_foreign": forced_required} } ) diff --git a/custom_libs/custom_version.txt b/custom_libs/custom_version.txt index 687b8e37a..52f3519d0 100644 --- a/custom_libs/custom_version.txt +++ b/custom_libs/custom_version.txt @@ -15,5 +15,4 @@ deathbycaptcha # unknown version, only found on gist git+https://github.com/pannal/libfilebot#egg=libfilebot git+https://github.com/RobinDavid/pyADS.git@28a2f6dbfb357f85b2c2f49add770b336e88840d#egg=pyads py7zr==0.7.0 # modified to prevent importing of modules that can't be vendored -subscene-api==1.0.0 # modified specificaly for Bazarr subliminal==2.1.0 # modified specifically for Bazarr diff --git a/custom_libs/subliminal_patch/converters/subscene.py b/custom_libs/subliminal_patch/converters/subscene.py deleted file mode 100644 index 1d1727c2e..000000000 --- a/custom_libs/subliminal_patch/converters/subscene.py +++ /dev/null @@ -1,92 +0,0 @@ -# coding=utf-8 - -from __future__ import absolute_import -from babelfish import LanguageReverseConverter -from subliminal.exceptions import ConfigurationError -from subzero.language import Language - - -# alpha3 codes extracted from `https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes` -# Subscene language list extracted from it's upload form -from_subscene = { - 'Farsi/Persian': 'fas', 'Greek': 'ell', 'Greenlandic': 'kal', - 'Malay': 'msa', 'Pashto': 'pus', 'Punjabi': 'pan', 'Swahili': 'swa' -} - -from_subscene_with_country = { - 'Brazillian Portuguese': ('por', 'BR') -} - -to_subscene_with_country = {val: key for key, val in from_subscene_with_country.items()} - - -to_subscene = {v: k for k, v in from_subscene.items()} - -exact_languages_alpha3 = [ - 'ara', 'aze', 'bel', 'ben', 'bos', 'bul', 'cat', 'ces', 'dan', 'deu', - 'eng', 'epo', 'est', 'eus', 'fin', 'fra', 'heb', 'hin', 'hrv', 'hun', - 'hye', 'ind', 'isl', 'ita', 'jpn', 'kat', 'kor', 'kur', 'lav', 'lit', - 'mal', 'mkd', 'mni', 'mon', 'mya', 'nld', 'nor', 'pol', 'por', 'ron', - 'rus', 'sin', 'slk', 'slv', 'som', 'spa', 'sqi', 'srp', 'sun', 'swe', - 'tam', 'tel', 'tgl', 'tha', 'tur', 'ukr', 'urd', 'vie', 'yor' -] - -language_ids = { - 'ara': 2, 'dan': 10, 'nld': 11, 'eng': 13, 'fas': 46, 'fin': 17, - 'fra': 18, 'heb': 22, 'ind': 44, 'ita': 26, 'msa': 50, 'nor': 30, - 'ron': 33, 'spa': 38, 'swe': 39, 'vie': 45, 'sqi': 1, 'hye': 73, - 'aze': 55, 'eus': 74, 'bel': 68, 'ben': 54, 'bos': 60, 'bul': 5, - 'mya': 61, 'cat': 49, 'hrv': 8, 'ces': 9, 'epo': 47, 'est': 16, - 'kat': 62, 'deu': 19, 'ell': 21, 'kal': 57, 'hin': 51, 'hun': 23, - 'isl': 25, 'jpn': 27, 'kor': 28, 'kur': 52, 'lav': 29, 'lit': 43, - 'mkd': 48, 'mal': 64, 'mni': 65, 'mon': 72, 'pus': 67, 'pol': 31, - 'por': 32, 'pan': 66, 'rus': 34, 'srp': 35, 'sin': 58, 'slk': 36, - 'slv': 37, 'som': 70, 'tgl': 53, 'tam': 59, 'tel': 63, 'tha': 40, - 'tur': 41, 'ukr': 56, 'urd': 42, 'yor': 71, 'pt-BR': 4 -} - -# TODO: specify codes for unspecified_languages -unspecified_languages = [ - 'Big 5 code', 'Bulgarian/ English', - 'Chinese BG code', 'Dutch/ English', 'English/ German', - 'Hungarian/ English', 'Rohingya' -] - -supported_languages = {Language(l) for l in exact_languages_alpha3} - -alpha3_of_code = {l.name: l.alpha3 for l in supported_languages} - -supported_languages.update({Language(l) for l in to_subscene}) - -supported_languages.update({Language(lang, cr) for lang, cr in to_subscene_with_country}) - - -class SubsceneConverter(LanguageReverseConverter): - codes = {l.name for l in supported_languages} - - def convert(self, alpha3, country=None, script=None): - if alpha3 in exact_languages_alpha3: - return Language(alpha3).name - - if alpha3 in to_subscene: - return to_subscene[alpha3] - - if (alpha3, country) in to_subscene_with_country: - return to_subscene_with_country[(alpha3, country)] - - raise ConfigurationError('Unsupported language for subscene: %s, %s, %s' % (alpha3, country, script)) - - def reverse(self, code): - if code in from_subscene_with_country: - return from_subscene_with_country[code] - - if code in from_subscene: - return (from_subscene[code],) - - if code in alpha3_of_code: - return (alpha3_of_code[code],) - - if code in unspecified_languages: - raise NotImplementedError("currently this language is unspecified: %s" % code) - - raise ConfigurationError('Unsupported language code for subscene: %s' % code) \ No newline at end of file diff --git a/custom_libs/subliminal_patch/providers/subscene.py b/custom_libs/subliminal_patch/providers/subscene.py deleted file mode 100644 index e11ab0b02..000000000 --- a/custom_libs/subliminal_patch/providers/subscene.py +++ /dev/null @@ -1,366 +0,0 @@ -# coding=utf-8 - -import io -import logging -import os -import time -import traceback -from urllib import parse - -import requests - -import inflect -import re -import json - -import html - -import zipfile -import rarfile -from babelfish import language_converters -from guessit import guessit -from dogpile.cache.api import NO_VALUE -from requests.exceptions import RequestException -from subliminal import Episode, ProviderError -from subliminal.video import Episode, Movie -from subliminal.exceptions import ConfigurationError, ServiceUnavailable -from subliminal.utils import sanitize_release_group -from subliminal.cache import region -from subliminal_patch.http import RetryingCFSession -from subliminal_patch.providers import Provider, reinitialize_on_error -from subliminal_patch.providers.mixins import ProviderSubtitleArchiveMixin -from subliminal_patch.subtitle import Subtitle, guess_matches -from subliminal_patch.converters.subscene import language_ids, supported_languages -from subscene_api.subscene import search, SearchTypes, Subtitle as APISubtitle, SITE_DOMAIN -from subzero.language import Language - -p = inflect.engine() - -language_converters.register('subscene = subliminal_patch.converters.subscene:SubsceneConverter') -logger = logging.getLogger(__name__) - - -class SubsceneSubtitle(Subtitle): - provider_name = 'subscene' - hearing_impaired_verifiable = True - is_pack = False - page_link = None - season = None - episode = None - releases = None - - def __init__(self, language, release_info, hearing_impaired=False, page_link=None, encoding=None, mods=None, - asked_for_release_group=None, asked_for_episode=None): - super(SubsceneSubtitle, self).__init__(language, hearing_impaired=hearing_impaired, page_link=page_link, - encoding=encoding, mods=mods) - self.release_info = self.releases = release_info - self.asked_for_episode = asked_for_episode - self.asked_for_release_group = asked_for_release_group - self.season = None - self.episode = None - - @classmethod - def from_api(cls, s): - return cls(Language.fromsubscene(s.language.strip()), s.title, hearing_impaired=s.hearing_impaired, - page_link=s.url) - - @property - def id(self): - return self.page_link - - @property - def numeric_id(self): - return self.page_link.split("/")[-1] - - def get_matches(self, video): - matches = set() - - if self.release_info.strip() == get_video_filename(video): - logger.debug("Using hash match as the release name is the same") - matches |= {"hash"} - - # episode - if isinstance(video, Episode): - guess = guessit(self.release_info, {'type': 'episode'}) - self.season = guess.get("season") - self.episode = guess.get("episode") - - matches |= guess_matches(video, guess) - if "season" in matches and "episode" not in guess: - # pack - matches.add("episode") - logger.debug("%r is a pack", self) - self.is_pack = True - - if "title" in guess and "year" in matches: - if video.series in guess['title']: - matches.add("series") - - # movie - else: - guess = guessit(self.release_info, {'type': 'movie'}) - matches |= guess_matches(video, guess) - - if video.release_group and "release_group" not in matches and "release_group" in guess: - if sanitize_release_group(video.release_group) in sanitize_release_group(guess["release_group"]): - matches.add("release_group") - - self.matches = matches - - return matches - - def get_download_link(self, session): - return APISubtitle.get_zipped_url(self.page_link, session) - - -def get_video_filename(video): - return os.path.splitext(os.path.basename(video.original_name))[0] - - -class SubsceneProvider(Provider, ProviderSubtitleArchiveMixin): - """ - This currently only searches for the filename on SubScene. It doesn't open every found subtitle page to avoid - massive hammering, thus it can't determine whether a subtitle is only-foreign or not. - """ - subtitle_class = SubsceneSubtitle - languages = supported_languages - languages.update(set(Language.rebuild(l, forced=True) for l in languages)) - languages.update(set(Language.rebuild(l, hi=True) for l in languages)) - video_types = (Episode, Movie) - session = None - skip_wrong_fps = False - hearing_impaired_verifiable = True - only_foreign = False - username = None - password = None - - search_throttle = 8 # seconds - - def __init__(self, only_foreign=False, username=None, password=None): - if not all((username, password)): - raise ConfigurationError('Username and password must be specified') - - self.only_foreign = only_foreign - self.username = username - self.password = password - - def initialize(self): - logger.info("Creating session") - self.session = RetryingCFSession() - - prev_cookies = region.get("subscene_cookies2") - if prev_cookies != NO_VALUE: - logger.debug("Re-using old subscene cookies: %r", prev_cookies) - self.session.cookies.update(prev_cookies) - - else: - logger.debug("Logging in") - self.login() - - def login(self): - r = self.session.get("https://subscene.com/account/login") - if "Server Error" in r.text: - logger.error("Login unavailable; Maintenance?") - raise ServiceUnavailable("Login unavailable; Maintenance?") - - match = re.search(r"", r.text) - - if match: - h = html - data = json.loads(h.unescape(match.group(1))) - login_url = parse.urljoin(data["siteUrl"], data["loginUrl"]) - time.sleep(1.0) - - r = self.session.post(login_url, - { - "username": self.username, - "password": self.password, - data["antiForgery"]["name"]: data["antiForgery"]["value"] - }) - pep_content = re.search(r"
" - r".+name=\"id_token\".+?value=\"(?P.+?)\".*?" - r"access_token\".+?value=\"(?P.+?)\".+?" - r"token_type.+?value=\"(?P.+?)\".+?" - r"expires_in.+?value=\"(?P.+?)\".+?" - r"scope.+?value=\"(?P.+?)\".+?" - r"state.+?value=\"(?P.+?)\".+?" - r"session_state.+?value=\"(?P.+?)\"", - r.text, re.MULTILINE | re.DOTALL) - - if pep_content: - r = self.session.post(SITE_DOMAIN, pep_content.groupdict()) - try: - r.raise_for_status() - except Exception: - raise ProviderError("Something went wrong when trying to log in: %s", traceback.format_exc()) - else: - cj = self.session.cookies.copy() - store_cks = ("scene", "idsrv", "idsrv.xsrf", "idsvr.clients", "idsvr.session", "idsvr.username") - for cn in self.session.cookies.keys(): - if cn not in store_cks: - del cj[cn] - - logger.debug("Storing cookies: %r", cj) - region.set("subscene_cookies2", cj) - return - raise ProviderError("Something went wrong when trying to log in #1") - - def terminate(self): - logger.info("Closing session") - self.session.close() - - def _create_filters(self, languages): - self.filters = dict(HearingImpaired="2") - acc_filters = self.filters.copy() - if self.only_foreign: - self.filters["ForeignOnly"] = "True" - acc_filters["ForeignOnly"] = self.filters["ForeignOnly"].lower() - logger.info("Only searching for foreign/forced subtitles") - - selected_ids = [] - for l in languages: - lid = language_ids.get(l.basename, language_ids.get(l.alpha3, None)) - if lid: - selected_ids.append(str(lid)) - - acc_filters["SelectedIds"] = selected_ids - self.filters["LanguageFilter"] = ",".join(acc_filters["SelectedIds"]) - - last_filters = region.get("subscene_filters") - if last_filters != acc_filters: - region.set("subscene_filters", acc_filters) - logger.debug("Setting account filters to %r", acc_filters) - self.session.post("https://u.subscene.com/filter", acc_filters, allow_redirects=False) - - logger.debug("Filter created: '%s'" % self.filters) - - def _enable_filters(self): - self.session.cookies.update(self.filters) - logger.debug("Filters applied") - - def list_subtitles(self, video, languages): - if not video.original_name: - logger.info("Skipping search because we don't know the original release name") - return [] - - self._create_filters(languages) - self._enable_filters() - - if isinstance(video, Episode): - international_titles = list(set([video.series] + video.alternative_series[:1])) - subtitles = [s for s in self.query(video, international_titles) if s.language in languages] - if not len(subtitles): - us_titles = [x + ' (US)' for x in international_titles] - subtitles = [s for s in self.query(video, us_titles) if s.language in languages] - return subtitles - else: - titles = list(set([video.title] + video.alternative_titles[:1])) - return [s for s in self.query(video, titles) if s.language in languages] - - def download_subtitle(self, subtitle): - if subtitle.pack_data: - logger.info("Using previously downloaded pack data") - if rarfile.is_rarfile(io.BytesIO(subtitle.pack_data)): - logger.debug('Identified rar archive') - archive = rarfile.RarFile(io.BytesIO(subtitle.pack_data)) - elif zipfile.is_zipfile(io.BytesIO(subtitle.pack_data)): - logger.debug('Identified zip archive') - archive = zipfile.ZipFile(io.BytesIO(subtitle.pack_data)) - else: - logger.error('Unsupported compressed format') - return - subtitle.pack_data = None - - try: - subtitle.content = self.get_subtitle_from_archive(subtitle, archive) - return - except ProviderError: - pass - - # open the archive - r = self.session.get(subtitle.get_download_link(self.session), timeout=10) - r.raise_for_status() - archive_stream = io.BytesIO(r.content) - - if rarfile.is_rarfile(archive_stream): - logger.debug('Identified rar archive') - archive = rarfile.RarFile(archive_stream) - elif zipfile.is_zipfile(archive_stream): - logger.debug('Identified zip archive') - archive = zipfile.ZipFile(archive_stream) - else: - logger.error('Unsupported compressed format') - return - - subtitle.content = self.get_subtitle_from_archive(subtitle, archive) - - # store archive as pack_data for later caching - subtitle.pack_data = r.content - - def parse_results(self, video, film): - subtitles = [] - for s in film.subtitles: - try: - subtitle = SubsceneSubtitle.from_api(s) - except NotImplementedError as e: - logger.info(e) - continue - subtitle.asked_for_release_group = video.release_group - if isinstance(video, Episode): - subtitle.asked_for_episode = video.episode - - if self.only_foreign: - subtitle.language = Language.rebuild(subtitle.language, forced=True) - - # set subtitle language to hi if it's hearing_impaired - if subtitle.hearing_impaired: - subtitle.language = Language.rebuild(subtitle.language, hi=True) - - subtitles.append(subtitle) - logger.debug('Found subtitle %r', subtitle) - - return subtitles - - def do_search(self, *args, **kwargs): - try: - return search(*args, **kwargs) - except requests.HTTPError: - region.delete("subscene_cookies2") - raise - - @reinitialize_on_error((RequestException,), attempts=1) - def query(self, video, titles): - subtitles = [] - if isinstance(video, Episode): - more_than_one = len(titles) > 1 - for series in titles: - term = u"%s - %s Season" % (series, p.number_to_words("%sth" % video.season).capitalize()) - logger.debug('Searching with series and season: %s', term) - film = self.do_search(term, session=self.session, release=False, throttle=self.search_throttle, - limit_to=SearchTypes.TvSerie) - if not film and video.season == 1: - logger.debug('Searching with series name: %s', series) - film = self.do_search(series, session=self.session, release=False, throttle=self.search_throttle, - limit_to=SearchTypes.TvSerie) - - if film and film.subtitles: - logger.debug('Searching found: %s', len(film.subtitles)) - subtitles += self.parse_results(video, film) - else: - logger.debug('No results found') - - if more_than_one: - time.sleep(self.search_throttle) - else: - more_than_one = len(titles) > 1 - for title in titles: - logger.debug('Searching for movie results: %r', title) - film = self.do_search(title, year=video.year, session=self.session, limit_to=None, release=False, - throttle=self.search_throttle) - if film and film.subtitles: - subtitles += self.parse_results(video, film) - if more_than_one: - time.sleep(self.search_throttle) - - logger.info("%s subtitles found" % len(subtitles)) - return subtitles diff --git a/custom_libs/subliminal_patch/providers/subscene_cloudscraper.py b/custom_libs/subliminal_patch/providers/subscene_cloudscraper.py deleted file mode 100644 index f9eead046..000000000 --- a/custom_libs/subliminal_patch/providers/subscene_cloudscraper.py +++ /dev/null @@ -1,410 +0,0 @@ -# -*- coding: utf-8 -*- - -from difflib import SequenceMatcher -import functools -import logging -import re -import time -import urllib.parse - -from bs4 import BeautifulSoup as bso -import cloudscraper -from guessit import guessit -from requests import Session -from requests.exceptions import HTTPError -from subliminal.exceptions import ProviderError -from subliminal_patch.core import Episode -from subliminal_patch.core import Movie -from subliminal_patch.exceptions import APIThrottled -from subliminal_patch.providers import Provider -from subliminal_patch.providers.utils import get_archive_from_bytes -from subliminal_patch.providers.utils import get_subtitle_from_archive -from subliminal_patch.providers.utils import update_matches -from subliminal_patch.subtitle import Subtitle -from subzero.language import Language - -logger = logging.getLogger(__name__) - - -class SubsceneSubtitle(Subtitle): - provider_name = "subscene_cloudscraper" - hash_verifiable = False - - def __init__(self, language, page_link, release_info, episode_number=None): - super().__init__(language, page_link=page_link) - - self.release_info = release_info - self.episode_number = episode_number - self.episode_title = None - - self._matches = set( - ("title", "year") - if episode_number is None - else ("title", "series", "year", "season", "episode") - ) - - def get_matches(self, video): - update_matches(self._matches, video, self.release_info) - - return self._matches - - @property - def id(self): - return self.page_link - - -_BASE_URL = "https://subscene.com" - -# TODO: add more seasons and languages - -_SEASONS = ( - "First", - "Second", - "Third", - "Fourth", - "Fifth", - "Sixth", - "Seventh", - "Eighth", - "Ninth", - "Tenth", - "Eleventh", - "Twelfth", - "Thirdteenth", - "Fourthteenth", - "Fifteenth", - "Sixteenth", - "Seventeenth", - "Eightheenth", - "Nineteenth", - "Tweentieth", -) - -_LANGUAGE_MAP = { - "english": "eng", - "farsi_persian": "per", - "arabic": "ara", - "spanish": "spa", - "portuguese": "por", - "italian": "ita", - "dutch": "dut", - "hebrew": "heb", - "indonesian": "ind", - "danish": "dan", - "norwegian": "nor", - "bengali": "ben", - "bulgarian": "bul", - "croatian": "hrv", - "swedish": "swe", - "vietnamese": "vie", - "czech": "cze", - "finnish": "fin", - "french": "fre", - "german": "ger", - "greek": "gre", - "hungarian": "hun", - "icelandic": "ice", - "japanese": "jpn", - "macedonian": "mac", - "malay": "may", - "polish": "pol", - "romanian": "rum", - "russian": "rus", - "serbian": "srp", - "thai": "tha", - "turkish": "tur", -} - - -class SubsceneProvider(Provider): - provider_name = "subscene_cloudscraper" - - _movie_title_regex = re.compile(r"^(.+?)( \((\d{4})\))?$") - _tv_show_title_regex = re.compile( - r"^(.+?) [-\(]\s?(.*?) (season|series)\)?( \((\d{4})\))?$" - ) - _supported_languages = {} - _supported_languages["brazillian-portuguese"] = Language("por", "BR") - - for key, val in _LANGUAGE_MAP.items(): - _supported_languages[key] = Language.fromalpha3b(val) - - _supported_languages_reversed = { - val: key for key, val in _supported_languages.items() - } - - languages = set(_supported_languages.values()) - - video_types = (Episode, Movie) - subtitle_class = SubsceneSubtitle - - def initialize(self): - pass - - def terminate(self): - pass - - def _scraper_call(self, url, retry=7, method="GET", sleep=5, **kwargs): - last_exc = None - - for n in range(retry): - # Creating an instance for every try in order to avoid dropped connections. - - # This could probably be improved! - scraper = cloudscraper.create_scraper() - if method == "GET": - req = scraper.get(url, **kwargs) - elif method == "POST": - req = scraper.post(url, **kwargs) - else: - raise NotImplementedError(f"{method} not allowed") - - try: - req.raise_for_status() - except HTTPError as error: - logger.debug( - "'%s' returned. Trying again [%d] in %s", error, n + 1, sleep - ) - last_exc = error - time.sleep(sleep) - else: - return req - - raise ProviderError("403 Retry count exceeded") from last_exc - - def _gen_results(self, query): - url = ( - f"{_BASE_URL}/subtitles/searchbytitle?query={urllib.parse.quote(query)}&l=" - ) - - result = self._scraper_call(url, method="POST") - soup = bso(result.content, "html.parser") - - for title in soup.select("li div[class='title'] a"): - yield title - - def _search_movie(self, title, year): - title = title.lower() - year = str(year) - - found_movie = None - - results = [] - for result in self._gen_results(title): - text = result.text.lower() - match = self._movie_title_regex.match(text) - if not match: - continue - match_title = match.group(1) - match_year = match.group(3) - if year == match_year: - results.append( - { - "href": result.get("href"), - "similarity": SequenceMatcher(None, title, match_title).ratio(), - } - ) - - if results: - results.sort(key=lambda x: x["similarity"], reverse=True) - found_movie = results[0]["href"] - logger.debug("Movie found: %s", results[0]) - return found_movie - - def _search_tv_show_season(self, title, season, year=None): - try: - season_str = _SEASONS[season - 1].lower() - except IndexError: - logger.debug("Season number not supported: %s", season) - return None - - found_tv_show_season = None - - results = [] - for result in self._gen_results(title): - text = result.text.lower() - - match = self._tv_show_title_regex.match(text) - if not match: - logger.debug("Series title not matched: %s", text) - continue - else: - logger.debug("Series title matched: %s", text) - - match_title = match.group(1) - match_season = match.group(2) - - # Match "complete series" titles as they usually contain season packs - if season_str == match_season or "complete" in match_season: - plus = 0.1 if year and str(year) in text else 0 - results.append( - { - "href": result.get("href"), - "similarity": SequenceMatcher(None, title, match_title).ratio() - + plus, - } - ) - - if results: - results.sort(key=lambda x: x["similarity"], reverse=True) - found_tv_show_season = results[0]["href"] - logger.debug("TV Show season found: %s", results[0]) - - return found_tv_show_season - - def _find_movie_subtitles(self, path, language): - soup = self._get_subtitle_page_soup(path, language) - - subtitles = [] - for item in soup.select("tr"): - subtitle = _get_subtitle_from_item(item, language) - if subtitle is None: - continue - - logger.debug("Found subtitle: %s", subtitle) - subtitles.append(subtitle) - - return subtitles - - def _find_episode_subtitles( - self, path, season, episode, language, episode_title=None - ): - soup = self._get_subtitle_page_soup(path, language) - - subtitles = [] - - for item in soup.select("tr"): - valid_item = None - clean_text = " ".join(item.text.split()) - - if not clean_text: - continue - - # It will return list values - guess = _memoized_episode_guess(clean_text) - - if "season" not in guess: - if "complete series" in clean_text.lower(): - logger.debug("Complete series pack found: %s", clean_text) - guess["season"] = [season] - else: - logger.debug("Nothing guessed from release: %s", clean_text) - continue - - if season in guess["season"] and episode in guess.get("episode", []): - logger.debug("Episode match found: %s - %s", guess, clean_text) - valid_item = item - - elif season in guess["season"] and not "episode" in guess: - logger.debug("Season pack found: %s", clean_text) - valid_item = item - - if valid_item is None: - continue - - subtitle = _get_subtitle_from_item(item, language, episode) - - if subtitle is None: - continue - - subtitle.episode_title = episode_title - - logger.debug("Found subtitle: %s", subtitle) - subtitles.append(subtitle) - - return subtitles - - def _get_subtitle_page_soup(self, path, language): - language_path = self._supported_languages_reversed[language] - result = self._scraper_call(f"{_BASE_URL}{path}/{language_path}") - return bso(result.content, "html.parser") - - def list_subtitles(self, video, languages): - is_episode = isinstance(video, Episode) - - if is_episode: - result = self._search_tv_show_season(video.series, video.season, video.year) - else: - result = self._search_movie(video.title, video.year) - - if result is None: - logger.debug("No results") - return [] - - subtitles = [] - - for language in languages: - if is_episode: - subtitles.extend( - self._find_episode_subtitles( - result, video.season, video.episode, language, video.title - ) - ) - else: - subtitles.extend(self._find_movie_subtitles(result, language)) - - return subtitles - - def download_subtitle(self, subtitle): - # TODO: add MustGetBlacklisted support - - result = self._scraper_call(subtitle.page_link) - soup = bso(result.content, "html.parser") - try: - download_url = _BASE_URL + str( - soup.select_one("a[id='downloadButton']")["href"] # type: ignore - ) - except (AttributeError, KeyError, TypeError): - raise APIThrottled(f"Couldn't get download url from {subtitle.page_link}") - - downloaded = self._scraper_call(download_url) - archive = get_archive_from_bytes(downloaded.content) - - if archive is None: - raise APIThrottled(f"Invalid archive: {subtitle.page_link}") - - subtitle.content = get_subtitle_from_archive( - archive, - episode=subtitle.episode_number, - episode_title=subtitle.episode_title, - ) - - -@functools.lru_cache(2048) -def _memoized_episode_guess(content): - # Use include to save time from unnecessary checks - return guessit( - content, - { - "type": "episode", - # Add codec keys to avoid matching x264, 5.1, etc as episode info - "includes": ["season", "episode", "video_codec", "audio_codec"], - "enforce_list": True, - }, - ) - - -def _get_subtitle_from_item(item, language, episode_number=None): - release_infos = [] - - try: - release_infos.append(item.find("td", {"class": "a6"}).text.strip()) - except (AttributeError, KeyError): - pass - - try: - release_infos.append( - item.find("td", {"class": "a1"}).find_all("span")[-1].text.strip() - ) - except (AttributeError, KeyError): - pass - - release_info = "".join(r_info for r_info in release_infos if r_info) - - try: - path = item.find("td", {"class": "a1"}).find("a")["href"] - except (AttributeError, KeyError): - logger.debug("Couldn't get path: %s", item) - return None - - return SubsceneSubtitle(language, _BASE_URL + path, release_info, episode_number) diff --git a/custom_libs/subscene_api/subscene.py b/custom_libs/subscene_api/subscene.py deleted file mode 100644 index 463e4e26d..000000000 --- a/custom_libs/subscene_api/subscene.py +++ /dev/null @@ -1,299 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: fenc=utf-8 ts=4 et sw=4 sts=4 - -# This file is part of Subscene-API. -# -# Subscene-API is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Subscene-API is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Python wrapper for Subscene subtitle database. - -since Subscene doesn't provide an official API, I wrote -this script that does the job by parsing the website"s pages. -""" - -# imports -import re -import enum -import sys -import requests -import time -import logging - -is_PY2 = sys.version_info[0] < 3 -if is_PY2: - from contextlib2 import suppress - from urllib2 import Request, urlopen -else: - from contextlib import suppress - from urllib.request import Request, urlopen - -from dogpile.cache.api import NO_VALUE -from subliminal.cache import region -from bs4 import BeautifulSoup, NavigableString - - -logger = logging.getLogger(__name__) - -# constants -HEADERS = { -} -SITE_DOMAIN = "https://subscene.com" - -DEFAULT_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWeb"\ - "Kit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36" - - -ENDPOINT_RE = re.compile(r'(?uis).*? Date: Thu, 9 May 2024 15:27:48 -0400 Subject: [PATCH 056/129] no log: removing leftover subscene remnants --- frontend/src/pages/Settings/Providers/list.ts | 15 --------------- frontend/src/types/settings.d.ts | 3 --- 2 files changed, 18 deletions(-) diff --git a/frontend/src/pages/Settings/Providers/list.ts b/frontend/src/pages/Settings/Providers/list.ts index a3b870d22..bb3f4e5a5 100644 --- a/frontend/src/pages/Settings/Providers/list.ts +++ b/frontend/src/pages/Settings/Providers/list.ts @@ -374,7 +374,6 @@ export const ProviderList: Readonly = [ { key: "subf2m", name: "subf2m.co", - description: "Subscene Alternative Provider", inputs: [ { type: "switch", @@ -406,20 +405,6 @@ export const ProviderList: Readonly = [ description: "Greek Subtitles Provider.\nRequires anti-captcha provider to solve captchas for each download.", }, - { - key: "subscene", - inputs: [ - { - type: "text", - key: "username", - }, - { - type: "password", - key: "password", - }, - ], - description: "Broken, may not work for some. Use subf2m instead.", - }, { key: "subscenter", description: "Hebrew Subtitles Provider" }, { key: "subsunacs", diff --git a/frontend/src/types/settings.d.ts b/frontend/src/types/settings.d.ts index d46a9734e..9ae6d8454 100644 --- a/frontend/src/types/settings.d.ts +++ b/frontend/src/types/settings.d.ts @@ -20,7 +20,6 @@ interface Settings { xsubs: Settings.XSubs; assrt: Settings.Assrt; napisy24: Settings.Napisy24; - subscene: Settings.Subscene; betaseries: Settings.Betaseries; titlovi: Settings.Titlovi; ktuvit: Settings.Ktuvit; @@ -211,8 +210,6 @@ declare namespace Settings { interface Napisy24 extends BaseProvider {} - interface Subscene extends BaseProvider {} - interface Titlovi extends BaseProvider {} interface Ktuvit { From 47011f429a57a8b214681ce6527b4f49eae0cd90 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Fri, 10 May 2024 06:36:04 -0400 Subject: [PATCH 057/129] Fixed issue with subsunacs provider comparing None with int. --- custom_libs/subliminal_patch/providers/subsunacs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/providers/subsunacs.py b/custom_libs/subliminal_patch/providers/subsunacs.py index df969bcc8..366a42546 100644 --- a/custom_libs/subliminal_patch/providers/subsunacs.py +++ b/custom_libs/subliminal_patch/providers/subsunacs.py @@ -108,7 +108,7 @@ class SubsUnacsSubtitle(Subtitle): guess_filename = guessit(self.filename, video.hints) matches |= guess_matches(video, guess_filename) - if isinstance(video, Movie) and (self.num_cds > 1 or 'cd' in guess_filename): + if isinstance(video, Movie) and ((isinstance(self.num_cds, int) and self.num_cds > 1) or 'cd' in guess_filename): # reduce score of subtitles for multi-disc movie releases return set() From 006ee0f63ac39dc1e73c761a161aacfc6d62b380 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Fri, 10 May 2024 06:46:50 -0400 Subject: [PATCH 058/129] Fixed issue with subssabbz provider comparing None with int. --- custom_libs/subliminal_patch/providers/subssabbz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/providers/subssabbz.py b/custom_libs/subliminal_patch/providers/subssabbz.py index 18c9ffbef..f0386e889 100644 --- a/custom_libs/subliminal_patch/providers/subssabbz.py +++ b/custom_libs/subliminal_patch/providers/subssabbz.py @@ -110,7 +110,7 @@ class SubsSabBzSubtitle(Subtitle): guess_filename = guessit(self.filename, video.hints) matches |= guess_matches(video, guess_filename) - if isinstance(video, Movie) and (self.num_cds > 1 or 'cd' in guess_filename): + if isinstance(video, Movie) and ((isinstance(self.num_cds, int) and self.num_cds > 1) or 'cd' in guess_filename): # reduce score of subtitles for multi-disc movie releases return set() From 86d34039a35387e33663f14b30a65cc1165b4fc7 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 11 May 2024 23:22:55 -0400 Subject: [PATCH 059/129] Updated apprise to 1.8.0 --- libs/apprise-1.7.6.dist-info/RECORD | 183 ------------------ libs/apprise-1.7.6.dist-info/REQUESTED | 0 .../INSTALLER | 0 .../LICENSE | 0 .../METADATA | 2 +- libs/apprise-1.8.0.dist-info/RECORD | 183 ++++++++++++++++++ .../REQUESTED | 0 .../WHEEL | 0 .../entry_points.txt | 0 .../top_level.txt | 0 libs/apprise/Apprise.py | 14 +- libs/apprise/__init__.py | 23 +-- ...iseAttachment.py => apprise_attachment.py} | 6 +- ...eAttachment.pyi => apprise_attachment.pyi} | 0 .../{AppriseConfig.py => apprise_config.py} | 4 +- .../{AppriseConfig.pyi => apprise_config.pyi} | 0 libs/apprise/{AppriseAsset.py => asset.py} | 2 +- libs/apprise/{AppriseAsset.pyi => asset.pyi} | 0 libs/apprise/attachment/__init__.py | 5 +- .../attachment/{AttachBase.py => base.py} | 9 +- .../attachment/{AttachBase.pyi => base.pyi} | 0 .../attachment/{AttachFile.py => file.py} | 4 +- .../attachment/{AttachHTTP.py => http.py} | 12 +- libs/apprise/config/__init__.py | 5 +- .../apprise/config/{ConfigBase.py => base.py} | 8 +- .../config/{ConfigBase.pyi => base.pyi} | 0 .../apprise/config/{ConfigFile.py => file.py} | 4 +- .../apprise/config/{ConfigHTTP.py => http.py} | 6 +- .../config/{ConfigMemory.py => memory.py} | 4 +- libs/apprise/conversion.py | 2 +- .../{CustomNotifyPlugin.py => base.py} | 7 +- libs/apprise/decorators/notify.py | 2 +- libs/apprise/i18n/en/LC_MESSAGES/apprise.mo | Bin 3959 -> 3959 bytes libs/apprise/{AppriseLocale.py => locale.py} | 0 libs/apprise/manager.py | 83 ++++---- ...chmentManager.py => manager_attachment.py} | 5 + ...figurationManager.py => manager_config.py} | 5 + ...ificationManager.py => manager_plugins.py} | 6 + libs/apprise/plugins/__init__.py | 8 +- .../{NotifyAppriseAPI.py => apprise_api.py} | 8 +- .../plugins/{NotifyAprs.py => aprs.py} | 6 +- .../plugins/{NotifyBark.py => bark.py} | 6 +- .../plugins/{NotifyBase.py => base.py} | 6 +- .../plugins/{NotifyBase.pyi => base.pyi} | 0 .../plugins/{NotifyBoxcar.py => boxcar.py} | 6 +- .../plugins/{NotifyBulkSMS.py => bulksms.py} | 6 +- .../plugins/{NotifyBulkVS.py => bulkvs.py} | 6 +- .../{NotifyBurstSMS.py => burstsms.py} | 6 +- .../{NotifyChantify.py => chantify.py} | 4 +- .../{NotifyClickSend.py => clicksend.py} | 27 ++- .../plugins/{NotifyForm.py => custom_form.py} | 6 +- .../plugins/{NotifyJSON.py => custom_json.py} | 6 +- .../plugins/{NotifyXML.py => custom_xml.py} | 6 +- .../{NotifyD7Networks.py => d7networks.py} | 4 +- .../plugins/{NotifyDapnet.py => dapnet.py} | 6 +- .../plugins/{NotifyDBus.py => dbus.py} | 4 +- .../{NotifyDingTalk.py => dingtalk.py} | 6 +- .../plugins/{NotifyDiscord.py => discord.py} | 6 +- .../plugins/{NotifyEmail.py => email.py} | 16 +- .../plugins/{NotifyEmby.py => emby.py} | 6 +- .../plugins/{NotifyEnigma2.py => enigma2.py} | 6 +- .../plugins/{NotifyFCM => fcm}/__init__.py | 6 +- .../plugins/{NotifyFCM => fcm}/color.py | 2 +- .../plugins/{NotifyFCM => fcm}/common.py | 0 .../plugins/{NotifyFCM => fcm}/oauth.py | 0 .../plugins/{NotifyFCM => fcm}/priority.py | 0 .../plugins/{NotifyFeishu.py => feishu.py} | 4 +- .../plugins/{NotifyFlock.py => flock.py} | 4 +- .../{NotifyFreeMobile.py => freemobile.py} | 5 +- .../plugins/{NotifyGnome.py => gnome.py} | 4 +- .../{NotifyGoogleChat.py => google_chat.py} | 4 +- .../plugins/{NotifyGotify.py => gotify.py} | 4 +- .../plugins/{NotifyGrowl.py => growl.py} | 6 +- .../plugins/{NotifyGuilded.py => guilded.py} | 5 +- ...tifyHomeAssistant.py => home_assistant.py} | 6 +- .../plugins/{NotifyHttpSMS.py => httpsms.py} | 4 +- .../plugins/{NotifyIFTTT.py => ifttt.py} | 4 +- .../plugins/{NotifyJoin.py => join.py} | 4 +- .../{NotifyKavenegar.py => kavenegar.py} | 4 +- .../plugins/{NotifyKumulos.py => kumulos.py} | 4 +- .../{NotifyLametric.py => lametric.py} | 4 +- .../plugins/{NotifyLine.py => line.py} | 6 +- .../plugins/{NotifyLunaSea.py => lunasea.py} | 6 +- .../plugins/{NotifyMacOSX.py => macosx.py} | 4 +- .../plugins/{NotifyMailgun.py => mailgun.py} | 4 +- .../{NotifyMastodon.py => mastodon.py} | 8 +- .../plugins/{NotifyMatrix.py => matrix.py} | 6 +- .../{NotifyMattermost.py => mattermost.py} | 4 +- .../{NotifyMessageBird.py => messagebird.py} | 4 +- .../plugins/{NotifyMisskey.py => misskey.py} | 4 +- .../plugins/{NotifyMQTT.py => mqtt.py} | 6 +- .../plugins/{NotifyMSG91.py => msg91.py} | 4 +- .../plugins/{NotifyMSTeams.py => msteams.py} | 6 +- .../{NotifyNextcloud.py => nextcloud.py} | 6 +- ...otifyNextcloudTalk.py => nextcloudtalk.py} | 6 +- .../plugins/{NotifyNotica.py => notica.py} | 6 +- .../{NotifyNotifiarr.py => notifiarr.py} | 4 +- .../{NotifyNotifico.py => notifico.py} | 4 +- .../plugins/{NotifyNtfy.py => ntfy.py} | 8 +- .../{NotifyOffice365.py => office365.py} | 6 +- .../{NotifyOneSignal.py => one_signal.py} | 4 +- .../{NotifyOpsgenie.py => opsgenie.py} | 4 +- .../{NotifyPagerDuty.py => pagerduty.py} | 6 +- .../{NotifyPagerTree.py => pagertree.py} | 4 +- ...otifyParsePlatform.py => parseplatform.py} | 4 +- ...tifyPopcornNotify.py => popcorn_notify.py} | 4 +- .../plugins/{NotifyProwl.py => prowl.py} | 4 +- .../{NotifyPushBullet.py => pushbullet.py} | 6 +- .../{NotifyPushDeer.py => pushdeer.py} | 4 +- .../plugins/{NotifyPushed.py => pushed.py} | 6 +- .../plugins/{NotifyPushjet.py => pushjet.py} | 6 +- .../plugins/{NotifyPushMe.py => pushme.py} | 4 +- .../{NotifyPushover.py => pushover.py} | 6 +- .../{NotifyPushSafer.py => pushsafer.py} | 4 +- .../plugins/{NotifyPushy.py => pushy.py} | 4 +- .../plugins/{NotifyReddit.py => reddit.py} | 6 +- .../plugins/{NotifyRevolt.py => revolt.py} | 4 +- .../{NotifyRocketChat.py => rocketchat.py} | 6 +- .../plugins/{NotifyRSyslog.py => rsyslog.py} | 4 +- .../plugins/{NotifyRyver.py => ryver.py} | 4 +- .../{NotifySendGrid.py => sendgrid.py} | 4 +- .../{NotifyServerChan.py => serverchan.py} | 4 +- libs/apprise/plugins/{NotifySES.py => ses.py} | 6 +- .../{NotifySignalAPI.py => signal_api.py} | 6 +- .../{NotifySimplePush.py => simplepush.py} | 6 +- .../plugins/{NotifySinch.py => sinch.py} | 6 +- .../plugins/{NotifySlack.py => slack.py} | 4 +- .../{NotifySMSEagle.py => smseagle.py} | 6 +- .../{NotifySMSManager.py => smsmanager.py} | 4 +- .../plugins/{NotifySMTP2Go.py => smtp2go.py} | 4 +- libs/apprise/plugins/{NotifySNS.py => sns.py} | 6 +- .../{NotifySparkPost.py => sparkpost.py} | 4 +- .../{NotifyStreamlabs.py => streamlabs.py} | 4 +- .../{NotifySynology.py => synology.py} | 6 +- .../plugins/{NotifySyslog.py => syslog.py} | 4 +- ...{NotifyTechulusPush.py => techuluspush.py} | 4 +- .../{NotifyTelegram.py => telegram.py} | 6 +- .../plugins/{NotifyThreema.py => threema.py} | 6 +- .../plugins/{NotifyTwilio.py => twilio.py} | 6 +- .../plugins/{NotifyTwist.py => twist.py} | 6 +- .../plugins/{NotifyTwitter.py => twitter.py} | 8 +- .../plugins/{NotifyVoipms.py => voipms.py} | 4 +- .../plugins/{NotifyVonage.py => vonage.py} | 6 +- .../{NotifyWebexTeams.py => webexteams.py} | 4 +- .../{NotifyWeComBot.py => wecombot.py} | 4 +- .../{NotifyWhatsApp.py => whatsapp.py} | 4 +- .../plugins/{NotifyWindows.py => windows.py} | 4 +- .../plugins/{NotifyXBMC.py => xbmc.py} | 6 +- .../plugins/{NotifyZulip.py => zulip.py} | 4 +- libs/apprise/{URLBase.py => url.py} | 9 +- libs/apprise/{URLBase.pyi => url.pyi} | 0 libs/apprise/utils.py | 13 +- libs/version.txt | 2 +- 153 files changed, 616 insertions(+), 569 deletions(-) delete mode 100644 libs/apprise-1.7.6.dist-info/RECORD delete mode 100644 libs/apprise-1.7.6.dist-info/REQUESTED rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/INSTALLER (100%) rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/LICENSE (100%) rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/METADATA (99%) create mode 100644 libs/apprise-1.8.0.dist-info/RECORD rename libs/{apprise-1.7.4.dist-info => apprise-1.8.0.dist-info}/REQUESTED (100%) rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/WHEEL (100%) rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/entry_points.txt (100%) rename libs/{apprise-1.7.6.dist-info => apprise-1.8.0.dist-info}/top_level.txt (100%) rename libs/apprise/{AppriseAttachment.py => apprise_attachment.py} (98%) rename libs/apprise/{AppriseAttachment.pyi => apprise_attachment.pyi} (100%) rename libs/apprise/{AppriseConfig.py => apprise_config.py} (99%) rename libs/apprise/{AppriseConfig.pyi => apprise_config.pyi} (100%) rename libs/apprise/{AppriseAsset.py => asset.py} (99%) rename libs/apprise/{AppriseAsset.pyi => asset.pyi} (100%) rename libs/apprise/attachment/{AttachBase.py => base.py} (98%) rename libs/apprise/attachment/{AttachBase.pyi => base.pyi} (100%) rename libs/apprise/attachment/{AttachFile.py => file.py} (98%) rename libs/apprise/attachment/{AttachHTTP.py => http.py} (98%) rename libs/apprise/config/{ConfigBase.py => base.py} (99%) rename libs/apprise/config/{ConfigBase.pyi => base.pyi} (100%) rename libs/apprise/config/{ConfigFile.py => file.py} (98%) rename libs/apprise/config/{ConfigHTTP.py => http.py} (98%) rename libs/apprise/config/{ConfigMemory.py => memory.py} (97%) rename libs/apprise/decorators/{CustomNotifyPlugin.py => base.py} (98%) rename libs/apprise/{AppriseLocale.py => locale.py} (100%) rename libs/apprise/{AttachmentManager.py => manager_attachment.py} (93%) rename libs/apprise/{ConfigurationManager.py => manager_config.py} (93%) rename libs/apprise/{NotificationManager.py => manager_plugins.py} (92%) rename libs/apprise/plugins/{NotifyAppriseAPI.py => apprise_api.py} (99%) rename libs/apprise/plugins/{NotifyAprs.py => aprs.py} (99%) rename libs/apprise/plugins/{NotifyBark.py => bark.py} (99%) rename libs/apprise/plugins/{NotifyBase.py => base.py} (99%) rename libs/apprise/plugins/{NotifyBase.pyi => base.pyi} (100%) rename libs/apprise/plugins/{NotifyBoxcar.py => boxcar.py} (99%) rename libs/apprise/plugins/{NotifyBulkSMS.py => bulksms.py} (99%) rename libs/apprise/plugins/{NotifyBulkVS.py => bulkvs.py} (99%) rename libs/apprise/plugins/{NotifyBurstSMS.py => burstsms.py} (99%) rename libs/apprise/plugins/{NotifyChantify.py => chantify.py} (98%) rename libs/apprise/plugins/{NotifyClickSend.py => clicksend.py} (94%) rename libs/apprise/plugins/{NotifyForm.py => custom_form.py} (99%) rename libs/apprise/plugins/{NotifyJSON.py => custom_json.py} (99%) rename libs/apprise/plugins/{NotifyXML.py => custom_xml.py} (99%) rename libs/apprise/plugins/{NotifyD7Networks.py => d7networks.py} (99%) rename libs/apprise/plugins/{NotifyDapnet.py => dapnet.py} (99%) rename libs/apprise/plugins/{NotifyDBus.py => dbus.py} (99%) rename libs/apprise/plugins/{NotifyDingTalk.py => dingtalk.py} (99%) rename libs/apprise/plugins/{NotifyDiscord.py => discord.py} (99%) rename libs/apprise/plugins/{NotifyEmail.py => email.py} (98%) rename libs/apprise/plugins/{NotifyEmby.py => emby.py} (99%) rename libs/apprise/plugins/{NotifyEnigma2.py => enigma2.py} (98%) rename libs/apprise/plugins/{NotifyFCM => fcm}/__init__.py (99%) rename libs/apprise/plugins/{NotifyFCM => fcm}/color.py (99%) rename libs/apprise/plugins/{NotifyFCM => fcm}/common.py (100%) rename libs/apprise/plugins/{NotifyFCM => fcm}/oauth.py (100%) rename libs/apprise/plugins/{NotifyFCM => fcm}/priority.py (100%) rename libs/apprise/plugins/{NotifyFeishu.py => feishu.py} (98%) rename libs/apprise/plugins/{NotifyFlock.py => flock.py} (99%) rename libs/apprise/plugins/{NotifyFreeMobile.py => freemobile.py} (98%) rename libs/apprise/plugins/{NotifyGnome.py => gnome.py} (99%) rename libs/apprise/plugins/{NotifyGoogleChat.py => google_chat.py} (99%) rename libs/apprise/plugins/{NotifyGotify.py => gotify.py} (99%) rename libs/apprise/plugins/{NotifyGrowl.py => growl.py} (99%) rename libs/apprise/plugins/{NotifyGuilded.py => guilded.py} (96%) rename libs/apprise/plugins/{NotifyHomeAssistant.py => home_assistant.py} (98%) rename libs/apprise/plugins/{NotifyHttpSMS.py => httpsms.py} (99%) rename libs/apprise/plugins/{NotifyIFTTT.py => ifttt.py} (99%) rename libs/apprise/plugins/{NotifyJoin.py => join.py} (99%) rename libs/apprise/plugins/{NotifyKavenegar.py => kavenegar.py} (99%) rename libs/apprise/plugins/{NotifyKumulos.py => kumulos.py} (99%) rename libs/apprise/plugins/{NotifyLametric.py => lametric.py} (99%) rename libs/apprise/plugins/{NotifyLine.py => line.py} (98%) rename libs/apprise/plugins/{NotifyLunaSea.py => lunasea.py} (99%) rename libs/apprise/plugins/{NotifyMacOSX.py => macosx.py} (99%) rename libs/apprise/plugins/{NotifyMailgun.py => mailgun.py} (99%) rename libs/apprise/plugins/{NotifyMastodon.py => mastodon.py} (99%) rename libs/apprise/plugins/{NotifyMatrix.py => matrix.py} (99%) rename libs/apprise/plugins/{NotifyMattermost.py => mattermost.py} (99%) rename libs/apprise/plugins/{NotifyMessageBird.py => messagebird.py} (99%) rename libs/apprise/plugins/{NotifyMisskey.py => misskey.py} (99%) rename libs/apprise/plugins/{NotifyMQTT.py => mqtt.py} (99%) rename libs/apprise/plugins/{NotifyMSG91.py => msg91.py} (99%) rename libs/apprise/plugins/{NotifyMSTeams.py => msteams.py} (99%) rename libs/apprise/plugins/{NotifyNextcloud.py => nextcloud.py} (99%) rename libs/apprise/plugins/{NotifyNextcloudTalk.py => nextcloudtalk.py} (98%) rename libs/apprise/plugins/{NotifyNotica.py => notica.py} (99%) rename libs/apprise/plugins/{NotifyNotifiarr.py => notifiarr.py} (99%) rename libs/apprise/plugins/{NotifyNotifico.py => notifico.py} (99%) rename libs/apprise/plugins/{NotifyNtfy.py => ntfy.py} (99%) rename libs/apprise/plugins/{NotifyOffice365.py => office365.py} (99%) rename libs/apprise/plugins/{NotifyOneSignal.py => one_signal.py} (99%) rename libs/apprise/plugins/{NotifyOpsgenie.py => opsgenie.py} (99%) rename libs/apprise/plugins/{NotifyPagerDuty.py => pagerduty.py} (99%) rename libs/apprise/plugins/{NotifyPagerTree.py => pagertree.py} (99%) rename libs/apprise/plugins/{NotifyParsePlatform.py => parseplatform.py} (99%) rename libs/apprise/plugins/{NotifyPopcornNotify.py => popcorn_notify.py} (99%) rename libs/apprise/plugins/{NotifyProwl.py => prowl.py} (99%) rename libs/apprise/plugins/{NotifyPushBullet.py => pushbullet.py} (99%) rename libs/apprise/plugins/{NotifyPushDeer.py => pushdeer.py} (98%) rename libs/apprise/plugins/{NotifyPushed.py => pushed.py} (99%) rename libs/apprise/plugins/{NotifyPushjet.py => pushjet.py} (98%) rename libs/apprise/plugins/{NotifyPushMe.py => pushme.py} (98%) rename libs/apprise/plugins/{NotifyPushover.py => pushover.py} (99%) rename libs/apprise/plugins/{NotifyPushSafer.py => pushsafer.py} (99%) rename libs/apprise/plugins/{NotifyPushy.py => pushy.py} (99%) rename libs/apprise/plugins/{NotifyReddit.py => reddit.py} (99%) rename libs/apprise/plugins/{NotifyRevolt.py => revolt.py} (99%) rename libs/apprise/plugins/{NotifyRocketChat.py => rocketchat.py} (99%) rename libs/apprise/plugins/{NotifyRSyslog.py => rsyslog.py} (99%) rename libs/apprise/plugins/{NotifyRyver.py => ryver.py} (99%) rename libs/apprise/plugins/{NotifySendGrid.py => sendgrid.py} (99%) rename libs/apprise/plugins/{NotifyServerChan.py => serverchan.py} (98%) rename libs/apprise/plugins/{NotifySES.py => ses.py} (99%) rename libs/apprise/plugins/{NotifySignalAPI.py => signal_api.py} (99%) rename libs/apprise/plugins/{NotifySimplePush.py => simplepush.py} (98%) rename libs/apprise/plugins/{NotifySinch.py => sinch.py} (99%) rename libs/apprise/plugins/{NotifySlack.py => slack.py} (99%) rename libs/apprise/plugins/{NotifySMSEagle.py => smseagle.py} (99%) rename libs/apprise/plugins/{NotifySMSManager.py => smsmanager.py} (99%) rename libs/apprise/plugins/{NotifySMTP2Go.py => smtp2go.py} (99%) rename libs/apprise/plugins/{NotifySNS.py => sns.py} (99%) rename libs/apprise/plugins/{NotifySparkPost.py => sparkpost.py} (99%) rename libs/apprise/plugins/{NotifyStreamlabs.py => streamlabs.py} (99%) rename libs/apprise/plugins/{NotifySynology.py => synology.py} (98%) rename libs/apprise/plugins/{NotifySyslog.py => syslog.py} (99%) rename libs/apprise/plugins/{NotifyTechulusPush.py => techuluspush.py} (98%) rename libs/apprise/plugins/{NotifyTelegram.py => telegram.py} (99%) rename libs/apprise/plugins/{NotifyThreema.py => threema.py} (99%) rename libs/apprise/plugins/{NotifyTwilio.py => twilio.py} (99%) rename libs/apprise/plugins/{NotifyTwist.py => twist.py} (99%) rename libs/apprise/plugins/{NotifyTwitter.py => twitter.py} (99%) rename libs/apprise/plugins/{NotifyVoipms.py => voipms.py} (99%) rename libs/apprise/plugins/{NotifyVonage.py => vonage.py} (99%) rename libs/apprise/plugins/{NotifyWebexTeams.py => webexteams.py} (99%) rename libs/apprise/plugins/{NotifyWeComBot.py => wecombot.py} (99%) rename libs/apprise/plugins/{NotifyWhatsApp.py => whatsapp.py} (99%) rename libs/apprise/plugins/{NotifyWindows.py => windows.py} (99%) rename libs/apprise/plugins/{NotifyXBMC.py => xbmc.py} (99%) rename libs/apprise/plugins/{NotifyZulip.py => zulip.py} (99%) rename libs/apprise/{URLBase.py => url.py} (99%) rename libs/apprise/{URLBase.pyi => url.pyi} (100%) diff --git a/libs/apprise-1.7.6.dist-info/RECORD b/libs/apprise-1.7.6.dist-info/RECORD deleted file mode 100644 index 250648105..000000000 --- a/libs/apprise-1.7.6.dist-info/RECORD +++ /dev/null @@ -1,183 +0,0 @@ -../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233 -apprise-1.7.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -apprise-1.7.6.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 -apprise-1.7.6.dist-info/METADATA,sha256=z_gaX2IdNJqw4T9q7AYQri9jcIs-OTGCo3t2EgEY-mw,44823 -apprise-1.7.6.dist-info/RECORD,, -apprise-1.7.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise-1.7.6.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 -apprise-1.7.6.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 -apprise-1.7.6.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 -apprise/Apprise.py,sha256=Stm2NhJprWRaMwQfTiIQG_nR1bLpHi_zcdwEcsCpa-A,32865 -apprise/Apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203 -apprise/AppriseAsset.py,sha256=jRW8Y1EcAvjVA9h_mINmsjO4DM3S0aDl6INIFVMcUCs,11647 -apprise/AppriseAsset.pyi,sha256=NYLXXYbScgRkspP27XGpRRM_uliPu1OCdWdZBPPvLng,979 -apprise/AppriseAttachment.py,sha256=vhrktSrp8GLr32aK4KqV6BX83IpI1lxZe-pGo1wiSFM,12540 -apprise/AppriseAttachment.pyi,sha256=R9-0dVqWpeaFrVpcREwPhGy3qHWztG5jEjYIOsbE5dM,1145 -apprise/AppriseConfig.py,sha256=wfuR6Mb3ZLHvjvqWdFp9lVmjjDRWs65unY9qa92RkCg,16909 -apprise/AppriseConfig.pyi,sha256=_mUlCnncqAq8sL01WxQTgZjnb2ic9kZXvtqZmVl-fc8,1568 -apprise/AppriseLocale.py,sha256=4uSr4Nj_rz6ISMMAfRVRk58wZVLKOofJgk2x0_E8NkQ,8994 -apprise/AttachmentManager.py,sha256=EwlnjuKn3fv_pioWcmMCkyDTsO178t6vkEOD8AjAPsw,2053 -apprise/ConfigurationManager.py,sha256=MUmGajxjgnr6FGN7xb3q0nD0VVgdTdvapBBR7CsI-rc,2058 -apprise/NotificationManager.py,sha256=ZJgkiCgcJ7Bz_6bwQ47flrcxvLMbA4Vbw0HG_yTsGdE,2041 -apprise/URLBase.py,sha256=xRP0-blocp9UudYh04Hb3fIEmTZWJaTv_tzjrqaB9fg,29423 -apprise/URLBase.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520 -apprise/__init__.py,sha256=ArtvoarAMnBcSfXF7L_hzq5CUJ9TUnHopiC7xafCe3c,3368 -apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986 -apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758 -apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646 -apprise/assets/themes/default/apprise-failure-128x128.png,sha256=66ps8TDPxVH3g9PlObJqF-0x952CjnqQyN3zvpRcOT8,16135 -apprise/assets/themes/default/apprise-failure-256x256.png,sha256=bQBsKKCsKfR9EqgYOZrcVcVa5y8qG58PN2mEqO5eNRI,41931 -apprise/assets/themes/default/apprise-failure-32x32.png,sha256=vH0pZffIDCvkejpr3fJHGXW__8Yc3R_p0bacX6t6l18,2437 -apprise/assets/themes/default/apprise-failure-72x72.png,sha256=EP5A8DHRDr9srgupFSwOoyQ308bNJ8aL192J_L4K-ec,7600 -apprise/assets/themes/default/apprise-info-128x128.ico,sha256=F5_CirmXueRCRI5Z_Crf6TS6jVIXTJlRD83zw1oJ66g,67646 -apprise/assets/themes/default/apprise-info-128x128.png,sha256=bBqRZAgQey-gkmJrnFhPbzjILSrljE59mRkgj3raMQo,16671 -apprise/assets/themes/default/apprise-info-256x256.png,sha256=B5r_O4d9MHCmSWZwfbqQgZSp-ZetTdiBSwKcMTF1aFA,43331 -apprise/assets/themes/default/apprise-info-32x32.png,sha256=lt3NZ95TzkiCNVNlurrB2fE2nriMa1wftl7nrNXmb6c,2485 -apprise/assets/themes/default/apprise-info-72x72.png,sha256=kDnsZpqNUZGqs9t1ECUup7FOfXUIL-rupnQCYJp9So4,7875 -apprise/assets/themes/default/apprise-logo.png,sha256=85ttALudKkLmiqilJT7mUQLUXRFmM1AK89rnwLm313s,160907 -apprise/assets/themes/default/apprise-success-128x128.ico,sha256=uCopPwdQjxgfohKazHaDzYs9y4oiaOpL048PYC6WRlg,67646 -apprise/assets/themes/default/apprise-success-128x128.png,sha256=nvDuU_QqhGlw6cMtdj7Mv-gPgqCEx-0DaaXn1KBLVYg,17446 -apprise/assets/themes/default/apprise-success-256x256.png,sha256=vXfKuxY3n0eeXHKdb9hTxICxOEn7HjAQ4IZpX0HSLzc,48729 -apprise/assets/themes/default/apprise-success-32x32.png,sha256=Jg9pFJh3YPI-LiPBebyJ7Z4Vt7BRecaE8AsRjQVIkME,2471 -apprise/assets/themes/default/apprise-success-72x72.png,sha256=FQbgvIhqKOhEK0yvrhaSpai0R7hrkTt_-GaC2KUgCCk,7858 -apprise/assets/themes/default/apprise-warning-128x128.ico,sha256=6XaQPOx0oWK_xbhr4Yhb7qNazCWwSs9lk2SYR2MHTrQ,67646 -apprise/assets/themes/default/apprise-warning-128x128.png,sha256=pf5c4Ph7jWH7gf39dJoieSj8TzAsY3TXI-sGISGVIW4,16784 -apprise/assets/themes/default/apprise-warning-256x256.png,sha256=SY-xlaiXaj420iEYKC2_fJxU-yj2SuaQg6xfPNi83bw,43708 -apprise/assets/themes/default/apprise-warning-32x32.png,sha256=97R2ywNvcwczhBoWEIgajVtWjgT8fLs4FCCz4wu0dwc,2472 -apprise/assets/themes/default/apprise-warning-72x72.png,sha256=L8moEInkO_OLxoOcuvN7rmrGZo64iJeH20o-24MQghE,7913 -apprise/attachment/AttachBase.py,sha256=T3WreGrTsqqGplXJO36jm-N14X7ymSc9xt7XdTYuXVE,13656 -apprise/attachment/AttachBase.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih9GP8,961 -apprise/attachment/AttachFile.py,sha256=MbHY_av0GeM_AIBKV02Hq7SHiZ9eCr1yTfvDMUgi2I4,4765 -apprise/attachment/AttachHTTP.py,sha256=_CMPp4QGLATfGO2-Nw57sxsQyed9z3ywgoB0vpK3KZk,13779 -apprise/attachment/__init__.py,sha256=xabgXpvV05X-YRuqIt3uGYMXwYNXjHyF6Dwd8HfZCFE,1658 -apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697 -apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593 -apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447 -apprise/config/ConfigBase.py,sha256=d1efIuQFCJr66WgpudV2DWtxY3-tuZAyMAhHXBzJ8p0,53194 -apprise/config/ConfigBase.pyi,sha256=cngfobwH6v2vxYbQrObDi5Z-t5wcquWF-wR0kBCr3Eg,54 -apprise/config/ConfigFile.py,sha256=u_SDaN3OHMyaAq2X7k_T4_PRKkVsDwleqBz9YIN5lbA,6138 -apprise/config/ConfigHTTP.py,sha256=Iy6Ji8_nX3xDjFgJGLrz4ftrMlMiyKiFGzYGJ7rMSMQ,9457 -apprise/config/ConfigMemory.py,sha256=epEAgNy-eJVWoQaUOvjivMWxXTofy6wAQ-NbCqYmuyE,2829 -apprise/config/__init__.py,sha256=lbsxrUpB1IYM2q7kjYhsXQGgPF-yZXJrKFE361tdIPY,1663 -apprise/conversion.py,sha256=0VZ0eCZfksN-97Vl0TjVjwnCTgus3XTRioceSFnP-gc,6277 -apprise/decorators/CustomNotifyPlugin.py,sha256=i4D-sgOsBWsxO5auWCN2bgXLLPuADaaLlJ1gUKLj2bU,7972 -apprise/decorators/__init__.py,sha256=e_PDAm0kQNzwDPx-NJZLPfLMd2VAABvNZtxx_iDviRM,1487 -apprise/decorators/notify.py,sha256=a2WupErNw1_SMAld7jPC273bskiChMpYy95BOog5A9w,5111 -apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738 -apprise/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=oUTuHREmLEYN07oqYqRMJ_kU71-o5o37NsF4RXlC5AU,3959 -apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921 -apprise/manager.py,sha256=R9w8jxQRNy6Z_XDcobkt4JYbrC4jtj2OwRw9Zrib3CA,26857 -apprise/plugins/NotifyAppriseAPI.py,sha256=ISBE0brD3eQdyw3XrGXd4Uc4kSYvIuI3SSUVCt-bkdo,16654 -apprise/plugins/NotifyAprs.py,sha256=xdL_aIVgb4ggxRFeCdkZAbgHYZ8DWLw9pRpLZQ0rHoE,25523 -apprise/plugins/NotifyBark.py,sha256=bsDvKooRy4k1Gg7tvBjv3DIx7-WZiV_mbTrkTwMtd9Q,15698 -apprise/plugins/NotifyBase.py,sha256=G3xkF_a2BWqNSxsrnOW7NUgHjOqBCYC5zihCifWemo8,30360 -apprise/plugins/NotifyBase.pyi,sha256=aKlZXRYUgG8lz_ZgGkYYJ_GKhuf18youTmMU-FlG7z8,21 -apprise/plugins/NotifyBoxcar.py,sha256=vR00-WggHa1nHYWyb-f5P2V-G4f683fU_-GBlIeJvD0,12867 -apprise/plugins/NotifyBulkSMS.py,sha256=stPWAFCfhBP617zYK9Dgk6pNJBN_WcyJtODzo0jR1QQ,16005 -apprise/plugins/NotifyBulkVS.py,sha256=viLGeyUDiirRRM7CgRqqElHSLYFnMugDtWE6Ytjqfaw,13290 -apprise/plugins/NotifyBurstSMS.py,sha256=cN2kRETKIK5LhwpQEA8C68LKv8KEUPmXYe-nTSegGls,15550 -apprise/plugins/NotifyChantify.py,sha256=GJJOAtSnVoIfKbJF_W1DTu7WsvS_zHdjO4T1XTKT87g,6673 -apprise/plugins/NotifyClickSend.py,sha256=UfOJqsas6WLjQskojuJE7I_-lrb5QrkMiBZv-po_Q9c,11229 -apprise/plugins/NotifyD7Networks.py,sha256=4E6Fh0kQoDlMMwgZJDOXky7c7KrdMMvqprcfm29scWU,15043 -apprise/plugins/NotifyDBus.py,sha256=1eVJHIL3XkFjDePMqfcll35Ie1vxggJ1iBsVFAIaF00,14379 -apprise/plugins/NotifyDapnet.py,sha256=KuXjBU0ZrIYtoDei85NeLZ-IP810T4w5oFXH9sWiSh0,13624 -apprise/plugins/NotifyDingTalk.py,sha256=NJyETgN6QjtRqtxQjfBLFVuFpURyWykRftm6WpQJVbY,12009 -apprise/plugins/NotifyDiscord.py,sha256=M_qmTzB7NNL5_agjYDX38KBN1jRzDBp2EMSNwEF_9Tw,26072 -apprise/plugins/NotifyEmail.py,sha256=Y_ZOrdK6hTUKHLvogKpV5VqD8byzDyDSvwIVmfdsC2g,39789 -apprise/plugins/NotifyEmby.py,sha256=OMVO8XsVl_XCBYNNNQi8ni2lS4voLfU8Puk1xJOAvHs,24039 -apprise/plugins/NotifyEnigma2.py,sha256=Hj0Q9YOeljSwbfiuMKLqXTVX_1g_mjNUGEts7wfrwno,11498 -apprise/plugins/NotifyFCM/__init__.py,sha256=mBFtIgIJuLIFnMB5ndx5Makjs9orVMc2oLoD7LaVT48,21669 -apprise/plugins/NotifyFCM/color.py,sha256=8iqDtadloQh2TMxkFmIFwenHqKp1pHHn1bwyWOzZ6TY,4592 -apprise/plugins/NotifyFCM/common.py,sha256=978uBUoNdtopCtylipGiKQdsQ8FTONxkFBp7uJMZHc8,1718 -apprise/plugins/NotifyFCM/oauth.py,sha256=Vvbd0-rd5BPIjAneG3rILU153JIzfSZ0kaDov6hm96M,11197 -apprise/plugins/NotifyFCM/priority.py,sha256=0WuRW1y1HVnybgjlTeCZPHzt7j8SwWnC7faNcjioAOc,8163 -apprise/plugins/NotifyFeishu.py,sha256=IpcABdLZJ1vcQdZHlmASVbNOiOCIrmgKFhz1hbdskY4,7266 -apprise/plugins/NotifyFlock.py,sha256=0rUIa9nToGsO8BTUgixh8Z_qdVixJeH479UNYjcE4EM,12748 -apprise/plugins/NotifyForm.py,sha256=38nL-2m1cf4gEQFQ4NpvA4j9i5_nNUgelReWFSjyV5U,17905 -apprise/plugins/NotifyFreeMobile.py,sha256=XCkgZLc3KKGlx_9UdeoMJVcHpeQrOml9T93S-DGf4bs,6644 -apprise/plugins/NotifyGnome.py,sha256=8MXTa8gZg1wTgNJfLlmq7_fl3WaYK-SX6VR91u308C4,9059 -apprise/plugins/NotifyGoogleChat.py,sha256=lnoN17m6lZANaXcElDTP8lcuVWjIZEK8C6_iqJNAnw4,12622 -apprise/plugins/NotifyGotify.py,sha256=DNlOIHyuYitO5use9oa_REPm2Fant7y9QSaatrZFNI0,10551 -apprise/plugins/NotifyGrowl.py,sha256=M6ViUz967VhEHtXrE7lbCKF3aB4pIXNEzJLjjGAmvhM,14023 -apprise/plugins/NotifyGuilded.py,sha256=eCMCoFFuE0XNY8HlLM21zoxgBNgqEKQ8dwYj8LihfRU,3641 -apprise/plugins/NotifyHomeAssistant.py,sha256=zqWu7TtdXhTbGNuflC8WfydbHsCLiEBw4uBUcF7YZtw,10739 -apprise/plugins/NotifyHttpSMS.py,sha256=pDEUHCCB18IhOgDcVK3_FFDJdAcrdTIfPzj0jNnZZBo,11136 -apprise/plugins/NotifyIFTTT.py,sha256=oMvTQ0bEu2eJQgw9BwxAwTNOtbZ_ER-zleJvWpWTj7w,13425 -apprise/plugins/NotifyJSON.py,sha256=70ctjmArGzuvM1gHNt1bCiQVWE7Fp9vd2nWhSXwFvw0,13851 -apprise/plugins/NotifyJoin.py,sha256=B8FHp7cblZBkxTgfrka6mNnf6oQVBXVuGISgSau00z0,13581 -apprise/plugins/NotifyKavenegar.py,sha256=F5xTUdebM1lK6yGFbZJQB9Zgw2LTI0angeA-3Nu-89w,12620 -apprise/plugins/NotifyKumulos.py,sha256=eCEW2ZverZqETOLHVWMC4E8Ll6rEhhEWOSD73RD80SM,8214 -apprise/plugins/NotifyLametric.py,sha256=h8vZoX-Ll5NBZRprBlxTO2H9w0lOiMxglGvUgJtK4_8,37534 -apprise/plugins/NotifyLine.py,sha256=OVI0ozMJcq_-dI8dodVX52dzUzgENlAbOik-Kw4l-rI,10676 -apprise/plugins/NotifyLunaSea.py,sha256=woN8XdkwAjhgxAXp7Zj4XsWLybNL80l4W3Dx5BvobZg,14459 -apprise/plugins/NotifyMQTT.py,sha256=cnuG4f3bYYNPhEj9qDX8SLmnxLVT9G1b8J5w6-mQGKY,19545 -apprise/plugins/NotifyMSG91.py,sha256=P7JPyT1xmucnaEeCZPf_6aJfe1gS_STYYwEM7hJ7QBw,12677 -apprise/plugins/NotifyMSTeams.py,sha256=dFH575hoLL3zRddbBKfozlYjxvPJGbj3BKvfJSIkvD0,22976 -apprise/plugins/NotifyMacOSX.py,sha256=y2fGpSZXomFiNwKbWImrXQUMVM4JR4uPCnsWpnxQrFA,8271 -apprise/plugins/NotifyMailgun.py,sha256=FNS_QLOQWMo62yVO-mMZkpiXudUtSdbHOjfSrLC4oIo,25409 -apprise/plugins/NotifyMastodon.py,sha256=2ovjQIOOITHH8lOinC8QCFCJN2QA8foIM2pjdknbblc,35277 -apprise/plugins/NotifyMatrix.py,sha256=I8kdaZUZS-drew0JExBbChQVe7Ib4EwAjQd0xE30XT0,50049 -apprise/plugins/NotifyMattermost.py,sha256=JgEc-wC-43FBMItezDJ62zv1Nc9ROFjDiwD_8bt8rgM,12722 -apprise/plugins/NotifyMessageBird.py,sha256=EUPwhs1PHiPZpluIrLiNKQMUPcdlKnx1sdnllCtN_Ns,12248 -apprise/plugins/NotifyMisskey.py,sha256=zYZkBKv0p3jJpm_HLDBugUgKeGb0qpLoPqy0ffwwxVg,9600 -apprise/plugins/NotifyNextcloud.py,sha256=M3EyvUzBMHbTKU3gxW_7fPA6vmQUF5x8GTMZQ78sWCA,12759 -apprise/plugins/NotifyNextcloudTalk.py,sha256=dLl_g7Knq5PVcadbzDuQsxbGHTZlC4r-pQC8wzYnmAo,11011 -apprise/plugins/NotifyNotica.py,sha256=yHmk8HiNFjzoI4Gewo_nBRrx9liEmhT95k1d10wqhYg,12990 -apprise/plugins/NotifyNotifiarr.py,sha256=ADwLJO9eenfLkNa09tXMGSBTM4c3zTY0SEePvyB8WYA,15857 -apprise/plugins/NotifyNotifico.py,sha256=Qe9jMN_M3GL4XlYIWkAf-w_Hf65g9Hde4bVuytGhUW4,12035 -apprise/plugins/NotifyNtfy.py,sha256=AtJt2zH35mMQTwRDxKia93NPy6-4rtixplP53zIYV2M,27979 -apprise/plugins/NotifyOffice365.py,sha256=8TxsVsdbUghmNj0kceMlmoZzTOKQTgn3priI8JuRuHE,25190 -apprise/plugins/NotifyOneSignal.py,sha256=gsw7ckW7xLiJDRUb7eJHNe_4bvdBXmt6_YsB1u_ghjw,18153 -apprise/plugins/NotifyOpsgenie.py,sha256=zJWpknjoHq35Iv9w88ucR62odaeIN3nrGFPtYnhDdjA,20515 -apprise/plugins/NotifyPagerDuty.py,sha256=lu6oNdygrs6UezYm6xgiQxQDeDz8EVUtfP-xsArRvyw,17874 -apprise/plugins/NotifyPagerTree.py,sha256=mPl6ejdelNlWUWGVs46kZT0VV4uFZoeCdcv4VJ_f_XQ,13849 -apprise/plugins/NotifyParsePlatform.py,sha256=6oFOTpu-HMhesaYXRBvu5oaESYlFrKBNYTHE-ItCBRk,10291 -apprise/plugins/NotifyPopcornNotify.py,sha256=kRstzG0tWBdxSRfn2RN2J7FhvIj2qYWlwUyLxxZCbPc,10587 -apprise/plugins/NotifyProwl.py,sha256=EGOdmiZq8CFbjxTtWWKLQEdYiSvr4czZfE_8aCMEokw,9782 -apprise/plugins/NotifyPushBullet.py,sha256=JVd2GQH-DWmPaKjuGBpsE6DXNCcZEUDH7tA5zbM1qEU,15372 -apprise/plugins/NotifyPushDeer.py,sha256=cG1UFG06PfzbmI1RxtrMqmfaHK_Ojk_W-QMEdtkEuUI,6922 -apprise/plugins/NotifyPushMe.py,sha256=ioRzeXbd2X5miTd3h3m7AwCqkIIfbXNm4PjYk0OOXZ0,7134 -apprise/plugins/NotifyPushSafer.py,sha256=hIcYHwUZapmC-VDvaO_UkDY9RSPTxHgF7m2FL-6JBZw,26756 -apprise/plugins/NotifyPushed.py,sha256=NqLMXD9gvihXLfLUtCcMfz5oUAhPM7sKXECqKgD0v-U,12270 -apprise/plugins/NotifyPushjet.py,sha256=8qWpIqM4dKWjO-BjOrRJXZYtvtJBt_mikdBWRxfibnE,8952 -apprise/plugins/NotifyPushover.py,sha256=MJDquV4zl1cNrGZOC55hLlt6lOb6625WeUcgS5ceCbk,21213 -apprise/plugins/NotifyPushy.py,sha256=mmWcnu905Fvc8ihYXvZ7lVYErGZH5Q-GbBNS20v5r48,12496 -apprise/plugins/NotifyRSyslog.py,sha256=W42LT90X65-pNoU7KdhdX1PBcmsz9RyV376CDa_H3CI,11982 -apprise/plugins/NotifyReddit.py,sha256=E78OSyDQfUalBEcg71sdMsNBOwdj7cVBnELrhrZEAXY,25785 -apprise/plugins/NotifyRevolt.py,sha256=DRA9Xylwl6leVjVFuJcP4L1cG49CIBtnQdxh4BKnAZ4,14500 -apprise/plugins/NotifyRocketChat.py,sha256=Cb_nasX0-G3FoPMYvNk55RJ-tHuXUCTLUn2wTSi4IcI,25738 -apprise/plugins/NotifyRyver.py,sha256=yhHPMLGeJtcHwBKSPPk0OBfp59DgTvXio1R59JhrJu4,11823 -apprise/plugins/NotifySES.py,sha256=wtRmpAZkS5mQma6sdiaPT6U1xcgoj77CB9mNFvSEAw8,33545 -apprise/plugins/NotifySMSEagle.py,sha256=voFNqOewD9OC1eRctD0YdUB_ZSWsb06rjUwBfCcxPYA,24161 -apprise/plugins/NotifySMSManager.py,sha256=DbVc35qLfYkNL7eq43_rPD6k-PELL9apf3S09S6qvDA,14125 -apprise/plugins/NotifySMTP2Go.py,sha256=foQ7aMMmNc5Oree8YwrxZJgMnF6yVMFAfqShm_nLbx0,19711 -apprise/plugins/NotifySNS.py,sha256=ZEBWf0ZJ9w_ftzUikKEvQWJ2fkxrUbrLhPmTRD2DvRQ,24159 -apprise/plugins/NotifySendGrid.py,sha256=IBdYmZcthkvGCz1N_Fs8vDnImtHug6LpuKv1mWT_Cdo,16213 -apprise/plugins/NotifyServerChan.py,sha256=WsrClO9f0xi-KpnLZGTUHV7PxeU3l1D875gvMaZRG_M,5779 -apprise/plugins/NotifySignalAPI.py,sha256=OwJ7qjJ-ZJyS8GS-dBWAtgizHMnGegg76GuwFobyWkw,16733 -apprise/plugins/NotifySimplePush.py,sha256=dUC6O8IGuUIAz5z6_H7A7jdv5Gj1plytNm5QyKnHAYg,10876 -apprise/plugins/NotifySinch.py,sha256=tmHLwQa9lWHEI3EcRfigl4i7JU46A6gKAi_GbY0PrX4,16813 -apprise/plugins/NotifySlack.py,sha256=3VdjruU5FPr3jT_s3axwRJKMcBYXP0lvJnyuKedIlcE,42521 -apprise/plugins/NotifySparkPost.py,sha256=6dRTwnYU50Lvmp6AlwCyePe0TMbVEXaSwNeGkg__EYo,27878 -apprise/plugins/NotifyStreamlabs.py,sha256=lx3N8T2ufUWFYIZ-kU_rOv50YyGWBqLSCKk7xim2_Io,16023 -apprise/plugins/NotifySynology.py,sha256=_jTqfgWeOuSi_I8geMOraHBVFtDkvm9mempzymrmeAo,11105 -apprise/plugins/NotifySyslog.py,sha256=J9Kain2bb-PDNiG5Ydb0q678cYjNE_NjZFqMG9oEXM0,10617 -apprise/plugins/NotifyTechulusPush.py,sha256=m43_Qj1scPcgCRX5Dr2Ul7nxMbaiVxNzm_HRuNmfgoA,7253 -apprise/plugins/NotifyTelegram.py,sha256=XE7PC9LRzcrfE2bpLKyor5lO_7B9LS4Xw1UlUmA4a2A,37187 -apprise/plugins/NotifyThreema.py,sha256=C_C3j0fJWgeF2uB7ceJFXOdC6Lt0TFBInFMs5Xlg04M,11885 -apprise/plugins/NotifyTwilio.py,sha256=WCo8eTI9OF1rtg3ueHHRDXt4Lp45eZ6h3IdTZVf5HM8,15976 -apprise/plugins/NotifyTwist.py,sha256=nZA73CYVe-p0tkVMy5q3vFRyflLM4yjUo9LECvkUwgc,28841 -apprise/plugins/NotifyTwitter.py,sha256=qML0jlBkLZMHrkKRxBpVUnBwAz8MWGYyI3cvwi-hrgM,30152 -apprise/plugins/NotifyVoipms.py,sha256=msy_D32YhP8OP4_Mj_L3OYd4iablqQETN-DvilGZeVQ,12552 -apprise/plugins/NotifyVonage.py,sha256=xmzZgobFaGA_whpQ5fDuG2poUrK9W4T77yP7dusHcSo,13431 -apprise/plugins/NotifyWeComBot.py,sha256=5lkhXDgyJ1edzknemKsO1sJVv7miR9F_7xI40Ag7ICI,8789 -apprise/plugins/NotifyWebexTeams.py,sha256=gbbRlHiPuOvUIZexE5m2QNd1dN_5_x0OdT5m6NSrcso,9164 -apprise/plugins/NotifyWhatsApp.py,sha256=PtzW0ue3d2wZ8Pva_LG29jUcpRRP03TFxO5SME_8Juo,19924 -apprise/plugins/NotifyWindows.py,sha256=QgWJfJF8AE6RWr-L81YYVZNWrnImK9Qr3B991HWanqU,8563 -apprise/plugins/NotifyXBMC.py,sha256=5hDuOTP3Kwtp4NEMaokNjWyEKEkQcN_fSx-cUPJvhaU,12096 -apprise/plugins/NotifyXML.py,sha256=WJnmdvXseuTRgioVMRqpR8a09cDfTpPTfuFlTnT_TfI,16973 -apprise/plugins/NotifyZulip.py,sha256=M8cSL7nZvtBYyTX6045g34tyn2vyybltgD1CoI4Xa7A,13968 -apprise/plugins/__init__.py,sha256=jTfLmW47kZC_Wf5eFFta2NoD2J-7_E7JaPrrVMIECkU,18725 -apprise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise/utils.py,sha256=SjRU2tb1UsVnTCTXPUyXVz3WpRbDWwAHH-d3ll38EHY,53185 diff --git a/libs/apprise-1.7.6.dist-info/REQUESTED b/libs/apprise-1.7.6.dist-info/REQUESTED deleted file mode 100644 index e69de29bb..000000000 diff --git a/libs/apprise-1.7.6.dist-info/INSTALLER b/libs/apprise-1.8.0.dist-info/INSTALLER similarity index 100% rename from libs/apprise-1.7.6.dist-info/INSTALLER rename to libs/apprise-1.8.0.dist-info/INSTALLER diff --git a/libs/apprise-1.7.6.dist-info/LICENSE b/libs/apprise-1.8.0.dist-info/LICENSE similarity index 100% rename from libs/apprise-1.7.6.dist-info/LICENSE rename to libs/apprise-1.8.0.dist-info/LICENSE diff --git a/libs/apprise-1.7.6.dist-info/METADATA b/libs/apprise-1.8.0.dist-info/METADATA similarity index 99% rename from libs/apprise-1.7.6.dist-info/METADATA rename to libs/apprise-1.8.0.dist-info/METADATA index ac7cb9aac..c01ef211a 100644 --- a/libs/apprise-1.7.6.dist-info/METADATA +++ b/libs/apprise-1.8.0.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: apprise -Version: 1.7.6 +Version: 1.8.0 Summary: Push Notifications that work with just about every platform! Home-page: https://github.com/caronc/apprise Author: Chris Caron diff --git a/libs/apprise-1.8.0.dist-info/RECORD b/libs/apprise-1.8.0.dist-info/RECORD new file mode 100644 index 000000000..bcfa733bc --- /dev/null +++ b/libs/apprise-1.8.0.dist-info/RECORD @@ -0,0 +1,183 @@ +../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233 +apprise-1.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +apprise-1.8.0.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 +apprise-1.8.0.dist-info/METADATA,sha256=if2D6aZrIVQZAe9BKWyqKoKH89ZGnYOTvXGRfqcSf_g,44823 +apprise-1.8.0.dist-info/RECORD,, +apprise-1.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise-1.8.0.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 +apprise-1.8.0.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 +apprise-1.8.0.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 +apprise/__init__.py,sha256=nBy2lkwvLxW1QTMEagOZZnM5AQWLM6CRUYt-Gr03X94,3388 +apprise/apprise.py,sha256=JWSqg5pYJOydubQyaIjR9gIPhb2okcrGe2mQ_NhPNS0,32837 +apprise/apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203 +apprise/apprise_attachment.py,sha256=eYVnNWTWQzlzKPDOgMCzGvVtXXDL6noMwtRKwwvyL8Q,12528 +apprise/apprise_attachment.pyi,sha256=R9-0dVqWpeaFrVpcREwPhGy3qHWztG5jEjYIOsbE5dM,1145 +apprise/apprise_config.py,sha256=qlNtd-OkldrY9rE9vT67fVPHINx9fkkbwQinhbBjQ_I,16896 +apprise/apprise_config.pyi,sha256=_mUlCnncqAq8sL01WxQTgZjnb2ic9kZXvtqZmVl-fc8,1568 +apprise/asset.py,sha256=ICXIjCi12Xq8E-WIR4mrVC7KcpPRiJDlKG8cHqoCll0,11643 +apprise/asset.pyi,sha256=NYLXXYbScgRkspP27XGpRRM_uliPu1OCdWdZBPPvLng,979 +apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986 +apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758 +apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646 +apprise/assets/themes/default/apprise-failure-128x128.png,sha256=66ps8TDPxVH3g9PlObJqF-0x952CjnqQyN3zvpRcOT8,16135 +apprise/assets/themes/default/apprise-failure-256x256.png,sha256=bQBsKKCsKfR9EqgYOZrcVcVa5y8qG58PN2mEqO5eNRI,41931 +apprise/assets/themes/default/apprise-failure-32x32.png,sha256=vH0pZffIDCvkejpr3fJHGXW__8Yc3R_p0bacX6t6l18,2437 +apprise/assets/themes/default/apprise-failure-72x72.png,sha256=EP5A8DHRDr9srgupFSwOoyQ308bNJ8aL192J_L4K-ec,7600 +apprise/assets/themes/default/apprise-info-128x128.ico,sha256=F5_CirmXueRCRI5Z_Crf6TS6jVIXTJlRD83zw1oJ66g,67646 +apprise/assets/themes/default/apprise-info-128x128.png,sha256=bBqRZAgQey-gkmJrnFhPbzjILSrljE59mRkgj3raMQo,16671 +apprise/assets/themes/default/apprise-info-256x256.png,sha256=B5r_O4d9MHCmSWZwfbqQgZSp-ZetTdiBSwKcMTF1aFA,43331 +apprise/assets/themes/default/apprise-info-32x32.png,sha256=lt3NZ95TzkiCNVNlurrB2fE2nriMa1wftl7nrNXmb6c,2485 +apprise/assets/themes/default/apprise-info-72x72.png,sha256=kDnsZpqNUZGqs9t1ECUup7FOfXUIL-rupnQCYJp9So4,7875 +apprise/assets/themes/default/apprise-logo.png,sha256=85ttALudKkLmiqilJT7mUQLUXRFmM1AK89rnwLm313s,160907 +apprise/assets/themes/default/apprise-success-128x128.ico,sha256=uCopPwdQjxgfohKazHaDzYs9y4oiaOpL048PYC6WRlg,67646 +apprise/assets/themes/default/apprise-success-128x128.png,sha256=nvDuU_QqhGlw6cMtdj7Mv-gPgqCEx-0DaaXn1KBLVYg,17446 +apprise/assets/themes/default/apprise-success-256x256.png,sha256=vXfKuxY3n0eeXHKdb9hTxICxOEn7HjAQ4IZpX0HSLzc,48729 +apprise/assets/themes/default/apprise-success-32x32.png,sha256=Jg9pFJh3YPI-LiPBebyJ7Z4Vt7BRecaE8AsRjQVIkME,2471 +apprise/assets/themes/default/apprise-success-72x72.png,sha256=FQbgvIhqKOhEK0yvrhaSpai0R7hrkTt_-GaC2KUgCCk,7858 +apprise/assets/themes/default/apprise-warning-128x128.ico,sha256=6XaQPOx0oWK_xbhr4Yhb7qNazCWwSs9lk2SYR2MHTrQ,67646 +apprise/assets/themes/default/apprise-warning-128x128.png,sha256=pf5c4Ph7jWH7gf39dJoieSj8TzAsY3TXI-sGISGVIW4,16784 +apprise/assets/themes/default/apprise-warning-256x256.png,sha256=SY-xlaiXaj420iEYKC2_fJxU-yj2SuaQg6xfPNi83bw,43708 +apprise/assets/themes/default/apprise-warning-32x32.png,sha256=97R2ywNvcwczhBoWEIgajVtWjgT8fLs4FCCz4wu0dwc,2472 +apprise/assets/themes/default/apprise-warning-72x72.png,sha256=L8moEInkO_OLxoOcuvN7rmrGZo64iJeH20o-24MQghE,7913 +apprise/attachment/__init__.py,sha256=9jSiGEbLllS-0Vbpgxo4MCpZfIJ-saezWWgQ1PofZ9I,1678 +apprise/attachment/base.py,sha256=phcM6C281j2eoEhtoH5FY0m5s_JvWiEm1AK9z9gyi0w,13692 +apprise/attachment/base.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih9GP8,961 +apprise/attachment/file.py,sha256=d9gpsunNIot-6h6w-WQmwWgFiCv7YLqk42zJr4LLqE4,4752 +apprise/attachment/http.py,sha256=C0L9zKmHi60wyzEZN-OJCF7LVoY-MMHYMqhXspFlLdU,13748 +apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697 +apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593 +apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447 +apprise/config/__init__.py,sha256=oDxdoqG2NEYu_bbpLsLaM3L9WKY3gNn5gjIwb2h3LU4,1679 +apprise/config/base.py,sha256=iWgrBk4-wm_SHtNbe7dCQOYnbkI3oKWP_b60qwjGSoU,53173 +apprise/config/base.pyi,sha256=cngfobwH6v2vxYbQrObDi5Z-t5wcquWF-wR0kBCr3Eg,54 +apprise/config/file.py,sha256=MB_H2X5lJZS-XAOahkMb3IsD2rgVebFjiEF76goITEg,6125 +apprise/config/http.py,sha256=CjQtv_OQJykMxD4ssiwbFI3P8CeQiPdYV_ZIiL_XqMw,9440 +apprise/config/memory.py,sha256=8VICU-WLux8KnW6i9sf9fgmns90J-MfVYI3pvTiyTno,2816 +apprise/conversion.py,sha256=fSn-pTE6-vNt9qVQCKib_NTMhZnCH88FoMt14x6co1M,6273 +apprise/decorators/__init__.py,sha256=e_PDAm0kQNzwDPx-NJZLPfLMd2VAABvNZtxx_iDviRM,1487 +apprise/decorators/base.py,sha256=KnRzhrTOxQxwm4KuJxfCfaYfX0MrFbqWTvXY1XuiNck,8019 +apprise/decorators/notify.py,sha256=FzIa7m-G5KnVVa__rjyn990zt2lqE8sdHW-XY79lbAU,5097 +apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738 +apprise/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=w-KW6RXYYMr14raaIvJzbop8tXU4qrsz50zm8rcCo3U,3959 +apprise/locale.py,sha256=4uSr4Nj_rz6ISMMAfRVRk58wZVLKOofJgk2x0_E8NkQ,8994 +apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921 +apprise/manager.py,sha256=_LiMG1Upk6h6GcxOw3GjsPtVT8ZJLhLlQkC_xL_dm6A,27218 +apprise/manager_attachment.py,sha256=EkrcKtKjbxTXXyDaKyiu4XSfDu9wKFSdJD7TOTpyOUc,2200 +apprise/manager_config.py,sha256=sCIOlBTH13bQ1cuhQVpUq2vyKWBArA8YRyPnXIB1iWQ,2205 +apprise/manager_plugins.py,sha256=Xbg5-xr-06zMIDoWviz-zKcbzusHj0iBBMchKPpUOkg,2211 +apprise/plugins/__init__.py,sha256=Hm-vt-Xtxir5mgOdehqlGLhIlc45LA31zjqV42uRnB8,18701 +apprise/plugins/apprise_api.py,sha256=D93eUOlEQ1xPZYdagnEtIxkSuzZ4b9XreuhntQW97F8,16638 +apprise/plugins/aprs.py,sha256=fyzhdsESVwsqHMbfjnlRuGuAqAz2Pk_-MrmiMihs8qY,25506 +apprise/plugins/bark.py,sha256=oSz7Nr7vxnXxqA5dRaO6ze2IljkgEQavUPs-MUUDSX4,15681 +apprise/plugins/base.py,sha256=720L5OyMMLqlM3UdYSNMpA3m0rCudzGP3HwKgu73Ons,30350 +apprise/plugins/base.pyi,sha256=aKlZXRYUgG8lz_ZgGkYYJ_GKhuf18youTmMU-FlG7z8,21 +apprise/plugins/boxcar.py,sha256=8ImhsSsOK9o3eiYp4dZ6fO6p6AeI-jOPWoFJ8qvRAxY,12850 +apprise/plugins/bulksms.py,sha256=XuP8X4dL1wWQU7TkLYWPWRAro7TymnF7PlF2ESe7o0o,15988 +apprise/plugins/bulkvs.py,sha256=sLpiKbHs63Yu0UjMc9erW1VSgLceuu5wvPie5iyl03k,13273 +apprise/plugins/burstsms.py,sha256=7YmuhElVvT-0FIxMFBILHjo6Fir0J4qHTuqj8KfWBaI,15533 +apprise/plugins/chantify.py,sha256=hOvrmhH051Us28_kv0mKkuaJ2BN_8pSsslF8eMCgp-Q,6660 +apprise/plugins/clicksend.py,sha256=kKUJgk9FcTcfXqcIV57uMz0VxBtSQqkpxocRkgn0-3Q,11408 +apprise/plugins/custom_form.py,sha256=fMYwSQyEa_u8IEw5QSylDn-y32FnmymkL5ng-tGxDoc,17888 +apprise/plugins/custom_json.py,sha256=UCXaegE1nD8UrKlFrSKQbxfbcrX1BHUvZwBZY6a_aJc,13834 +apprise/plugins/custom_xml.py,sha256=lrPCRngPz8_oV2A8ZfiIY56BRQc1iv0PN0--2LOlxpA,16956 +apprise/plugins/d7networks.py,sha256=2o_wDgoak_S0sUJJa7HPtu0PPKLVmIOrq1rV9llOnHY,15030 +apprise/plugins/dapnet.py,sha256=fclGE69ggMejgpyvBpJqTwFNF3g2w7qpFySOMcc9vi0,13607 +apprise/plugins/dbus.py,sha256=lvHLmOkJYJAr4TjrigdIgP5bBlQXHrNCDR0k80LNsks,14366 +apprise/plugins/dingtalk.py,sha256=1twAGYUZzjhQepg2zhaR7u8QJuWvM5WymkbPQSUKBck,11992 +apprise/plugins/discord.py,sha256=kdR09bKU80-AosBIrTkMSxoOkAUHMXKwSr1P3H-M_f4,26053 +apprise/plugins/email.py,sha256=pVVWhEpT9Zi7qyUvpI2UxbjxkXYe6FoJj8E0NTw4P98,39912 +apprise/plugins/emby.py,sha256=HWYxlgHE99yAPKii4IE1XQQ-B1HViklWOkefjmq6SW4,24022 +apprise/plugins/enigma2.py,sha256=40Uo0m1JtLUcwRnfqKqdX_8ZR1-SvPfJ5470uijgdfw,11481 +apprise/plugins/fcm/__init__.py,sha256=eTjX-ahPHrPddX0v_Ix77qEr2G4NQ7reClCMzX3Nxwk,21657 +apprise/plugins/fcm/color.py,sha256=rQxVRL_CcRlgnNTQXkjB1Bfs0Xc933zOB1tURSdhi7Y,4585 +apprise/plugins/fcm/common.py,sha256=978uBUoNdtopCtylipGiKQdsQ8FTONxkFBp7uJMZHc8,1718 +apprise/plugins/fcm/oauth.py,sha256=Vvbd0-rd5BPIjAneG3rILU153JIzfSZ0kaDov6hm96M,11197 +apprise/plugins/fcm/priority.py,sha256=0WuRW1y1HVnybgjlTeCZPHzt7j8SwWnC7faNcjioAOc,8163 +apprise/plugins/feishu.py,sha256=buGBg1YlcwHjwUGzUfju4cdYbmEOFl3EHU_NzEBqtlM,7253 +apprise/plugins/flock.py,sha256=_bhH-ub0LbuU7k2p1ZwUPZ-TbC3kpD48GKNtLebUpoM,12735 +apprise/plugins/freemobile.py,sha256=NZf553UWkl1FVEVyWQKJN3ZD6opYSv3I-L6DRaKbiCw,6679 +apprise/plugins/gnome.py,sha256=UYtaiMKxILhTdsxe4aeLaJwjV6Af-8VhjVR3W3CyPPk,9046 +apprise/plugins/google_chat.py,sha256=f7F4gPxwwkmhDBlXMHzYoHRhPX3w8tyD7OimGBjmNxg,12609 +apprise/plugins/gotify.py,sha256=ISzmuQ4-Wd46OlaxGSAZBbWDTq9mHBUYO0R-7tAX_zc,10538 +apprise/plugins/growl.py,sha256=mlpN1QXGlEf2BA3BORnvkyT0oiTsWBev2y5gzpOm9r0,14006 +apprise/plugins/guilded.py,sha256=lhE6gK7laQNYEubD9lBGRemFK-IHis_u2tNekbUoGgM,3707 +apprise/plugins/home_assistant.py,sha256=Wstut0QaQL2dE66kH8MOqMANayhvGOq7CuxVz9O-T60,10722 +apprise/plugins/httpsms.py,sha256=2TtIlAFa4cjXwpMyrjfieCFHgaZl4H_YT6IoPIMDOcg,11123 +apprise/plugins/ifttt.py,sha256=ZHtu7pW9I0aHVEC4DshKi6XZCwqQ0A_XGLyFJujwdKY,13412 +apprise/plugins/join.py,sha256=mRop7thH24kQmvIEvtnh0HT0VyBrUXm2sS-P69c0iFE,13568 +apprise/plugins/kavenegar.py,sha256=1vdms7QBaLcTiLXeQGfprMU5rw9wgImRLtALIb78-ro,12607 +apprise/plugins/kumulos.py,sha256=6M_way1Qi5bELm8Csay86AVtVhzmRyv5hfxGrFvyzN4,8201 +apprise/plugins/lametric.py,sha256=JR0XjdxkEphT4VKvKfElNxpAoOpXZCb-40b2SJQP9XA,37521 +apprise/plugins/line.py,sha256=-xONdu4LFkS3sGgZeEGiPGbBIvBEd20lCbyxKLhQwAg,10659 +apprise/plugins/lunasea.py,sha256=WxJ-RIbFDiS1-5HjCs8XVdNv7nt2EHimsXGCDaK0sC0,14442 +apprise/plugins/macosx.py,sha256=4vIXz1xlsq_xlEyeu_rEvzJQZYcWq7pVmSn5VjF_dCY,8258 +apprise/plugins/mailgun.py,sha256=ad3jzdrbdgwWmOAAu4e_inLEAo-cii4z0OnIJQXoSIA,25396 +apprise/plugins/mastodon.py,sha256=X8CBxnuWORLo9JEOLCJcSafFNVrcEd778R38doyNWUo,35254 +apprise/plugins/matrix.py,sha256=zJPTLFqRT6mofXFVi421_poF0Qm7KaWSgNXTBKq3YoI,50032 +apprise/plugins/mattermost.py,sha256=KfHfqt3LO4XuMsY-qNV-i_OAAamF-35RgjHzxuq2deM,12709 +apprise/plugins/messagebird.py,sha256=3TJ_31Vc1liwQKKKwEiO_rP5e4jP4QkcU_wHyRGtf5Q,12235 +apprise/plugins/misskey.py,sha256=E5UrDrVryb7zV2D_qJCPsCyvpynF4-22JqRVco6PcgM,9587 +apprise/plugins/mqtt.py,sha256=3LcRhT44o2QoSH5rJs23xRbi2vob-po5FAPnnEU_cgs,19528 +apprise/plugins/msg91.py,sha256=V3PwdCS5r-LbVHNNTFZGUHt7GPwQOHnQC0mx8G90wGQ,12664 +apprise/plugins/msteams.py,sha256=4maXBIQFEVfH-IVzZW6t4jirsozmKwF4ySPP5h20cTQ,22964 +apprise/plugins/nextcloud.py,sha256=iYmKGH-xZ4Kj3HvXA_krQody7LRqJKzMRu39Ivk1ve4,12742 +apprise/plugins/nextcloudtalk.py,sha256=TTK8qhy0VPjG0N1yFUBJWTieV-1DqT9Jux9D5PRiYAE,10994 +apprise/plugins/notica.py,sha256=MzryA8x-dSt-oRk9g3dlTTrb1ul7mnbRNajbiwTx1Mc,12973 +apprise/plugins/notifiarr.py,sha256=ZI2mjXwKLWx7SXb6d3S-sk2JmCswJ23UTPj9jVNSN0U,15844 +apprise/plugins/notifico.py,sha256=IKkiXU8HdbRnEWzkQKPFo1g-ZnT8AIwFYer7jF2Yjac,12022 +apprise/plugins/ntfy.py,sha256=jOmPZETJByyNmYSzr-2F3k5jAXFVNldcd3wK-tQRpyE,27956 +apprise/plugins/office365.py,sha256=bCg53GDruS7A3_PQNCjiSERhfub_FHTOVCsRwjgbjXU,25173 +apprise/plugins/one_signal.py,sha256=0ljKuz6Wyn5K8Rxp3BT8aARzHWYff0q2TWA5f-ZLJSA,18140 +apprise/plugins/opsgenie.py,sha256=wWUREzEn8FDQmmVGhmTlN7kxsYlzNj1tK3wA5t8UbCA,20502 +apprise/plugins/pagerduty.py,sha256=1vQ2rfbDUKnM0XdkVMrvTnWIPK7lTxrm-Nslt3LW5Ng,17857 +apprise/plugins/pagertree.py,sha256=uhZL2SxIJr0cguMGhJP_RIJxJZLd86uoAcMhHsBOKm8,13836 +apprise/plugins/parseplatform.py,sha256=G99l6AnHZtaw11y7oBBVVvgdg7lSM8gWFm4tAWhJ8d4,10278 +apprise/plugins/popcorn_notify.py,sha256=ppOqxPrZrOPa-TFicO3Lo_jgwOOPCNEXMgmEo10DAZA,10574 +apprise/plugins/prowl.py,sha256=fYsv6s8xA6OC0yFajgLf3jgL6IwiaOTZaJKisW4eNyw,9769 +apprise/plugins/pushbullet.py,sha256=g-7_Y0N4x-a8YE2gqDqW5OIi74YY7rja_jQ8WVKc4FI,15353 +apprise/plugins/pushdeer.py,sha256=8StOcyK1jlx3pc_CnmFUSqdjfvuv0qa6bf--FHP2_KE,6909 +apprise/plugins/pushed.py,sha256=pm3T7JGExjYeqVWQVN8J8JHkn1XBMfix_sdmr1ItXY8,12253 +apprise/plugins/pushjet.py,sha256=Xk0hzx6l5bNHtGgL0M_znRY5JRPUJf4_PhleCyFbe0I,8935 +apprise/plugins/pushme.py,sha256=EswSmup0uL3QmANeJPDhDYyyeBwSrTnqdjMlfvWFnFA,7121 +apprise/plugins/pushover.py,sha256=I6u9C1SVAEVnsLUySFq5UyHi4cQQtew5za4r86aTK10,21194 +apprise/plugins/pushsafer.py,sha256=aGQ1ibggAWGpcBwpD4oQQYMUcKVcV_xsTlc5_wdjssY,26743 +apprise/plugins/pushy.py,sha256=Ai1tKhcus5IuwvhQgtaj--RMPahgtpleWrBbr_ZQJfM,12483 +apprise/plugins/reddit.py,sha256=z54HpGeJxkyS_LH38nqPa6rqW8mrJqSEGTNOh4Cf-78,25768 +apprise/plugins/revolt.py,sha256=kVrJvTb7mBFXFZd9GIVrX2jVIvaUc5D5via48nOXNSA,14487 +apprise/plugins/rocketchat.py,sha256=oSaFzQ7XjOzjhPIPhdS7r_LGa1IJXV6VK_3xNH9Gq9M,25721 +apprise/plugins/rsyslog.py,sha256=9NTg4sYNbbAzw_MnN51YKxVQpb2o6WZUMyKpRtgoupM,11969 +apprise/plugins/ryver.py,sha256=OM_AG-oiRnNYIua-70Wp8lUpZzIkZ7erIiDE6Fd4Q6s,11810 +apprise/plugins/sendgrid.py,sha256=PGSdbkhERhFSZZN2j7km6mdojdlo7sFxs28lmOeDpgQ,16200 +apprise/plugins/serverchan.py,sha256=fcnuindQTPaKZ5gbS-nAxvL31YqkEF-vi4w8g_h7dKc,5766 +apprise/plugins/ses.py,sha256=CF17VZOEZybwdgqqu00qSoombTP5z0M8P_XxPmyc2f4,33528 +apprise/plugins/signal_api.py,sha256=PeCB_1YJc4lZwDdArTgzHP-Vhe9iwCNIYe4WVW_HgUo,16716 +apprise/plugins/simplepush.py,sha256=bL0WiRUCp39ST9X_0FNAFJJ1BFtd1t5OD_JTRneUGFY,10859 +apprise/plugins/sinch.py,sha256=0-z_cNIWMJu4kdTca-6B3raAIRV-PG1E7fUtbxUDECo,16796 +apprise/plugins/slack.py,sha256=tLbJsr97Ddp8bq1ozSEp1ssLsh913uC0Y24Xm3TN4Bo,42508 +apprise/plugins/smseagle.py,sha256=WbULwfwnUEkPw_z-2cVp2TzrupS7fkPfcQgq779nG8k,24144 +apprise/plugins/smsmanager.py,sha256=aSSZcvpWjKEdDg-XPyD-9fTkaQ0FodvuuO7Hqg7N6Tk,14112 +apprise/plugins/smtp2go.py,sha256=D3C4rWGCJ-oqaZH8JgoMEV_3BYHCCHMd6M7cG0piIvA,19698 +apprise/plugins/sns.py,sha256=dA58nElGwKKnVAr--rh1deIa9BNPbZElK6cb5G1o3g8,24142 +apprise/plugins/sparkpost.py,sha256=ZR4866e1DefqOuuu3z_vBhS8C5YX0sj1G9WEhdWFMEk,27865 +apprise/plugins/streamlabs.py,sha256=Pyrz2xY2rDo8rnTgu4L5t2u1EPrZWVcIP37k8pE2SaE,16010 +apprise/plugins/synology.py,sha256=-g-Cesx8g-nG10shjUAnYtyLpMoGfUNDcTIGfi2UXDE,11088 +apprise/plugins/syslog.py,sha256=sNKh0iSNc3ARuMIO13uwRirMHzW2VxxTRwyZ-0UYL60,10604 +apprise/plugins/techuluspush.py,sha256=HV1m72pRsC3IfVN66zWK9joslDJsKRKYD6_uRfqjnHk,7240 +apprise/plugins/telegram.py,sha256=zsmCUJU-5INyJ-RDUYIHUEXpwnwShOISWc4Xf7Epanw,37168 +apprise/plugins/threema.py,sha256=aWvFfN5Ve0DZCDX7qJVlY8INT_C-AWh-jnYnPTd7Ufs,11868 +apprise/plugins/twilio.py,sha256=0iYa2ug_2_Rbz4N_lGVihtSSxxFaK2f7Sdr9oK_Wg18,15959 +apprise/plugins/twist.py,sha256=hLqabH_STcZiAwRCgFHO7n5qYx7eZgoDw4xT5Zl_y6Q,28824 +apprise/plugins/twitter.py,sha256=ZTHLuZYowQ6qSw9ujsRB5oNp9F9PyutU-iAKAUOkmT8,30129 +apprise/plugins/voipms.py,sha256=NVbrsY7fJG0MbF6eId--0wo9cRC2kehhg8L0sASQs2w,12539 +apprise/plugins/vonage.py,sha256=PRFMdjn0ARqOEhL8kXySz8pi9vsApl2fh9mZyLVySgo,13414 +apprise/plugins/webexteams.py,sha256=DErDmHhvnHfsAyp4L3mF6vSfq0WabL1lbbl-jDUKYTw,9151 +apprise/plugins/wecombot.py,sha256=o5CO0IyHN7wAXhf7es08QiPCmUl8shZCgN_nyJxycVA,8776 +apprise/plugins/whatsapp.py,sha256=9KObQEvUOtOclnudKhNj7qRrJ0uii7dhfxgwzuMHKxY,19911 +apprise/plugins/windows.py,sha256=88bDJQBnuuA0u7_xoTC-1Ppxt4tz39UMZseRY3EX_FE,8550 +apprise/plugins/xbmc.py,sha256=2ssX2AItDUy5FJFDY9r_JI4u7qc0CLGvG-hW0KtgWag,12079 +apprise/plugins/zulip.py,sha256=PJMB8RmV_ddP5waNAQ0Z29cpIut1WFY31DLFeg8-Nao,13955 +apprise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise/url.py,sha256=QEoqmWzapzXPwBXe85SdJaPWq7B4gcv4QCeJtndN4_E,29455 +apprise/url.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520 +apprise/utils.py,sha256=Sy0pVslC8FDKh5RY8o6eVB-sPHFxBXFTdbhKSVf4UDM,53461 diff --git a/libs/apprise-1.7.4.dist-info/REQUESTED b/libs/apprise-1.8.0.dist-info/REQUESTED similarity index 100% rename from libs/apprise-1.7.4.dist-info/REQUESTED rename to libs/apprise-1.8.0.dist-info/REQUESTED diff --git a/libs/apprise-1.7.6.dist-info/WHEEL b/libs/apprise-1.8.0.dist-info/WHEEL similarity index 100% rename from libs/apprise-1.7.6.dist-info/WHEEL rename to libs/apprise-1.8.0.dist-info/WHEEL diff --git a/libs/apprise-1.7.6.dist-info/entry_points.txt b/libs/apprise-1.8.0.dist-info/entry_points.txt similarity index 100% rename from libs/apprise-1.7.6.dist-info/entry_points.txt rename to libs/apprise-1.8.0.dist-info/entry_points.txt diff --git a/libs/apprise-1.7.6.dist-info/top_level.txt b/libs/apprise-1.8.0.dist-info/top_level.txt similarity index 100% rename from libs/apprise-1.7.6.dist-info/top_level.txt rename to libs/apprise-1.8.0.dist-info/top_level.txt diff --git a/libs/apprise/Apprise.py b/libs/apprise/Apprise.py index 9a3e8dfc7..05a2ee3cc 100644 --- a/libs/apprise/Apprise.py +++ b/libs/apprise/Apprise.py @@ -33,18 +33,18 @@ from itertools import chain from . import common from .conversion import convert_between from .utils import is_exclusive_match -from .NotificationManager import NotificationManager +from .manager_plugins import NotificationManager from .utils import parse_list from .utils import parse_urls from .utils import cwe312_url from .emojis import apply_emojis from .logger import logger -from .AppriseAsset import AppriseAsset -from .AppriseConfig import AppriseConfig -from .AppriseAttachment import AppriseAttachment -from .AppriseLocale import AppriseLocale -from .config.ConfigBase import ConfigBase -from .plugins.NotifyBase import NotifyBase +from .asset import AppriseAsset +from .apprise_config import AppriseConfig +from .apprise_attachment import AppriseAttachment +from .locale import AppriseLocale +from .config.base import ConfigBase +from .plugins.base import NotifyBase from . import plugins from . import __version__ diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py index 81373c75b..457528594 100644 --- a/libs/apprise/__init__.py +++ b/libs/apprise/__init__.py @@ -27,7 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. __title__ = 'Apprise' -__version__ = '1.7.6' +__version__ = '1.8.0' __author__ = 'Chris Caron' __license__ = 'BSD' __copywrite__ = 'Copyright (C) 2024 Chris Caron ' @@ -49,16 +49,17 @@ from .common import CONTENT_INCLUDE_MODES from .common import ContentLocation from .common import CONTENT_LOCATIONS -from .URLBase import URLBase -from .URLBase import PrivacyMode -from .plugins.NotifyBase import NotifyBase -from .config.ConfigBase import ConfigBase -from .attachment.AttachBase import AttachBase +from .url import URLBase +from .url import PrivacyMode +from .plugins.base import NotifyBase +from .config.base import ConfigBase +from .attachment.base import AttachBase -from .Apprise import Apprise -from .AppriseAsset import AppriseAsset -from .AppriseConfig import AppriseConfig -from .AppriseAttachment import AppriseAttachment +from .apprise import Apprise +from .locale import AppriseLocale +from .asset import AppriseAsset +from .apprise_config import AppriseConfig +from .apprise_attachment import AppriseAttachment from . import decorators @@ -73,7 +74,7 @@ logging.getLogger(__name__).addHandler(logging.NullHandler()) __all__ = [ # Core 'Apprise', 'AppriseAsset', 'AppriseConfig', 'AppriseAttachment', 'URLBase', - 'NotifyBase', 'ConfigBase', 'AttachBase', + 'NotifyBase', 'ConfigBase', 'AttachBase', 'AppriseLocale', # Reference 'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'OverflowMode', diff --git a/libs/apprise/AppriseAttachment.py b/libs/apprise/apprise_attachment.py similarity index 98% rename from libs/apprise/AppriseAttachment.py rename to libs/apprise/apprise_attachment.py index fcfed3af6..3c33f9e73 100644 --- a/libs/apprise/AppriseAttachment.py +++ b/libs/apprise/apprise_attachment.py @@ -27,9 +27,9 @@ # POSSIBILITY OF SUCH DAMAGE. from . import URLBase -from .attachment.AttachBase import AttachBase -from .AppriseAsset import AppriseAsset -from .AttachmentManager import AttachmentManager +from .attachment.base import AttachBase +from .asset import AppriseAsset +from .manager_attachment import AttachmentManager from .logger import logger from .common import ContentLocation from .common import CONTENT_LOCATIONS diff --git a/libs/apprise/AppriseAttachment.pyi b/libs/apprise/apprise_attachment.pyi similarity index 100% rename from libs/apprise/AppriseAttachment.pyi rename to libs/apprise/apprise_attachment.pyi diff --git a/libs/apprise/AppriseConfig.py b/libs/apprise/apprise_config.py similarity index 99% rename from libs/apprise/AppriseConfig.py rename to libs/apprise/apprise_config.py index 7e5a9126f..080f70d30 100644 --- a/libs/apprise/AppriseConfig.py +++ b/libs/apprise/apprise_config.py @@ -28,9 +28,9 @@ from . import ConfigBase from . import CONFIG_FORMATS -from .ConfigurationManager import ConfigurationManager +from .manager_config import ConfigurationManager from . import URLBase -from .AppriseAsset import AppriseAsset +from .asset import AppriseAsset from . import common from .utils import GET_SCHEMA_RE from .utils import parse_list diff --git a/libs/apprise/AppriseConfig.pyi b/libs/apprise/apprise_config.pyi similarity index 100% rename from libs/apprise/AppriseConfig.pyi rename to libs/apprise/apprise_config.pyi diff --git a/libs/apprise/AppriseAsset.py b/libs/apprise/asset.py similarity index 99% rename from libs/apprise/AppriseAsset.py rename to libs/apprise/asset.py index 97a7bccfb..450303d0e 100644 --- a/libs/apprise/AppriseAsset.py +++ b/libs/apprise/asset.py @@ -33,7 +33,7 @@ from os.path import dirname from os.path import isfile from os.path import abspath from .common import NotifyType -from .NotificationManager import NotificationManager +from .manager_plugins import NotificationManager # Grant access to our Notification Manager Singleton diff --git a/libs/apprise/AppriseAsset.pyi b/libs/apprise/asset.pyi similarity index 100% rename from libs/apprise/AppriseAsset.pyi rename to libs/apprise/asset.pyi diff --git a/libs/apprise/attachment/__init__.py b/libs/apprise/attachment/__init__.py index 0a88313d6..c2aef1eec 100644 --- a/libs/apprise/attachment/__init__.py +++ b/libs/apprise/attachment/__init__.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # Used for testing -from .AttachBase import AttachBase -from ..AttachmentManager import AttachmentManager +from .base import AttachBase +from ..manager_attachment import AttachmentManager # Initalize our Attachment Manager Singleton A_MGR = AttachmentManager() @@ -36,4 +36,5 @@ A_MGR = AttachmentManager() __all__ = [ # Reference 'AttachBase', + 'AttachmentManager', ] diff --git a/libs/apprise/attachment/AttachBase.py b/libs/apprise/attachment/base.py similarity index 98% rename from libs/apprise/attachment/AttachBase.py rename to libs/apprise/attachment/base.py index 8cb6bd5cb..71e3a4d0d 100644 --- a/libs/apprise/attachment/AttachBase.py +++ b/libs/apprise/attachment/base.py @@ -29,10 +29,10 @@ import os import time import mimetypes -from ..URLBase import URLBase +from ..url import URLBase from ..utils import parse_bool from ..common import ContentLocation -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class AttachBase(URLBase): @@ -315,7 +315,7 @@ class AttachBase(URLBase): "download() is implimented by the child class.") @staticmethod - def parse_url(url, verify_host=True, mimetype_db=None): + def parse_url(url, verify_host=True, mimetype_db=None, sanitize=True): """Parses the URL and returns it broken apart into a dictionary. This is very specific and customized for Apprise. @@ -333,7 +333,8 @@ class AttachBase(URLBase): successful, otherwise None is returned. """ - results = URLBase.parse_url(url, verify_host=verify_host) + results = URLBase.parse_url( + url, verify_host=verify_host, sanitize=sanitize) if not results: # We're done; we failed to parse our url diff --git a/libs/apprise/attachment/AttachBase.pyi b/libs/apprise/attachment/base.pyi similarity index 100% rename from libs/apprise/attachment/AttachBase.pyi rename to libs/apprise/attachment/base.pyi diff --git a/libs/apprise/attachment/AttachFile.py b/libs/apprise/attachment/file.py similarity index 98% rename from libs/apprise/attachment/AttachFile.py rename to libs/apprise/attachment/file.py index 4c9c8f136..c48a707ae 100644 --- a/libs/apprise/attachment/AttachFile.py +++ b/libs/apprise/attachment/file.py @@ -28,9 +28,9 @@ import re import os -from .AttachBase import AttachBase +from .base import AttachBase from ..common import ContentLocation -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class AttachFile(AttachBase): diff --git a/libs/apprise/attachment/AttachHTTP.py b/libs/apprise/attachment/http.py similarity index 98% rename from libs/apprise/attachment/AttachHTTP.py rename to libs/apprise/attachment/http.py index 5a3af9467..aa075d671 100644 --- a/libs/apprise/attachment/AttachHTTP.py +++ b/libs/apprise/attachment/http.py @@ -31,10 +31,10 @@ import os import requests import threading from tempfile import NamedTemporaryFile -from .AttachBase import AttachBase +from .base import AttachBase from ..common import ContentLocation -from ..URLBase import PrivacyMode -from ..AppriseLocale import gettext_lazy as _ +from ..url import PrivacyMode +from ..locale import gettext_lazy as _ class AttachHTTP(AttachBase): @@ -296,8 +296,7 @@ class AttachHTTP(AttachBase): """ Tidy memory if open """ - with self._lock: - self.invalidate() + self.invalidate() def url(self, privacy=False, *args, **kwargs): """ @@ -363,8 +362,7 @@ class AttachHTTP(AttachBase): us to re-instantiate this object. """ - results = AttachBase.parse_url(url) - + results = AttachBase.parse_url(url, sanitize=False) if not results: # We're done early as we couldn't load the results return results diff --git a/libs/apprise/config/__init__.py b/libs/apprise/config/__init__.py index efbace687..24957e88e 100644 --- a/libs/apprise/config/__init__.py +++ b/libs/apprise/config/__init__.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # Used for testing -from .ConfigBase import ConfigBase -from ..ConfigurationManager import ConfigurationManager +from .base import ConfigBase +from ..manager_config import ConfigurationManager # Initalize our Config Manager Singleton C_MGR = ConfigurationManager() @@ -36,4 +36,5 @@ C_MGR = ConfigurationManager() __all__ = [ # Reference 'ConfigBase', + 'ConfigurationManager', ] diff --git a/libs/apprise/config/ConfigBase.py b/libs/apprise/config/base.py similarity index 99% rename from libs/apprise/config/ConfigBase.py rename to libs/apprise/config/base.py index 32e1bde34..953cee394 100644 --- a/libs/apprise/config/ConfigBase.py +++ b/libs/apprise/config/base.py @@ -33,15 +33,15 @@ import time from .. import plugins from .. import common -from ..AppriseAsset import AppriseAsset -from ..URLBase import URLBase -from ..ConfigurationManager import ConfigurationManager +from ..asset import AppriseAsset +from ..url import URLBase from ..utils import GET_SCHEMA_RE from ..utils import parse_list from ..utils import parse_bool from ..utils import parse_urls from ..utils import cwe312_url -from ..NotificationManager import NotificationManager +from ..manager_config import ConfigurationManager +from ..manager_plugins import NotificationManager # Test whether token is valid or not VALID_TOKEN = re.compile( diff --git a/libs/apprise/config/ConfigBase.pyi b/libs/apprise/config/base.pyi similarity index 100% rename from libs/apprise/config/ConfigBase.pyi rename to libs/apprise/config/base.pyi diff --git a/libs/apprise/config/ConfigFile.py b/libs/apprise/config/file.py similarity index 98% rename from libs/apprise/config/ConfigFile.py rename to libs/apprise/config/file.py index 172d699f8..9f29ca20b 100644 --- a/libs/apprise/config/ConfigFile.py +++ b/libs/apprise/config/file.py @@ -28,10 +28,10 @@ import re import os -from .ConfigBase import ConfigBase +from .base import ConfigBase from ..common import ConfigFormat from ..common import ContentIncludeMode -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class ConfigFile(ConfigBase): diff --git a/libs/apprise/config/ConfigHTTP.py b/libs/apprise/config/http.py similarity index 98% rename from libs/apprise/config/ConfigHTTP.py rename to libs/apprise/config/http.py index f6faba8d4..2e2ba299b 100644 --- a/libs/apprise/config/ConfigHTTP.py +++ b/libs/apprise/config/http.py @@ -28,11 +28,11 @@ import re import requests -from .ConfigBase import ConfigBase +from .base import ConfigBase from ..common import ConfigFormat from ..common import ContentIncludeMode -from ..URLBase import PrivacyMode -from ..AppriseLocale import gettext_lazy as _ +from ..url import PrivacyMode +from ..locale import gettext_lazy as _ # Support YAML formats # text/yaml diff --git a/libs/apprise/config/ConfigMemory.py b/libs/apprise/config/memory.py similarity index 97% rename from libs/apprise/config/ConfigMemory.py rename to libs/apprise/config/memory.py index 413956dfc..181d76236 100644 --- a/libs/apprise/config/ConfigMemory.py +++ b/libs/apprise/config/memory.py @@ -26,8 +26,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .ConfigBase import ConfigBase -from ..AppriseLocale import gettext_lazy as _ +from .base import ConfigBase +from ..locale import gettext_lazy as _ class ConfigMemory(ConfigBase): diff --git a/libs/apprise/conversion.py b/libs/apprise/conversion.py index 4d5632f59..5b6d1a941 100644 --- a/libs/apprise/conversion.py +++ b/libs/apprise/conversion.py @@ -29,7 +29,7 @@ import re from markdown import markdown from .common import NotifyFormat -from .URLBase import URLBase +from .url import URLBase from html.parser import HTMLParser diff --git a/libs/apprise/decorators/CustomNotifyPlugin.py b/libs/apprise/decorators/base.py similarity index 98% rename from libs/apprise/decorators/CustomNotifyPlugin.py rename to libs/apprise/decorators/base.py index eb5f17b78..2661db0aa 100644 --- a/libs/apprise/decorators/CustomNotifyPlugin.py +++ b/libs/apprise/decorators/base.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE.USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from ..plugins.NotifyBase import NotifyBase -from ..NotificationManager import NotificationManager +from ..plugins.base import NotifyBase +from ..manager_plugins import NotificationManager from ..utils import URL_DETAILS_RE from ..utils import parse_url from ..utils import url_assembly @@ -55,6 +55,9 @@ class CustomNotifyPlugin(NotifyBase): # should be treated differently. category = 'custom' + # Support Attachments + attachment_support = True + # Define object templates templates = ( '{schema}://', diff --git a/libs/apprise/decorators/notify.py b/libs/apprise/decorators/notify.py index 2dd5f5099..892c3adfe 100644 --- a/libs/apprise/decorators/notify.py +++ b/libs/apprise/decorators/notify.py @@ -26,7 +26,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .CustomNotifyPlugin import CustomNotifyPlugin +from .base import CustomNotifyPlugin def notify(on, name=None): diff --git a/libs/apprise/i18n/en/LC_MESSAGES/apprise.mo b/libs/apprise/i18n/en/LC_MESSAGES/apprise.mo index 1d22b89a68a0bcb2532b945c30a51bab4eccad96..f108b538df44a22b2a18b2bb6140e8554b0428c1 100644 GIT binary patch delta 14 Vcmew^_g!v7HaDa3<{WNr762`h1ik((?!_)[A-Za-z0-9]+))$') + # thread safe loading _lock = threading.Lock() @@ -177,7 +180,7 @@ class PluginManager(metaclass=Singleton): # The .py extension is optional as we support loading directories # too module_re = re.compile( - r'^(?P' + self.fname_prefix + r'[a-z0-9]+)(\.py)?$', + r'^(?P(?!base|_)[a-z0-9_]+)(\.py)?$', re.I) t_start = time.time() @@ -188,10 +191,6 @@ class PluginManager(metaclass=Singleton): # keep going continue - elif match.group('name') == f'{self.fname_prefix}Base': - # keep going - continue - # Store our notification/plugin name: module_name = match.group('name') module_pyname = '{}.{}'.format(module_name_prefix, module_name) @@ -216,7 +215,47 @@ class PluginManager(metaclass=Singleton): # logging found in import_module and not needed here continue - if not hasattr(module, module_name): + module_class = None + for m_class in [obj for obj in dir(module) + if self.module_filter_re.match(obj)]: + # Get our plugin + plugin = getattr(module, m_class) + if not hasattr(plugin, 'app_id'): + # Filter out non-notification modules + logger.trace( + "(%s) import failed; no app_id defined in %s", + self.name, m_class, os.path.join(module_path, f)) + continue + + # Add our plugin name to our module map + self._module_map[module_name] = { + 'plugin': set([plugin]), + 'module': module, + 'path': '{}.{}'.format( + module_name_prefix, module_name), + 'native': True, + } + + fn = getattr(plugin, 'schemas', None) + schemas = set([]) if not callable(fn) else fn(plugin) + + # map our schema to our plugin + for schema in schemas: + if schema in self._schema_map: + logger.error( + "{} schema ({}) mismatch detected - {} to {}" + .format(self.name, schema, self._schema_map, + plugin)) + continue + + # Assign plugin + self._schema_map[schema] = plugin + + # Store our class + module_class = m_class + break + + if not module_class: # Not a library we can load as it doesn't follow the simple # rule that the class must bear the same name as the # notification file itself. @@ -226,38 +265,6 @@ class PluginManager(metaclass=Singleton): self.name, module_name, os.path.join(module_path, f)) continue - # Get our plugin - plugin = getattr(module, module_name) - if not hasattr(plugin, 'app_id'): - # Filter out non-notification modules - logger.trace( - "(%s) import failed; no app_id defined in %s", - self.name, module_name, os.path.join(module_path, f)) - continue - - # Add our plugin name to our module map - self._module_map[module_name] = { - 'plugin': set([plugin]), - 'module': module, - 'path': '{}.{}'.format(module_name_prefix, module_name), - 'native': True, - } - - fn = getattr(plugin, 'schemas', None) - schemas = set([]) if not callable(fn) else fn(plugin) - - # map our schema to our plugin - for schema in schemas: - if schema in self._schema_map: - logger.error( - "{} schema ({}) mismatch detected - {} to {}" - .format(self.name, schema, self._schema_map, - plugin)) - continue - - # Assign plugin - self._schema_map[schema] = plugin - logger.trace( '{} {} loaded in {:.6f}s'.format( self.name, module_name, (time.time() - tl_start))) diff --git a/libs/apprise/AttachmentManager.py b/libs/apprise/manager_attachment.py similarity index 93% rename from libs/apprise/AttachmentManager.py rename to libs/apprise/manager_attachment.py index d296a4996..d1288a943 100644 --- a/libs/apprise/AttachmentManager.py +++ b/libs/apprise/manager_attachment.py @@ -26,6 +26,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +import re from os.path import dirname from os.path import abspath from os.path import join @@ -52,3 +53,7 @@ class AttachmentManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) + + # For filtering our result set + module_filter_re = re.compile( + r'^(?P' + fname_prefix + r'(?!Base)[A-Za-z0-9]+)$') diff --git a/libs/apprise/ConfigurationManager.py b/libs/apprise/manager_config.py similarity index 93% rename from libs/apprise/ConfigurationManager.py rename to libs/apprise/manager_config.py index 6696895b9..69a6bedb9 100644 --- a/libs/apprise/ConfigurationManager.py +++ b/libs/apprise/manager_config.py @@ -26,6 +26,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +import re from os.path import dirname from os.path import abspath from os.path import join @@ -52,3 +53,7 @@ class ConfigurationManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) + + # For filtering our result set + module_filter_re = re.compile( + r'^(?P' + fname_prefix + r'(?!Base)[A-Za-z0-9]+)$') diff --git a/libs/apprise/NotificationManager.py b/libs/apprise/manager_plugins.py similarity index 92% rename from libs/apprise/NotificationManager.py rename to libs/apprise/manager_plugins.py index abbbdd203..74ed370ea 100644 --- a/libs/apprise/NotificationManager.py +++ b/libs/apprise/manager_plugins.py @@ -26,6 +26,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. +import re from os.path import dirname from os.path import abspath from os.path import join @@ -52,3 +53,8 @@ class NotificationManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) + + # For filtering our result set + module_filter_re = re.compile( + r'^(?P' + fname_prefix + + r'(?!Base|ImageSize|Type)[A-Za-z0-9]+)$') diff --git a/libs/apprise/plugins/__init__.py b/libs/apprise/plugins/__init__.py index 72cb08fbf..bfce14371 100644 --- a/libs/apprise/plugins/__init__.py +++ b/libs/apprise/plugins/__init__.py @@ -30,7 +30,7 @@ import os import copy # Used for testing -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NOTIFY_IMAGE_SIZES @@ -40,9 +40,9 @@ from ..utils import parse_list from ..utils import cwe312_url from ..utils import GET_SCHEMA_RE from ..logger import logger -from ..AppriseLocale import gettext_lazy as _ -from ..AppriseLocale import LazyTranslation -from ..NotificationManager import NotificationManager +from ..locale import gettext_lazy as _ +from ..locale import LazyTranslation +from ..manager_plugins import NotificationManager # Grant access to our Notification Manager Singleton diff --git a/libs/apprise/plugins/NotifyAppriseAPI.py b/libs/apprise/plugins/apprise_api.py similarity index 99% rename from libs/apprise/plugins/NotifyAppriseAPI.py rename to libs/apprise/plugins/apprise_api.py index 34c34a6d4..fd71236b5 100644 --- a/libs/apprise/plugins/NotifyAppriseAPI.py +++ b/libs/apprise/plugins/apprise_api.py @@ -31,12 +31,12 @@ import requests from json import dumps import base64 -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class AppriseAPIMethod: @@ -123,7 +123,7 @@ class NotifyAppriseAPI(NotifyBase): 'type': 'string', 'required': True, 'private': True, - 'regex': (r'^[A-Z0-9_-]{1,32}$', 'i'), + 'regex': (r'^[A-Z0-9_-]{1,128}$', 'i'), }, }) diff --git a/libs/apprise/plugins/NotifyAprs.py b/libs/apprise/plugins/aprs.py similarity index 99% rename from libs/apprise/plugins/NotifyAprs.py rename to libs/apprise/plugins/aprs.py index 5d8c3c100..b8adef5aa 100644 --- a/libs/apprise/plugins/NotifyAprs.py +++ b/libs/apprise/plugins/aprs.py @@ -70,9 +70,9 @@ import socket import sys from itertools import chain -from .NotifyBase import NotifyBase -from ..AppriseLocale import gettext_lazy as _ -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..locale import gettext_lazy as _ +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_call_sign from ..utils import parse_call_sign diff --git a/libs/apprise/plugins/NotifyBark.py b/libs/apprise/plugins/bark.py similarity index 99% rename from libs/apprise/plugins/NotifyBark.py rename to libs/apprise/plugins/bark.py index 781a1515e..e2f5bbfb4 100644 --- a/libs/apprise/plugins/NotifyBark.py +++ b/libs/apprise/plugins/bark.py @@ -32,13 +32,13 @@ import requests import json -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Sounds generated off of: https://github.com/Finb/Bark/tree/master/Sounds diff --git a/libs/apprise/plugins/NotifyBase.py b/libs/apprise/plugins/base.py similarity index 99% rename from libs/apprise/plugins/NotifyBase.py rename to libs/apprise/plugins/base.py index c29417c60..1abc3410e 100644 --- a/libs/apprise/plugins/NotifyBase.py +++ b/libs/apprise/plugins/base.py @@ -30,7 +30,7 @@ import asyncio import re from functools import partial -from ..URLBase import URLBase +from ..url import URLBase from ..common import NotifyType from ..utils import parse_bool from ..common import NOTIFY_TYPES @@ -38,8 +38,8 @@ from ..common import NotifyFormat from ..common import NOTIFY_FORMATS from ..common import OverflowMode from ..common import OVERFLOW_MODES -from ..AppriseLocale import gettext_lazy as _ -from ..AppriseAttachment import AppriseAttachment +from ..locale import gettext_lazy as _ +from ..apprise_attachment import AppriseAttachment class NotifyBase(URLBase): diff --git a/libs/apprise/plugins/NotifyBase.pyi b/libs/apprise/plugins/base.pyi similarity index 100% rename from libs/apprise/plugins/NotifyBase.pyi rename to libs/apprise/plugins/base.pyi diff --git a/libs/apprise/plugins/NotifyBoxcar.py b/libs/apprise/plugins/boxcar.py similarity index 99% rename from libs/apprise/plugins/NotifyBoxcar.py rename to libs/apprise/plugins/boxcar.py index 808920ed5..851cdd3d8 100644 --- a/libs/apprise/plugins/NotifyBoxcar.py +++ b/libs/apprise/plugins/boxcar.py @@ -35,14 +35,14 @@ from hashlib import sha1 from itertools import chain from urllib.parse import urlparse -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex from ..common import NotifyType from ..common import NotifyImageSize -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default to sending to all devices if nothing is specified DEFAULT_TAG = '@all' diff --git a/libs/apprise/plugins/NotifyBulkSMS.py b/libs/apprise/plugins/bulksms.py similarity index 99% rename from libs/apprise/plugins/NotifyBulkSMS.py rename to libs/apprise/plugins/bulksms.py index 33664fb00..29c4d7fac 100644 --- a/libs/apprise/plugins/NotifyBulkSMS.py +++ b/libs/apprise/plugins/bulksms.py @@ -36,13 +36,13 @@ import re import requests import json from itertools import chain -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ IS_GROUP_RE = re.compile( diff --git a/libs/apprise/plugins/NotifyBulkVS.py b/libs/apprise/plugins/bulkvs.py similarity index 99% rename from libs/apprise/plugins/NotifyBulkVS.py rename to libs/apprise/plugins/bulkvs.py index e912dff25..53a363008 100644 --- a/libs/apprise/plugins/NotifyBulkVS.py +++ b/libs/apprise/plugins/bulkvs.py @@ -35,13 +35,13 @@ # Messaging/post_messageSend import requests import json -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyBulkVS(NotifyBase): diff --git a/libs/apprise/plugins/NotifyBurstSMS.py b/libs/apprise/plugins/burstsms.py similarity index 99% rename from libs/apprise/plugins/NotifyBurstSMS.py rename to libs/apprise/plugins/burstsms.py index 39606abba..eb19df8e4 100644 --- a/libs/apprise/plugins/NotifyBurstSMS.py +++ b/libs/apprise/plugins/burstsms.py @@ -33,14 +33,14 @@ # import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class BurstSMSCountryCode: diff --git a/libs/apprise/plugins/NotifyChantify.py b/libs/apprise/plugins/chantify.py similarity index 98% rename from libs/apprise/plugins/NotifyChantify.py rename to libs/apprise/plugins/chantify.py index d912bd257..d549a59fa 100644 --- a/libs/apprise/plugins/NotifyChantify.py +++ b/libs/apprise/plugins/chantify.py @@ -35,10 +35,10 @@ import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyChantify(NotifyBase): diff --git a/libs/apprise/plugins/NotifyClickSend.py b/libs/apprise/plugins/clicksend.py similarity index 94% rename from libs/apprise/plugins/NotifyClickSend.py rename to libs/apprise/plugins/clicksend.py index 5e345fe10..9ade1055e 100644 --- a/libs/apprise/plugins/NotifyClickSend.py +++ b/libs/apprise/plugins/clicksend.py @@ -41,15 +41,14 @@ # import requests from json import dumps -from base64 import b64encode -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages CLICKSEND_HTTP_ERROR_MAP = { @@ -89,7 +88,7 @@ class NotifyClickSend(NotifyBase): # Define object templates templates = ( - '{schema}://{user}:{password}@{targets}', + '{schema}://{user}:{apikey}@{targets}', ) # Define our template tokens @@ -99,11 +98,12 @@ class NotifyClickSend(NotifyBase): 'type': 'string', 'required': True, }, - 'password': { - 'name': _('Password'), + 'apikey': { + 'name': _('API Key'), 'type': 'string', 'private': True, 'required': True, + 'map_to': 'password', }, 'target_phone': { 'name': _('Target Phone No'), @@ -124,6 +124,9 @@ class NotifyClickSend(NotifyBase): 'to': { 'alias_of': 'targets', }, + 'key': { + 'alias_of': 'apikey', + }, 'batch': { 'name': _('Batch Mode'), 'type': 'bool', @@ -174,9 +177,6 @@ class NotifyClickSend(NotifyBase): headers = { 'User-Agent': self.app_id, 'Content-Type': 'application/json; charset=utf-8', - 'Authorization': 'Basic {}'.format( - b64encode('{}:{}'.format( - self.user, self.password).encode('utf-8'))), } # error tracking (used for function return) @@ -208,6 +208,7 @@ class NotifyClickSend(NotifyBase): r = requests.post( self.notify_url, data=dumps(payload), + auth=(self.user, self.password), headers=headers, verify=self.verify_certificate, timeout=self.request_timeout, @@ -322,6 +323,12 @@ class NotifyClickSend(NotifyBase): results['batch'] = \ parse_bool(results['qsd'].get('batch', False)) + # API Key + if 'key' in results['qsd'] and len(results['qsd']['key']): + # Extract the API Key from an argument + results['password'] = \ + NotifyClickSend.unquote(results['qsd']['key']) + # Support the 'to' variable so that we can support rooms this way too # The 'to' makes it easier to use yaml configuration if 'to' in results['qsd'] and len(results['qsd']['to']): diff --git a/libs/apprise/plugins/NotifyForm.py b/libs/apprise/plugins/custom_form.py similarity index 99% rename from libs/apprise/plugins/NotifyForm.py rename to libs/apprise/plugins/custom_form.py index 9690cd4f5..0f36643f7 100644 --- a/libs/apprise/plugins/NotifyForm.py +++ b/libs/apprise/plugins/custom_form.py @@ -29,11 +29,11 @@ import re import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class FORMPayloadField: diff --git a/libs/apprise/plugins/NotifyJSON.py b/libs/apprise/plugins/custom_json.py similarity index 99% rename from libs/apprise/plugins/NotifyJSON.py rename to libs/apprise/plugins/custom_json.py index 182ff77cf..e0d7a6753 100644 --- a/libs/apprise/plugins/NotifyJSON.py +++ b/libs/apprise/plugins/custom_json.py @@ -30,11 +30,11 @@ import requests import base64 from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class JSONPayloadField: diff --git a/libs/apprise/plugins/NotifyXML.py b/libs/apprise/plugins/custom_xml.py similarity index 99% rename from libs/apprise/plugins/NotifyXML.py rename to libs/apprise/plugins/custom_xml.py index 21ccb79d3..b7928fceb 100644 --- a/libs/apprise/plugins/NotifyXML.py +++ b/libs/apprise/plugins/custom_xml.py @@ -30,11 +30,11 @@ import re import requests import base64 -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class XMLPayloadField: diff --git a/libs/apprise/plugins/NotifyD7Networks.py b/libs/apprise/plugins/d7networks.py similarity index 99% rename from libs/apprise/plugins/NotifyD7Networks.py rename to libs/apprise/plugins/d7networks.py index 906ec2fb9..ad55e2197 100644 --- a/libs/apprise/plugins/NotifyD7Networks.py +++ b/libs/apprise/plugins/d7networks.py @@ -39,13 +39,13 @@ import requests from json import dumps from json import loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages D7NETWORKS_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/NotifyDapnet.py b/libs/apprise/plugins/dapnet.py similarity index 99% rename from libs/apprise/plugins/NotifyDapnet.py rename to libs/apprise/plugins/dapnet.py index ae7199c94..60a18acd4 100644 --- a/libs/apprise/plugins/NotifyDapnet.py +++ b/libs/apprise/plugins/dapnet.py @@ -51,9 +51,9 @@ from json import dumps import requests from requests.auth import HTTPBasicAuth -from .NotifyBase import NotifyBase -from ..AppriseLocale import gettext_lazy as _ -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..locale import gettext_lazy as _ +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_call_sign from ..utils import parse_call_sign diff --git a/libs/apprise/plugins/NotifyDBus.py b/libs/apprise/plugins/dbus.py similarity index 99% rename from libs/apprise/plugins/NotifyDBus.py rename to libs/apprise/plugins/dbus.py index 52e119813..f2361fd62 100644 --- a/libs/apprise/plugins/NotifyDBus.py +++ b/libs/apprise/plugins/dbus.py @@ -27,11 +27,11 @@ # POSSIBILITY OF SUCH DAMAGE. import sys -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_DBUS_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyDingTalk.py b/libs/apprise/plugins/dingtalk.py similarity index 99% rename from libs/apprise/plugins/NotifyDingTalk.py rename to libs/apprise/plugins/dingtalk.py index d4a492fc7..2ca1bc55b 100644 --- a/libs/apprise/plugins/NotifyDingTalk.py +++ b/libs/apprise/plugins/dingtalk.py @@ -34,13 +34,13 @@ import base64 import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Register at https://dingtalk.com # - Download their PC based software as it is the only way you can create diff --git a/libs/apprise/plugins/NotifyDiscord.py b/libs/apprise/plugins/discord.py similarity index 99% rename from libs/apprise/plugins/NotifyDiscord.py rename to libs/apprise/plugins/discord.py index 82d764f50..14c6152ba 100644 --- a/libs/apprise/plugins/NotifyDiscord.py +++ b/libs/apprise/plugins/discord.py @@ -50,14 +50,14 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase # Used to detect user/role IDs diff --git a/libs/apprise/plugins/NotifyEmail.py b/libs/apprise/plugins/email.py similarity index 98% rename from libs/apprise/plugins/NotifyEmail.py rename to libs/apprise/plugins/email.py index 80f88bf61..142c93cfb 100644 --- a/libs/apprise/plugins/NotifyEmail.py +++ b/libs/apprise/plugins/email.py @@ -41,12 +41,12 @@ from socket import error as SocketError from datetime import datetime from datetime import timezone -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat, NotifyType from ..conversion import convert_between -from ..utils import is_email, parse_emails, is_hostname -from ..AppriseLocale import gettext_lazy as _ +from ..utils import is_ipaddr, is_email, parse_emails, is_hostname +from ..locale import gettext_lazy as _ from ..logger import logger # Globally Default encoding mode set to Quoted Printable. @@ -1053,8 +1053,12 @@ class NotifyEmail(NotifyBase): # Prepare our target lists results['targets'] = [] - if not is_hostname(results['host'], ipv4=False, ipv6=False, - underscore=False): + if is_ipaddr(results['host']): + # Silently move on and do not disrupt any configuration + pass + + elif not is_hostname(results['host'], ipv4=False, ipv6=False, + underscore=False): if is_email(NotifyEmail.unquote(results['host'])): # Don't lose defined email addresses diff --git a/libs/apprise/plugins/NotifyEmby.py b/libs/apprise/plugins/emby.py similarity index 99% rename from libs/apprise/plugins/NotifyEmby.py rename to libs/apprise/plugins/emby.py index ce96553a2..5e4e0b89f 100644 --- a/libs/apprise/plugins/NotifyEmby.py +++ b/libs/apprise/plugins/emby.py @@ -34,12 +34,12 @@ import hashlib from json import dumps from json import loads -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..utils import parse_bool from ..common import NotifyType from .. import __version__ as VERSION -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyEmby(NotifyBase): diff --git a/libs/apprise/plugins/NotifyEnigma2.py b/libs/apprise/plugins/enigma2.py similarity index 98% rename from libs/apprise/plugins/NotifyEnigma2.py rename to libs/apprise/plugins/enigma2.py index 313149993..8b1fff687 100644 --- a/libs/apprise/plugins/NotifyEnigma2.py +++ b/libs/apprise/plugins/enigma2.py @@ -37,10 +37,10 @@ import requests from json import loads -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class Enigma2MessageType: diff --git a/libs/apprise/plugins/NotifyFCM/__init__.py b/libs/apprise/plugins/fcm/__init__.py similarity index 99% rename from libs/apprise/plugins/NotifyFCM/__init__.py rename to libs/apprise/plugins/fcm/__init__.py index 54b6c9cc7..9dc0679f1 100644 --- a/libs/apprise/plugins/NotifyFCM/__init__.py +++ b/libs/apprise/plugins/fcm/__init__.py @@ -50,15 +50,15 @@ # You will need this in order to send an apprise messag import requests from json import dumps -from ..NotifyBase import NotifyBase +from ..base import NotifyBase from ...common import NotifyType from ...utils import validate_regex from ...utils import parse_list from ...utils import parse_bool from ...utils import dict_full_update from ...common import NotifyImageSize -from ...AppriseAttachment import AppriseAttachment -from ...AppriseLocale import gettext_lazy as _ +from ...apprise_attachment import AppriseAttachment +from ...locale import gettext_lazy as _ from .common import (FCMMode, FCM_MODES) from .priority import (FCM_PRIORITIES, FCMPriorityManager) from .color import FCMColorManager diff --git a/libs/apprise/plugins/NotifyFCM/color.py b/libs/apprise/plugins/fcm/color.py similarity index 99% rename from libs/apprise/plugins/NotifyFCM/color.py rename to libs/apprise/plugins/fcm/color.py index c7da209a7..20149eedd 100644 --- a/libs/apprise/plugins/NotifyFCM/color.py +++ b/libs/apprise/plugins/fcm/color.py @@ -36,7 +36,7 @@ import re from ...utils import parse_bool from ...common import NotifyType -from ...AppriseAsset import AppriseAsset +from ...asset import AppriseAsset class FCMColorManager: diff --git a/libs/apprise/plugins/NotifyFCM/common.py b/libs/apprise/plugins/fcm/common.py similarity index 100% rename from libs/apprise/plugins/NotifyFCM/common.py rename to libs/apprise/plugins/fcm/common.py diff --git a/libs/apprise/plugins/NotifyFCM/oauth.py b/libs/apprise/plugins/fcm/oauth.py similarity index 100% rename from libs/apprise/plugins/NotifyFCM/oauth.py rename to libs/apprise/plugins/fcm/oauth.py diff --git a/libs/apprise/plugins/NotifyFCM/priority.py b/libs/apprise/plugins/fcm/priority.py similarity index 100% rename from libs/apprise/plugins/NotifyFCM/priority.py rename to libs/apprise/plugins/fcm/priority.py diff --git a/libs/apprise/plugins/NotifyFeishu.py b/libs/apprise/plugins/feishu.py similarity index 98% rename from libs/apprise/plugins/NotifyFeishu.py rename to libs/apprise/plugins/feishu.py index e6988333c..961523bab 100644 --- a/libs/apprise/plugins/NotifyFeishu.py +++ b/libs/apprise/plugins/feishu.py @@ -36,10 +36,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyFeishu(NotifyBase): diff --git a/libs/apprise/plugins/NotifyFlock.py b/libs/apprise/plugins/flock.py similarity index 99% rename from libs/apprise/plugins/NotifyFlock.py rename to libs/apprise/plugins/flock.py index f1d12067e..bf2cd131d 100644 --- a/libs/apprise/plugins/NotifyFlock.py +++ b/libs/apprise/plugins/flock.py @@ -44,14 +44,14 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..common import NotifyImageSize from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages diff --git a/libs/apprise/plugins/NotifyFreeMobile.py b/libs/apprise/plugins/freemobile.py similarity index 98% rename from libs/apprise/plugins/NotifyFreeMobile.py rename to libs/apprise/plugins/freemobile.py index 4aad8db3d..4ff3d4822 100644 --- a/libs/apprise/plugins/NotifyFreeMobile.py +++ b/libs/apprise/plugins/freemobile.py @@ -36,9 +36,9 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyFreeMobile(NotifyBase): @@ -126,6 +126,7 @@ class NotifyFreeMobile(NotifyBase): # prepare our headers headers = { 'User-Agent': self.app_id, + 'Content-Type': 'application/json', } # Prepare our payload diff --git a/libs/apprise/plugins/NotifyGnome.py b/libs/apprise/plugins/gnome.py similarity index 99% rename from libs/apprise/plugins/NotifyGnome.py rename to libs/apprise/plugins/gnome.py index 67129216d..b64b51304 100644 --- a/libs/apprise/plugins/NotifyGnome.py +++ b/libs/apprise/plugins/gnome.py @@ -26,11 +26,11 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_GNOME_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyGoogleChat.py b/libs/apprise/plugins/google_chat.py similarity index 99% rename from libs/apprise/plugins/NotifyGoogleChat.py rename to libs/apprise/plugins/google_chat.py index d2a6cc8a8..f30cdae49 100644 --- a/libs/apprise/plugins/NotifyGoogleChat.py +++ b/libs/apprise/plugins/google_chat.py @@ -58,11 +58,11 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyGoogleChat(NotifyBase): diff --git a/libs/apprise/plugins/NotifyGotify.py b/libs/apprise/plugins/gotify.py similarity index 99% rename from libs/apprise/plugins/NotifyGotify.py rename to libs/apprise/plugins/gotify.py index 3f4ce132d..bf6c1b281 100644 --- a/libs/apprise/plugins/NotifyGotify.py +++ b/libs/apprise/plugins/gotify.py @@ -34,10 +34,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType, NotifyFormat from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Priorities diff --git a/libs/apprise/plugins/NotifyGrowl.py b/libs/apprise/plugins/growl.py similarity index 99% rename from libs/apprise/plugins/NotifyGrowl.py rename to libs/apprise/plugins/growl.py index 0b42e3bec..0b3672188 100644 --- a/libs/apprise/plugins/NotifyGrowl.py +++ b/libs/apprise/plugins/growl.py @@ -26,12 +26,12 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_GROWL_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyGuilded.py b/libs/apprise/plugins/guilded.py similarity index 96% rename from libs/apprise/plugins/NotifyGuilded.py rename to libs/apprise/plugins/guilded.py index 0ea36d9f8..0ee07018e 100644 --- a/libs/apprise/plugins/NotifyGuilded.py +++ b/libs/apprise/plugins/guilded.py @@ -45,10 +45,11 @@ # import re -from .NotifyDiscord import NotifyDiscord +# Import namespace so the class won't conflict with the actual Notify object +from . import discord -class NotifyGuilded(NotifyDiscord): +class NotifyGuilded(discord.NotifyDiscord): """ A wrapper to Guilded Notifications diff --git a/libs/apprise/plugins/NotifyHomeAssistant.py b/libs/apprise/plugins/home_assistant.py similarity index 98% rename from libs/apprise/plugins/NotifyHomeAssistant.py rename to libs/apprise/plugins/home_assistant.py index 0829381b9..b0ffcaa64 100644 --- a/libs/apprise/plugins/NotifyHomeAssistant.py +++ b/libs/apprise/plugins/home_assistant.py @@ -34,11 +34,11 @@ from json import dumps from uuid import uuid4 -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyHomeAssistant(NotifyBase): diff --git a/libs/apprise/plugins/NotifyHttpSMS.py b/libs/apprise/plugins/httpsms.py similarity index 99% rename from libs/apprise/plugins/NotifyHttpSMS.py rename to libs/apprise/plugins/httpsms.py index 647100949..b36e286d7 100644 --- a/libs/apprise/plugins/NotifyHttpSMS.py +++ b/libs/apprise/plugins/httpsms.py @@ -31,12 +31,12 @@ # https://httpsms.com import requests import json -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyHttpSMS(NotifyBase): diff --git a/libs/apprise/plugins/NotifyIFTTT.py b/libs/apprise/plugins/ifttt.py similarity index 99% rename from libs/apprise/plugins/NotifyIFTTT.py rename to libs/apprise/plugins/ifttt.py index 9174640d0..9d89b1464 100644 --- a/libs/apprise/plugins/NotifyIFTTT.py +++ b/libs/apprise/plugins/ifttt.py @@ -44,11 +44,11 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyIFTTT(NotifyBase): diff --git a/libs/apprise/plugins/NotifyJoin.py b/libs/apprise/plugins/join.py similarity index 99% rename from libs/apprise/plugins/NotifyJoin.py rename to libs/apprise/plugins/join.py index c6b0d91e9..b92bb37a3 100644 --- a/libs/apprise/plugins/NotifyJoin.py +++ b/libs/apprise/plugins/join.py @@ -39,13 +39,13 @@ import re import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages JOIN_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/NotifyKavenegar.py b/libs/apprise/plugins/kavenegar.py similarity index 99% rename from libs/apprise/plugins/NotifyKavenegar.py rename to libs/apprise/plugins/kavenegar.py index 2a9c169d7..e4963f409 100644 --- a/libs/apprise/plugins/NotifyKavenegar.py +++ b/libs/apprise/plugins/kavenegar.py @@ -38,12 +38,12 @@ import requests from json import loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages # Based on https://kavenegar.com/rest.html diff --git a/libs/apprise/plugins/NotifyKumulos.py b/libs/apprise/plugins/kumulos.py similarity index 99% rename from libs/apprise/plugins/NotifyKumulos.py rename to libs/apprise/plugins/kumulos.py index da372e773..504dcc379 100644 --- a/libs/apprise/plugins/NotifyKumulos.py +++ b/libs/apprise/plugins/kumulos.py @@ -39,10 +39,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages KUMULOS_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/NotifyLametric.py b/libs/apprise/plugins/lametric.py similarity index 99% rename from libs/apprise/plugins/NotifyLametric.py rename to libs/apprise/plugins/lametric.py index 5825d9176..411b9ea21 100644 --- a/libs/apprise/plugins/NotifyLametric.py +++ b/libs/apprise/plugins/lametric.py @@ -90,10 +90,10 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from ..utils import is_hostname from ..utils import is_ipaddr diff --git a/libs/apprise/plugins/NotifyLine.py b/libs/apprise/plugins/line.py similarity index 98% rename from libs/apprise/plugins/NotifyLine.py rename to libs/apprise/plugins/line.py index 61e4f3703..07a01e769 100644 --- a/libs/apprise/plugins/NotifyLine.py +++ b/libs/apprise/plugins/line.py @@ -33,14 +33,14 @@ import requests import re from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_list from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Used to break path apart into list of streams diff --git a/libs/apprise/plugins/NotifyLunaSea.py b/libs/apprise/plugins/lunasea.py similarity index 99% rename from libs/apprise/plugins/NotifyLunaSea.py rename to libs/apprise/plugins/lunasea.py index 51d820915..2af519179 100644 --- a/libs/apprise/plugins/NotifyLunaSea.py +++ b/libs/apprise/plugins/lunasea.py @@ -33,15 +33,15 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..utils import parse_list from ..utils import is_hostname from ..utils import is_ipaddr from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ -from ..URLBase import PrivacyMode +from ..locale import gettext_lazy as _ +from ..url import PrivacyMode class LunaSeaMode: diff --git a/libs/apprise/plugins/NotifyMacOSX.py b/libs/apprise/plugins/macosx.py similarity index 99% rename from libs/apprise/plugins/NotifyMacOSX.py rename to libs/apprise/plugins/macosx.py index dd53369fe..31b7101bd 100644 --- a/libs/apprise/plugins/NotifyMacOSX.py +++ b/libs/apprise/plugins/macosx.py @@ -30,11 +30,11 @@ import platform import subprocess import os -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_MACOSX_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyMailgun.py b/libs/apprise/plugins/mailgun.py similarity index 99% rename from libs/apprise/plugins/NotifyMailgun.py rename to libs/apprise/plugins/mailgun.py index 82cf970bf..69ab72dda 100644 --- a/libs/apprise/plugins/NotifyMailgun.py +++ b/libs/apprise/plugins/mailgun.py @@ -56,7 +56,7 @@ # import requests from email.utils import formataddr -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import parse_emails @@ -64,7 +64,7 @@ from ..utils import parse_bool from ..utils import is_email from ..utils import validate_regex from ..logger import logger -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Provide some known codes Mailgun uses and what they translate to: # Based on https://documentation.mailgun.com/en/latest/api-intro.html#errors diff --git a/libs/apprise/plugins/NotifyMastodon.py b/libs/apprise/plugins/mastodon.py similarity index 99% rename from libs/apprise/plugins/NotifyMastodon.py rename to libs/apprise/plugins/mastodon.py index 0d2f27df3..b6e451ad1 100644 --- a/libs/apprise/plugins/NotifyMastodon.py +++ b/libs/apprise/plugins/mastodon.py @@ -33,16 +33,16 @@ from json import dumps, loads from datetime import datetime from datetime import timezone -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase # Accept: # - @username diff --git a/libs/apprise/plugins/NotifyMatrix.py b/libs/apprise/plugins/matrix.py similarity index 99% rename from libs/apprise/plugins/NotifyMatrix.py rename to libs/apprise/plugins/matrix.py index 594274761..c1401f238 100644 --- a/libs/apprise/plugins/NotifyMatrix.py +++ b/libs/apprise/plugins/matrix.py @@ -37,8 +37,8 @@ from json import dumps from json import loads from time import time -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..common import NotifyFormat @@ -46,7 +46,7 @@ from ..utils import parse_bool from ..utils import parse_list from ..utils import is_hostname from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Define default path MATRIX_V1_WEBHOOK_PATH = '/api/v1/matrix/hook' diff --git a/libs/apprise/plugins/NotifyMattermost.py b/libs/apprise/plugins/mattermost.py similarity index 99% rename from libs/apprise/plugins/NotifyMattermost.py rename to libs/apprise/plugins/mattermost.py index dbb5f0dd3..481a9b852 100644 --- a/libs/apprise/plugins/NotifyMattermost.py +++ b/libs/apprise/plugins/mattermost.py @@ -39,13 +39,13 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Some Reference Locations: # - https://docs.mattermost.com/developer/webhooks-incoming.html diff --git a/libs/apprise/plugins/NotifyMessageBird.py b/libs/apprise/plugins/messagebird.py similarity index 99% rename from libs/apprise/plugins/NotifyMessageBird.py rename to libs/apprise/plugins/messagebird.py index 42d880acd..c496d347e 100644 --- a/libs/apprise/plugins/NotifyMessageBird.py +++ b/libs/apprise/plugins/messagebird.py @@ -34,12 +34,12 @@ import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyMessageBird(NotifyBase): diff --git a/libs/apprise/plugins/NotifyMisskey.py b/libs/apprise/plugins/misskey.py similarity index 99% rename from libs/apprise/plugins/NotifyMisskey.py rename to libs/apprise/plugins/misskey.py index 8965a0f7b..73b8f7c6e 100644 --- a/libs/apprise/plugins/NotifyMisskey.py +++ b/libs/apprise/plugins/misskey.py @@ -47,10 +47,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class MisskeyVisibility: diff --git a/libs/apprise/plugins/NotifyMQTT.py b/libs/apprise/plugins/mqtt.py similarity index 99% rename from libs/apprise/plugins/NotifyMQTT.py rename to libs/apprise/plugins/mqtt.py index 49380d330..1e09cd147 100644 --- a/libs/apprise/plugins/NotifyMQTT.py +++ b/libs/apprise/plugins/mqtt.py @@ -38,12 +38,12 @@ import re from time import sleep from datetime import datetime from os.path import isfile -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_MQTT_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyMSG91.py b/libs/apprise/plugins/msg91.py similarity index 99% rename from libs/apprise/plugins/NotifyMSG91.py rename to libs/apprise/plugins/msg91.py index a7bd9c473..28a5bf183 100644 --- a/libs/apprise/plugins/NotifyMSG91.py +++ b/libs/apprise/plugins/msg91.py @@ -38,12 +38,12 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no, parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class MSG91PayloadField: diff --git a/libs/apprise/plugins/NotifyMSTeams.py b/libs/apprise/plugins/msteams.py similarity index 99% rename from libs/apprise/plugins/NotifyMSTeams.py rename to libs/apprise/plugins/msteams.py index 06572c3e6..2e0957f32 100644 --- a/libs/apprise/plugins/NotifyMSTeams.py +++ b/libs/apprise/plugins/msteams.py @@ -81,7 +81,7 @@ import requests import json from json.decoder import JSONDecodeError -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..common import NotifyFormat @@ -89,8 +89,8 @@ from ..utils import parse_bool from ..utils import validate_regex from ..utils import apply_template from ..utils import TemplateType -from ..AppriseAttachment import AppriseAttachment -from ..AppriseLocale import gettext_lazy as _ +from ..apprise_attachment import AppriseAttachment +from ..locale import gettext_lazy as _ class NotifyMSTeams(NotifyBase): diff --git a/libs/apprise/plugins/NotifyNextcloud.py b/libs/apprise/plugins/nextcloud.py similarity index 99% rename from libs/apprise/plugins/NotifyNextcloud.py rename to libs/apprise/plugins/nextcloud.py index fd471d9eb..9acfc43d5 100644 --- a/libs/apprise/plugins/NotifyNextcloud.py +++ b/libs/apprise/plugins/nextcloud.py @@ -28,11 +28,11 @@ import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyNextcloud(NotifyBase): diff --git a/libs/apprise/plugins/NotifyNextcloudTalk.py b/libs/apprise/plugins/nextcloudtalk.py similarity index 98% rename from libs/apprise/plugins/NotifyNextcloudTalk.py rename to libs/apprise/plugins/nextcloudtalk.py index 4bfced282..b1b014770 100644 --- a/libs/apprise/plugins/NotifyNextcloudTalk.py +++ b/libs/apprise/plugins/nextcloudtalk.py @@ -29,11 +29,11 @@ import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyNextcloudTalk(NotifyBase): diff --git a/libs/apprise/plugins/NotifyNotica.py b/libs/apprise/plugins/notica.py similarity index 99% rename from libs/apprise/plugins/NotifyNotica.py rename to libs/apprise/plugins/notica.py index 33a94fc96..661fde1d1 100644 --- a/libs/apprise/plugins/NotifyNotica.py +++ b/libs/apprise/plugins/notica.py @@ -43,11 +43,11 @@ import re import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NoticaMode: diff --git a/libs/apprise/plugins/NotifyNotifiarr.py b/libs/apprise/plugins/notifiarr.py similarity index 99% rename from libs/apprise/plugins/NotifyNotifiarr.py rename to libs/apprise/plugins/notifiarr.py index e195cbd32..cc13e1a56 100644 --- a/libs/apprise/plugins/NotifyNotifiarr.py +++ b/libs/apprise/plugins/notifiarr.py @@ -31,9 +31,9 @@ import requests from json import dumps from itertools import chain -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from ..common import NotifyImageSize from ..utils import parse_list, parse_bool from ..utils import validate_regex diff --git a/libs/apprise/plugins/NotifyNotifico.py b/libs/apprise/plugins/notifico.py similarity index 99% rename from libs/apprise/plugins/NotifyNotifico.py rename to libs/apprise/plugins/notifico.py index 27ce29a6e..5cb0d666e 100644 --- a/libs/apprise/plugins/NotifyNotifico.py +++ b/libs/apprise/plugins/notifico.py @@ -44,11 +44,11 @@ import re import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotificoFormat: diff --git a/libs/apprise/plugins/NotifyNtfy.py b/libs/apprise/plugins/ntfy.py similarity index 99% rename from libs/apprise/plugins/NotifyNtfy.py rename to libs/apprise/plugins/ntfy.py index 40834eece..805b87260 100644 --- a/libs/apprise/plugins/NotifyNtfy.py +++ b/libs/apprise/plugins/ntfy.py @@ -41,18 +41,18 @@ from json import loads from json import dumps from os.path import basename -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..common import NotifyImageSize -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from ..utils import parse_list from ..utils import parse_bool from ..utils import is_hostname from ..utils import is_ipaddr from ..utils import validate_regex -from ..URLBase import PrivacyMode -from ..attachment.AttachBase import AttachBase +from ..url import PrivacyMode +from ..attachment.base import AttachBase class NtfyMode: diff --git a/libs/apprise/plugins/NotifyOffice365.py b/libs/apprise/plugins/office365.py similarity index 99% rename from libs/apprise/plugins/NotifyOffice365.py rename to libs/apprise/plugins/office365.py index 0c62279f9..b04f7a037 100644 --- a/libs/apprise/plugins/NotifyOffice365.py +++ b/libs/apprise/plugins/office365.py @@ -64,14 +64,14 @@ from datetime import datetime from datetime import timedelta from json import loads from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import is_email from ..utils import parse_emails from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyOffice365(NotifyBase): diff --git a/libs/apprise/plugins/NotifyOneSignal.py b/libs/apprise/plugins/one_signal.py similarity index 99% rename from libs/apprise/plugins/NotifyOneSignal.py rename to libs/apprise/plugins/one_signal.py index eb1e10f7a..76ec212f4 100644 --- a/libs/apprise/plugins/NotifyOneSignal.py +++ b/libs/apprise/plugins/one_signal.py @@ -37,14 +37,14 @@ import requests from json import dumps from itertools import chain -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_list from ..utils import parse_bool from ..utils import is_email -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class OneSignalCategory: diff --git a/libs/apprise/plugins/NotifyOpsgenie.py b/libs/apprise/plugins/opsgenie.py similarity index 99% rename from libs/apprise/plugins/NotifyOpsgenie.py rename to libs/apprise/plugins/opsgenie.py index c2dfed232..5327ec803 100644 --- a/libs/apprise/plugins/NotifyOpsgenie.py +++ b/libs/apprise/plugins/opsgenie.py @@ -49,13 +49,13 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex from ..utils import is_uuid from ..utils import parse_list from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class OpsgenieCategory(NotifyBase): diff --git a/libs/apprise/plugins/NotifyPagerDuty.py b/libs/apprise/plugins/pagerduty.py similarity index 99% rename from libs/apprise/plugins/NotifyPagerDuty.py rename to libs/apprise/plugins/pagerduty.py index 0896b41b1..c9d555527 100644 --- a/libs/apprise/plugins/NotifyPagerDuty.py +++ b/libs/apprise/plugins/pagerduty.py @@ -34,13 +34,13 @@ import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class PagerDutySeverity: diff --git a/libs/apprise/plugins/NotifyPagerTree.py b/libs/apprise/plugins/pagertree.py similarity index 99% rename from libs/apprise/plugins/NotifyPagerTree.py rename to libs/apprise/plugins/pagertree.py index c9290f2f7..8a041a358 100644 --- a/libs/apprise/plugins/NotifyPagerTree.py +++ b/libs/apprise/plugins/pagertree.py @@ -31,11 +31,11 @@ from json import dumps from uuid import uuid4 -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Actions diff --git a/libs/apprise/plugins/NotifyParsePlatform.py b/libs/apprise/plugins/parseplatform.py similarity index 99% rename from libs/apprise/plugins/NotifyParsePlatform.py rename to libs/apprise/plugins/parseplatform.py index 2a182ed31..cd59d0575 100644 --- a/libs/apprise/plugins/NotifyParsePlatform.py +++ b/libs/apprise/plugins/parseplatform.py @@ -30,10 +30,10 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Used to break path apart into list of targets TARGET_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+') diff --git a/libs/apprise/plugins/NotifyPopcornNotify.py b/libs/apprise/plugins/popcorn_notify.py similarity index 99% rename from libs/apprise/plugins/NotifyPopcornNotify.py rename to libs/apprise/plugins/popcorn_notify.py index 0ecd8af68..388aa2199 100644 --- a/libs/apprise/plugins/NotifyPopcornNotify.py +++ b/libs/apprise/plugins/popcorn_notify.py @@ -28,14 +28,14 @@ import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_email from ..utils import is_phone_no from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyPopcornNotify(NotifyBase): diff --git a/libs/apprise/plugins/NotifyProwl.py b/libs/apprise/plugins/prowl.py similarity index 99% rename from libs/apprise/plugins/NotifyProwl.py rename to libs/apprise/plugins/prowl.py index e5c07bf4e..c174615c0 100644 --- a/libs/apprise/plugins/NotifyProwl.py +++ b/libs/apprise/plugins/prowl.py @@ -28,10 +28,10 @@ import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Priorities diff --git a/libs/apprise/plugins/NotifyPushBullet.py b/libs/apprise/plugins/pushbullet.py similarity index 99% rename from libs/apprise/plugins/NotifyPushBullet.py rename to libs/apprise/plugins/pushbullet.py index 5e9c43fbf..8e006db1d 100644 --- a/libs/apprise/plugins/NotifyPushBullet.py +++ b/libs/apprise/plugins/pushbullet.py @@ -30,13 +30,13 @@ import requests from json import dumps from json import loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..utils import is_email from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase # Flag used as a placeholder to sending to all devices PUSHBULLET_SEND_TO_ALL = 'ALL_DEVICES' diff --git a/libs/apprise/plugins/NotifyPushDeer.py b/libs/apprise/plugins/pushdeer.py similarity index 98% rename from libs/apprise/plugins/NotifyPushDeer.py rename to libs/apprise/plugins/pushdeer.py index 6766dc7ff..fa888b159 100644 --- a/libs/apprise/plugins/NotifyPushDeer.py +++ b/libs/apprise/plugins/pushdeer.py @@ -29,9 +29,9 @@ import requests from ..common import NotifyType -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Syntax: # schan://{key}/ diff --git a/libs/apprise/plugins/NotifyPushed.py b/libs/apprise/plugins/pushed.py similarity index 99% rename from libs/apprise/plugins/NotifyPushed.py rename to libs/apprise/plugins/pushed.py index a50970f99..1ed83b9e4 100644 --- a/libs/apprise/plugins/NotifyPushed.py +++ b/libs/apprise/plugins/pushed.py @@ -31,12 +31,12 @@ import requests from json import dumps from itertools import chain -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Used to detect and parse channels IS_CHANNEL = re.compile(r'^#?(?P[A-Za-z0-9]+)$') diff --git a/libs/apprise/plugins/NotifyPushjet.py b/libs/apprise/plugins/pushjet.py similarity index 98% rename from libs/apprise/plugins/NotifyPushjet.py rename to libs/apprise/plugins/pushjet.py index 253ac6818..f8dcfdf31 100644 --- a/libs/apprise/plugins/NotifyPushjet.py +++ b/libs/apprise/plugins/pushjet.py @@ -29,11 +29,11 @@ import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyPushjet(NotifyBase): diff --git a/libs/apprise/plugins/NotifyPushMe.py b/libs/apprise/plugins/pushme.py similarity index 98% rename from libs/apprise/plugins/NotifyPushMe.py rename to libs/apprise/plugins/pushme.py index 30889f868..abbed7944 100644 --- a/libs/apprise/plugins/NotifyPushMe.py +++ b/libs/apprise/plugins/pushme.py @@ -28,12 +28,12 @@ import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import validate_regex from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyPushMe(NotifyBase): diff --git a/libs/apprise/plugins/NotifyPushover.py b/libs/apprise/plugins/pushover.py similarity index 99% rename from libs/apprise/plugins/NotifyPushover.py rename to libs/apprise/plugins/pushover.py index be6ada289..954e7dd0f 100644 --- a/libs/apprise/plugins/NotifyPushover.py +++ b/libs/apprise/plugins/pushover.py @@ -30,14 +30,14 @@ import re import requests from itertools import chain -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..conversion import convert_between from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase # Flag used as a placeholder to sending to all devices PUSHOVER_SEND_TO_ALL = 'ALL_DEVICES' diff --git a/libs/apprise/plugins/NotifyPushSafer.py b/libs/apprise/plugins/pushsafer.py similarity index 99% rename from libs/apprise/plugins/NotifyPushSafer.py rename to libs/apprise/plugins/pushsafer.py index 32cfa82fe..7bdca7a69 100644 --- a/libs/apprise/plugins/NotifyPushSafer.py +++ b/libs/apprise/plugins/pushsafer.py @@ -30,11 +30,11 @@ import base64 import requests from json import loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class PushSaferSound: diff --git a/libs/apprise/plugins/NotifyPushy.py b/libs/apprise/plugins/pushy.py similarity index 99% rename from libs/apprise/plugins/NotifyPushy.py rename to libs/apprise/plugins/pushy.py index 097017dac..bb2a24ecd 100644 --- a/libs/apprise/plugins/NotifyPushy.py +++ b/libs/apprise/plugins/pushy.py @@ -32,11 +32,11 @@ import requests from itertools import chain from json import dumps, loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Used to detect a Device and Topic VALIDATE_DEVICE = re.compile(r'^@(?P[a-z0-9]+)$', re.I) diff --git a/libs/apprise/plugins/NotifyReddit.py b/libs/apprise/plugins/reddit.py similarity index 99% rename from libs/apprise/plugins/NotifyReddit.py rename to libs/apprise/plugins/reddit.py index 022a0a50d..3a60b5e03 100644 --- a/libs/apprise/plugins/NotifyReddit.py +++ b/libs/apprise/plugins/reddit.py @@ -53,14 +53,14 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from .. import __title__, __version__ # Extend HTTP Error Messages diff --git a/libs/apprise/plugins/NotifyRevolt.py b/libs/apprise/plugins/revolt.py similarity index 99% rename from libs/apprise/plugins/NotifyRevolt.py rename to libs/apprise/plugins/revolt.py index ae0a43b10..1f518540b 100644 --- a/libs/apprise/plugins/NotifyRevolt.py +++ b/libs/apprise/plugins/revolt.py @@ -42,13 +42,13 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import validate_regex from ..utils import parse_list -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyRevolt(NotifyBase): diff --git a/libs/apprise/plugins/NotifyRocketChat.py b/libs/apprise/plugins/rocketchat.py similarity index 99% rename from libs/apprise/plugins/NotifyRocketChat.py rename to libs/apprise/plugins/rocketchat.py index 9011a5e71..973651e30 100644 --- a/libs/apprise/plugins/NotifyRocketChat.py +++ b/libs/apprise/plugins/rocketchat.py @@ -32,14 +32,14 @@ from json import loads from json import dumps from itertools import chain -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ IS_CHANNEL = re.compile(r'^#(?P[A-Za-z0-9_-]+)$') IS_USER = re.compile(r'^@(?P[A-Za-z0-9._-]+)$') diff --git a/libs/apprise/plugins/NotifyRSyslog.py b/libs/apprise/plugins/rsyslog.py similarity index 99% rename from libs/apprise/plugins/NotifyRSyslog.py rename to libs/apprise/plugins/rsyslog.py index e39744627..9631c72fd 100644 --- a/libs/apprise/plugins/NotifyRSyslog.py +++ b/libs/apprise/plugins/rsyslog.py @@ -29,10 +29,10 @@ import os import socket -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class syslog: diff --git a/libs/apprise/plugins/NotifyRyver.py b/libs/apprise/plugins/ryver.py similarity index 99% rename from libs/apprise/plugins/NotifyRyver.py rename to libs/apprise/plugins/ryver.py index 0872f3e52..114dc6a0c 100644 --- a/libs/apprise/plugins/NotifyRyver.py +++ b/libs/apprise/plugins/ryver.py @@ -38,12 +38,12 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class RyverWebhookMode: diff --git a/libs/apprise/plugins/NotifySendGrid.py b/libs/apprise/plugins/sendgrid.py similarity index 99% rename from libs/apprise/plugins/NotifySendGrid.py rename to libs/apprise/plugins/sendgrid.py index b82e3e60d..d50839f17 100644 --- a/libs/apprise/plugins/NotifySendGrid.py +++ b/libs/apprise/plugins/sendgrid.py @@ -49,13 +49,13 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import is_email from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages SENDGRID_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/NotifyServerChan.py b/libs/apprise/plugins/serverchan.py similarity index 98% rename from libs/apprise/plugins/NotifyServerChan.py rename to libs/apprise/plugins/serverchan.py index cf250cf5b..e3abaa354 100644 --- a/libs/apprise/plugins/NotifyServerChan.py +++ b/libs/apprise/plugins/serverchan.py @@ -30,9 +30,9 @@ import re import requests from ..common import NotifyType -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Register at https://sct.ftqq.com/ diff --git a/libs/apprise/plugins/NotifySES.py b/libs/apprise/plugins/ses.py similarity index 99% rename from libs/apprise/plugins/NotifySES.py rename to libs/apprise/plugins/ses.py index b580b14d6..5a2c047a7 100644 --- a/libs/apprise/plugins/NotifySES.py +++ b/libs/apprise/plugins/ses.py @@ -95,13 +95,13 @@ from email.utils import formataddr from email.header import Header from urllib.parse import quote -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_emails from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from ..utils import is_email # Our Regin Identifier diff --git a/libs/apprise/plugins/NotifySignalAPI.py b/libs/apprise/plugins/signal_api.py similarity index 99% rename from libs/apprise/plugins/NotifySignalAPI.py rename to libs/apprise/plugins/signal_api.py index b35b4989e..7e557133c 100644 --- a/libs/apprise/plugins/NotifySignalAPI.py +++ b/libs/apprise/plugins/signal_api.py @@ -31,13 +31,13 @@ import requests from json import dumps import base64 -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..URLBase import PrivacyMode -from ..AppriseLocale import gettext_lazy as _ +from ..url import PrivacyMode +from ..locale import gettext_lazy as _ GROUP_REGEX = re.compile( diff --git a/libs/apprise/plugins/NotifySimplePush.py b/libs/apprise/plugins/simplepush.py similarity index 98% rename from libs/apprise/plugins/NotifySimplePush.py rename to libs/apprise/plugins/simplepush.py index 3851e1e3c..10b01b0f8 100644 --- a/libs/apprise/plugins/NotifySimplePush.py +++ b/libs/apprise/plugins/simplepush.py @@ -30,11 +30,11 @@ from os import urandom from json import loads import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ from base64 import urlsafe_b64encode import hashlib diff --git a/libs/apprise/plugins/NotifySinch.py b/libs/apprise/plugins/sinch.py similarity index 99% rename from libs/apprise/plugins/NotifySinch.py rename to libs/apprise/plugins/sinch.py index 74b3c452a..06bd5b1e7 100644 --- a/libs/apprise/plugins/NotifySinch.py +++ b/libs/apprise/plugins/sinch.py @@ -39,13 +39,13 @@ import requests import json -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class SinchRegion: diff --git a/libs/apprise/plugins/NotifySlack.py b/libs/apprise/plugins/slack.py similarity index 99% rename from libs/apprise/plugins/NotifySlack.py rename to libs/apprise/plugins/slack.py index b66fe99f4..b929dfb46 100644 --- a/libs/apprise/plugins/NotifySlack.py +++ b/libs/apprise/plugins/slack.py @@ -78,7 +78,7 @@ from json import dumps from json import loads from time import time -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..common import NotifyFormat @@ -86,7 +86,7 @@ from ..utils import is_email from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages SLACK_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/NotifySMSEagle.py b/libs/apprise/plugins/smseagle.py similarity index 99% rename from libs/apprise/plugins/NotifySMSEagle.py rename to libs/apprise/plugins/smseagle.py index 33b4af752..8eddca588 100644 --- a/libs/apprise/plugins/NotifySMSEagle.py +++ b/libs/apprise/plugins/smseagle.py @@ -32,14 +32,14 @@ from json import dumps, loads import base64 from itertools import chain -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..URLBase import PrivacyMode -from ..AppriseLocale import gettext_lazy as _ +from ..url import PrivacyMode +from ..locale import gettext_lazy as _ GROUP_REGEX = re.compile( diff --git a/libs/apprise/plugins/NotifySMSManager.py b/libs/apprise/plugins/smsmanager.py similarity index 99% rename from libs/apprise/plugins/NotifySMSManager.py rename to libs/apprise/plugins/smsmanager.py index efc158b62..1d352daf2 100644 --- a/libs/apprise/plugins/NotifySMSManager.py +++ b/libs/apprise/plugins/smsmanager.py @@ -35,13 +35,13 @@ # 2. Generate an API key in web administration. import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class SMSManagerGateway(object): diff --git a/libs/apprise/plugins/NotifySMTP2Go.py b/libs/apprise/plugins/smtp2go.py similarity index 99% rename from libs/apprise/plugins/NotifySMTP2Go.py rename to libs/apprise/plugins/smtp2go.py index a34492d05..017da8111 100644 --- a/libs/apprise/plugins/NotifySMTP2Go.py +++ b/libs/apprise/plugins/smtp2go.py @@ -49,14 +49,14 @@ import base64 import requests from json import dumps from email.utils import formataddr -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import parse_emails from ..utils import parse_bool from ..utils import is_email from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ SMTP2GO_HTTP_ERROR_MAP = { 429: 'To many requests.', diff --git a/libs/apprise/plugins/NotifySNS.py b/libs/apprise/plugins/sns.py similarity index 99% rename from libs/apprise/plugins/NotifySNS.py rename to libs/apprise/plugins/sns.py index 5a287e37e..cc6e83076 100644 --- a/libs/apprise/plugins/NotifySNS.py +++ b/libs/apprise/plugins/sns.py @@ -36,13 +36,13 @@ from collections import OrderedDict from xml.etree import ElementTree from itertools import chain -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Topic Detection # Summary: 256 Characters max, only alpha/numeric plus underscore (_) and diff --git a/libs/apprise/plugins/NotifySparkPost.py b/libs/apprise/plugins/sparkpost.py similarity index 99% rename from libs/apprise/plugins/NotifySparkPost.py rename to libs/apprise/plugins/sparkpost.py index 255db0709..b873d6b0e 100644 --- a/libs/apprise/plugins/NotifySparkPost.py +++ b/libs/apprise/plugins/sparkpost.py @@ -58,7 +58,7 @@ import requests import base64 from json import loads from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import is_email @@ -66,7 +66,7 @@ from email.utils import formataddr from ..utils import validate_regex from ..utils import parse_emails from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Provide some known codes SparkPost uses and what they translate to: # Based on https://www.sparkpost.com/docs/tech-resources/extended-error-codes/ diff --git a/libs/apprise/plugins/NotifyStreamlabs.py b/libs/apprise/plugins/streamlabs.py similarity index 99% rename from libs/apprise/plugins/NotifyStreamlabs.py rename to libs/apprise/plugins/streamlabs.py index d1e4186a6..c534f1ba6 100644 --- a/libs/apprise/plugins/NotifyStreamlabs.py +++ b/libs/apprise/plugins/streamlabs.py @@ -38,10 +38,10 @@ # import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # calls diff --git a/libs/apprise/plugins/NotifySynology.py b/libs/apprise/plugins/synology.py similarity index 98% rename from libs/apprise/plugins/NotifySynology.py rename to libs/apprise/plugins/synology.py index be58c0643..ed85f80c0 100644 --- a/libs/apprise/plugins/NotifySynology.py +++ b/libs/apprise/plugins/synology.py @@ -29,10 +29,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # For API Details see: # https://kb.synology.com/en-au/DSM/help/Chat/chat_integration diff --git a/libs/apprise/plugins/NotifySyslog.py b/libs/apprise/plugins/syslog.py similarity index 99% rename from libs/apprise/plugins/NotifySyslog.py rename to libs/apprise/plugins/syslog.py index 5540fc758..935111eea 100644 --- a/libs/apprise/plugins/NotifySyslog.py +++ b/libs/apprise/plugins/syslog.py @@ -28,10 +28,10 @@ import syslog -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class SyslogFacility: diff --git a/libs/apprise/plugins/NotifyTechulusPush.py b/libs/apprise/plugins/techuluspush.py similarity index 98% rename from libs/apprise/plugins/NotifyTechulusPush.py rename to libs/apprise/plugins/techuluspush.py index 4d0b99348..682bf088e 100644 --- a/libs/apprise/plugins/NotifyTechulusPush.py +++ b/libs/apprise/plugins/techuluspush.py @@ -53,10 +53,10 @@ import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Token required as part of the API request # Used to prepare our UUID regex matching diff --git a/libs/apprise/plugins/NotifyTelegram.py b/libs/apprise/plugins/telegram.py similarity index 99% rename from libs/apprise/plugins/NotifyTelegram.py rename to libs/apprise/plugins/telegram.py index cce8af625..6b997f574 100644 --- a/libs/apprise/plugins/NotifyTelegram.py +++ b/libs/apprise/plugins/telegram.py @@ -59,15 +59,15 @@ import os from json import loads from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..common import NotifyFormat from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase TELEGRAM_IMAGE_XY = NotifyImageSize.XY_256 diff --git a/libs/apprise/plugins/NotifyThreema.py b/libs/apprise/plugins/threema.py similarity index 99% rename from libs/apprise/plugins/NotifyThreema.py rename to libs/apprise/plugins/threema.py index c2ad82e2e..423c23124 100644 --- a/libs/apprise/plugins/NotifyThreema.py +++ b/libs/apprise/plugins/threema.py @@ -35,14 +35,14 @@ import requests from itertools import chain -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import validate_regex from ..utils import is_email -from ..URLBase import PrivacyMode +from ..url import PrivacyMode from ..utils import parse_list -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class ThreemaRecipientTypes: diff --git a/libs/apprise/plugins/NotifyTwilio.py b/libs/apprise/plugins/twilio.py similarity index 99% rename from libs/apprise/plugins/NotifyTwilio.py rename to libs/apprise/plugins/twilio.py index 863b09a94..4205e37f0 100644 --- a/libs/apprise/plugins/NotifyTwilio.py +++ b/libs/apprise/plugins/twilio.py @@ -46,13 +46,13 @@ import requests from json import loads -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyTwilio(NotifyBase): diff --git a/libs/apprise/plugins/NotifyTwist.py b/libs/apprise/plugins/twist.py similarity index 99% rename from libs/apprise/plugins/NotifyTwist.py rename to libs/apprise/plugins/twist.py index fa26feb84..62d729f4f 100644 --- a/libs/apprise/plugins/NotifyTwist.py +++ b/libs/apprise/plugins/twist.py @@ -35,13 +35,13 @@ import requests from json import loads from itertools import chain -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import is_email -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # A workspace can also be interpreted as a team name too! diff --git a/libs/apprise/plugins/NotifyTwitter.py b/libs/apprise/plugins/twitter.py similarity index 99% rename from libs/apprise/plugins/NotifyTwitter.py rename to libs/apprise/plugins/twitter.py index 7a0813c1f..8000a8151 100644 --- a/libs/apprise/plugins/NotifyTwitter.py +++ b/libs/apprise/plugins/twitter.py @@ -36,14 +36,14 @@ from datetime import timezone from requests_oauthlib import OAuth1 from json import dumps from json import loads -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ -from ..attachment.AttachBase import AttachBase +from ..locale import gettext_lazy as _ +from ..attachment.base import AttachBase IS_USER = re.compile(r'^\s*@?(?P[A-Z0-9_]+)$', re.I) diff --git a/libs/apprise/plugins/NotifyVoipms.py b/libs/apprise/plugins/voipms.py similarity index 99% rename from libs/apprise/plugins/NotifyVoipms.py rename to libs/apprise/plugins/voipms.py index a023589a7..3a4e6d25f 100644 --- a/libs/apprise/plugins/NotifyVoipms.py +++ b/libs/apprise/plugins/voipms.py @@ -37,12 +37,12 @@ import requests from json import loads -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import is_email from ..utils import parse_phone_no -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyVoipms(NotifyBase): diff --git a/libs/apprise/plugins/NotifyVonage.py b/libs/apprise/plugins/vonage.py similarity index 99% rename from libs/apprise/plugins/NotifyVonage.py rename to libs/apprise/plugins/vonage.py index e9b1422ad..441a6ba6b 100644 --- a/libs/apprise/plugins/NotifyVonage.py +++ b/libs/apprise/plugins/vonage.py @@ -33,13 +33,13 @@ # import requests -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyVonage(NotifyBase): diff --git a/libs/apprise/plugins/NotifyWebexTeams.py b/libs/apprise/plugins/webexteams.py similarity index 99% rename from libs/apprise/plugins/NotifyWebexTeams.py rename to libs/apprise/plugins/webexteams.py index c91864bad..bd0bdb577 100644 --- a/libs/apprise/plugins/NotifyWebexTeams.py +++ b/libs/apprise/plugins/webexteams.py @@ -63,11 +63,11 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Extend HTTP Error Messages # Based on: https://developer.webex.com/docs/api/basics/rate-limiting diff --git a/libs/apprise/plugins/NotifyWeComBot.py b/libs/apprise/plugins/wecombot.py similarity index 99% rename from libs/apprise/plugins/NotifyWeComBot.py rename to libs/apprise/plugins/wecombot.py index 4289b39e9..ab6341718 100644 --- a/libs/apprise/plugins/NotifyWeComBot.py +++ b/libs/apprise/plugins/wecombot.py @@ -59,10 +59,10 @@ import re import requests from json import dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyWeComBot(NotifyBase): diff --git a/libs/apprise/plugins/NotifyWhatsApp.py b/libs/apprise/plugins/whatsapp.py similarity index 99% rename from libs/apprise/plugins/NotifyWhatsApp.py rename to libs/apprise/plugins/whatsapp.py index 4ccbcbdaf..7120d736c 100644 --- a/libs/apprise/plugins/NotifyWhatsApp.py +++ b/libs/apprise/plugins/whatsapp.py @@ -44,12 +44,12 @@ import re import requests from json import loads, dumps -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyWhatsApp(NotifyBase): diff --git a/libs/apprise/plugins/NotifyWindows.py b/libs/apprise/plugins/windows.py similarity index 99% rename from libs/apprise/plugins/NotifyWindows.py rename to libs/apprise/plugins/windows.py index 207e0f221..746fcd1dd 100644 --- a/libs/apprise/plugins/NotifyWindows.py +++ b/libs/apprise/plugins/windows.py @@ -28,11 +28,11 @@ from time import sleep -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # Default our global support flag NOTIFY_WINDOWS_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/NotifyXBMC.py b/libs/apprise/plugins/xbmc.py similarity index 99% rename from libs/apprise/plugins/NotifyXBMC.py rename to libs/apprise/plugins/xbmc.py index 7d4462e41..8006e1005 100644 --- a/libs/apprise/plugins/NotifyXBMC.py +++ b/libs/apprise/plugins/xbmc.py @@ -29,12 +29,12 @@ import requests from json import dumps -from .NotifyBase import NotifyBase -from ..URLBase import PrivacyMode +from .base import NotifyBase +from ..url import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import parse_bool -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ class NotifyXBMC(NotifyBase): diff --git a/libs/apprise/plugins/NotifyZulip.py b/libs/apprise/plugins/zulip.py similarity index 99% rename from libs/apprise/plugins/NotifyZulip.py rename to libs/apprise/plugins/zulip.py index 54fe2d062..e829e6f6d 100644 --- a/libs/apprise/plugins/NotifyZulip.py +++ b/libs/apprise/plugins/zulip.py @@ -61,13 +61,13 @@ import re import requests -from .NotifyBase import NotifyBase +from .base import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex from ..utils import is_email from ..utils import remove_suffix -from ..AppriseLocale import gettext_lazy as _ +from ..locale import gettext_lazy as _ # A Valid Bot Name VALIDATE_BOTNAME = re.compile(r'(?P[A-Z0-9_-]{1,32})', re.I) diff --git a/libs/apprise/URLBase.py b/libs/apprise/url.py similarity index 99% rename from libs/apprise/URLBase.py rename to libs/apprise/url.py index 90ea85c66..39daec867 100644 --- a/libs/apprise/URLBase.py +++ b/libs/apprise/url.py @@ -35,8 +35,8 @@ from xml.sax.saxutils import escape as sax_escape from urllib.parse import unquote as _unquote from urllib.parse import quote as _quote -from .AppriseLocale import gettext_lazy as _ -from .AppriseAsset import AppriseAsset +from .locale import gettext_lazy as _ +from .asset import AppriseAsset from .utils import urlencode from .utils import parse_url from .utils import parse_bool @@ -744,7 +744,7 @@ class URLBase: @staticmethod def parse_url(url, verify_host=True, plus_to_space=False, - strict_port=False): + strict_port=False, sanitize=True): """Parses the URL and returns it broken apart into a dictionary. This is very specific and customized for Apprise. @@ -765,7 +765,8 @@ class URLBase: results = parse_url( url, default_schema='unknown', verify_host=verify_host, - plus_to_space=plus_to_space, strict_port=strict_port) + plus_to_space=plus_to_space, strict_port=strict_port, + sanitize=sanitize) if not results: # We're done; we failed to parse our url diff --git a/libs/apprise/URLBase.pyi b/libs/apprise/url.pyi similarity index 100% rename from libs/apprise/URLBase.pyi rename to libs/apprise/url.pyi diff --git a/libs/apprise/utils.py b/libs/apprise/utils.py index e1881f314..b33ec0749 100644 --- a/libs/apprise/utils.py +++ b/libs/apprise/utils.py @@ -541,7 +541,7 @@ def tidy_path(path): return path -def parse_qsd(qs, simple=False, plus_to_space=False): +def parse_qsd(qs, simple=False, plus_to_space=False, sanitize=True): """ Query String Dictionary Builder @@ -568,6 +568,8 @@ def parse_qsd(qs, simple=False, plus_to_space=False): per normal URL Encoded defininition. Normal URL parsing applies this, but `+` is very actively used character with passwords, api keys, tokens, etc. So Apprise does not do this by default. + + if sanitize is set to False, then kwargs are not placed into lowercase """ # Our return result set: @@ -608,7 +610,7 @@ def parse_qsd(qs, simple=False, plus_to_space=False): # Always Query String Dictionary (qsd) for every entry we have # content is always made lowercase for easy indexing - result['qsd'][key.lower().strip()] = val + result['qsd'][key.lower().strip() if sanitize else key] = val if simple: # move along @@ -636,7 +638,7 @@ def parse_qsd(qs, simple=False, plus_to_space=False): def parse_url(url, default_schema='http', verify_host=True, strict_port=False, - simple=False, plus_to_space=False): + simple=False, plus_to_space=False, sanitize=True): """A function that greatly simplifies the parsing of a url specified by the end user. @@ -691,6 +693,8 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False, If the URL can't be parsed then None is returned + If sanitize is set to False, then kwargs are not placed in lowercase + and wrapping whitespace is not removed """ if not isinstance(url, str): @@ -750,7 +754,8 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False, # while ensuring that all keys are lowercase if qsdata: result.update(parse_qsd( - qsdata, simple=simple, plus_to_space=plus_to_space)) + qsdata, simple=simple, plus_to_space=plus_to_space, + sanitize=sanitize)) # Now do a proper extraction of data; http:// is just substitued in place # to allow urlparse() to function as expected, we'll swap this back to the diff --git a/libs/version.txt b/libs/version.txt index ccabf65cc..8ac12d74d 100644 --- a/libs/version.txt +++ b/libs/version.txt @@ -2,7 +2,7 @@ alembic==1.13.1 aniso8601==9.0.1 argparse==1.4.0 -apprise==1.7.6 +apprise==1.8.0 apscheduler<=3.10.4 attrs==23.2.0 blinker==1.7.0 From fd0a8c3d3bd1beadb1fed1a58013e386d3f29653 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 12 May 2024 10:11:43 -0400 Subject: [PATCH 060/129] Emergency fix following Apprise 1.8.0 upgrade --- bazarr/app/notifier.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bazarr/app/notifier.py b/bazarr/app/notifier.py index 931a700a4..f248dfcce 100644 --- a/bazarr/app/notifier.py +++ b/bazarr/app/notifier.py @@ -1,6 +1,6 @@ # coding=utf-8 -import apprise +from apprise import Apprise, AppriseAsset import logging from .database import TableSettingsNotifier, TableEpisodes, TableShows, TableMovies, database, insert, delete, select @@ -8,7 +8,7 @@ from .database import TableSettingsNotifier, TableEpisodes, TableShows, TableMov def update_notifier(): # define apprise object - a = apprise.Apprise() + a = Apprise() # Retrieve all the details results = a.details() @@ -70,9 +70,9 @@ def send_notifications(sonarr_series_id, sonarr_episode_id, message): if not episode: return - asset = apprise.AppriseAsset(async_mode=False) + asset = AppriseAsset(async_mode=False) - apobj = apprise.Apprise(asset=asset) + apobj = Apprise(asset=asset) for provider in providers: if provider.url is not None: @@ -101,9 +101,9 @@ def send_notifications_movie(radarr_id, message): else: movie_year = '' - asset = apprise.AppriseAsset(async_mode=False) + asset = AppriseAsset(async_mode=False) - apobj = apprise.Apprise(asset=asset) + apobj = Apprise(asset=asset) for provider in providers: if provider.url is not None: From b3a5d43a10befd2451711a1abbafe4f5c65f3c76 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 12 May 2024 10:13:21 -0400 Subject: [PATCH 061/129] Fixed issue while saving some odd case ASS embedded subtitles. --- custom_libs/subliminal_patch/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index cb21f4581..1282c2e4b 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -1203,7 +1203,7 @@ def save_subtitles(file_path, subtitles, single=False, directory=None, chmod=Non continue # create subtitle path - if bool(re.search(HI_REGEX, subtitle.text)): + if subtitle.text and bool(re.search(HI_REGEX, subtitle.text)): subtitle.language.hi = True subtitle_path = get_subtitle_path(file_path, None if single else subtitle.language, forced_tag=subtitle.language.forced, From d70a92e9476e61b12a79c5642629ed81c9ad703f Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 12 May 2024 23:13:21 -0400 Subject: [PATCH 062/129] Fixed uppercase issue in Apprise module name. --- libs/apprise/apprise.py | 887 +++++++++++++++++++++++++++++++++++++++ libs/apprise/apprise.pyi | 62 +++ 2 files changed, 949 insertions(+) create mode 100644 libs/apprise/apprise.py create mode 100644 libs/apprise/apprise.pyi diff --git a/libs/apprise/apprise.py b/libs/apprise/apprise.py new file mode 100644 index 000000000..05a2ee3cc --- /dev/null +++ b/libs/apprise/apprise.py @@ -0,0 +1,887 @@ +# -*- coding: utf-8 -*- +# BSD 2-Clause License +# +# Apprise - Push Notification Library. +# Copyright (c) 2024, Chris Caron +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import asyncio +import concurrent.futures as cf +import os +from itertools import chain +from . import common +from .conversion import convert_between +from .utils import is_exclusive_match +from .manager_plugins import NotificationManager +from .utils import parse_list +from .utils import parse_urls +from .utils import cwe312_url +from .emojis import apply_emojis +from .logger import logger +from .asset import AppriseAsset +from .apprise_config import AppriseConfig +from .apprise_attachment import AppriseAttachment +from .locale import AppriseLocale +from .config.base import ConfigBase +from .plugins.base import NotifyBase + +from . import plugins +from . import __version__ + +# Grant access to our Notification Manager Singleton +N_MGR = NotificationManager() + + +class Apprise: + """ + Our Notification Manager + + """ + + def __init__(self, servers=None, asset=None, location=None, debug=False): + """ + Loads a set of server urls while applying the Asset() module to each + if specified. + + If no asset is provided, then the default asset is used. + + Optionally specify a global ContentLocation for a more strict means + of handling Attachments. + """ + + # Initialize a server list of URLs + self.servers = list() + + # Assigns an central asset object that will be later passed into each + # notification plugin. Assets contain information such as the local + # directory images can be found in. It can also identify remote + # URL paths that contain the images you want to present to the end + # user. If no asset is specified, then the default one is used. + self.asset = \ + asset if isinstance(asset, AppriseAsset) else AppriseAsset() + + if servers: + self.add(servers) + + # Initialize our locale object + self.locale = AppriseLocale() + + # Set our debug flag + self.debug = debug + + # Store our hosting location for optional strict rule handling + # of Attachments. Setting this to None removes any attachment + # restrictions. + self.location = location + + @staticmethod + def instantiate(url, asset=None, tag=None, suppress_exceptions=True): + """ + Returns the instance of a instantiated plugin based on the provided + Server URL. If the url fails to be parsed, then None is returned. + + The specified url can be either a string (the URL itself) or a + dictionary containing all of the components needed to istantiate + the notification service. If identifying a dictionary, at the bare + minimum, one must specify the schema. + + An example of a url dictionary object might look like: + { + schema: 'mailto', + host: 'google.com', + user: 'myuser', + password: 'mypassword', + } + + Alternatively the string is much easier to specify: + mailto://user:mypassword@google.com + + The dictionary works well for people who are calling details() to + extract the components they need to build the URL manually. + """ + + # Initialize our result set + results = None + + # Prepare our Asset Object + asset = asset if isinstance(asset, AppriseAsset) else AppriseAsset() + + if isinstance(url, str): + # Acquire our url tokens + results = plugins.url_to_dict( + url, secure_logging=asset.secure_logging) + + if results is None: + # Failed to parse the server URL; detailed logging handled + # inside url_to_dict - nothing to report here. + return None + + elif isinstance(url, dict): + # We already have our result set + results = url + + if results.get('schema') not in N_MGR: + # schema is a mandatory dictionary item as it is the only way + # we can index into our loaded plugins + logger.error('Dictionary does not include a "schema" entry.') + logger.trace( + 'Invalid dictionary unpacked as:{}{}'.format( + os.linesep, os.linesep.join( + ['{}="{}"'.format(k, v) + for k, v in results.items()]))) + return None + + logger.trace( + 'Dictionary unpacked as:{}{}'.format( + os.linesep, os.linesep.join( + ['{}="{}"'.format(k, v) for k, v in results.items()]))) + + # Otherwise we handle the invalid input specified + else: + logger.error( + 'An invalid URL type (%s) was specified for instantiation', + type(url)) + return None + + if not N_MGR[results['schema']].enabled: + # + # First Plugin Enable Check (Pre Initialization) + # + + # Plugin has been disabled at a global level + logger.error( + '%s:// is disabled on this system.', results['schema']) + return None + + # Build a list of tags to associate with the newly added notifications + results['tag'] = set(parse_list(tag)) + + # Set our Asset Object + results['asset'] = asset + + if suppress_exceptions: + try: + # Attempt to create an instance of our plugin using the parsed + # URL information + plugin = N_MGR[results['schema']](**results) + + # Create log entry of loaded URL + logger.debug( + 'Loaded {} URL: {}'.format( + N_MGR[results['schema']].service_name, + plugin.url(privacy=asset.secure_logging))) + + except Exception: + # CWE-312 (Secure Logging) Handling + loggable_url = url if not asset.secure_logging \ + else cwe312_url(url) + + # the arguments are invalid or can not be used. + logger.error( + 'Could not load {} URL: {}'.format( + N_MGR[results['schema']].service_name, + loggable_url)) + return None + + else: + # Attempt to create an instance of our plugin using the parsed + # URL information but don't wrap it in a try catch + plugin = N_MGR[results['schema']](**results) + + if not plugin.enabled: + # + # Second Plugin Enable Check (Post Initialization) + # + + # Service/Plugin is disabled (on a more local level). This is a + # case where the plugin was initially enabled but then after the + # __init__() was called under the hood something pre-determined + # that it could no longer be used. + + # The only downside to doing it this way is services are + # initialized prior to returning the details() if 3rd party tools + # are polling what is available. These services that become + # disabled thereafter are shown initially that they can be used. + logger.error( + '%s:// has become disabled on this system.', results['schema']) + return None + + return plugin + + def add(self, servers, asset=None, tag=None): + """ + Adds one or more server URLs into our list. + + You can override the global asset if you wish by including it with the + server(s) that you add. + + The tag allows you to associate 1 or more tag values to the server(s) + being added. tagging a service allows you to exclusively access them + when calling the notify() function. + """ + + # Initialize our return status + return_status = True + + if asset is None: + # prepare default asset + asset = self.asset + + if isinstance(servers, str): + # build our server list + servers = parse_urls(servers) + if len(servers) == 0: + return False + + elif isinstance(servers, dict): + # no problem, we support kwargs, convert it to a list + servers = [servers] + + elif isinstance(servers, (ConfigBase, NotifyBase, AppriseConfig)): + # Go ahead and just add our plugin into our list + self.servers.append(servers) + return True + + elif not isinstance(servers, (tuple, set, list)): + logger.error( + "An invalid notification (type={}) was specified.".format( + type(servers))) + return False + + for _server in servers: + + if isinstance(_server, (ConfigBase, NotifyBase, AppriseConfig)): + # Go ahead and just add our plugin into our list + self.servers.append(_server) + continue + + elif not isinstance(_server, (str, dict)): + logger.error( + "An invalid notification (type={}) was specified.".format( + type(_server))) + return_status = False + continue + + # Instantiate ourselves an object, this function throws or + # returns None if it fails + instance = Apprise.instantiate(_server, asset=asset, tag=tag) + if not isinstance(instance, NotifyBase): + # No logging is required as instantiate() handles failure + # and/or success reasons for us + return_status = False + continue + + # Add our initialized plugin to our server listings + self.servers.append(instance) + + # Return our status + return return_status + + def clear(self): + """ + Empties our server list + + """ + self.servers[:] = [] + + def find(self, tag=common.MATCH_ALL_TAG, match_always=True): + """ + Returns a list of all servers matching against the tag specified. + + """ + + # Build our tag setup + # - top level entries are treated as an 'or' + # - second level (or more) entries are treated as 'and' + # + # examples: + # tag="tagA, tagB" = tagA or tagB + # tag=['tagA', 'tagB'] = tagA or tagB + # tag=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB + # tag=[('tagB', 'tagC')] = tagB and tagC + + # A match_always flag allows us to pick up on our 'any' keyword + # and notify these services under all circumstances + match_always = common.MATCH_ALWAYS_TAG if match_always else None + + # Iterate over our loaded plugins + for entry in self.servers: + + if isinstance(entry, (ConfigBase, AppriseConfig)): + # load our servers + servers = entry.servers() + + else: + servers = [entry, ] + + for server in servers: + # Apply our tag matching based on our defined logic + if is_exclusive_match( + logic=tag, data=server.tags, + match_all=common.MATCH_ALL_TAG, + match_always=match_always): + yield server + return + + def notify(self, body, title='', notify_type=common.NotifyType.INFO, + body_format=None, tag=common.MATCH_ALL_TAG, match_always=True, + attach=None, interpret_escapes=None): + """ + Send a notification to all the plugins previously loaded. + + If the body_format specified is NotifyFormat.MARKDOWN, it will + be converted to HTML if the Notification type expects this. + + if the tag is specified (either a string or a set/list/tuple + of strings), then only the notifications flagged with that + tagged value are notified. By default, all added services + are notified (tag=MATCH_ALL_TAG) + + This function returns True if all notifications were successfully + sent, False if even just one of them fails, and None if no + notifications were sent at all as a result of tag filtering and/or + simply having empty configuration files that were read. + + Attach can contain a list of attachment URLs. attach can also be + represented by an AttachBase() (or list of) object(s). This + identifies the products you wish to notify + + Set interpret_escapes to True if you want to pre-escape a string + such as turning a \n into an actual new line, etc. + """ + + try: + # Process arguments and build synchronous and asynchronous calls + # (this step can throw internal errors). + sequential_calls, parallel_calls = self._create_notify_calls( + body, title, + notify_type=notify_type, body_format=body_format, + tag=tag, match_always=match_always, attach=attach, + interpret_escapes=interpret_escapes, + ) + + except TypeError: + # No notifications sent, and there was an internal error. + return False + + if not sequential_calls and not parallel_calls: + # Nothing to send + return None + + sequential_result = Apprise._notify_sequential(*sequential_calls) + parallel_result = Apprise._notify_parallel_threadpool(*parallel_calls) + return sequential_result and parallel_result + + async def async_notify(self, *args, **kwargs): + """ + Send a notification to all the plugins previously loaded, for + asynchronous callers. + + The arguments are identical to those of Apprise.notify(). + + """ + try: + # Process arguments and build synchronous and asynchronous calls + # (this step can throw internal errors). + sequential_calls, parallel_calls = self._create_notify_calls( + *args, **kwargs) + + except TypeError: + # No notifications sent, and there was an internal error. + return False + + if not sequential_calls and not parallel_calls: + # Nothing to send + return None + + sequential_result = Apprise._notify_sequential(*sequential_calls) + parallel_result = \ + await Apprise._notify_parallel_asyncio(*parallel_calls) + return sequential_result and parallel_result + + def _create_notify_calls(self, *args, **kwargs): + """ + Creates notifications for all the plugins loaded. + + Returns a list of (server, notify() kwargs) tuples for plugins with + parallelism disabled and another list for plugins with parallelism + enabled. + """ + + all_calls = list(self._create_notify_gen(*args, **kwargs)) + + # Split into sequential and parallel notify() calls. + sequential, parallel = [], [] + for (server, notify_kwargs) in all_calls: + if server.asset.async_mode: + parallel.append((server, notify_kwargs)) + else: + sequential.append((server, notify_kwargs)) + + return sequential, parallel + + def _create_notify_gen(self, body, title='', + notify_type=common.NotifyType.INFO, + body_format=None, tag=common.MATCH_ALL_TAG, + match_always=True, attach=None, + interpret_escapes=None): + """ + Internal generator function for _create_notify_calls(). + """ + + if len(self) == 0: + # Nothing to notify + msg = "There are no service(s) to notify" + logger.error(msg) + raise TypeError(msg) + + if not (title or body or attach): + msg = "No message content specified to deliver" + logger.error(msg) + raise TypeError(msg) + + try: + if title and isinstance(title, bytes): + title = title.decode(self.asset.encoding) + + if body and isinstance(body, bytes): + body = body.decode(self.asset.encoding) + + except UnicodeDecodeError: + msg = 'The content passed into Apprise was not of encoding ' \ + 'type: {}'.format(self.asset.encoding) + logger.error(msg) + raise TypeError(msg) + + # Tracks conversions + conversion_body_map = dict() + conversion_title_map = dict() + + # Prepare attachments if required + if attach is not None and not isinstance(attach, AppriseAttachment): + attach = AppriseAttachment( + attach, asset=self.asset, location=self.location) + + # Allow Asset default value + body_format = self.asset.body_format \ + if body_format is None else body_format + + # Allow Asset default value + interpret_escapes = self.asset.interpret_escapes \ + if interpret_escapes is None else interpret_escapes + + # Iterate over our loaded plugins + for server in self.find(tag, match_always=match_always): + # If our code reaches here, we either did not define a tag (it + # was set to None), or we did define a tag and the logic above + # determined we need to notify the service it's associated with + + # First we need to generate a key we will use to determine if we + # need to build our data out. Entries without are merged with + # the body at this stage. + key = server.notify_format if server.title_maxlen > 0\ + else f'_{server.notify_format}' + + if server.interpret_emojis: + # alter our key slightly to handle emojis since their value is + # pulled out of the notification + key += "-emojis" + + if key not in conversion_title_map: + + # Prepare our title + conversion_title_map[key] = '' if not title else title + + # Conversion of title only occurs for services where the title + # is blended with the body (title_maxlen <= 0) + if conversion_title_map[key] and server.title_maxlen <= 0: + conversion_title_map[key] = convert_between( + body_format, server.notify_format, + content=conversion_title_map[key]) + + # Our body is always converted no matter what + conversion_body_map[key] = \ + convert_between( + body_format, server.notify_format, content=body) + + if interpret_escapes: + # + # Escape our content + # + + try: + # Added overhead required due to Python 3 Encoding Bug + # identified here: https://bugs.python.org/issue21331 + conversion_body_map[key] = \ + conversion_body_map[key]\ + .encode('ascii', 'backslashreplace')\ + .decode('unicode-escape') + + conversion_title_map[key] = \ + conversion_title_map[key]\ + .encode('ascii', 'backslashreplace')\ + .decode('unicode-escape') + + except AttributeError: + # Must be of string type + msg = 'Failed to escape message body' + logger.error(msg) + raise TypeError(msg) + + if server.interpret_emojis: + # + # Convert our :emoji: definitions + # + + conversion_body_map[key] = \ + apply_emojis(conversion_body_map[key]) + conversion_title_map[key] = \ + apply_emojis(conversion_title_map[key]) + + kwargs = dict( + body=conversion_body_map[key], + title=conversion_title_map[key], + notify_type=notify_type, + attach=attach, + body_format=body_format + ) + yield (server, kwargs) + + @staticmethod + def _notify_sequential(*servers_kwargs): + """ + Process a list of notify() calls sequentially and synchronously. + """ + + success = True + + for (server, kwargs) in servers_kwargs: + try: + # Send notification + result = server.notify(**kwargs) + success = success and result + + except TypeError: + # These are our internally thrown notifications. + success = False + + except Exception: + # A catch all so we don't have to abort early + # just because one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + success = False + + return success + + @staticmethod + def _notify_parallel_threadpool(*servers_kwargs): + """ + Process a list of notify() calls in parallel and synchronously. + """ + + n_calls = len(servers_kwargs) + + # 0-length case + if n_calls == 0: + return True + + # There's no need to use a thread pool for just a single notification + if n_calls == 1: + return Apprise._notify_sequential(servers_kwargs[0]) + + # Create log entry + logger.info( + 'Notifying %d service(s) with threads.', len(servers_kwargs)) + + with cf.ThreadPoolExecutor() as executor: + success = True + futures = [executor.submit(server.notify, **kwargs) + for (server, kwargs) in servers_kwargs] + + for future in cf.as_completed(futures): + try: + result = future.result() + success = success and result + + except TypeError: + # These are our internally thrown notifications. + success = False + + except Exception: + # A catch all so we don't have to abort early + # just because one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + success = False + + return success + + @staticmethod + async def _notify_parallel_asyncio(*servers_kwargs): + """ + Process a list of async_notify() calls in parallel and asynchronously. + """ + + n_calls = len(servers_kwargs) + + # 0-length case + if n_calls == 0: + return True + + # (Unlike with the thread pool, we don't optimize for the single- + # notification case because asyncio can do useful work while waiting + # for that thread to complete) + + # Create log entry + logger.info( + 'Notifying %d service(s) asynchronously.', len(servers_kwargs)) + + async def do_call(server, kwargs): + return await server.async_notify(**kwargs) + + cors = (do_call(server, kwargs) for (server, kwargs) in servers_kwargs) + results = await asyncio.gather(*cors, return_exceptions=True) + + if any(isinstance(status, Exception) + and not isinstance(status, TypeError) for status in results): + # A catch all so we don't have to abort early just because + # one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + return False + + if any(isinstance(status, TypeError) for status in results): + # These are our internally thrown notifications. + return False + + return all(results) + + def details(self, lang=None, show_requirements=False, show_disabled=False): + """ + Returns the details associated with the Apprise object + + """ + + # general object returned + response = { + # Defines the current version of Apprise + 'version': __version__, + # Lists all of the currently supported Notifications + 'schemas': [], + # Includes the configured asset details + 'asset': self.asset.details(), + } + + for plugin in N_MGR.plugins(): + # Iterate over our hashed plugins and dynamically build details on + # their status: + + content = { + 'service_name': getattr(plugin, 'service_name', None), + 'service_url': getattr(plugin, 'service_url', None), + 'setup_url': getattr(plugin, 'setup_url', None), + # Placeholder - populated below + 'details': None, + + # Let upstream service know of the plugins that support + # attachments + 'attachment_support': getattr( + plugin, 'attachment_support', False), + + # Differentiat between what is a custom loaded plugin and + # which is native. + 'category': getattr(plugin, 'category', None) + } + + # Standard protocol(s) should be None or a tuple + enabled = getattr(plugin, 'enabled', True) + if not show_disabled and not enabled: + # Do not show inactive plugins + continue + + elif show_disabled: + # Add current state to response + content['enabled'] = enabled + + # Standard protocol(s) should be None or a tuple + protocols = getattr(plugin, 'protocol', None) + if isinstance(protocols, str): + protocols = (protocols, ) + + # Secure protocol(s) should be None or a tuple + secure_protocols = getattr(plugin, 'secure_protocol', None) + if isinstance(secure_protocols, str): + secure_protocols = (secure_protocols, ) + + # Add our protocol details to our content + content.update({ + 'protocols': protocols, + 'secure_protocols': secure_protocols, + }) + + if not lang: + # Simply return our results + content['details'] = plugins.details(plugin) + if show_requirements: + content['requirements'] = plugins.requirements(plugin) + + else: + # Emulate the specified language when returning our results + with self.locale.lang_at(lang): + content['details'] = plugins.details(plugin) + if show_requirements: + content['requirements'] = plugins.requirements(plugin) + + # Build our response object + response['schemas'].append(content) + + return response + + def urls(self, privacy=False): + """ + Returns all of the loaded URLs defined in this apprise object. + """ + return [x.url(privacy=privacy) for x in self.servers] + + def pop(self, index): + """ + Removes an indexed Notification Service from the stack and returns it. + + The thing is we can never pop AppriseConfig() entries, only what was + loaded within them. So pop needs to carefully iterate over our list + and only track actual entries. + """ + + # Tracking variables + prev_offset = -1 + offset = prev_offset + + for idx, s in enumerate(self.servers): + if isinstance(s, (ConfigBase, AppriseConfig)): + servers = s.servers() + if len(servers) > 0: + # Acquire a new maximum offset to work with + offset = prev_offset + len(servers) + + if offset >= index: + # we can pop an element from our config stack + fn = s.pop if isinstance(s, ConfigBase) \ + else s.server_pop + + return fn(index if prev_offset == -1 + else (index - prev_offset - 1)) + + else: + offset = prev_offset + 1 + if offset == index: + return self.servers.pop(idx) + + # Update our old offset + prev_offset = offset + + # If we reach here, then we indexed out of range + raise IndexError('list index out of range') + + def __getitem__(self, index): + """ + Returns the indexed server entry of a loaded notification server + """ + # Tracking variables + prev_offset = -1 + offset = prev_offset + + for idx, s in enumerate(self.servers): + if isinstance(s, (ConfigBase, AppriseConfig)): + # Get our list of servers associate with our config object + servers = s.servers() + if len(servers) > 0: + # Acquire a new maximum offset to work with + offset = prev_offset + len(servers) + + if offset >= index: + return servers[index if prev_offset == -1 + else (index - prev_offset - 1)] + + else: + offset = prev_offset + 1 + if offset == index: + return self.servers[idx] + + # Update our old offset + prev_offset = offset + + # If we reach here, then we indexed out of range + raise IndexError('list index out of range') + + def __getstate__(self): + """ + Pickle Support dumps() + """ + attributes = { + 'asset': self.asset, + # Prepare our URL list as we need to extract the associated tags + # and asset details associated with it + 'urls': [{ + 'url': server.url(privacy=False), + 'tag': server.tags if server.tags else None, + 'asset': server.asset} for server in self.servers], + 'locale': self.locale, + 'debug': self.debug, + 'location': self.location, + } + + return attributes + + def __setstate__(self, state): + """ + Pickle Support loads() + """ + self.servers = list() + self.asset = state['asset'] + self.locale = state['locale'] + self.location = state['location'] + for entry in state['urls']: + self.add(entry['url'], asset=entry['asset'], tag=entry['tag']) + + def __bool__(self): + """ + Allows the Apprise object to be wrapped in an 'if statement'. + True is returned if at least one service has been loaded. + """ + return len(self) > 0 + + def __iter__(self): + """ + Returns an iterator to each of our servers loaded. This includes those + found inside configuration. + """ + return chain(*[[s] if not isinstance(s, (ConfigBase, AppriseConfig)) + else iter(s.servers()) for s in self.servers]) + + def __len__(self): + """ + Returns the number of servers loaded; this includes those found within + loaded configuration. This funtion nnever actually counts the + Config entry themselves (if they exist), only what they contain. + """ + return sum([1 if not isinstance(s, (ConfigBase, AppriseConfig)) + else len(s.servers()) for s in self.servers]) diff --git a/libs/apprise/apprise.pyi b/libs/apprise/apprise.pyi new file mode 100644 index 000000000..5a34c9c65 --- /dev/null +++ b/libs/apprise/apprise.pyi @@ -0,0 +1,62 @@ +from typing import Any, Dict, List, Iterable, Iterator, Optional + +from . import (AppriseAsset, AppriseAttachment, AppriseConfig, ConfigBase, + NotifyBase, NotifyFormat, NotifyType) +from .common import ContentLocation + +_Server = Union[str, ConfigBase, NotifyBase, AppriseConfig] +_Servers = Union[_Server, Dict[Any, _Server], Iterable[_Server]] +# Can't define this recursively as mypy doesn't support recursive types: +# https://github.com/python/mypy/issues/731 +_Tag = Union[str, Iterable[Union[str, Iterable[str]]]] + +class Apprise: + def __init__( + self, + servers: _Servers = ..., + asset: Optional[AppriseAsset] = ..., + location: Optional[ContentLocation] = ..., + debug: bool = ... + ) -> None: ... + @staticmethod + def instantiate( + url: Union[str, Dict[str, NotifyBase]], + asset: Optional[AppriseAsset] = ..., + tag: Optional[_Tag] = ..., + suppress_exceptions: bool = ... + ) -> NotifyBase: ... + def add( + self, + servers: _Servers = ..., + asset: Optional[AppriseAsset] = ..., + tag: Optional[_Tag] = ... + ) -> bool: ... + def clear(self) -> None: ... + def find(self, tag: str = ...) -> Iterator[Apprise]: ... + def notify( + self, + body: str, + title: str = ..., + notify_type: NotifyType = ..., + body_format: NotifyFormat = ..., + tag: _Tag = ..., + attach: Optional[AppriseAttachment] = ..., + interpret_escapes: Optional[bool] = ... + ) -> bool: ... + async def async_notify( + self, + body: str, + title: str = ..., + notify_type: NotifyType = ..., + body_format: NotifyFormat = ..., + tag: _Tag = ..., + attach: Optional[AppriseAttachment] = ..., + interpret_escapes: Optional[bool] = ... + ) -> bool: ... + def details(self, lang: Optional[str] = ...) -> Dict[str, Any]: ... + def urls(self, privacy: bool = ...) -> Iterable[str]: ... + def pop(self, index: int) -> ConfigBase: ... + def __getitem__(self, index: int) -> ConfigBase: ... + def __bool__(self) -> bool: ... + def __iter__(self) -> Iterator[ConfigBase]: ... + def __len__(self) -> int: ... \ No newline at end of file From 9cb2708909420dbd2d9b0ae637a3bbde410e5176 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 12 May 2024 23:16:01 -0400 Subject: [PATCH 063/129] no log: Delete libs/apprise/Apprise.pyi --- libs/apprise/Apprise.pyi | 62 ---------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 libs/apprise/Apprise.pyi diff --git a/libs/apprise/Apprise.pyi b/libs/apprise/Apprise.pyi deleted file mode 100644 index 5a34c9c65..000000000 --- a/libs/apprise/Apprise.pyi +++ /dev/null @@ -1,62 +0,0 @@ -from typing import Any, Dict, List, Iterable, Iterator, Optional - -from . import (AppriseAsset, AppriseAttachment, AppriseConfig, ConfigBase, - NotifyBase, NotifyFormat, NotifyType) -from .common import ContentLocation - -_Server = Union[str, ConfigBase, NotifyBase, AppriseConfig] -_Servers = Union[_Server, Dict[Any, _Server], Iterable[_Server]] -# Can't define this recursively as mypy doesn't support recursive types: -# https://github.com/python/mypy/issues/731 -_Tag = Union[str, Iterable[Union[str, Iterable[str]]]] - -class Apprise: - def __init__( - self, - servers: _Servers = ..., - asset: Optional[AppriseAsset] = ..., - location: Optional[ContentLocation] = ..., - debug: bool = ... - ) -> None: ... - @staticmethod - def instantiate( - url: Union[str, Dict[str, NotifyBase]], - asset: Optional[AppriseAsset] = ..., - tag: Optional[_Tag] = ..., - suppress_exceptions: bool = ... - ) -> NotifyBase: ... - def add( - self, - servers: _Servers = ..., - asset: Optional[AppriseAsset] = ..., - tag: Optional[_Tag] = ... - ) -> bool: ... - def clear(self) -> None: ... - def find(self, tag: str = ...) -> Iterator[Apprise]: ... - def notify( - self, - body: str, - title: str = ..., - notify_type: NotifyType = ..., - body_format: NotifyFormat = ..., - tag: _Tag = ..., - attach: Optional[AppriseAttachment] = ..., - interpret_escapes: Optional[bool] = ... - ) -> bool: ... - async def async_notify( - self, - body: str, - title: str = ..., - notify_type: NotifyType = ..., - body_format: NotifyFormat = ..., - tag: _Tag = ..., - attach: Optional[AppriseAttachment] = ..., - interpret_escapes: Optional[bool] = ... - ) -> bool: ... - def details(self, lang: Optional[str] = ...) -> Dict[str, Any]: ... - def urls(self, privacy: bool = ...) -> Iterable[str]: ... - def pop(self, index: int) -> ConfigBase: ... - def __getitem__(self, index: int) -> ConfigBase: ... - def __bool__(self) -> bool: ... - def __iter__(self) -> Iterator[ConfigBase]: ... - def __len__(self) -> int: ... \ No newline at end of file From 811394cec31e144727318820df5ad9506bdfcafd Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sun, 12 May 2024 23:16:17 -0400 Subject: [PATCH 064/129] no log: Delete libs/apprise/Apprise.py --- libs/apprise/Apprise.py | 887 ---------------------------------------- 1 file changed, 887 deletions(-) delete mode 100644 libs/apprise/Apprise.py diff --git a/libs/apprise/Apprise.py b/libs/apprise/Apprise.py deleted file mode 100644 index 05a2ee3cc..000000000 --- a/libs/apprise/Apprise.py +++ /dev/null @@ -1,887 +0,0 @@ -# -*- coding: utf-8 -*- -# BSD 2-Clause License -# -# Apprise - Push Notification Library. -# Copyright (c) 2024, Chris Caron -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import asyncio -import concurrent.futures as cf -import os -from itertools import chain -from . import common -from .conversion import convert_between -from .utils import is_exclusive_match -from .manager_plugins import NotificationManager -from .utils import parse_list -from .utils import parse_urls -from .utils import cwe312_url -from .emojis import apply_emojis -from .logger import logger -from .asset import AppriseAsset -from .apprise_config import AppriseConfig -from .apprise_attachment import AppriseAttachment -from .locale import AppriseLocale -from .config.base import ConfigBase -from .plugins.base import NotifyBase - -from . import plugins -from . import __version__ - -# Grant access to our Notification Manager Singleton -N_MGR = NotificationManager() - - -class Apprise: - """ - Our Notification Manager - - """ - - def __init__(self, servers=None, asset=None, location=None, debug=False): - """ - Loads a set of server urls while applying the Asset() module to each - if specified. - - If no asset is provided, then the default asset is used. - - Optionally specify a global ContentLocation for a more strict means - of handling Attachments. - """ - - # Initialize a server list of URLs - self.servers = list() - - # Assigns an central asset object that will be later passed into each - # notification plugin. Assets contain information such as the local - # directory images can be found in. It can also identify remote - # URL paths that contain the images you want to present to the end - # user. If no asset is specified, then the default one is used. - self.asset = \ - asset if isinstance(asset, AppriseAsset) else AppriseAsset() - - if servers: - self.add(servers) - - # Initialize our locale object - self.locale = AppriseLocale() - - # Set our debug flag - self.debug = debug - - # Store our hosting location for optional strict rule handling - # of Attachments. Setting this to None removes any attachment - # restrictions. - self.location = location - - @staticmethod - def instantiate(url, asset=None, tag=None, suppress_exceptions=True): - """ - Returns the instance of a instantiated plugin based on the provided - Server URL. If the url fails to be parsed, then None is returned. - - The specified url can be either a string (the URL itself) or a - dictionary containing all of the components needed to istantiate - the notification service. If identifying a dictionary, at the bare - minimum, one must specify the schema. - - An example of a url dictionary object might look like: - { - schema: 'mailto', - host: 'google.com', - user: 'myuser', - password: 'mypassword', - } - - Alternatively the string is much easier to specify: - mailto://user:mypassword@google.com - - The dictionary works well for people who are calling details() to - extract the components they need to build the URL manually. - """ - - # Initialize our result set - results = None - - # Prepare our Asset Object - asset = asset if isinstance(asset, AppriseAsset) else AppriseAsset() - - if isinstance(url, str): - # Acquire our url tokens - results = plugins.url_to_dict( - url, secure_logging=asset.secure_logging) - - if results is None: - # Failed to parse the server URL; detailed logging handled - # inside url_to_dict - nothing to report here. - return None - - elif isinstance(url, dict): - # We already have our result set - results = url - - if results.get('schema') not in N_MGR: - # schema is a mandatory dictionary item as it is the only way - # we can index into our loaded plugins - logger.error('Dictionary does not include a "schema" entry.') - logger.trace( - 'Invalid dictionary unpacked as:{}{}'.format( - os.linesep, os.linesep.join( - ['{}="{}"'.format(k, v) - for k, v in results.items()]))) - return None - - logger.trace( - 'Dictionary unpacked as:{}{}'.format( - os.linesep, os.linesep.join( - ['{}="{}"'.format(k, v) for k, v in results.items()]))) - - # Otherwise we handle the invalid input specified - else: - logger.error( - 'An invalid URL type (%s) was specified for instantiation', - type(url)) - return None - - if not N_MGR[results['schema']].enabled: - # - # First Plugin Enable Check (Pre Initialization) - # - - # Plugin has been disabled at a global level - logger.error( - '%s:// is disabled on this system.', results['schema']) - return None - - # Build a list of tags to associate with the newly added notifications - results['tag'] = set(parse_list(tag)) - - # Set our Asset Object - results['asset'] = asset - - if suppress_exceptions: - try: - # Attempt to create an instance of our plugin using the parsed - # URL information - plugin = N_MGR[results['schema']](**results) - - # Create log entry of loaded URL - logger.debug( - 'Loaded {} URL: {}'.format( - N_MGR[results['schema']].service_name, - plugin.url(privacy=asset.secure_logging))) - - except Exception: - # CWE-312 (Secure Logging) Handling - loggable_url = url if not asset.secure_logging \ - else cwe312_url(url) - - # the arguments are invalid or can not be used. - logger.error( - 'Could not load {} URL: {}'.format( - N_MGR[results['schema']].service_name, - loggable_url)) - return None - - else: - # Attempt to create an instance of our plugin using the parsed - # URL information but don't wrap it in a try catch - plugin = N_MGR[results['schema']](**results) - - if not plugin.enabled: - # - # Second Plugin Enable Check (Post Initialization) - # - - # Service/Plugin is disabled (on a more local level). This is a - # case where the plugin was initially enabled but then after the - # __init__() was called under the hood something pre-determined - # that it could no longer be used. - - # The only downside to doing it this way is services are - # initialized prior to returning the details() if 3rd party tools - # are polling what is available. These services that become - # disabled thereafter are shown initially that they can be used. - logger.error( - '%s:// has become disabled on this system.', results['schema']) - return None - - return plugin - - def add(self, servers, asset=None, tag=None): - """ - Adds one or more server URLs into our list. - - You can override the global asset if you wish by including it with the - server(s) that you add. - - The tag allows you to associate 1 or more tag values to the server(s) - being added. tagging a service allows you to exclusively access them - when calling the notify() function. - """ - - # Initialize our return status - return_status = True - - if asset is None: - # prepare default asset - asset = self.asset - - if isinstance(servers, str): - # build our server list - servers = parse_urls(servers) - if len(servers) == 0: - return False - - elif isinstance(servers, dict): - # no problem, we support kwargs, convert it to a list - servers = [servers] - - elif isinstance(servers, (ConfigBase, NotifyBase, AppriseConfig)): - # Go ahead and just add our plugin into our list - self.servers.append(servers) - return True - - elif not isinstance(servers, (tuple, set, list)): - logger.error( - "An invalid notification (type={}) was specified.".format( - type(servers))) - return False - - for _server in servers: - - if isinstance(_server, (ConfigBase, NotifyBase, AppriseConfig)): - # Go ahead and just add our plugin into our list - self.servers.append(_server) - continue - - elif not isinstance(_server, (str, dict)): - logger.error( - "An invalid notification (type={}) was specified.".format( - type(_server))) - return_status = False - continue - - # Instantiate ourselves an object, this function throws or - # returns None if it fails - instance = Apprise.instantiate(_server, asset=asset, tag=tag) - if not isinstance(instance, NotifyBase): - # No logging is required as instantiate() handles failure - # and/or success reasons for us - return_status = False - continue - - # Add our initialized plugin to our server listings - self.servers.append(instance) - - # Return our status - return return_status - - def clear(self): - """ - Empties our server list - - """ - self.servers[:] = [] - - def find(self, tag=common.MATCH_ALL_TAG, match_always=True): - """ - Returns a list of all servers matching against the tag specified. - - """ - - # Build our tag setup - # - top level entries are treated as an 'or' - # - second level (or more) entries are treated as 'and' - # - # examples: - # tag="tagA, tagB" = tagA or tagB - # tag=['tagA', 'tagB'] = tagA or tagB - # tag=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB - # tag=[('tagB', 'tagC')] = tagB and tagC - - # A match_always flag allows us to pick up on our 'any' keyword - # and notify these services under all circumstances - match_always = common.MATCH_ALWAYS_TAG if match_always else None - - # Iterate over our loaded plugins - for entry in self.servers: - - if isinstance(entry, (ConfigBase, AppriseConfig)): - # load our servers - servers = entry.servers() - - else: - servers = [entry, ] - - for server in servers: - # Apply our tag matching based on our defined logic - if is_exclusive_match( - logic=tag, data=server.tags, - match_all=common.MATCH_ALL_TAG, - match_always=match_always): - yield server - return - - def notify(self, body, title='', notify_type=common.NotifyType.INFO, - body_format=None, tag=common.MATCH_ALL_TAG, match_always=True, - attach=None, interpret_escapes=None): - """ - Send a notification to all the plugins previously loaded. - - If the body_format specified is NotifyFormat.MARKDOWN, it will - be converted to HTML if the Notification type expects this. - - if the tag is specified (either a string or a set/list/tuple - of strings), then only the notifications flagged with that - tagged value are notified. By default, all added services - are notified (tag=MATCH_ALL_TAG) - - This function returns True if all notifications were successfully - sent, False if even just one of them fails, and None if no - notifications were sent at all as a result of tag filtering and/or - simply having empty configuration files that were read. - - Attach can contain a list of attachment URLs. attach can also be - represented by an AttachBase() (or list of) object(s). This - identifies the products you wish to notify - - Set interpret_escapes to True if you want to pre-escape a string - such as turning a \n into an actual new line, etc. - """ - - try: - # Process arguments and build synchronous and asynchronous calls - # (this step can throw internal errors). - sequential_calls, parallel_calls = self._create_notify_calls( - body, title, - notify_type=notify_type, body_format=body_format, - tag=tag, match_always=match_always, attach=attach, - interpret_escapes=interpret_escapes, - ) - - except TypeError: - # No notifications sent, and there was an internal error. - return False - - if not sequential_calls and not parallel_calls: - # Nothing to send - return None - - sequential_result = Apprise._notify_sequential(*sequential_calls) - parallel_result = Apprise._notify_parallel_threadpool(*parallel_calls) - return sequential_result and parallel_result - - async def async_notify(self, *args, **kwargs): - """ - Send a notification to all the plugins previously loaded, for - asynchronous callers. - - The arguments are identical to those of Apprise.notify(). - - """ - try: - # Process arguments and build synchronous and asynchronous calls - # (this step can throw internal errors). - sequential_calls, parallel_calls = self._create_notify_calls( - *args, **kwargs) - - except TypeError: - # No notifications sent, and there was an internal error. - return False - - if not sequential_calls and not parallel_calls: - # Nothing to send - return None - - sequential_result = Apprise._notify_sequential(*sequential_calls) - parallel_result = \ - await Apprise._notify_parallel_asyncio(*parallel_calls) - return sequential_result and parallel_result - - def _create_notify_calls(self, *args, **kwargs): - """ - Creates notifications for all the plugins loaded. - - Returns a list of (server, notify() kwargs) tuples for plugins with - parallelism disabled and another list for plugins with parallelism - enabled. - """ - - all_calls = list(self._create_notify_gen(*args, **kwargs)) - - # Split into sequential and parallel notify() calls. - sequential, parallel = [], [] - for (server, notify_kwargs) in all_calls: - if server.asset.async_mode: - parallel.append((server, notify_kwargs)) - else: - sequential.append((server, notify_kwargs)) - - return sequential, parallel - - def _create_notify_gen(self, body, title='', - notify_type=common.NotifyType.INFO, - body_format=None, tag=common.MATCH_ALL_TAG, - match_always=True, attach=None, - interpret_escapes=None): - """ - Internal generator function for _create_notify_calls(). - """ - - if len(self) == 0: - # Nothing to notify - msg = "There are no service(s) to notify" - logger.error(msg) - raise TypeError(msg) - - if not (title or body or attach): - msg = "No message content specified to deliver" - logger.error(msg) - raise TypeError(msg) - - try: - if title and isinstance(title, bytes): - title = title.decode(self.asset.encoding) - - if body and isinstance(body, bytes): - body = body.decode(self.asset.encoding) - - except UnicodeDecodeError: - msg = 'The content passed into Apprise was not of encoding ' \ - 'type: {}'.format(self.asset.encoding) - logger.error(msg) - raise TypeError(msg) - - # Tracks conversions - conversion_body_map = dict() - conversion_title_map = dict() - - # Prepare attachments if required - if attach is not None and not isinstance(attach, AppriseAttachment): - attach = AppriseAttachment( - attach, asset=self.asset, location=self.location) - - # Allow Asset default value - body_format = self.asset.body_format \ - if body_format is None else body_format - - # Allow Asset default value - interpret_escapes = self.asset.interpret_escapes \ - if interpret_escapes is None else interpret_escapes - - # Iterate over our loaded plugins - for server in self.find(tag, match_always=match_always): - # If our code reaches here, we either did not define a tag (it - # was set to None), or we did define a tag and the logic above - # determined we need to notify the service it's associated with - - # First we need to generate a key we will use to determine if we - # need to build our data out. Entries without are merged with - # the body at this stage. - key = server.notify_format if server.title_maxlen > 0\ - else f'_{server.notify_format}' - - if server.interpret_emojis: - # alter our key slightly to handle emojis since their value is - # pulled out of the notification - key += "-emojis" - - if key not in conversion_title_map: - - # Prepare our title - conversion_title_map[key] = '' if not title else title - - # Conversion of title only occurs for services where the title - # is blended with the body (title_maxlen <= 0) - if conversion_title_map[key] and server.title_maxlen <= 0: - conversion_title_map[key] = convert_between( - body_format, server.notify_format, - content=conversion_title_map[key]) - - # Our body is always converted no matter what - conversion_body_map[key] = \ - convert_between( - body_format, server.notify_format, content=body) - - if interpret_escapes: - # - # Escape our content - # - - try: - # Added overhead required due to Python 3 Encoding Bug - # identified here: https://bugs.python.org/issue21331 - conversion_body_map[key] = \ - conversion_body_map[key]\ - .encode('ascii', 'backslashreplace')\ - .decode('unicode-escape') - - conversion_title_map[key] = \ - conversion_title_map[key]\ - .encode('ascii', 'backslashreplace')\ - .decode('unicode-escape') - - except AttributeError: - # Must be of string type - msg = 'Failed to escape message body' - logger.error(msg) - raise TypeError(msg) - - if server.interpret_emojis: - # - # Convert our :emoji: definitions - # - - conversion_body_map[key] = \ - apply_emojis(conversion_body_map[key]) - conversion_title_map[key] = \ - apply_emojis(conversion_title_map[key]) - - kwargs = dict( - body=conversion_body_map[key], - title=conversion_title_map[key], - notify_type=notify_type, - attach=attach, - body_format=body_format - ) - yield (server, kwargs) - - @staticmethod - def _notify_sequential(*servers_kwargs): - """ - Process a list of notify() calls sequentially and synchronously. - """ - - success = True - - for (server, kwargs) in servers_kwargs: - try: - # Send notification - result = server.notify(**kwargs) - success = success and result - - except TypeError: - # These are our internally thrown notifications. - success = False - - except Exception: - # A catch all so we don't have to abort early - # just because one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - success = False - - return success - - @staticmethod - def _notify_parallel_threadpool(*servers_kwargs): - """ - Process a list of notify() calls in parallel and synchronously. - """ - - n_calls = len(servers_kwargs) - - # 0-length case - if n_calls == 0: - return True - - # There's no need to use a thread pool for just a single notification - if n_calls == 1: - return Apprise._notify_sequential(servers_kwargs[0]) - - # Create log entry - logger.info( - 'Notifying %d service(s) with threads.', len(servers_kwargs)) - - with cf.ThreadPoolExecutor() as executor: - success = True - futures = [executor.submit(server.notify, **kwargs) - for (server, kwargs) in servers_kwargs] - - for future in cf.as_completed(futures): - try: - result = future.result() - success = success and result - - except TypeError: - # These are our internally thrown notifications. - success = False - - except Exception: - # A catch all so we don't have to abort early - # just because one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - success = False - - return success - - @staticmethod - async def _notify_parallel_asyncio(*servers_kwargs): - """ - Process a list of async_notify() calls in parallel and asynchronously. - """ - - n_calls = len(servers_kwargs) - - # 0-length case - if n_calls == 0: - return True - - # (Unlike with the thread pool, we don't optimize for the single- - # notification case because asyncio can do useful work while waiting - # for that thread to complete) - - # Create log entry - logger.info( - 'Notifying %d service(s) asynchronously.', len(servers_kwargs)) - - async def do_call(server, kwargs): - return await server.async_notify(**kwargs) - - cors = (do_call(server, kwargs) for (server, kwargs) in servers_kwargs) - results = await asyncio.gather(*cors, return_exceptions=True) - - if any(isinstance(status, Exception) - and not isinstance(status, TypeError) for status in results): - # A catch all so we don't have to abort early just because - # one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - return False - - if any(isinstance(status, TypeError) for status in results): - # These are our internally thrown notifications. - return False - - return all(results) - - def details(self, lang=None, show_requirements=False, show_disabled=False): - """ - Returns the details associated with the Apprise object - - """ - - # general object returned - response = { - # Defines the current version of Apprise - 'version': __version__, - # Lists all of the currently supported Notifications - 'schemas': [], - # Includes the configured asset details - 'asset': self.asset.details(), - } - - for plugin in N_MGR.plugins(): - # Iterate over our hashed plugins and dynamically build details on - # their status: - - content = { - 'service_name': getattr(plugin, 'service_name', None), - 'service_url': getattr(plugin, 'service_url', None), - 'setup_url': getattr(plugin, 'setup_url', None), - # Placeholder - populated below - 'details': None, - - # Let upstream service know of the plugins that support - # attachments - 'attachment_support': getattr( - plugin, 'attachment_support', False), - - # Differentiat between what is a custom loaded plugin and - # which is native. - 'category': getattr(plugin, 'category', None) - } - - # Standard protocol(s) should be None or a tuple - enabled = getattr(plugin, 'enabled', True) - if not show_disabled and not enabled: - # Do not show inactive plugins - continue - - elif show_disabled: - # Add current state to response - content['enabled'] = enabled - - # Standard protocol(s) should be None or a tuple - protocols = getattr(plugin, 'protocol', None) - if isinstance(protocols, str): - protocols = (protocols, ) - - # Secure protocol(s) should be None or a tuple - secure_protocols = getattr(plugin, 'secure_protocol', None) - if isinstance(secure_protocols, str): - secure_protocols = (secure_protocols, ) - - # Add our protocol details to our content - content.update({ - 'protocols': protocols, - 'secure_protocols': secure_protocols, - }) - - if not lang: - # Simply return our results - content['details'] = plugins.details(plugin) - if show_requirements: - content['requirements'] = plugins.requirements(plugin) - - else: - # Emulate the specified language when returning our results - with self.locale.lang_at(lang): - content['details'] = plugins.details(plugin) - if show_requirements: - content['requirements'] = plugins.requirements(plugin) - - # Build our response object - response['schemas'].append(content) - - return response - - def urls(self, privacy=False): - """ - Returns all of the loaded URLs defined in this apprise object. - """ - return [x.url(privacy=privacy) for x in self.servers] - - def pop(self, index): - """ - Removes an indexed Notification Service from the stack and returns it. - - The thing is we can never pop AppriseConfig() entries, only what was - loaded within them. So pop needs to carefully iterate over our list - and only track actual entries. - """ - - # Tracking variables - prev_offset = -1 - offset = prev_offset - - for idx, s in enumerate(self.servers): - if isinstance(s, (ConfigBase, AppriseConfig)): - servers = s.servers() - if len(servers) > 0: - # Acquire a new maximum offset to work with - offset = prev_offset + len(servers) - - if offset >= index: - # we can pop an element from our config stack - fn = s.pop if isinstance(s, ConfigBase) \ - else s.server_pop - - return fn(index if prev_offset == -1 - else (index - prev_offset - 1)) - - else: - offset = prev_offset + 1 - if offset == index: - return self.servers.pop(idx) - - # Update our old offset - prev_offset = offset - - # If we reach here, then we indexed out of range - raise IndexError('list index out of range') - - def __getitem__(self, index): - """ - Returns the indexed server entry of a loaded notification server - """ - # Tracking variables - prev_offset = -1 - offset = prev_offset - - for idx, s in enumerate(self.servers): - if isinstance(s, (ConfigBase, AppriseConfig)): - # Get our list of servers associate with our config object - servers = s.servers() - if len(servers) > 0: - # Acquire a new maximum offset to work with - offset = prev_offset + len(servers) - - if offset >= index: - return servers[index if prev_offset == -1 - else (index - prev_offset - 1)] - - else: - offset = prev_offset + 1 - if offset == index: - return self.servers[idx] - - # Update our old offset - prev_offset = offset - - # If we reach here, then we indexed out of range - raise IndexError('list index out of range') - - def __getstate__(self): - """ - Pickle Support dumps() - """ - attributes = { - 'asset': self.asset, - # Prepare our URL list as we need to extract the associated tags - # and asset details associated with it - 'urls': [{ - 'url': server.url(privacy=False), - 'tag': server.tags if server.tags else None, - 'asset': server.asset} for server in self.servers], - 'locale': self.locale, - 'debug': self.debug, - 'location': self.location, - } - - return attributes - - def __setstate__(self, state): - """ - Pickle Support loads() - """ - self.servers = list() - self.asset = state['asset'] - self.locale = state['locale'] - self.location = state['location'] - for entry in state['urls']: - self.add(entry['url'], asset=entry['asset'], tag=entry['tag']) - - def __bool__(self): - """ - Allows the Apprise object to be wrapped in an 'if statement'. - True is returned if at least one service has been loaded. - """ - return len(self) > 0 - - def __iter__(self): - """ - Returns an iterator to each of our servers loaded. This includes those - found inside configuration. - """ - return chain(*[[s] if not isinstance(s, (ConfigBase, AppriseConfig)) - else iter(s.servers()) for s in self.servers]) - - def __len__(self): - """ - Returns the number of servers loaded; this includes those found within - loaded configuration. This funtion nnever actually counts the - Config entry themselves (if they exist), only what they contain. - """ - return sum([1 if not isinstance(s, (ConfigBase, AppriseConfig)) - else len(s.servers()) for s in self.servers]) From d8e58cac836f0615cddbd85b62d2af8498e7df40 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 13 May 2024 22:11:40 -0400 Subject: [PATCH 065/129] no log: fixed empty subtitles being saved --- custom_libs/subliminal_patch/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index 1282c2e4b..872896e9c 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -1193,7 +1193,7 @@ def save_subtitles(file_path, subtitles, single=False, directory=None, chmod=Non must_remove_hi = 'remove_HI' in subtitle.mods # check content - if subtitle.content is None: + if subtitle.content is None or subtitle.text is None: logger.error('Skipping subtitle %r: no content', subtitle) continue From 07534282a24b48efb8bdd98a4c96ddd1114581cb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 May 2024 22:24:04 -0400 Subject: [PATCH 066/129] no log: Bump @types/lodash from 4.17.0 to 4.17.1 in /frontend (#2495) Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.17.0 to 4.17.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash) --- updated-dependencies: - dependency-name: "@types/lodash" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- frontend/package-lock.json | 8 ++++---- frontend/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 42c99c60c..bfc24c17f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -33,7 +33,7 @@ "@testing-library/react": "^15.0.5", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", - "@types/lodash": "^4.17.0", + "@types/lodash": "^4.17.1", "@types/node": "^20.12.6", "@types/react": "^18.2.75", "@types/react-dom": "^18.2.24", @@ -3882,9 +3882,9 @@ "dev": true }, "node_modules/@types/lodash": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.0.tgz", - "integrity": "sha512-t7dhREVv6dbNj0q17X12j7yDG4bD/DHYX7o5/DbDxobP0HnGPgpRz2Ej77aL7TZT3DSw13fqUTj8J4mMnqa7WA==", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.1.tgz", + "integrity": "sha512-X+2qazGS3jxLAIz5JDXDzglAF3KpijdhFxlf/V1+hEsOUc+HnWi81L/uv/EvGuV90WY+7mPGFCUDGfQC3Gj95Q==", "dev": true }, "node_modules/@types/node": { diff --git a/frontend/package.json b/frontend/package.json index df5b6918a..eadb26496 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -37,7 +37,7 @@ "@testing-library/react": "^15.0.5", "@testing-library/user-event": "^14.5.2", "@types/jest": "^29.5.12", - "@types/lodash": "^4.17.0", + "@types/lodash": "^4.17.1", "@types/node": "^20.12.6", "@types/react": "^18.2.75", "@types/react-dom": "^18.2.24", From 3e929d8ef90fcb77bba0abeb4662d4d5e2882e6a Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Thu, 23 May 2024 20:46:07 -0400 Subject: [PATCH 067/129] Fixed upgrade process that was broken since Apprise 1.8.0 update. #2497 --- bazarr/app/check_update.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bazarr/app/check_update.py b/bazarr/app/check_update.py index 4a8f64868..97617c75b 100644 --- a/bazarr/app/check_update.py +++ b/bazarr/app/check_update.py @@ -229,6 +229,9 @@ def update_cleaner(zipfile, bazarr_dir, config_dir): dir_to_ignore_regex = re.compile(dir_to_ignore_regex_string) file_to_ignore = ['nssm.exe', '7za.exe', 'unins000.exe', 'unins000.dat'] + # prevent deletion of leftover Apprise.py/pyi files after 1.8.0 version that caused issue on case-insensitive + # filesystem. This could be removed in a couple of major versions. + file_to_ignore += ['Apprise.py', 'Apprise.pyi', 'apprise.py', 'apprise.pyi'] logging.debug(f'BAZARR upgrade leftover cleaner will ignore those files: {", ".join(file_to_ignore)}') extension_to_ignore = ['.pyc'] logging.debug( From 5ca733eac0ec43ebd3ca68e867bfd6ef0fb30cc2 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Fri, 24 May 2024 13:19:37 -0400 Subject: [PATCH 068/129] Reverted to apprise 1.7.6 to fix an issue with the upgrade process first. 1.8.0 will get back in nightly shortly. #2497 --- .../INSTALLER | 0 .../LICENSE | 0 .../METADATA | 2 +- libs/apprise-1.7.6.dist-info/RECORD | 183 ++++ .../REQUESTED | 0 .../WHEEL | 0 .../entry_points.txt | 0 .../top_level.txt | 0 libs/apprise-1.8.0.dist-info/RECORD | 183 ---- libs/apprise/Apprise.py | 887 ++++++++++++++++++ libs/apprise/Apprise.pyi | 62 ++ libs/apprise/{asset.py => AppriseAsset.py} | 2 +- libs/apprise/{asset.pyi => AppriseAsset.pyi} | 0 ...ise_attachment.py => AppriseAttachment.py} | 6 +- ...e_attachment.pyi => AppriseAttachment.pyi} | 0 .../{apprise_config.py => AppriseConfig.py} | 4 +- .../{apprise_config.pyi => AppriseConfig.pyi} | 0 libs/apprise/{locale.py => AppriseLocale.py} | 0 ...ger_attachment.py => AttachmentManager.py} | 5 - ...ager_config.py => ConfigurationManager.py} | 5 - ...ager_plugins.py => NotificationManager.py} | 6 - libs/apprise/{url.py => URLBase.py} | 9 +- libs/apprise/{url.pyi => URLBase.pyi} | 0 libs/apprise/__init__.py | 23 +- .../attachment/{base.py => AttachBase.py} | 9 +- .../attachment/{base.pyi => AttachBase.pyi} | 0 .../attachment/{file.py => AttachFile.py} | 4 +- .../attachment/{http.py => AttachHTTP.py} | 12 +- libs/apprise/attachment/__init__.py | 5 +- .../apprise/config/{base.py => ConfigBase.py} | 8 +- .../config/{base.pyi => ConfigBase.pyi} | 0 .../apprise/config/{file.py => ConfigFile.py} | 4 +- .../apprise/config/{http.py => ConfigHTTP.py} | 6 +- .../config/{memory.py => ConfigMemory.py} | 4 +- libs/apprise/config/__init__.py | 5 +- libs/apprise/conversion.py | 2 +- .../{base.py => CustomNotifyPlugin.py} | 7 +- libs/apprise/decorators/notify.py | 2 +- libs/apprise/i18n/en/LC_MESSAGES/apprise.mo | Bin 3959 -> 3959 bytes libs/apprise/manager.py | 83 +- .../{apprise_api.py => NotifyAppriseAPI.py} | 8 +- .../plugins/{aprs.py => NotifyAprs.py} | 6 +- .../plugins/{bark.py => NotifyBark.py} | 6 +- .../plugins/{base.py => NotifyBase.py} | 6 +- .../plugins/{base.pyi => NotifyBase.pyi} | 0 .../plugins/{boxcar.py => NotifyBoxcar.py} | 6 +- .../plugins/{bulksms.py => NotifyBulkSMS.py} | 6 +- .../plugins/{bulkvs.py => NotifyBulkVS.py} | 6 +- .../{burstsms.py => NotifyBurstSMS.py} | 6 +- .../{chantify.py => NotifyChantify.py} | 4 +- .../{clicksend.py => NotifyClickSend.py} | 27 +- .../{d7networks.py => NotifyD7Networks.py} | 4 +- .../plugins/{dbus.py => NotifyDBus.py} | 4 +- .../plugins/{dapnet.py => NotifyDapnet.py} | 6 +- .../{dingtalk.py => NotifyDingTalk.py} | 6 +- .../plugins/{discord.py => NotifyDiscord.py} | 6 +- .../plugins/{email.py => NotifyEmail.py} | 16 +- .../plugins/{emby.py => NotifyEmby.py} | 6 +- .../plugins/{enigma2.py => NotifyEnigma2.py} | 6 +- .../plugins/{fcm => NotifyFCM}/__init__.py | 6 +- .../plugins/{fcm => NotifyFCM}/color.py | 2 +- .../plugins/{fcm => NotifyFCM}/common.py | 0 .../plugins/{fcm => NotifyFCM}/oauth.py | 0 .../plugins/{fcm => NotifyFCM}/priority.py | 0 .../plugins/{feishu.py => NotifyFeishu.py} | 4 +- .../plugins/{flock.py => NotifyFlock.py} | 4 +- .../plugins/{custom_form.py => NotifyForm.py} | 6 +- .../{freemobile.py => NotifyFreeMobile.py} | 5 +- .../plugins/{gnome.py => NotifyGnome.py} | 4 +- .../{google_chat.py => NotifyGoogleChat.py} | 4 +- .../plugins/{gotify.py => NotifyGotify.py} | 4 +- .../plugins/{growl.py => NotifyGrowl.py} | 6 +- .../plugins/{guilded.py => NotifyGuilded.py} | 5 +- ...me_assistant.py => NotifyHomeAssistant.py} | 6 +- .../plugins/{httpsms.py => NotifyHttpSMS.py} | 4 +- .../plugins/{ifttt.py => NotifyIFTTT.py} | 4 +- .../plugins/{custom_json.py => NotifyJSON.py} | 6 +- .../plugins/{join.py => NotifyJoin.py} | 4 +- .../{kavenegar.py => NotifyKavenegar.py} | 4 +- .../plugins/{kumulos.py => NotifyKumulos.py} | 4 +- .../{lametric.py => NotifyLametric.py} | 4 +- .../plugins/{line.py => NotifyLine.py} | 6 +- .../plugins/{lunasea.py => NotifyLunaSea.py} | 6 +- .../plugins/{mqtt.py => NotifyMQTT.py} | 6 +- .../plugins/{msg91.py => NotifyMSG91.py} | 4 +- .../plugins/{msteams.py => NotifyMSTeams.py} | 6 +- .../plugins/{macosx.py => NotifyMacOSX.py} | 4 +- .../plugins/{mailgun.py => NotifyMailgun.py} | 4 +- .../{mastodon.py => NotifyMastodon.py} | 8 +- .../plugins/{matrix.py => NotifyMatrix.py} | 6 +- .../{mattermost.py => NotifyMattermost.py} | 4 +- .../{messagebird.py => NotifyMessageBird.py} | 4 +- .../plugins/{misskey.py => NotifyMisskey.py} | 4 +- .../{nextcloud.py => NotifyNextcloud.py} | 6 +- ...extcloudtalk.py => NotifyNextcloudTalk.py} | 6 +- .../plugins/{notica.py => NotifyNotica.py} | 6 +- .../{notifiarr.py => NotifyNotifiarr.py} | 4 +- .../{notifico.py => NotifyNotifico.py} | 4 +- .../plugins/{ntfy.py => NotifyNtfy.py} | 8 +- .../{office365.py => NotifyOffice365.py} | 6 +- .../{one_signal.py => NotifyOneSignal.py} | 4 +- .../{opsgenie.py => NotifyOpsgenie.py} | 4 +- .../{pagerduty.py => NotifyPagerDuty.py} | 6 +- .../{pagertree.py => NotifyPagerTree.py} | 4 +- ...arseplatform.py => NotifyParsePlatform.py} | 4 +- ...pcorn_notify.py => NotifyPopcornNotify.py} | 4 +- .../plugins/{prowl.py => NotifyProwl.py} | 4 +- .../{pushbullet.py => NotifyPushBullet.py} | 6 +- .../{pushdeer.py => NotifyPushDeer.py} | 4 +- .../plugins/{pushme.py => NotifyPushMe.py} | 4 +- .../{pushsafer.py => NotifyPushSafer.py} | 4 +- .../plugins/{pushed.py => NotifyPushed.py} | 6 +- .../plugins/{pushjet.py => NotifyPushjet.py} | 6 +- .../{pushover.py => NotifyPushover.py} | 6 +- .../plugins/{pushy.py => NotifyPushy.py} | 4 +- .../plugins/{rsyslog.py => NotifyRSyslog.py} | 4 +- .../plugins/{reddit.py => NotifyReddit.py} | 6 +- .../plugins/{revolt.py => NotifyRevolt.py} | 4 +- .../{rocketchat.py => NotifyRocketChat.py} | 6 +- .../plugins/{ryver.py => NotifyRyver.py} | 4 +- libs/apprise/plugins/{ses.py => NotifySES.py} | 6 +- .../{smseagle.py => NotifySMSEagle.py} | 6 +- .../{smsmanager.py => NotifySMSManager.py} | 4 +- .../plugins/{smtp2go.py => NotifySMTP2Go.py} | 4 +- libs/apprise/plugins/{sns.py => NotifySNS.py} | 6 +- .../{sendgrid.py => NotifySendGrid.py} | 4 +- .../{serverchan.py => NotifyServerChan.py} | 4 +- .../{signal_api.py => NotifySignalAPI.py} | 6 +- .../{simplepush.py => NotifySimplePush.py} | 6 +- .../plugins/{sinch.py => NotifySinch.py} | 6 +- .../plugins/{slack.py => NotifySlack.py} | 4 +- .../{sparkpost.py => NotifySparkPost.py} | 4 +- .../{streamlabs.py => NotifyStreamlabs.py} | 4 +- .../{synology.py => NotifySynology.py} | 6 +- .../plugins/{syslog.py => NotifySyslog.py} | 4 +- ...{techuluspush.py => NotifyTechulusPush.py} | 4 +- .../{telegram.py => NotifyTelegram.py} | 6 +- .../plugins/{threema.py => NotifyThreema.py} | 6 +- .../plugins/{twilio.py => NotifyTwilio.py} | 6 +- .../plugins/{twist.py => NotifyTwist.py} | 6 +- .../plugins/{twitter.py => NotifyTwitter.py} | 8 +- .../plugins/{voipms.py => NotifyVoipms.py} | 4 +- .../plugins/{vonage.py => NotifyVonage.py} | 6 +- .../{wecombot.py => NotifyWeComBot.py} | 4 +- .../{webexteams.py => NotifyWebexTeams.py} | 4 +- .../{whatsapp.py => NotifyWhatsApp.py} | 4 +- .../plugins/{windows.py => NotifyWindows.py} | 4 +- .../plugins/{xbmc.py => NotifyXBMC.py} | 6 +- .../plugins/{custom_xml.py => NotifyXML.py} | 6 +- .../plugins/{zulip.py => NotifyZulip.py} | 4 +- libs/apprise/plugins/__init__.py | 8 +- libs/apprise/utils.py | 13 +- libs/version.txt | 2 +- 153 files changed, 1511 insertions(+), 609 deletions(-) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/INSTALLER (100%) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/LICENSE (100%) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/METADATA (99%) create mode 100644 libs/apprise-1.7.6.dist-info/RECORD rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/REQUESTED (100%) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/WHEEL (100%) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/entry_points.txt (100%) rename libs/{apprise-1.8.0.dist-info => apprise-1.7.6.dist-info}/top_level.txt (100%) delete mode 100644 libs/apprise-1.8.0.dist-info/RECORD create mode 100644 libs/apprise/Apprise.py create mode 100644 libs/apprise/Apprise.pyi rename libs/apprise/{asset.py => AppriseAsset.py} (99%) rename libs/apprise/{asset.pyi => AppriseAsset.pyi} (100%) rename libs/apprise/{apprise_attachment.py => AppriseAttachment.py} (98%) rename libs/apprise/{apprise_attachment.pyi => AppriseAttachment.pyi} (100%) rename libs/apprise/{apprise_config.py => AppriseConfig.py} (99%) rename libs/apprise/{apprise_config.pyi => AppriseConfig.pyi} (100%) rename libs/apprise/{locale.py => AppriseLocale.py} (100%) rename libs/apprise/{manager_attachment.py => AttachmentManager.py} (93%) rename libs/apprise/{manager_config.py => ConfigurationManager.py} (93%) rename libs/apprise/{manager_plugins.py => NotificationManager.py} (92%) rename libs/apprise/{url.py => URLBase.py} (99%) rename libs/apprise/{url.pyi => URLBase.pyi} (100%) rename libs/apprise/attachment/{base.py => AttachBase.py} (98%) rename libs/apprise/attachment/{base.pyi => AttachBase.pyi} (100%) rename libs/apprise/attachment/{file.py => AttachFile.py} (98%) rename libs/apprise/attachment/{http.py => AttachHTTP.py} (98%) rename libs/apprise/config/{base.py => ConfigBase.py} (99%) rename libs/apprise/config/{base.pyi => ConfigBase.pyi} (100%) rename libs/apprise/config/{file.py => ConfigFile.py} (98%) rename libs/apprise/config/{http.py => ConfigHTTP.py} (98%) rename libs/apprise/config/{memory.py => ConfigMemory.py} (97%) rename libs/apprise/decorators/{base.py => CustomNotifyPlugin.py} (98%) rename libs/apprise/plugins/{apprise_api.py => NotifyAppriseAPI.py} (99%) rename libs/apprise/plugins/{aprs.py => NotifyAprs.py} (99%) rename libs/apprise/plugins/{bark.py => NotifyBark.py} (99%) rename libs/apprise/plugins/{base.py => NotifyBase.py} (99%) rename libs/apprise/plugins/{base.pyi => NotifyBase.pyi} (100%) rename libs/apprise/plugins/{boxcar.py => NotifyBoxcar.py} (99%) rename libs/apprise/plugins/{bulksms.py => NotifyBulkSMS.py} (99%) rename libs/apprise/plugins/{bulkvs.py => NotifyBulkVS.py} (99%) rename libs/apprise/plugins/{burstsms.py => NotifyBurstSMS.py} (99%) rename libs/apprise/plugins/{chantify.py => NotifyChantify.py} (98%) rename libs/apprise/plugins/{clicksend.py => NotifyClickSend.py} (94%) rename libs/apprise/plugins/{d7networks.py => NotifyD7Networks.py} (99%) rename libs/apprise/plugins/{dbus.py => NotifyDBus.py} (99%) rename libs/apprise/plugins/{dapnet.py => NotifyDapnet.py} (99%) rename libs/apprise/plugins/{dingtalk.py => NotifyDingTalk.py} (99%) rename libs/apprise/plugins/{discord.py => NotifyDiscord.py} (99%) rename libs/apprise/plugins/{email.py => NotifyEmail.py} (98%) rename libs/apprise/plugins/{emby.py => NotifyEmby.py} (99%) rename libs/apprise/plugins/{enigma2.py => NotifyEnigma2.py} (98%) rename libs/apprise/plugins/{fcm => NotifyFCM}/__init__.py (99%) rename libs/apprise/plugins/{fcm => NotifyFCM}/color.py (99%) rename libs/apprise/plugins/{fcm => NotifyFCM}/common.py (100%) rename libs/apprise/plugins/{fcm => NotifyFCM}/oauth.py (100%) rename libs/apprise/plugins/{fcm => NotifyFCM}/priority.py (100%) rename libs/apprise/plugins/{feishu.py => NotifyFeishu.py} (98%) rename libs/apprise/plugins/{flock.py => NotifyFlock.py} (99%) rename libs/apprise/plugins/{custom_form.py => NotifyForm.py} (99%) rename libs/apprise/plugins/{freemobile.py => NotifyFreeMobile.py} (98%) rename libs/apprise/plugins/{gnome.py => NotifyGnome.py} (99%) rename libs/apprise/plugins/{google_chat.py => NotifyGoogleChat.py} (99%) rename libs/apprise/plugins/{gotify.py => NotifyGotify.py} (99%) rename libs/apprise/plugins/{growl.py => NotifyGrowl.py} (99%) rename libs/apprise/plugins/{guilded.py => NotifyGuilded.py} (96%) rename libs/apprise/plugins/{home_assistant.py => NotifyHomeAssistant.py} (98%) rename libs/apprise/plugins/{httpsms.py => NotifyHttpSMS.py} (99%) rename libs/apprise/plugins/{ifttt.py => NotifyIFTTT.py} (99%) rename libs/apprise/plugins/{custom_json.py => NotifyJSON.py} (99%) rename libs/apprise/plugins/{join.py => NotifyJoin.py} (99%) rename libs/apprise/plugins/{kavenegar.py => NotifyKavenegar.py} (99%) rename libs/apprise/plugins/{kumulos.py => NotifyKumulos.py} (99%) rename libs/apprise/plugins/{lametric.py => NotifyLametric.py} (99%) rename libs/apprise/plugins/{line.py => NotifyLine.py} (98%) rename libs/apprise/plugins/{lunasea.py => NotifyLunaSea.py} (99%) rename libs/apprise/plugins/{mqtt.py => NotifyMQTT.py} (99%) rename libs/apprise/plugins/{msg91.py => NotifyMSG91.py} (99%) rename libs/apprise/plugins/{msteams.py => NotifyMSTeams.py} (99%) rename libs/apprise/plugins/{macosx.py => NotifyMacOSX.py} (99%) rename libs/apprise/plugins/{mailgun.py => NotifyMailgun.py} (99%) rename libs/apprise/plugins/{mastodon.py => NotifyMastodon.py} (99%) rename libs/apprise/plugins/{matrix.py => NotifyMatrix.py} (99%) rename libs/apprise/plugins/{mattermost.py => NotifyMattermost.py} (99%) rename libs/apprise/plugins/{messagebird.py => NotifyMessageBird.py} (99%) rename libs/apprise/plugins/{misskey.py => NotifyMisskey.py} (99%) rename libs/apprise/plugins/{nextcloud.py => NotifyNextcloud.py} (99%) rename libs/apprise/plugins/{nextcloudtalk.py => NotifyNextcloudTalk.py} (98%) rename libs/apprise/plugins/{notica.py => NotifyNotica.py} (99%) rename libs/apprise/plugins/{notifiarr.py => NotifyNotifiarr.py} (99%) rename libs/apprise/plugins/{notifico.py => NotifyNotifico.py} (99%) rename libs/apprise/plugins/{ntfy.py => NotifyNtfy.py} (99%) rename libs/apprise/plugins/{office365.py => NotifyOffice365.py} (99%) rename libs/apprise/plugins/{one_signal.py => NotifyOneSignal.py} (99%) rename libs/apprise/plugins/{opsgenie.py => NotifyOpsgenie.py} (99%) rename libs/apprise/plugins/{pagerduty.py => NotifyPagerDuty.py} (99%) rename libs/apprise/plugins/{pagertree.py => NotifyPagerTree.py} (99%) rename libs/apprise/plugins/{parseplatform.py => NotifyParsePlatform.py} (99%) rename libs/apprise/plugins/{popcorn_notify.py => NotifyPopcornNotify.py} (99%) rename libs/apprise/plugins/{prowl.py => NotifyProwl.py} (99%) rename libs/apprise/plugins/{pushbullet.py => NotifyPushBullet.py} (99%) rename libs/apprise/plugins/{pushdeer.py => NotifyPushDeer.py} (98%) rename libs/apprise/plugins/{pushme.py => NotifyPushMe.py} (98%) rename libs/apprise/plugins/{pushsafer.py => NotifyPushSafer.py} (99%) rename libs/apprise/plugins/{pushed.py => NotifyPushed.py} (99%) rename libs/apprise/plugins/{pushjet.py => NotifyPushjet.py} (98%) rename libs/apprise/plugins/{pushover.py => NotifyPushover.py} (99%) rename libs/apprise/plugins/{pushy.py => NotifyPushy.py} (99%) rename libs/apprise/plugins/{rsyslog.py => NotifyRSyslog.py} (99%) rename libs/apprise/plugins/{reddit.py => NotifyReddit.py} (99%) rename libs/apprise/plugins/{revolt.py => NotifyRevolt.py} (99%) rename libs/apprise/plugins/{rocketchat.py => NotifyRocketChat.py} (99%) rename libs/apprise/plugins/{ryver.py => NotifyRyver.py} (99%) rename libs/apprise/plugins/{ses.py => NotifySES.py} (99%) rename libs/apprise/plugins/{smseagle.py => NotifySMSEagle.py} (99%) rename libs/apprise/plugins/{smsmanager.py => NotifySMSManager.py} (99%) rename libs/apprise/plugins/{smtp2go.py => NotifySMTP2Go.py} (99%) rename libs/apprise/plugins/{sns.py => NotifySNS.py} (99%) rename libs/apprise/plugins/{sendgrid.py => NotifySendGrid.py} (99%) rename libs/apprise/plugins/{serverchan.py => NotifyServerChan.py} (98%) rename libs/apprise/plugins/{signal_api.py => NotifySignalAPI.py} (99%) rename libs/apprise/plugins/{simplepush.py => NotifySimplePush.py} (98%) rename libs/apprise/plugins/{sinch.py => NotifySinch.py} (99%) rename libs/apprise/plugins/{slack.py => NotifySlack.py} (99%) rename libs/apprise/plugins/{sparkpost.py => NotifySparkPost.py} (99%) rename libs/apprise/plugins/{streamlabs.py => NotifyStreamlabs.py} (99%) rename libs/apprise/plugins/{synology.py => NotifySynology.py} (98%) rename libs/apprise/plugins/{syslog.py => NotifySyslog.py} (99%) rename libs/apprise/plugins/{techuluspush.py => NotifyTechulusPush.py} (98%) rename libs/apprise/plugins/{telegram.py => NotifyTelegram.py} (99%) rename libs/apprise/plugins/{threema.py => NotifyThreema.py} (99%) rename libs/apprise/plugins/{twilio.py => NotifyTwilio.py} (99%) rename libs/apprise/plugins/{twist.py => NotifyTwist.py} (99%) rename libs/apprise/plugins/{twitter.py => NotifyTwitter.py} (99%) rename libs/apprise/plugins/{voipms.py => NotifyVoipms.py} (99%) rename libs/apprise/plugins/{vonage.py => NotifyVonage.py} (99%) rename libs/apprise/plugins/{wecombot.py => NotifyWeComBot.py} (99%) rename libs/apprise/plugins/{webexteams.py => NotifyWebexTeams.py} (99%) rename libs/apprise/plugins/{whatsapp.py => NotifyWhatsApp.py} (99%) rename libs/apprise/plugins/{windows.py => NotifyWindows.py} (99%) rename libs/apprise/plugins/{xbmc.py => NotifyXBMC.py} (99%) rename libs/apprise/plugins/{custom_xml.py => NotifyXML.py} (99%) rename libs/apprise/plugins/{zulip.py => NotifyZulip.py} (99%) diff --git a/libs/apprise-1.8.0.dist-info/INSTALLER b/libs/apprise-1.7.6.dist-info/INSTALLER similarity index 100% rename from libs/apprise-1.8.0.dist-info/INSTALLER rename to libs/apprise-1.7.6.dist-info/INSTALLER diff --git a/libs/apprise-1.8.0.dist-info/LICENSE b/libs/apprise-1.7.6.dist-info/LICENSE similarity index 100% rename from libs/apprise-1.8.0.dist-info/LICENSE rename to libs/apprise-1.7.6.dist-info/LICENSE diff --git a/libs/apprise-1.8.0.dist-info/METADATA b/libs/apprise-1.7.6.dist-info/METADATA similarity index 99% rename from libs/apprise-1.8.0.dist-info/METADATA rename to libs/apprise-1.7.6.dist-info/METADATA index c01ef211a..ac7cb9aac 100644 --- a/libs/apprise-1.8.0.dist-info/METADATA +++ b/libs/apprise-1.7.6.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: apprise -Version: 1.8.0 +Version: 1.7.6 Summary: Push Notifications that work with just about every platform! Home-page: https://github.com/caronc/apprise Author: Chris Caron diff --git a/libs/apprise-1.7.6.dist-info/RECORD b/libs/apprise-1.7.6.dist-info/RECORD new file mode 100644 index 000000000..250648105 --- /dev/null +++ b/libs/apprise-1.7.6.dist-info/RECORD @@ -0,0 +1,183 @@ +../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233 +apprise-1.7.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +apprise-1.7.6.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 +apprise-1.7.6.dist-info/METADATA,sha256=z_gaX2IdNJqw4T9q7AYQri9jcIs-OTGCo3t2EgEY-mw,44823 +apprise-1.7.6.dist-info/RECORD,, +apprise-1.7.6.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise-1.7.6.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 +apprise-1.7.6.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 +apprise-1.7.6.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 +apprise/Apprise.py,sha256=Stm2NhJprWRaMwQfTiIQG_nR1bLpHi_zcdwEcsCpa-A,32865 +apprise/Apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203 +apprise/AppriseAsset.py,sha256=jRW8Y1EcAvjVA9h_mINmsjO4DM3S0aDl6INIFVMcUCs,11647 +apprise/AppriseAsset.pyi,sha256=NYLXXYbScgRkspP27XGpRRM_uliPu1OCdWdZBPPvLng,979 +apprise/AppriseAttachment.py,sha256=vhrktSrp8GLr32aK4KqV6BX83IpI1lxZe-pGo1wiSFM,12540 +apprise/AppriseAttachment.pyi,sha256=R9-0dVqWpeaFrVpcREwPhGy3qHWztG5jEjYIOsbE5dM,1145 +apprise/AppriseConfig.py,sha256=wfuR6Mb3ZLHvjvqWdFp9lVmjjDRWs65unY9qa92RkCg,16909 +apprise/AppriseConfig.pyi,sha256=_mUlCnncqAq8sL01WxQTgZjnb2ic9kZXvtqZmVl-fc8,1568 +apprise/AppriseLocale.py,sha256=4uSr4Nj_rz6ISMMAfRVRk58wZVLKOofJgk2x0_E8NkQ,8994 +apprise/AttachmentManager.py,sha256=EwlnjuKn3fv_pioWcmMCkyDTsO178t6vkEOD8AjAPsw,2053 +apprise/ConfigurationManager.py,sha256=MUmGajxjgnr6FGN7xb3q0nD0VVgdTdvapBBR7CsI-rc,2058 +apprise/NotificationManager.py,sha256=ZJgkiCgcJ7Bz_6bwQ47flrcxvLMbA4Vbw0HG_yTsGdE,2041 +apprise/URLBase.py,sha256=xRP0-blocp9UudYh04Hb3fIEmTZWJaTv_tzjrqaB9fg,29423 +apprise/URLBase.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520 +apprise/__init__.py,sha256=ArtvoarAMnBcSfXF7L_hzq5CUJ9TUnHopiC7xafCe3c,3368 +apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986 +apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758 +apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646 +apprise/assets/themes/default/apprise-failure-128x128.png,sha256=66ps8TDPxVH3g9PlObJqF-0x952CjnqQyN3zvpRcOT8,16135 +apprise/assets/themes/default/apprise-failure-256x256.png,sha256=bQBsKKCsKfR9EqgYOZrcVcVa5y8qG58PN2mEqO5eNRI,41931 +apprise/assets/themes/default/apprise-failure-32x32.png,sha256=vH0pZffIDCvkejpr3fJHGXW__8Yc3R_p0bacX6t6l18,2437 +apprise/assets/themes/default/apprise-failure-72x72.png,sha256=EP5A8DHRDr9srgupFSwOoyQ308bNJ8aL192J_L4K-ec,7600 +apprise/assets/themes/default/apprise-info-128x128.ico,sha256=F5_CirmXueRCRI5Z_Crf6TS6jVIXTJlRD83zw1oJ66g,67646 +apprise/assets/themes/default/apprise-info-128x128.png,sha256=bBqRZAgQey-gkmJrnFhPbzjILSrljE59mRkgj3raMQo,16671 +apprise/assets/themes/default/apprise-info-256x256.png,sha256=B5r_O4d9MHCmSWZwfbqQgZSp-ZetTdiBSwKcMTF1aFA,43331 +apprise/assets/themes/default/apprise-info-32x32.png,sha256=lt3NZ95TzkiCNVNlurrB2fE2nriMa1wftl7nrNXmb6c,2485 +apprise/assets/themes/default/apprise-info-72x72.png,sha256=kDnsZpqNUZGqs9t1ECUup7FOfXUIL-rupnQCYJp9So4,7875 +apprise/assets/themes/default/apprise-logo.png,sha256=85ttALudKkLmiqilJT7mUQLUXRFmM1AK89rnwLm313s,160907 +apprise/assets/themes/default/apprise-success-128x128.ico,sha256=uCopPwdQjxgfohKazHaDzYs9y4oiaOpL048PYC6WRlg,67646 +apprise/assets/themes/default/apprise-success-128x128.png,sha256=nvDuU_QqhGlw6cMtdj7Mv-gPgqCEx-0DaaXn1KBLVYg,17446 +apprise/assets/themes/default/apprise-success-256x256.png,sha256=vXfKuxY3n0eeXHKdb9hTxICxOEn7HjAQ4IZpX0HSLzc,48729 +apprise/assets/themes/default/apprise-success-32x32.png,sha256=Jg9pFJh3YPI-LiPBebyJ7Z4Vt7BRecaE8AsRjQVIkME,2471 +apprise/assets/themes/default/apprise-success-72x72.png,sha256=FQbgvIhqKOhEK0yvrhaSpai0R7hrkTt_-GaC2KUgCCk,7858 +apprise/assets/themes/default/apprise-warning-128x128.ico,sha256=6XaQPOx0oWK_xbhr4Yhb7qNazCWwSs9lk2SYR2MHTrQ,67646 +apprise/assets/themes/default/apprise-warning-128x128.png,sha256=pf5c4Ph7jWH7gf39dJoieSj8TzAsY3TXI-sGISGVIW4,16784 +apprise/assets/themes/default/apprise-warning-256x256.png,sha256=SY-xlaiXaj420iEYKC2_fJxU-yj2SuaQg6xfPNi83bw,43708 +apprise/assets/themes/default/apprise-warning-32x32.png,sha256=97R2ywNvcwczhBoWEIgajVtWjgT8fLs4FCCz4wu0dwc,2472 +apprise/assets/themes/default/apprise-warning-72x72.png,sha256=L8moEInkO_OLxoOcuvN7rmrGZo64iJeH20o-24MQghE,7913 +apprise/attachment/AttachBase.py,sha256=T3WreGrTsqqGplXJO36jm-N14X7ymSc9xt7XdTYuXVE,13656 +apprise/attachment/AttachBase.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih9GP8,961 +apprise/attachment/AttachFile.py,sha256=MbHY_av0GeM_AIBKV02Hq7SHiZ9eCr1yTfvDMUgi2I4,4765 +apprise/attachment/AttachHTTP.py,sha256=_CMPp4QGLATfGO2-Nw57sxsQyed9z3ywgoB0vpK3KZk,13779 +apprise/attachment/__init__.py,sha256=xabgXpvV05X-YRuqIt3uGYMXwYNXjHyF6Dwd8HfZCFE,1658 +apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697 +apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593 +apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447 +apprise/config/ConfigBase.py,sha256=d1efIuQFCJr66WgpudV2DWtxY3-tuZAyMAhHXBzJ8p0,53194 +apprise/config/ConfigBase.pyi,sha256=cngfobwH6v2vxYbQrObDi5Z-t5wcquWF-wR0kBCr3Eg,54 +apprise/config/ConfigFile.py,sha256=u_SDaN3OHMyaAq2X7k_T4_PRKkVsDwleqBz9YIN5lbA,6138 +apprise/config/ConfigHTTP.py,sha256=Iy6Ji8_nX3xDjFgJGLrz4ftrMlMiyKiFGzYGJ7rMSMQ,9457 +apprise/config/ConfigMemory.py,sha256=epEAgNy-eJVWoQaUOvjivMWxXTofy6wAQ-NbCqYmuyE,2829 +apprise/config/__init__.py,sha256=lbsxrUpB1IYM2q7kjYhsXQGgPF-yZXJrKFE361tdIPY,1663 +apprise/conversion.py,sha256=0VZ0eCZfksN-97Vl0TjVjwnCTgus3XTRioceSFnP-gc,6277 +apprise/decorators/CustomNotifyPlugin.py,sha256=i4D-sgOsBWsxO5auWCN2bgXLLPuADaaLlJ1gUKLj2bU,7972 +apprise/decorators/__init__.py,sha256=e_PDAm0kQNzwDPx-NJZLPfLMd2VAABvNZtxx_iDviRM,1487 +apprise/decorators/notify.py,sha256=a2WupErNw1_SMAld7jPC273bskiChMpYy95BOog5A9w,5111 +apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738 +apprise/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=oUTuHREmLEYN07oqYqRMJ_kU71-o5o37NsF4RXlC5AU,3959 +apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921 +apprise/manager.py,sha256=R9w8jxQRNy6Z_XDcobkt4JYbrC4jtj2OwRw9Zrib3CA,26857 +apprise/plugins/NotifyAppriseAPI.py,sha256=ISBE0brD3eQdyw3XrGXd4Uc4kSYvIuI3SSUVCt-bkdo,16654 +apprise/plugins/NotifyAprs.py,sha256=xdL_aIVgb4ggxRFeCdkZAbgHYZ8DWLw9pRpLZQ0rHoE,25523 +apprise/plugins/NotifyBark.py,sha256=bsDvKooRy4k1Gg7tvBjv3DIx7-WZiV_mbTrkTwMtd9Q,15698 +apprise/plugins/NotifyBase.py,sha256=G3xkF_a2BWqNSxsrnOW7NUgHjOqBCYC5zihCifWemo8,30360 +apprise/plugins/NotifyBase.pyi,sha256=aKlZXRYUgG8lz_ZgGkYYJ_GKhuf18youTmMU-FlG7z8,21 +apprise/plugins/NotifyBoxcar.py,sha256=vR00-WggHa1nHYWyb-f5P2V-G4f683fU_-GBlIeJvD0,12867 +apprise/plugins/NotifyBulkSMS.py,sha256=stPWAFCfhBP617zYK9Dgk6pNJBN_WcyJtODzo0jR1QQ,16005 +apprise/plugins/NotifyBulkVS.py,sha256=viLGeyUDiirRRM7CgRqqElHSLYFnMugDtWE6Ytjqfaw,13290 +apprise/plugins/NotifyBurstSMS.py,sha256=cN2kRETKIK5LhwpQEA8C68LKv8KEUPmXYe-nTSegGls,15550 +apprise/plugins/NotifyChantify.py,sha256=GJJOAtSnVoIfKbJF_W1DTu7WsvS_zHdjO4T1XTKT87g,6673 +apprise/plugins/NotifyClickSend.py,sha256=UfOJqsas6WLjQskojuJE7I_-lrb5QrkMiBZv-po_Q9c,11229 +apprise/plugins/NotifyD7Networks.py,sha256=4E6Fh0kQoDlMMwgZJDOXky7c7KrdMMvqprcfm29scWU,15043 +apprise/plugins/NotifyDBus.py,sha256=1eVJHIL3XkFjDePMqfcll35Ie1vxggJ1iBsVFAIaF00,14379 +apprise/plugins/NotifyDapnet.py,sha256=KuXjBU0ZrIYtoDei85NeLZ-IP810T4w5oFXH9sWiSh0,13624 +apprise/plugins/NotifyDingTalk.py,sha256=NJyETgN6QjtRqtxQjfBLFVuFpURyWykRftm6WpQJVbY,12009 +apprise/plugins/NotifyDiscord.py,sha256=M_qmTzB7NNL5_agjYDX38KBN1jRzDBp2EMSNwEF_9Tw,26072 +apprise/plugins/NotifyEmail.py,sha256=Y_ZOrdK6hTUKHLvogKpV5VqD8byzDyDSvwIVmfdsC2g,39789 +apprise/plugins/NotifyEmby.py,sha256=OMVO8XsVl_XCBYNNNQi8ni2lS4voLfU8Puk1xJOAvHs,24039 +apprise/plugins/NotifyEnigma2.py,sha256=Hj0Q9YOeljSwbfiuMKLqXTVX_1g_mjNUGEts7wfrwno,11498 +apprise/plugins/NotifyFCM/__init__.py,sha256=mBFtIgIJuLIFnMB5ndx5Makjs9orVMc2oLoD7LaVT48,21669 +apprise/plugins/NotifyFCM/color.py,sha256=8iqDtadloQh2TMxkFmIFwenHqKp1pHHn1bwyWOzZ6TY,4592 +apprise/plugins/NotifyFCM/common.py,sha256=978uBUoNdtopCtylipGiKQdsQ8FTONxkFBp7uJMZHc8,1718 +apprise/plugins/NotifyFCM/oauth.py,sha256=Vvbd0-rd5BPIjAneG3rILU153JIzfSZ0kaDov6hm96M,11197 +apprise/plugins/NotifyFCM/priority.py,sha256=0WuRW1y1HVnybgjlTeCZPHzt7j8SwWnC7faNcjioAOc,8163 +apprise/plugins/NotifyFeishu.py,sha256=IpcABdLZJ1vcQdZHlmASVbNOiOCIrmgKFhz1hbdskY4,7266 +apprise/plugins/NotifyFlock.py,sha256=0rUIa9nToGsO8BTUgixh8Z_qdVixJeH479UNYjcE4EM,12748 +apprise/plugins/NotifyForm.py,sha256=38nL-2m1cf4gEQFQ4NpvA4j9i5_nNUgelReWFSjyV5U,17905 +apprise/plugins/NotifyFreeMobile.py,sha256=XCkgZLc3KKGlx_9UdeoMJVcHpeQrOml9T93S-DGf4bs,6644 +apprise/plugins/NotifyGnome.py,sha256=8MXTa8gZg1wTgNJfLlmq7_fl3WaYK-SX6VR91u308C4,9059 +apprise/plugins/NotifyGoogleChat.py,sha256=lnoN17m6lZANaXcElDTP8lcuVWjIZEK8C6_iqJNAnw4,12622 +apprise/plugins/NotifyGotify.py,sha256=DNlOIHyuYitO5use9oa_REPm2Fant7y9QSaatrZFNI0,10551 +apprise/plugins/NotifyGrowl.py,sha256=M6ViUz967VhEHtXrE7lbCKF3aB4pIXNEzJLjjGAmvhM,14023 +apprise/plugins/NotifyGuilded.py,sha256=eCMCoFFuE0XNY8HlLM21zoxgBNgqEKQ8dwYj8LihfRU,3641 +apprise/plugins/NotifyHomeAssistant.py,sha256=zqWu7TtdXhTbGNuflC8WfydbHsCLiEBw4uBUcF7YZtw,10739 +apprise/plugins/NotifyHttpSMS.py,sha256=pDEUHCCB18IhOgDcVK3_FFDJdAcrdTIfPzj0jNnZZBo,11136 +apprise/plugins/NotifyIFTTT.py,sha256=oMvTQ0bEu2eJQgw9BwxAwTNOtbZ_ER-zleJvWpWTj7w,13425 +apprise/plugins/NotifyJSON.py,sha256=70ctjmArGzuvM1gHNt1bCiQVWE7Fp9vd2nWhSXwFvw0,13851 +apprise/plugins/NotifyJoin.py,sha256=B8FHp7cblZBkxTgfrka6mNnf6oQVBXVuGISgSau00z0,13581 +apprise/plugins/NotifyKavenegar.py,sha256=F5xTUdebM1lK6yGFbZJQB9Zgw2LTI0angeA-3Nu-89w,12620 +apprise/plugins/NotifyKumulos.py,sha256=eCEW2ZverZqETOLHVWMC4E8Ll6rEhhEWOSD73RD80SM,8214 +apprise/plugins/NotifyLametric.py,sha256=h8vZoX-Ll5NBZRprBlxTO2H9w0lOiMxglGvUgJtK4_8,37534 +apprise/plugins/NotifyLine.py,sha256=OVI0ozMJcq_-dI8dodVX52dzUzgENlAbOik-Kw4l-rI,10676 +apprise/plugins/NotifyLunaSea.py,sha256=woN8XdkwAjhgxAXp7Zj4XsWLybNL80l4W3Dx5BvobZg,14459 +apprise/plugins/NotifyMQTT.py,sha256=cnuG4f3bYYNPhEj9qDX8SLmnxLVT9G1b8J5w6-mQGKY,19545 +apprise/plugins/NotifyMSG91.py,sha256=P7JPyT1xmucnaEeCZPf_6aJfe1gS_STYYwEM7hJ7QBw,12677 +apprise/plugins/NotifyMSTeams.py,sha256=dFH575hoLL3zRddbBKfozlYjxvPJGbj3BKvfJSIkvD0,22976 +apprise/plugins/NotifyMacOSX.py,sha256=y2fGpSZXomFiNwKbWImrXQUMVM4JR4uPCnsWpnxQrFA,8271 +apprise/plugins/NotifyMailgun.py,sha256=FNS_QLOQWMo62yVO-mMZkpiXudUtSdbHOjfSrLC4oIo,25409 +apprise/plugins/NotifyMastodon.py,sha256=2ovjQIOOITHH8lOinC8QCFCJN2QA8foIM2pjdknbblc,35277 +apprise/plugins/NotifyMatrix.py,sha256=I8kdaZUZS-drew0JExBbChQVe7Ib4EwAjQd0xE30XT0,50049 +apprise/plugins/NotifyMattermost.py,sha256=JgEc-wC-43FBMItezDJ62zv1Nc9ROFjDiwD_8bt8rgM,12722 +apprise/plugins/NotifyMessageBird.py,sha256=EUPwhs1PHiPZpluIrLiNKQMUPcdlKnx1sdnllCtN_Ns,12248 +apprise/plugins/NotifyMisskey.py,sha256=zYZkBKv0p3jJpm_HLDBugUgKeGb0qpLoPqy0ffwwxVg,9600 +apprise/plugins/NotifyNextcloud.py,sha256=M3EyvUzBMHbTKU3gxW_7fPA6vmQUF5x8GTMZQ78sWCA,12759 +apprise/plugins/NotifyNextcloudTalk.py,sha256=dLl_g7Knq5PVcadbzDuQsxbGHTZlC4r-pQC8wzYnmAo,11011 +apprise/plugins/NotifyNotica.py,sha256=yHmk8HiNFjzoI4Gewo_nBRrx9liEmhT95k1d10wqhYg,12990 +apprise/plugins/NotifyNotifiarr.py,sha256=ADwLJO9eenfLkNa09tXMGSBTM4c3zTY0SEePvyB8WYA,15857 +apprise/plugins/NotifyNotifico.py,sha256=Qe9jMN_M3GL4XlYIWkAf-w_Hf65g9Hde4bVuytGhUW4,12035 +apprise/plugins/NotifyNtfy.py,sha256=AtJt2zH35mMQTwRDxKia93NPy6-4rtixplP53zIYV2M,27979 +apprise/plugins/NotifyOffice365.py,sha256=8TxsVsdbUghmNj0kceMlmoZzTOKQTgn3priI8JuRuHE,25190 +apprise/plugins/NotifyOneSignal.py,sha256=gsw7ckW7xLiJDRUb7eJHNe_4bvdBXmt6_YsB1u_ghjw,18153 +apprise/plugins/NotifyOpsgenie.py,sha256=zJWpknjoHq35Iv9w88ucR62odaeIN3nrGFPtYnhDdjA,20515 +apprise/plugins/NotifyPagerDuty.py,sha256=lu6oNdygrs6UezYm6xgiQxQDeDz8EVUtfP-xsArRvyw,17874 +apprise/plugins/NotifyPagerTree.py,sha256=mPl6ejdelNlWUWGVs46kZT0VV4uFZoeCdcv4VJ_f_XQ,13849 +apprise/plugins/NotifyParsePlatform.py,sha256=6oFOTpu-HMhesaYXRBvu5oaESYlFrKBNYTHE-ItCBRk,10291 +apprise/plugins/NotifyPopcornNotify.py,sha256=kRstzG0tWBdxSRfn2RN2J7FhvIj2qYWlwUyLxxZCbPc,10587 +apprise/plugins/NotifyProwl.py,sha256=EGOdmiZq8CFbjxTtWWKLQEdYiSvr4czZfE_8aCMEokw,9782 +apprise/plugins/NotifyPushBullet.py,sha256=JVd2GQH-DWmPaKjuGBpsE6DXNCcZEUDH7tA5zbM1qEU,15372 +apprise/plugins/NotifyPushDeer.py,sha256=cG1UFG06PfzbmI1RxtrMqmfaHK_Ojk_W-QMEdtkEuUI,6922 +apprise/plugins/NotifyPushMe.py,sha256=ioRzeXbd2X5miTd3h3m7AwCqkIIfbXNm4PjYk0OOXZ0,7134 +apprise/plugins/NotifyPushSafer.py,sha256=hIcYHwUZapmC-VDvaO_UkDY9RSPTxHgF7m2FL-6JBZw,26756 +apprise/plugins/NotifyPushed.py,sha256=NqLMXD9gvihXLfLUtCcMfz5oUAhPM7sKXECqKgD0v-U,12270 +apprise/plugins/NotifyPushjet.py,sha256=8qWpIqM4dKWjO-BjOrRJXZYtvtJBt_mikdBWRxfibnE,8952 +apprise/plugins/NotifyPushover.py,sha256=MJDquV4zl1cNrGZOC55hLlt6lOb6625WeUcgS5ceCbk,21213 +apprise/plugins/NotifyPushy.py,sha256=mmWcnu905Fvc8ihYXvZ7lVYErGZH5Q-GbBNS20v5r48,12496 +apprise/plugins/NotifyRSyslog.py,sha256=W42LT90X65-pNoU7KdhdX1PBcmsz9RyV376CDa_H3CI,11982 +apprise/plugins/NotifyReddit.py,sha256=E78OSyDQfUalBEcg71sdMsNBOwdj7cVBnELrhrZEAXY,25785 +apprise/plugins/NotifyRevolt.py,sha256=DRA9Xylwl6leVjVFuJcP4L1cG49CIBtnQdxh4BKnAZ4,14500 +apprise/plugins/NotifyRocketChat.py,sha256=Cb_nasX0-G3FoPMYvNk55RJ-tHuXUCTLUn2wTSi4IcI,25738 +apprise/plugins/NotifyRyver.py,sha256=yhHPMLGeJtcHwBKSPPk0OBfp59DgTvXio1R59JhrJu4,11823 +apprise/plugins/NotifySES.py,sha256=wtRmpAZkS5mQma6sdiaPT6U1xcgoj77CB9mNFvSEAw8,33545 +apprise/plugins/NotifySMSEagle.py,sha256=voFNqOewD9OC1eRctD0YdUB_ZSWsb06rjUwBfCcxPYA,24161 +apprise/plugins/NotifySMSManager.py,sha256=DbVc35qLfYkNL7eq43_rPD6k-PELL9apf3S09S6qvDA,14125 +apprise/plugins/NotifySMTP2Go.py,sha256=foQ7aMMmNc5Oree8YwrxZJgMnF6yVMFAfqShm_nLbx0,19711 +apprise/plugins/NotifySNS.py,sha256=ZEBWf0ZJ9w_ftzUikKEvQWJ2fkxrUbrLhPmTRD2DvRQ,24159 +apprise/plugins/NotifySendGrid.py,sha256=IBdYmZcthkvGCz1N_Fs8vDnImtHug6LpuKv1mWT_Cdo,16213 +apprise/plugins/NotifyServerChan.py,sha256=WsrClO9f0xi-KpnLZGTUHV7PxeU3l1D875gvMaZRG_M,5779 +apprise/plugins/NotifySignalAPI.py,sha256=OwJ7qjJ-ZJyS8GS-dBWAtgizHMnGegg76GuwFobyWkw,16733 +apprise/plugins/NotifySimplePush.py,sha256=dUC6O8IGuUIAz5z6_H7A7jdv5Gj1plytNm5QyKnHAYg,10876 +apprise/plugins/NotifySinch.py,sha256=tmHLwQa9lWHEI3EcRfigl4i7JU46A6gKAi_GbY0PrX4,16813 +apprise/plugins/NotifySlack.py,sha256=3VdjruU5FPr3jT_s3axwRJKMcBYXP0lvJnyuKedIlcE,42521 +apprise/plugins/NotifySparkPost.py,sha256=6dRTwnYU50Lvmp6AlwCyePe0TMbVEXaSwNeGkg__EYo,27878 +apprise/plugins/NotifyStreamlabs.py,sha256=lx3N8T2ufUWFYIZ-kU_rOv50YyGWBqLSCKk7xim2_Io,16023 +apprise/plugins/NotifySynology.py,sha256=_jTqfgWeOuSi_I8geMOraHBVFtDkvm9mempzymrmeAo,11105 +apprise/plugins/NotifySyslog.py,sha256=J9Kain2bb-PDNiG5Ydb0q678cYjNE_NjZFqMG9oEXM0,10617 +apprise/plugins/NotifyTechulusPush.py,sha256=m43_Qj1scPcgCRX5Dr2Ul7nxMbaiVxNzm_HRuNmfgoA,7253 +apprise/plugins/NotifyTelegram.py,sha256=XE7PC9LRzcrfE2bpLKyor5lO_7B9LS4Xw1UlUmA4a2A,37187 +apprise/plugins/NotifyThreema.py,sha256=C_C3j0fJWgeF2uB7ceJFXOdC6Lt0TFBInFMs5Xlg04M,11885 +apprise/plugins/NotifyTwilio.py,sha256=WCo8eTI9OF1rtg3ueHHRDXt4Lp45eZ6h3IdTZVf5HM8,15976 +apprise/plugins/NotifyTwist.py,sha256=nZA73CYVe-p0tkVMy5q3vFRyflLM4yjUo9LECvkUwgc,28841 +apprise/plugins/NotifyTwitter.py,sha256=qML0jlBkLZMHrkKRxBpVUnBwAz8MWGYyI3cvwi-hrgM,30152 +apprise/plugins/NotifyVoipms.py,sha256=msy_D32YhP8OP4_Mj_L3OYd4iablqQETN-DvilGZeVQ,12552 +apprise/plugins/NotifyVonage.py,sha256=xmzZgobFaGA_whpQ5fDuG2poUrK9W4T77yP7dusHcSo,13431 +apprise/plugins/NotifyWeComBot.py,sha256=5lkhXDgyJ1edzknemKsO1sJVv7miR9F_7xI40Ag7ICI,8789 +apprise/plugins/NotifyWebexTeams.py,sha256=gbbRlHiPuOvUIZexE5m2QNd1dN_5_x0OdT5m6NSrcso,9164 +apprise/plugins/NotifyWhatsApp.py,sha256=PtzW0ue3d2wZ8Pva_LG29jUcpRRP03TFxO5SME_8Juo,19924 +apprise/plugins/NotifyWindows.py,sha256=QgWJfJF8AE6RWr-L81YYVZNWrnImK9Qr3B991HWanqU,8563 +apprise/plugins/NotifyXBMC.py,sha256=5hDuOTP3Kwtp4NEMaokNjWyEKEkQcN_fSx-cUPJvhaU,12096 +apprise/plugins/NotifyXML.py,sha256=WJnmdvXseuTRgioVMRqpR8a09cDfTpPTfuFlTnT_TfI,16973 +apprise/plugins/NotifyZulip.py,sha256=M8cSL7nZvtBYyTX6045g34tyn2vyybltgD1CoI4Xa7A,13968 +apprise/plugins/__init__.py,sha256=jTfLmW47kZC_Wf5eFFta2NoD2J-7_E7JaPrrVMIECkU,18725 +apprise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +apprise/utils.py,sha256=SjRU2tb1UsVnTCTXPUyXVz3WpRbDWwAHH-d3ll38EHY,53185 diff --git a/libs/apprise-1.8.0.dist-info/REQUESTED b/libs/apprise-1.7.6.dist-info/REQUESTED similarity index 100% rename from libs/apprise-1.8.0.dist-info/REQUESTED rename to libs/apprise-1.7.6.dist-info/REQUESTED diff --git a/libs/apprise-1.8.0.dist-info/WHEEL b/libs/apprise-1.7.6.dist-info/WHEEL similarity index 100% rename from libs/apprise-1.8.0.dist-info/WHEEL rename to libs/apprise-1.7.6.dist-info/WHEEL diff --git a/libs/apprise-1.8.0.dist-info/entry_points.txt b/libs/apprise-1.7.6.dist-info/entry_points.txt similarity index 100% rename from libs/apprise-1.8.0.dist-info/entry_points.txt rename to libs/apprise-1.7.6.dist-info/entry_points.txt diff --git a/libs/apprise-1.8.0.dist-info/top_level.txt b/libs/apprise-1.7.6.dist-info/top_level.txt similarity index 100% rename from libs/apprise-1.8.0.dist-info/top_level.txt rename to libs/apprise-1.7.6.dist-info/top_level.txt diff --git a/libs/apprise-1.8.0.dist-info/RECORD b/libs/apprise-1.8.0.dist-info/RECORD deleted file mode 100644 index bcfa733bc..000000000 --- a/libs/apprise-1.8.0.dist-info/RECORD +++ /dev/null @@ -1,183 +0,0 @@ -../../bin/apprise,sha256=ZJ-e4qqxNLtdW_DAvpuPPX5iROIiQd8I6nvg7vtAv-g,233 -apprise-1.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -apprise-1.8.0.dist-info/LICENSE,sha256=gt7qKBxRhVcdmXCYVtrWP6DtYjD0DzONet600dkU994,1343 -apprise-1.8.0.dist-info/METADATA,sha256=if2D6aZrIVQZAe9BKWyqKoKH89ZGnYOTvXGRfqcSf_g,44823 -apprise-1.8.0.dist-info/RECORD,, -apprise-1.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise-1.8.0.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92 -apprise-1.8.0.dist-info/entry_points.txt,sha256=71YypBuNdjAKiaLsiMG40HEfLHxkU4Mi7o_S0s0d8wI,45 -apprise-1.8.0.dist-info/top_level.txt,sha256=JrCRn-_rXw5LMKXkIgMSE4E0t1Ks9TYrBH54Pflwjkk,8 -apprise/__init__.py,sha256=nBy2lkwvLxW1QTMEagOZZnM5AQWLM6CRUYt-Gr03X94,3388 -apprise/apprise.py,sha256=JWSqg5pYJOydubQyaIjR9gIPhb2okcrGe2mQ_NhPNS0,32837 -apprise/apprise.pyi,sha256=_4TBKvT-QVj3s6PuTh3YX-BbQMeJTdBGdVpubLMY4_k,2203 -apprise/apprise_attachment.py,sha256=eYVnNWTWQzlzKPDOgMCzGvVtXXDL6noMwtRKwwvyL8Q,12528 -apprise/apprise_attachment.pyi,sha256=R9-0dVqWpeaFrVpcREwPhGy3qHWztG5jEjYIOsbE5dM,1145 -apprise/apprise_config.py,sha256=qlNtd-OkldrY9rE9vT67fVPHINx9fkkbwQinhbBjQ_I,16896 -apprise/apprise_config.pyi,sha256=_mUlCnncqAq8sL01WxQTgZjnb2ic9kZXvtqZmVl-fc8,1568 -apprise/asset.py,sha256=ICXIjCi12Xq8E-WIR4mrVC7KcpPRiJDlKG8cHqoCll0,11643 -apprise/asset.pyi,sha256=NYLXXYbScgRkspP27XGpRRM_uliPu1OCdWdZBPPvLng,979 -apprise/assets/NotifyXML-1.0.xsd,sha256=292qQ_IUl5EWDhPyzm9UTT0C2rVvJkyGar8jiODkJs8,986 -apprise/assets/NotifyXML-1.1.xsd,sha256=bjR3CGG4AEXoJjYkGCbDttKHSkPP1FlIWO02E7G59g4,1758 -apprise/assets/themes/default/apprise-failure-128x128.ico,sha256=Mt0ptfHJaN3Wsv5UCNDn9_3lyEDHxVDv1JdaDEI_xCA,67646 -apprise/assets/themes/default/apprise-failure-128x128.png,sha256=66ps8TDPxVH3g9PlObJqF-0x952CjnqQyN3zvpRcOT8,16135 -apprise/assets/themes/default/apprise-failure-256x256.png,sha256=bQBsKKCsKfR9EqgYOZrcVcVa5y8qG58PN2mEqO5eNRI,41931 -apprise/assets/themes/default/apprise-failure-32x32.png,sha256=vH0pZffIDCvkejpr3fJHGXW__8Yc3R_p0bacX6t6l18,2437 -apprise/assets/themes/default/apprise-failure-72x72.png,sha256=EP5A8DHRDr9srgupFSwOoyQ308bNJ8aL192J_L4K-ec,7600 -apprise/assets/themes/default/apprise-info-128x128.ico,sha256=F5_CirmXueRCRI5Z_Crf6TS6jVIXTJlRD83zw1oJ66g,67646 -apprise/assets/themes/default/apprise-info-128x128.png,sha256=bBqRZAgQey-gkmJrnFhPbzjILSrljE59mRkgj3raMQo,16671 -apprise/assets/themes/default/apprise-info-256x256.png,sha256=B5r_O4d9MHCmSWZwfbqQgZSp-ZetTdiBSwKcMTF1aFA,43331 -apprise/assets/themes/default/apprise-info-32x32.png,sha256=lt3NZ95TzkiCNVNlurrB2fE2nriMa1wftl7nrNXmb6c,2485 -apprise/assets/themes/default/apprise-info-72x72.png,sha256=kDnsZpqNUZGqs9t1ECUup7FOfXUIL-rupnQCYJp9So4,7875 -apprise/assets/themes/default/apprise-logo.png,sha256=85ttALudKkLmiqilJT7mUQLUXRFmM1AK89rnwLm313s,160907 -apprise/assets/themes/default/apprise-success-128x128.ico,sha256=uCopPwdQjxgfohKazHaDzYs9y4oiaOpL048PYC6WRlg,67646 -apprise/assets/themes/default/apprise-success-128x128.png,sha256=nvDuU_QqhGlw6cMtdj7Mv-gPgqCEx-0DaaXn1KBLVYg,17446 -apprise/assets/themes/default/apprise-success-256x256.png,sha256=vXfKuxY3n0eeXHKdb9hTxICxOEn7HjAQ4IZpX0HSLzc,48729 -apprise/assets/themes/default/apprise-success-32x32.png,sha256=Jg9pFJh3YPI-LiPBebyJ7Z4Vt7BRecaE8AsRjQVIkME,2471 -apprise/assets/themes/default/apprise-success-72x72.png,sha256=FQbgvIhqKOhEK0yvrhaSpai0R7hrkTt_-GaC2KUgCCk,7858 -apprise/assets/themes/default/apprise-warning-128x128.ico,sha256=6XaQPOx0oWK_xbhr4Yhb7qNazCWwSs9lk2SYR2MHTrQ,67646 -apprise/assets/themes/default/apprise-warning-128x128.png,sha256=pf5c4Ph7jWH7gf39dJoieSj8TzAsY3TXI-sGISGVIW4,16784 -apprise/assets/themes/default/apprise-warning-256x256.png,sha256=SY-xlaiXaj420iEYKC2_fJxU-yj2SuaQg6xfPNi83bw,43708 -apprise/assets/themes/default/apprise-warning-32x32.png,sha256=97R2ywNvcwczhBoWEIgajVtWjgT8fLs4FCCz4wu0dwc,2472 -apprise/assets/themes/default/apprise-warning-72x72.png,sha256=L8moEInkO_OLxoOcuvN7rmrGZo64iJeH20o-24MQghE,7913 -apprise/attachment/__init__.py,sha256=9jSiGEbLllS-0Vbpgxo4MCpZfIJ-saezWWgQ1PofZ9I,1678 -apprise/attachment/base.py,sha256=phcM6C281j2eoEhtoH5FY0m5s_JvWiEm1AK9z9gyi0w,13692 -apprise/attachment/base.pyi,sha256=w0XG_QKauiMLJ7eQ4S57IiLIURZHm_Snw7l6-ih9GP8,961 -apprise/attachment/file.py,sha256=d9gpsunNIot-6h6w-WQmwWgFiCv7YLqk42zJr4LLqE4,4752 -apprise/attachment/http.py,sha256=C0L9zKmHi60wyzEZN-OJCF7LVoY-MMHYMqhXspFlLdU,13748 -apprise/cli.py,sha256=h-pWSQPqQficH6J-OEp3MTGydWyt6vMYnDZvHCeAt4Y,20697 -apprise/common.py,sha256=I6wfrndggCL7l7KAl7Cm4uwAX9n0l3SN4-BVvTE0L0M,5593 -apprise/common.pyi,sha256=luF3QRiClDCk8Z23rI6FCGYsVmodOt_JYfYyzGogdNM,447 -apprise/config/__init__.py,sha256=oDxdoqG2NEYu_bbpLsLaM3L9WKY3gNn5gjIwb2h3LU4,1679 -apprise/config/base.py,sha256=iWgrBk4-wm_SHtNbe7dCQOYnbkI3oKWP_b60qwjGSoU,53173 -apprise/config/base.pyi,sha256=cngfobwH6v2vxYbQrObDi5Z-t5wcquWF-wR0kBCr3Eg,54 -apprise/config/file.py,sha256=MB_H2X5lJZS-XAOahkMb3IsD2rgVebFjiEF76goITEg,6125 -apprise/config/http.py,sha256=CjQtv_OQJykMxD4ssiwbFI3P8CeQiPdYV_ZIiL_XqMw,9440 -apprise/config/memory.py,sha256=8VICU-WLux8KnW6i9sf9fgmns90J-MfVYI3pvTiyTno,2816 -apprise/conversion.py,sha256=fSn-pTE6-vNt9qVQCKib_NTMhZnCH88FoMt14x6co1M,6273 -apprise/decorators/__init__.py,sha256=e_PDAm0kQNzwDPx-NJZLPfLMd2VAABvNZtxx_iDviRM,1487 -apprise/decorators/base.py,sha256=KnRzhrTOxQxwm4KuJxfCfaYfX0MrFbqWTvXY1XuiNck,8019 -apprise/decorators/notify.py,sha256=FzIa7m-G5KnVVa__rjyn990zt2lqE8sdHW-XY79lbAU,5097 -apprise/emojis.py,sha256=ONF0t8dY9f2XlEkLUG79-ybKVAj2GqbPj2-Be97vAoI,87738 -apprise/i18n/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise/i18n/en/LC_MESSAGES/apprise.mo,sha256=w-KW6RXYYMr14raaIvJzbop8tXU4qrsz50zm8rcCo3U,3959 -apprise/locale.py,sha256=4uSr4Nj_rz6ISMMAfRVRk58wZVLKOofJgk2x0_E8NkQ,8994 -apprise/logger.py,sha256=131hqhed8cUj9x_mfXDEvwA2YbcYDFAYiWVK1HgxRVY,6921 -apprise/manager.py,sha256=_LiMG1Upk6h6GcxOw3GjsPtVT8ZJLhLlQkC_xL_dm6A,27218 -apprise/manager_attachment.py,sha256=EkrcKtKjbxTXXyDaKyiu4XSfDu9wKFSdJD7TOTpyOUc,2200 -apprise/manager_config.py,sha256=sCIOlBTH13bQ1cuhQVpUq2vyKWBArA8YRyPnXIB1iWQ,2205 -apprise/manager_plugins.py,sha256=Xbg5-xr-06zMIDoWviz-zKcbzusHj0iBBMchKPpUOkg,2211 -apprise/plugins/__init__.py,sha256=Hm-vt-Xtxir5mgOdehqlGLhIlc45LA31zjqV42uRnB8,18701 -apprise/plugins/apprise_api.py,sha256=D93eUOlEQ1xPZYdagnEtIxkSuzZ4b9XreuhntQW97F8,16638 -apprise/plugins/aprs.py,sha256=fyzhdsESVwsqHMbfjnlRuGuAqAz2Pk_-MrmiMihs8qY,25506 -apprise/plugins/bark.py,sha256=oSz7Nr7vxnXxqA5dRaO6ze2IljkgEQavUPs-MUUDSX4,15681 -apprise/plugins/base.py,sha256=720L5OyMMLqlM3UdYSNMpA3m0rCudzGP3HwKgu73Ons,30350 -apprise/plugins/base.pyi,sha256=aKlZXRYUgG8lz_ZgGkYYJ_GKhuf18youTmMU-FlG7z8,21 -apprise/plugins/boxcar.py,sha256=8ImhsSsOK9o3eiYp4dZ6fO6p6AeI-jOPWoFJ8qvRAxY,12850 -apprise/plugins/bulksms.py,sha256=XuP8X4dL1wWQU7TkLYWPWRAro7TymnF7PlF2ESe7o0o,15988 -apprise/plugins/bulkvs.py,sha256=sLpiKbHs63Yu0UjMc9erW1VSgLceuu5wvPie5iyl03k,13273 -apprise/plugins/burstsms.py,sha256=7YmuhElVvT-0FIxMFBILHjo6Fir0J4qHTuqj8KfWBaI,15533 -apprise/plugins/chantify.py,sha256=hOvrmhH051Us28_kv0mKkuaJ2BN_8pSsslF8eMCgp-Q,6660 -apprise/plugins/clicksend.py,sha256=kKUJgk9FcTcfXqcIV57uMz0VxBtSQqkpxocRkgn0-3Q,11408 -apprise/plugins/custom_form.py,sha256=fMYwSQyEa_u8IEw5QSylDn-y32FnmymkL5ng-tGxDoc,17888 -apprise/plugins/custom_json.py,sha256=UCXaegE1nD8UrKlFrSKQbxfbcrX1BHUvZwBZY6a_aJc,13834 -apprise/plugins/custom_xml.py,sha256=lrPCRngPz8_oV2A8ZfiIY56BRQc1iv0PN0--2LOlxpA,16956 -apprise/plugins/d7networks.py,sha256=2o_wDgoak_S0sUJJa7HPtu0PPKLVmIOrq1rV9llOnHY,15030 -apprise/plugins/dapnet.py,sha256=fclGE69ggMejgpyvBpJqTwFNF3g2w7qpFySOMcc9vi0,13607 -apprise/plugins/dbus.py,sha256=lvHLmOkJYJAr4TjrigdIgP5bBlQXHrNCDR0k80LNsks,14366 -apprise/plugins/dingtalk.py,sha256=1twAGYUZzjhQepg2zhaR7u8QJuWvM5WymkbPQSUKBck,11992 -apprise/plugins/discord.py,sha256=kdR09bKU80-AosBIrTkMSxoOkAUHMXKwSr1P3H-M_f4,26053 -apprise/plugins/email.py,sha256=pVVWhEpT9Zi7qyUvpI2UxbjxkXYe6FoJj8E0NTw4P98,39912 -apprise/plugins/emby.py,sha256=HWYxlgHE99yAPKii4IE1XQQ-B1HViklWOkefjmq6SW4,24022 -apprise/plugins/enigma2.py,sha256=40Uo0m1JtLUcwRnfqKqdX_8ZR1-SvPfJ5470uijgdfw,11481 -apprise/plugins/fcm/__init__.py,sha256=eTjX-ahPHrPddX0v_Ix77qEr2G4NQ7reClCMzX3Nxwk,21657 -apprise/plugins/fcm/color.py,sha256=rQxVRL_CcRlgnNTQXkjB1Bfs0Xc933zOB1tURSdhi7Y,4585 -apprise/plugins/fcm/common.py,sha256=978uBUoNdtopCtylipGiKQdsQ8FTONxkFBp7uJMZHc8,1718 -apprise/plugins/fcm/oauth.py,sha256=Vvbd0-rd5BPIjAneG3rILU153JIzfSZ0kaDov6hm96M,11197 -apprise/plugins/fcm/priority.py,sha256=0WuRW1y1HVnybgjlTeCZPHzt7j8SwWnC7faNcjioAOc,8163 -apprise/plugins/feishu.py,sha256=buGBg1YlcwHjwUGzUfju4cdYbmEOFl3EHU_NzEBqtlM,7253 -apprise/plugins/flock.py,sha256=_bhH-ub0LbuU7k2p1ZwUPZ-TbC3kpD48GKNtLebUpoM,12735 -apprise/plugins/freemobile.py,sha256=NZf553UWkl1FVEVyWQKJN3ZD6opYSv3I-L6DRaKbiCw,6679 -apprise/plugins/gnome.py,sha256=UYtaiMKxILhTdsxe4aeLaJwjV6Af-8VhjVR3W3CyPPk,9046 -apprise/plugins/google_chat.py,sha256=f7F4gPxwwkmhDBlXMHzYoHRhPX3w8tyD7OimGBjmNxg,12609 -apprise/plugins/gotify.py,sha256=ISzmuQ4-Wd46OlaxGSAZBbWDTq9mHBUYO0R-7tAX_zc,10538 -apprise/plugins/growl.py,sha256=mlpN1QXGlEf2BA3BORnvkyT0oiTsWBev2y5gzpOm9r0,14006 -apprise/plugins/guilded.py,sha256=lhE6gK7laQNYEubD9lBGRemFK-IHis_u2tNekbUoGgM,3707 -apprise/plugins/home_assistant.py,sha256=Wstut0QaQL2dE66kH8MOqMANayhvGOq7CuxVz9O-T60,10722 -apprise/plugins/httpsms.py,sha256=2TtIlAFa4cjXwpMyrjfieCFHgaZl4H_YT6IoPIMDOcg,11123 -apprise/plugins/ifttt.py,sha256=ZHtu7pW9I0aHVEC4DshKi6XZCwqQ0A_XGLyFJujwdKY,13412 -apprise/plugins/join.py,sha256=mRop7thH24kQmvIEvtnh0HT0VyBrUXm2sS-P69c0iFE,13568 -apprise/plugins/kavenegar.py,sha256=1vdms7QBaLcTiLXeQGfprMU5rw9wgImRLtALIb78-ro,12607 -apprise/plugins/kumulos.py,sha256=6M_way1Qi5bELm8Csay86AVtVhzmRyv5hfxGrFvyzN4,8201 -apprise/plugins/lametric.py,sha256=JR0XjdxkEphT4VKvKfElNxpAoOpXZCb-40b2SJQP9XA,37521 -apprise/plugins/line.py,sha256=-xONdu4LFkS3sGgZeEGiPGbBIvBEd20lCbyxKLhQwAg,10659 -apprise/plugins/lunasea.py,sha256=WxJ-RIbFDiS1-5HjCs8XVdNv7nt2EHimsXGCDaK0sC0,14442 -apprise/plugins/macosx.py,sha256=4vIXz1xlsq_xlEyeu_rEvzJQZYcWq7pVmSn5VjF_dCY,8258 -apprise/plugins/mailgun.py,sha256=ad3jzdrbdgwWmOAAu4e_inLEAo-cii4z0OnIJQXoSIA,25396 -apprise/plugins/mastodon.py,sha256=X8CBxnuWORLo9JEOLCJcSafFNVrcEd778R38doyNWUo,35254 -apprise/plugins/matrix.py,sha256=zJPTLFqRT6mofXFVi421_poF0Qm7KaWSgNXTBKq3YoI,50032 -apprise/plugins/mattermost.py,sha256=KfHfqt3LO4XuMsY-qNV-i_OAAamF-35RgjHzxuq2deM,12709 -apprise/plugins/messagebird.py,sha256=3TJ_31Vc1liwQKKKwEiO_rP5e4jP4QkcU_wHyRGtf5Q,12235 -apprise/plugins/misskey.py,sha256=E5UrDrVryb7zV2D_qJCPsCyvpynF4-22JqRVco6PcgM,9587 -apprise/plugins/mqtt.py,sha256=3LcRhT44o2QoSH5rJs23xRbi2vob-po5FAPnnEU_cgs,19528 -apprise/plugins/msg91.py,sha256=V3PwdCS5r-LbVHNNTFZGUHt7GPwQOHnQC0mx8G90wGQ,12664 -apprise/plugins/msteams.py,sha256=4maXBIQFEVfH-IVzZW6t4jirsozmKwF4ySPP5h20cTQ,22964 -apprise/plugins/nextcloud.py,sha256=iYmKGH-xZ4Kj3HvXA_krQody7LRqJKzMRu39Ivk1ve4,12742 -apprise/plugins/nextcloudtalk.py,sha256=TTK8qhy0VPjG0N1yFUBJWTieV-1DqT9Jux9D5PRiYAE,10994 -apprise/plugins/notica.py,sha256=MzryA8x-dSt-oRk9g3dlTTrb1ul7mnbRNajbiwTx1Mc,12973 -apprise/plugins/notifiarr.py,sha256=ZI2mjXwKLWx7SXb6d3S-sk2JmCswJ23UTPj9jVNSN0U,15844 -apprise/plugins/notifico.py,sha256=IKkiXU8HdbRnEWzkQKPFo1g-ZnT8AIwFYer7jF2Yjac,12022 -apprise/plugins/ntfy.py,sha256=jOmPZETJByyNmYSzr-2F3k5jAXFVNldcd3wK-tQRpyE,27956 -apprise/plugins/office365.py,sha256=bCg53GDruS7A3_PQNCjiSERhfub_FHTOVCsRwjgbjXU,25173 -apprise/plugins/one_signal.py,sha256=0ljKuz6Wyn5K8Rxp3BT8aARzHWYff0q2TWA5f-ZLJSA,18140 -apprise/plugins/opsgenie.py,sha256=wWUREzEn8FDQmmVGhmTlN7kxsYlzNj1tK3wA5t8UbCA,20502 -apprise/plugins/pagerduty.py,sha256=1vQ2rfbDUKnM0XdkVMrvTnWIPK7lTxrm-Nslt3LW5Ng,17857 -apprise/plugins/pagertree.py,sha256=uhZL2SxIJr0cguMGhJP_RIJxJZLd86uoAcMhHsBOKm8,13836 -apprise/plugins/parseplatform.py,sha256=G99l6AnHZtaw11y7oBBVVvgdg7lSM8gWFm4tAWhJ8d4,10278 -apprise/plugins/popcorn_notify.py,sha256=ppOqxPrZrOPa-TFicO3Lo_jgwOOPCNEXMgmEo10DAZA,10574 -apprise/plugins/prowl.py,sha256=fYsv6s8xA6OC0yFajgLf3jgL6IwiaOTZaJKisW4eNyw,9769 -apprise/plugins/pushbullet.py,sha256=g-7_Y0N4x-a8YE2gqDqW5OIi74YY7rja_jQ8WVKc4FI,15353 -apprise/plugins/pushdeer.py,sha256=8StOcyK1jlx3pc_CnmFUSqdjfvuv0qa6bf--FHP2_KE,6909 -apprise/plugins/pushed.py,sha256=pm3T7JGExjYeqVWQVN8J8JHkn1XBMfix_sdmr1ItXY8,12253 -apprise/plugins/pushjet.py,sha256=Xk0hzx6l5bNHtGgL0M_znRY5JRPUJf4_PhleCyFbe0I,8935 -apprise/plugins/pushme.py,sha256=EswSmup0uL3QmANeJPDhDYyyeBwSrTnqdjMlfvWFnFA,7121 -apprise/plugins/pushover.py,sha256=I6u9C1SVAEVnsLUySFq5UyHi4cQQtew5za4r86aTK10,21194 -apprise/plugins/pushsafer.py,sha256=aGQ1ibggAWGpcBwpD4oQQYMUcKVcV_xsTlc5_wdjssY,26743 -apprise/plugins/pushy.py,sha256=Ai1tKhcus5IuwvhQgtaj--RMPahgtpleWrBbr_ZQJfM,12483 -apprise/plugins/reddit.py,sha256=z54HpGeJxkyS_LH38nqPa6rqW8mrJqSEGTNOh4Cf-78,25768 -apprise/plugins/revolt.py,sha256=kVrJvTb7mBFXFZd9GIVrX2jVIvaUc5D5via48nOXNSA,14487 -apprise/plugins/rocketchat.py,sha256=oSaFzQ7XjOzjhPIPhdS7r_LGa1IJXV6VK_3xNH9Gq9M,25721 -apprise/plugins/rsyslog.py,sha256=9NTg4sYNbbAzw_MnN51YKxVQpb2o6WZUMyKpRtgoupM,11969 -apprise/plugins/ryver.py,sha256=OM_AG-oiRnNYIua-70Wp8lUpZzIkZ7erIiDE6Fd4Q6s,11810 -apprise/plugins/sendgrid.py,sha256=PGSdbkhERhFSZZN2j7km6mdojdlo7sFxs28lmOeDpgQ,16200 -apprise/plugins/serverchan.py,sha256=fcnuindQTPaKZ5gbS-nAxvL31YqkEF-vi4w8g_h7dKc,5766 -apprise/plugins/ses.py,sha256=CF17VZOEZybwdgqqu00qSoombTP5z0M8P_XxPmyc2f4,33528 -apprise/plugins/signal_api.py,sha256=PeCB_1YJc4lZwDdArTgzHP-Vhe9iwCNIYe4WVW_HgUo,16716 -apprise/plugins/simplepush.py,sha256=bL0WiRUCp39ST9X_0FNAFJJ1BFtd1t5OD_JTRneUGFY,10859 -apprise/plugins/sinch.py,sha256=0-z_cNIWMJu4kdTca-6B3raAIRV-PG1E7fUtbxUDECo,16796 -apprise/plugins/slack.py,sha256=tLbJsr97Ddp8bq1ozSEp1ssLsh913uC0Y24Xm3TN4Bo,42508 -apprise/plugins/smseagle.py,sha256=WbULwfwnUEkPw_z-2cVp2TzrupS7fkPfcQgq779nG8k,24144 -apprise/plugins/smsmanager.py,sha256=aSSZcvpWjKEdDg-XPyD-9fTkaQ0FodvuuO7Hqg7N6Tk,14112 -apprise/plugins/smtp2go.py,sha256=D3C4rWGCJ-oqaZH8JgoMEV_3BYHCCHMd6M7cG0piIvA,19698 -apprise/plugins/sns.py,sha256=dA58nElGwKKnVAr--rh1deIa9BNPbZElK6cb5G1o3g8,24142 -apprise/plugins/sparkpost.py,sha256=ZR4866e1DefqOuuu3z_vBhS8C5YX0sj1G9WEhdWFMEk,27865 -apprise/plugins/streamlabs.py,sha256=Pyrz2xY2rDo8rnTgu4L5t2u1EPrZWVcIP37k8pE2SaE,16010 -apprise/plugins/synology.py,sha256=-g-Cesx8g-nG10shjUAnYtyLpMoGfUNDcTIGfi2UXDE,11088 -apprise/plugins/syslog.py,sha256=sNKh0iSNc3ARuMIO13uwRirMHzW2VxxTRwyZ-0UYL60,10604 -apprise/plugins/techuluspush.py,sha256=HV1m72pRsC3IfVN66zWK9joslDJsKRKYD6_uRfqjnHk,7240 -apprise/plugins/telegram.py,sha256=zsmCUJU-5INyJ-RDUYIHUEXpwnwShOISWc4Xf7Epanw,37168 -apprise/plugins/threema.py,sha256=aWvFfN5Ve0DZCDX7qJVlY8INT_C-AWh-jnYnPTd7Ufs,11868 -apprise/plugins/twilio.py,sha256=0iYa2ug_2_Rbz4N_lGVihtSSxxFaK2f7Sdr9oK_Wg18,15959 -apprise/plugins/twist.py,sha256=hLqabH_STcZiAwRCgFHO7n5qYx7eZgoDw4xT5Zl_y6Q,28824 -apprise/plugins/twitter.py,sha256=ZTHLuZYowQ6qSw9ujsRB5oNp9F9PyutU-iAKAUOkmT8,30129 -apprise/plugins/voipms.py,sha256=NVbrsY7fJG0MbF6eId--0wo9cRC2kehhg8L0sASQs2w,12539 -apprise/plugins/vonage.py,sha256=PRFMdjn0ARqOEhL8kXySz8pi9vsApl2fh9mZyLVySgo,13414 -apprise/plugins/webexteams.py,sha256=DErDmHhvnHfsAyp4L3mF6vSfq0WabL1lbbl-jDUKYTw,9151 -apprise/plugins/wecombot.py,sha256=o5CO0IyHN7wAXhf7es08QiPCmUl8shZCgN_nyJxycVA,8776 -apprise/plugins/whatsapp.py,sha256=9KObQEvUOtOclnudKhNj7qRrJ0uii7dhfxgwzuMHKxY,19911 -apprise/plugins/windows.py,sha256=88bDJQBnuuA0u7_xoTC-1Ppxt4tz39UMZseRY3EX_FE,8550 -apprise/plugins/xbmc.py,sha256=2ssX2AItDUy5FJFDY9r_JI4u7qc0CLGvG-hW0KtgWag,12079 -apprise/plugins/zulip.py,sha256=PJMB8RmV_ddP5waNAQ0Z29cpIut1WFY31DLFeg8-Nao,13955 -apprise/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -apprise/url.py,sha256=QEoqmWzapzXPwBXe85SdJaPWq7B4gcv4QCeJtndN4_E,29455 -apprise/url.pyi,sha256=WLaRREH7FzZ5x3-qkDkupojWGFC4uFwJ1EDt02lVs8c,520 -apprise/utils.py,sha256=Sy0pVslC8FDKh5RY8o6eVB-sPHFxBXFTdbhKSVf4UDM,53461 diff --git a/libs/apprise/Apprise.py b/libs/apprise/Apprise.py new file mode 100644 index 000000000..9a3e8dfc7 --- /dev/null +++ b/libs/apprise/Apprise.py @@ -0,0 +1,887 @@ +# -*- coding: utf-8 -*- +# BSD 2-Clause License +# +# Apprise - Push Notification Library. +# Copyright (c) 2024, Chris Caron +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import asyncio +import concurrent.futures as cf +import os +from itertools import chain +from . import common +from .conversion import convert_between +from .utils import is_exclusive_match +from .NotificationManager import NotificationManager +from .utils import parse_list +from .utils import parse_urls +from .utils import cwe312_url +from .emojis import apply_emojis +from .logger import logger +from .AppriseAsset import AppriseAsset +from .AppriseConfig import AppriseConfig +from .AppriseAttachment import AppriseAttachment +from .AppriseLocale import AppriseLocale +from .config.ConfigBase import ConfigBase +from .plugins.NotifyBase import NotifyBase + +from . import plugins +from . import __version__ + +# Grant access to our Notification Manager Singleton +N_MGR = NotificationManager() + + +class Apprise: + """ + Our Notification Manager + + """ + + def __init__(self, servers=None, asset=None, location=None, debug=False): + """ + Loads a set of server urls while applying the Asset() module to each + if specified. + + If no asset is provided, then the default asset is used. + + Optionally specify a global ContentLocation for a more strict means + of handling Attachments. + """ + + # Initialize a server list of URLs + self.servers = list() + + # Assigns an central asset object that will be later passed into each + # notification plugin. Assets contain information such as the local + # directory images can be found in. It can also identify remote + # URL paths that contain the images you want to present to the end + # user. If no asset is specified, then the default one is used. + self.asset = \ + asset if isinstance(asset, AppriseAsset) else AppriseAsset() + + if servers: + self.add(servers) + + # Initialize our locale object + self.locale = AppriseLocale() + + # Set our debug flag + self.debug = debug + + # Store our hosting location for optional strict rule handling + # of Attachments. Setting this to None removes any attachment + # restrictions. + self.location = location + + @staticmethod + def instantiate(url, asset=None, tag=None, suppress_exceptions=True): + """ + Returns the instance of a instantiated plugin based on the provided + Server URL. If the url fails to be parsed, then None is returned. + + The specified url can be either a string (the URL itself) or a + dictionary containing all of the components needed to istantiate + the notification service. If identifying a dictionary, at the bare + minimum, one must specify the schema. + + An example of a url dictionary object might look like: + { + schema: 'mailto', + host: 'google.com', + user: 'myuser', + password: 'mypassword', + } + + Alternatively the string is much easier to specify: + mailto://user:mypassword@google.com + + The dictionary works well for people who are calling details() to + extract the components they need to build the URL manually. + """ + + # Initialize our result set + results = None + + # Prepare our Asset Object + asset = asset if isinstance(asset, AppriseAsset) else AppriseAsset() + + if isinstance(url, str): + # Acquire our url tokens + results = plugins.url_to_dict( + url, secure_logging=asset.secure_logging) + + if results is None: + # Failed to parse the server URL; detailed logging handled + # inside url_to_dict - nothing to report here. + return None + + elif isinstance(url, dict): + # We already have our result set + results = url + + if results.get('schema') not in N_MGR: + # schema is a mandatory dictionary item as it is the only way + # we can index into our loaded plugins + logger.error('Dictionary does not include a "schema" entry.') + logger.trace( + 'Invalid dictionary unpacked as:{}{}'.format( + os.linesep, os.linesep.join( + ['{}="{}"'.format(k, v) + for k, v in results.items()]))) + return None + + logger.trace( + 'Dictionary unpacked as:{}{}'.format( + os.linesep, os.linesep.join( + ['{}="{}"'.format(k, v) for k, v in results.items()]))) + + # Otherwise we handle the invalid input specified + else: + logger.error( + 'An invalid URL type (%s) was specified for instantiation', + type(url)) + return None + + if not N_MGR[results['schema']].enabled: + # + # First Plugin Enable Check (Pre Initialization) + # + + # Plugin has been disabled at a global level + logger.error( + '%s:// is disabled on this system.', results['schema']) + return None + + # Build a list of tags to associate with the newly added notifications + results['tag'] = set(parse_list(tag)) + + # Set our Asset Object + results['asset'] = asset + + if suppress_exceptions: + try: + # Attempt to create an instance of our plugin using the parsed + # URL information + plugin = N_MGR[results['schema']](**results) + + # Create log entry of loaded URL + logger.debug( + 'Loaded {} URL: {}'.format( + N_MGR[results['schema']].service_name, + plugin.url(privacy=asset.secure_logging))) + + except Exception: + # CWE-312 (Secure Logging) Handling + loggable_url = url if not asset.secure_logging \ + else cwe312_url(url) + + # the arguments are invalid or can not be used. + logger.error( + 'Could not load {} URL: {}'.format( + N_MGR[results['schema']].service_name, + loggable_url)) + return None + + else: + # Attempt to create an instance of our plugin using the parsed + # URL information but don't wrap it in a try catch + plugin = N_MGR[results['schema']](**results) + + if not plugin.enabled: + # + # Second Plugin Enable Check (Post Initialization) + # + + # Service/Plugin is disabled (on a more local level). This is a + # case where the plugin was initially enabled but then after the + # __init__() was called under the hood something pre-determined + # that it could no longer be used. + + # The only downside to doing it this way is services are + # initialized prior to returning the details() if 3rd party tools + # are polling what is available. These services that become + # disabled thereafter are shown initially that they can be used. + logger.error( + '%s:// has become disabled on this system.', results['schema']) + return None + + return plugin + + def add(self, servers, asset=None, tag=None): + """ + Adds one or more server URLs into our list. + + You can override the global asset if you wish by including it with the + server(s) that you add. + + The tag allows you to associate 1 or more tag values to the server(s) + being added. tagging a service allows you to exclusively access them + when calling the notify() function. + """ + + # Initialize our return status + return_status = True + + if asset is None: + # prepare default asset + asset = self.asset + + if isinstance(servers, str): + # build our server list + servers = parse_urls(servers) + if len(servers) == 0: + return False + + elif isinstance(servers, dict): + # no problem, we support kwargs, convert it to a list + servers = [servers] + + elif isinstance(servers, (ConfigBase, NotifyBase, AppriseConfig)): + # Go ahead and just add our plugin into our list + self.servers.append(servers) + return True + + elif not isinstance(servers, (tuple, set, list)): + logger.error( + "An invalid notification (type={}) was specified.".format( + type(servers))) + return False + + for _server in servers: + + if isinstance(_server, (ConfigBase, NotifyBase, AppriseConfig)): + # Go ahead and just add our plugin into our list + self.servers.append(_server) + continue + + elif not isinstance(_server, (str, dict)): + logger.error( + "An invalid notification (type={}) was specified.".format( + type(_server))) + return_status = False + continue + + # Instantiate ourselves an object, this function throws or + # returns None if it fails + instance = Apprise.instantiate(_server, asset=asset, tag=tag) + if not isinstance(instance, NotifyBase): + # No logging is required as instantiate() handles failure + # and/or success reasons for us + return_status = False + continue + + # Add our initialized plugin to our server listings + self.servers.append(instance) + + # Return our status + return return_status + + def clear(self): + """ + Empties our server list + + """ + self.servers[:] = [] + + def find(self, tag=common.MATCH_ALL_TAG, match_always=True): + """ + Returns a list of all servers matching against the tag specified. + + """ + + # Build our tag setup + # - top level entries are treated as an 'or' + # - second level (or more) entries are treated as 'and' + # + # examples: + # tag="tagA, tagB" = tagA or tagB + # tag=['tagA', 'tagB'] = tagA or tagB + # tag=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB + # tag=[('tagB', 'tagC')] = tagB and tagC + + # A match_always flag allows us to pick up on our 'any' keyword + # and notify these services under all circumstances + match_always = common.MATCH_ALWAYS_TAG if match_always else None + + # Iterate over our loaded plugins + for entry in self.servers: + + if isinstance(entry, (ConfigBase, AppriseConfig)): + # load our servers + servers = entry.servers() + + else: + servers = [entry, ] + + for server in servers: + # Apply our tag matching based on our defined logic + if is_exclusive_match( + logic=tag, data=server.tags, + match_all=common.MATCH_ALL_TAG, + match_always=match_always): + yield server + return + + def notify(self, body, title='', notify_type=common.NotifyType.INFO, + body_format=None, tag=common.MATCH_ALL_TAG, match_always=True, + attach=None, interpret_escapes=None): + """ + Send a notification to all the plugins previously loaded. + + If the body_format specified is NotifyFormat.MARKDOWN, it will + be converted to HTML if the Notification type expects this. + + if the tag is specified (either a string or a set/list/tuple + of strings), then only the notifications flagged with that + tagged value are notified. By default, all added services + are notified (tag=MATCH_ALL_TAG) + + This function returns True if all notifications were successfully + sent, False if even just one of them fails, and None if no + notifications were sent at all as a result of tag filtering and/or + simply having empty configuration files that were read. + + Attach can contain a list of attachment URLs. attach can also be + represented by an AttachBase() (or list of) object(s). This + identifies the products you wish to notify + + Set interpret_escapes to True if you want to pre-escape a string + such as turning a \n into an actual new line, etc. + """ + + try: + # Process arguments and build synchronous and asynchronous calls + # (this step can throw internal errors). + sequential_calls, parallel_calls = self._create_notify_calls( + body, title, + notify_type=notify_type, body_format=body_format, + tag=tag, match_always=match_always, attach=attach, + interpret_escapes=interpret_escapes, + ) + + except TypeError: + # No notifications sent, and there was an internal error. + return False + + if not sequential_calls and not parallel_calls: + # Nothing to send + return None + + sequential_result = Apprise._notify_sequential(*sequential_calls) + parallel_result = Apprise._notify_parallel_threadpool(*parallel_calls) + return sequential_result and parallel_result + + async def async_notify(self, *args, **kwargs): + """ + Send a notification to all the plugins previously loaded, for + asynchronous callers. + + The arguments are identical to those of Apprise.notify(). + + """ + try: + # Process arguments and build synchronous and asynchronous calls + # (this step can throw internal errors). + sequential_calls, parallel_calls = self._create_notify_calls( + *args, **kwargs) + + except TypeError: + # No notifications sent, and there was an internal error. + return False + + if not sequential_calls and not parallel_calls: + # Nothing to send + return None + + sequential_result = Apprise._notify_sequential(*sequential_calls) + parallel_result = \ + await Apprise._notify_parallel_asyncio(*parallel_calls) + return sequential_result and parallel_result + + def _create_notify_calls(self, *args, **kwargs): + """ + Creates notifications for all the plugins loaded. + + Returns a list of (server, notify() kwargs) tuples for plugins with + parallelism disabled and another list for plugins with parallelism + enabled. + """ + + all_calls = list(self._create_notify_gen(*args, **kwargs)) + + # Split into sequential and parallel notify() calls. + sequential, parallel = [], [] + for (server, notify_kwargs) in all_calls: + if server.asset.async_mode: + parallel.append((server, notify_kwargs)) + else: + sequential.append((server, notify_kwargs)) + + return sequential, parallel + + def _create_notify_gen(self, body, title='', + notify_type=common.NotifyType.INFO, + body_format=None, tag=common.MATCH_ALL_TAG, + match_always=True, attach=None, + interpret_escapes=None): + """ + Internal generator function for _create_notify_calls(). + """ + + if len(self) == 0: + # Nothing to notify + msg = "There are no service(s) to notify" + logger.error(msg) + raise TypeError(msg) + + if not (title or body or attach): + msg = "No message content specified to deliver" + logger.error(msg) + raise TypeError(msg) + + try: + if title and isinstance(title, bytes): + title = title.decode(self.asset.encoding) + + if body and isinstance(body, bytes): + body = body.decode(self.asset.encoding) + + except UnicodeDecodeError: + msg = 'The content passed into Apprise was not of encoding ' \ + 'type: {}'.format(self.asset.encoding) + logger.error(msg) + raise TypeError(msg) + + # Tracks conversions + conversion_body_map = dict() + conversion_title_map = dict() + + # Prepare attachments if required + if attach is not None and not isinstance(attach, AppriseAttachment): + attach = AppriseAttachment( + attach, asset=self.asset, location=self.location) + + # Allow Asset default value + body_format = self.asset.body_format \ + if body_format is None else body_format + + # Allow Asset default value + interpret_escapes = self.asset.interpret_escapes \ + if interpret_escapes is None else interpret_escapes + + # Iterate over our loaded plugins + for server in self.find(tag, match_always=match_always): + # If our code reaches here, we either did not define a tag (it + # was set to None), or we did define a tag and the logic above + # determined we need to notify the service it's associated with + + # First we need to generate a key we will use to determine if we + # need to build our data out. Entries without are merged with + # the body at this stage. + key = server.notify_format if server.title_maxlen > 0\ + else f'_{server.notify_format}' + + if server.interpret_emojis: + # alter our key slightly to handle emojis since their value is + # pulled out of the notification + key += "-emojis" + + if key not in conversion_title_map: + + # Prepare our title + conversion_title_map[key] = '' if not title else title + + # Conversion of title only occurs for services where the title + # is blended with the body (title_maxlen <= 0) + if conversion_title_map[key] and server.title_maxlen <= 0: + conversion_title_map[key] = convert_between( + body_format, server.notify_format, + content=conversion_title_map[key]) + + # Our body is always converted no matter what + conversion_body_map[key] = \ + convert_between( + body_format, server.notify_format, content=body) + + if interpret_escapes: + # + # Escape our content + # + + try: + # Added overhead required due to Python 3 Encoding Bug + # identified here: https://bugs.python.org/issue21331 + conversion_body_map[key] = \ + conversion_body_map[key]\ + .encode('ascii', 'backslashreplace')\ + .decode('unicode-escape') + + conversion_title_map[key] = \ + conversion_title_map[key]\ + .encode('ascii', 'backslashreplace')\ + .decode('unicode-escape') + + except AttributeError: + # Must be of string type + msg = 'Failed to escape message body' + logger.error(msg) + raise TypeError(msg) + + if server.interpret_emojis: + # + # Convert our :emoji: definitions + # + + conversion_body_map[key] = \ + apply_emojis(conversion_body_map[key]) + conversion_title_map[key] = \ + apply_emojis(conversion_title_map[key]) + + kwargs = dict( + body=conversion_body_map[key], + title=conversion_title_map[key], + notify_type=notify_type, + attach=attach, + body_format=body_format + ) + yield (server, kwargs) + + @staticmethod + def _notify_sequential(*servers_kwargs): + """ + Process a list of notify() calls sequentially and synchronously. + """ + + success = True + + for (server, kwargs) in servers_kwargs: + try: + # Send notification + result = server.notify(**kwargs) + success = success and result + + except TypeError: + # These are our internally thrown notifications. + success = False + + except Exception: + # A catch all so we don't have to abort early + # just because one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + success = False + + return success + + @staticmethod + def _notify_parallel_threadpool(*servers_kwargs): + """ + Process a list of notify() calls in parallel and synchronously. + """ + + n_calls = len(servers_kwargs) + + # 0-length case + if n_calls == 0: + return True + + # There's no need to use a thread pool for just a single notification + if n_calls == 1: + return Apprise._notify_sequential(servers_kwargs[0]) + + # Create log entry + logger.info( + 'Notifying %d service(s) with threads.', len(servers_kwargs)) + + with cf.ThreadPoolExecutor() as executor: + success = True + futures = [executor.submit(server.notify, **kwargs) + for (server, kwargs) in servers_kwargs] + + for future in cf.as_completed(futures): + try: + result = future.result() + success = success and result + + except TypeError: + # These are our internally thrown notifications. + success = False + + except Exception: + # A catch all so we don't have to abort early + # just because one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + success = False + + return success + + @staticmethod + async def _notify_parallel_asyncio(*servers_kwargs): + """ + Process a list of async_notify() calls in parallel and asynchronously. + """ + + n_calls = len(servers_kwargs) + + # 0-length case + if n_calls == 0: + return True + + # (Unlike with the thread pool, we don't optimize for the single- + # notification case because asyncio can do useful work while waiting + # for that thread to complete) + + # Create log entry + logger.info( + 'Notifying %d service(s) asynchronously.', len(servers_kwargs)) + + async def do_call(server, kwargs): + return await server.async_notify(**kwargs) + + cors = (do_call(server, kwargs) for (server, kwargs) in servers_kwargs) + results = await asyncio.gather(*cors, return_exceptions=True) + + if any(isinstance(status, Exception) + and not isinstance(status, TypeError) for status in results): + # A catch all so we don't have to abort early just because + # one of our plugins has a bug in it. + logger.exception("Unhandled Notification Exception") + return False + + if any(isinstance(status, TypeError) for status in results): + # These are our internally thrown notifications. + return False + + return all(results) + + def details(self, lang=None, show_requirements=False, show_disabled=False): + """ + Returns the details associated with the Apprise object + + """ + + # general object returned + response = { + # Defines the current version of Apprise + 'version': __version__, + # Lists all of the currently supported Notifications + 'schemas': [], + # Includes the configured asset details + 'asset': self.asset.details(), + } + + for plugin in N_MGR.plugins(): + # Iterate over our hashed plugins and dynamically build details on + # their status: + + content = { + 'service_name': getattr(plugin, 'service_name', None), + 'service_url': getattr(plugin, 'service_url', None), + 'setup_url': getattr(plugin, 'setup_url', None), + # Placeholder - populated below + 'details': None, + + # Let upstream service know of the plugins that support + # attachments + 'attachment_support': getattr( + plugin, 'attachment_support', False), + + # Differentiat between what is a custom loaded plugin and + # which is native. + 'category': getattr(plugin, 'category', None) + } + + # Standard protocol(s) should be None or a tuple + enabled = getattr(plugin, 'enabled', True) + if not show_disabled and not enabled: + # Do not show inactive plugins + continue + + elif show_disabled: + # Add current state to response + content['enabled'] = enabled + + # Standard protocol(s) should be None or a tuple + protocols = getattr(plugin, 'protocol', None) + if isinstance(protocols, str): + protocols = (protocols, ) + + # Secure protocol(s) should be None or a tuple + secure_protocols = getattr(plugin, 'secure_protocol', None) + if isinstance(secure_protocols, str): + secure_protocols = (secure_protocols, ) + + # Add our protocol details to our content + content.update({ + 'protocols': protocols, + 'secure_protocols': secure_protocols, + }) + + if not lang: + # Simply return our results + content['details'] = plugins.details(plugin) + if show_requirements: + content['requirements'] = plugins.requirements(plugin) + + else: + # Emulate the specified language when returning our results + with self.locale.lang_at(lang): + content['details'] = plugins.details(plugin) + if show_requirements: + content['requirements'] = plugins.requirements(plugin) + + # Build our response object + response['schemas'].append(content) + + return response + + def urls(self, privacy=False): + """ + Returns all of the loaded URLs defined in this apprise object. + """ + return [x.url(privacy=privacy) for x in self.servers] + + def pop(self, index): + """ + Removes an indexed Notification Service from the stack and returns it. + + The thing is we can never pop AppriseConfig() entries, only what was + loaded within them. So pop needs to carefully iterate over our list + and only track actual entries. + """ + + # Tracking variables + prev_offset = -1 + offset = prev_offset + + for idx, s in enumerate(self.servers): + if isinstance(s, (ConfigBase, AppriseConfig)): + servers = s.servers() + if len(servers) > 0: + # Acquire a new maximum offset to work with + offset = prev_offset + len(servers) + + if offset >= index: + # we can pop an element from our config stack + fn = s.pop if isinstance(s, ConfigBase) \ + else s.server_pop + + return fn(index if prev_offset == -1 + else (index - prev_offset - 1)) + + else: + offset = prev_offset + 1 + if offset == index: + return self.servers.pop(idx) + + # Update our old offset + prev_offset = offset + + # If we reach here, then we indexed out of range + raise IndexError('list index out of range') + + def __getitem__(self, index): + """ + Returns the indexed server entry of a loaded notification server + """ + # Tracking variables + prev_offset = -1 + offset = prev_offset + + for idx, s in enumerate(self.servers): + if isinstance(s, (ConfigBase, AppriseConfig)): + # Get our list of servers associate with our config object + servers = s.servers() + if len(servers) > 0: + # Acquire a new maximum offset to work with + offset = prev_offset + len(servers) + + if offset >= index: + return servers[index if prev_offset == -1 + else (index - prev_offset - 1)] + + else: + offset = prev_offset + 1 + if offset == index: + return self.servers[idx] + + # Update our old offset + prev_offset = offset + + # If we reach here, then we indexed out of range + raise IndexError('list index out of range') + + def __getstate__(self): + """ + Pickle Support dumps() + """ + attributes = { + 'asset': self.asset, + # Prepare our URL list as we need to extract the associated tags + # and asset details associated with it + 'urls': [{ + 'url': server.url(privacy=False), + 'tag': server.tags if server.tags else None, + 'asset': server.asset} for server in self.servers], + 'locale': self.locale, + 'debug': self.debug, + 'location': self.location, + } + + return attributes + + def __setstate__(self, state): + """ + Pickle Support loads() + """ + self.servers = list() + self.asset = state['asset'] + self.locale = state['locale'] + self.location = state['location'] + for entry in state['urls']: + self.add(entry['url'], asset=entry['asset'], tag=entry['tag']) + + def __bool__(self): + """ + Allows the Apprise object to be wrapped in an 'if statement'. + True is returned if at least one service has been loaded. + """ + return len(self) > 0 + + def __iter__(self): + """ + Returns an iterator to each of our servers loaded. This includes those + found inside configuration. + """ + return chain(*[[s] if not isinstance(s, (ConfigBase, AppriseConfig)) + else iter(s.servers()) for s in self.servers]) + + def __len__(self): + """ + Returns the number of servers loaded; this includes those found within + loaded configuration. This funtion nnever actually counts the + Config entry themselves (if they exist), only what they contain. + """ + return sum([1 if not isinstance(s, (ConfigBase, AppriseConfig)) + else len(s.servers()) for s in self.servers]) diff --git a/libs/apprise/Apprise.pyi b/libs/apprise/Apprise.pyi new file mode 100644 index 000000000..5a34c9c65 --- /dev/null +++ b/libs/apprise/Apprise.pyi @@ -0,0 +1,62 @@ +from typing import Any, Dict, List, Iterable, Iterator, Optional + +from . import (AppriseAsset, AppriseAttachment, AppriseConfig, ConfigBase, + NotifyBase, NotifyFormat, NotifyType) +from .common import ContentLocation + +_Server = Union[str, ConfigBase, NotifyBase, AppriseConfig] +_Servers = Union[_Server, Dict[Any, _Server], Iterable[_Server]] +# Can't define this recursively as mypy doesn't support recursive types: +# https://github.com/python/mypy/issues/731 +_Tag = Union[str, Iterable[Union[str, Iterable[str]]]] + +class Apprise: + def __init__( + self, + servers: _Servers = ..., + asset: Optional[AppriseAsset] = ..., + location: Optional[ContentLocation] = ..., + debug: bool = ... + ) -> None: ... + @staticmethod + def instantiate( + url: Union[str, Dict[str, NotifyBase]], + asset: Optional[AppriseAsset] = ..., + tag: Optional[_Tag] = ..., + suppress_exceptions: bool = ... + ) -> NotifyBase: ... + def add( + self, + servers: _Servers = ..., + asset: Optional[AppriseAsset] = ..., + tag: Optional[_Tag] = ... + ) -> bool: ... + def clear(self) -> None: ... + def find(self, tag: str = ...) -> Iterator[Apprise]: ... + def notify( + self, + body: str, + title: str = ..., + notify_type: NotifyType = ..., + body_format: NotifyFormat = ..., + tag: _Tag = ..., + attach: Optional[AppriseAttachment] = ..., + interpret_escapes: Optional[bool] = ... + ) -> bool: ... + async def async_notify( + self, + body: str, + title: str = ..., + notify_type: NotifyType = ..., + body_format: NotifyFormat = ..., + tag: _Tag = ..., + attach: Optional[AppriseAttachment] = ..., + interpret_escapes: Optional[bool] = ... + ) -> bool: ... + def details(self, lang: Optional[str] = ...) -> Dict[str, Any]: ... + def urls(self, privacy: bool = ...) -> Iterable[str]: ... + def pop(self, index: int) -> ConfigBase: ... + def __getitem__(self, index: int) -> ConfigBase: ... + def __bool__(self) -> bool: ... + def __iter__(self) -> Iterator[ConfigBase]: ... + def __len__(self) -> int: ... \ No newline at end of file diff --git a/libs/apprise/asset.py b/libs/apprise/AppriseAsset.py similarity index 99% rename from libs/apprise/asset.py rename to libs/apprise/AppriseAsset.py index 450303d0e..97a7bccfb 100644 --- a/libs/apprise/asset.py +++ b/libs/apprise/AppriseAsset.py @@ -33,7 +33,7 @@ from os.path import dirname from os.path import isfile from os.path import abspath from .common import NotifyType -from .manager_plugins import NotificationManager +from .NotificationManager import NotificationManager # Grant access to our Notification Manager Singleton diff --git a/libs/apprise/asset.pyi b/libs/apprise/AppriseAsset.pyi similarity index 100% rename from libs/apprise/asset.pyi rename to libs/apprise/AppriseAsset.pyi diff --git a/libs/apprise/apprise_attachment.py b/libs/apprise/AppriseAttachment.py similarity index 98% rename from libs/apprise/apprise_attachment.py rename to libs/apprise/AppriseAttachment.py index 3c33f9e73..fcfed3af6 100644 --- a/libs/apprise/apprise_attachment.py +++ b/libs/apprise/AppriseAttachment.py @@ -27,9 +27,9 @@ # POSSIBILITY OF SUCH DAMAGE. from . import URLBase -from .attachment.base import AttachBase -from .asset import AppriseAsset -from .manager_attachment import AttachmentManager +from .attachment.AttachBase import AttachBase +from .AppriseAsset import AppriseAsset +from .AttachmentManager import AttachmentManager from .logger import logger from .common import ContentLocation from .common import CONTENT_LOCATIONS diff --git a/libs/apprise/apprise_attachment.pyi b/libs/apprise/AppriseAttachment.pyi similarity index 100% rename from libs/apprise/apprise_attachment.pyi rename to libs/apprise/AppriseAttachment.pyi diff --git a/libs/apprise/apprise_config.py b/libs/apprise/AppriseConfig.py similarity index 99% rename from libs/apprise/apprise_config.py rename to libs/apprise/AppriseConfig.py index 080f70d30..7e5a9126f 100644 --- a/libs/apprise/apprise_config.py +++ b/libs/apprise/AppriseConfig.py @@ -28,9 +28,9 @@ from . import ConfigBase from . import CONFIG_FORMATS -from .manager_config import ConfigurationManager +from .ConfigurationManager import ConfigurationManager from . import URLBase -from .asset import AppriseAsset +from .AppriseAsset import AppriseAsset from . import common from .utils import GET_SCHEMA_RE from .utils import parse_list diff --git a/libs/apprise/apprise_config.pyi b/libs/apprise/AppriseConfig.pyi similarity index 100% rename from libs/apprise/apprise_config.pyi rename to libs/apprise/AppriseConfig.pyi diff --git a/libs/apprise/locale.py b/libs/apprise/AppriseLocale.py similarity index 100% rename from libs/apprise/locale.py rename to libs/apprise/AppriseLocale.py diff --git a/libs/apprise/manager_attachment.py b/libs/apprise/AttachmentManager.py similarity index 93% rename from libs/apprise/manager_attachment.py rename to libs/apprise/AttachmentManager.py index d1288a943..d296a4996 100644 --- a/libs/apprise/manager_attachment.py +++ b/libs/apprise/AttachmentManager.py @@ -26,7 +26,6 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import re from os.path import dirname from os.path import abspath from os.path import join @@ -53,7 +52,3 @@ class AttachmentManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) - - # For filtering our result set - module_filter_re = re.compile( - r'^(?P' + fname_prefix + r'(?!Base)[A-Za-z0-9]+)$') diff --git a/libs/apprise/manager_config.py b/libs/apprise/ConfigurationManager.py similarity index 93% rename from libs/apprise/manager_config.py rename to libs/apprise/ConfigurationManager.py index 69a6bedb9..6696895b9 100644 --- a/libs/apprise/manager_config.py +++ b/libs/apprise/ConfigurationManager.py @@ -26,7 +26,6 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import re from os.path import dirname from os.path import abspath from os.path import join @@ -53,7 +52,3 @@ class ConfigurationManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) - - # For filtering our result set - module_filter_re = re.compile( - r'^(?P' + fname_prefix + r'(?!Base)[A-Za-z0-9]+)$') diff --git a/libs/apprise/manager_plugins.py b/libs/apprise/NotificationManager.py similarity index 92% rename from libs/apprise/manager_plugins.py rename to libs/apprise/NotificationManager.py index 74ed370ea..abbbdd203 100644 --- a/libs/apprise/manager_plugins.py +++ b/libs/apprise/NotificationManager.py @@ -26,7 +26,6 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -import re from os.path import dirname from os.path import abspath from os.path import join @@ -53,8 +52,3 @@ class NotificationManager(PluginManager): # The module path to scan module_path = join(abspath(dirname(__file__)), _id) - - # For filtering our result set - module_filter_re = re.compile( - r'^(?P' + fname_prefix + - r'(?!Base|ImageSize|Type)[A-Za-z0-9]+)$') diff --git a/libs/apprise/url.py b/libs/apprise/URLBase.py similarity index 99% rename from libs/apprise/url.py rename to libs/apprise/URLBase.py index 39daec867..90ea85c66 100644 --- a/libs/apprise/url.py +++ b/libs/apprise/URLBase.py @@ -35,8 +35,8 @@ from xml.sax.saxutils import escape as sax_escape from urllib.parse import unquote as _unquote from urllib.parse import quote as _quote -from .locale import gettext_lazy as _ -from .asset import AppriseAsset +from .AppriseLocale import gettext_lazy as _ +from .AppriseAsset import AppriseAsset from .utils import urlencode from .utils import parse_url from .utils import parse_bool @@ -744,7 +744,7 @@ class URLBase: @staticmethod def parse_url(url, verify_host=True, plus_to_space=False, - strict_port=False, sanitize=True): + strict_port=False): """Parses the URL and returns it broken apart into a dictionary. This is very specific and customized for Apprise. @@ -765,8 +765,7 @@ class URLBase: results = parse_url( url, default_schema='unknown', verify_host=verify_host, - plus_to_space=plus_to_space, strict_port=strict_port, - sanitize=sanitize) + plus_to_space=plus_to_space, strict_port=strict_port) if not results: # We're done; we failed to parse our url diff --git a/libs/apprise/url.pyi b/libs/apprise/URLBase.pyi similarity index 100% rename from libs/apprise/url.pyi rename to libs/apprise/URLBase.pyi diff --git a/libs/apprise/__init__.py b/libs/apprise/__init__.py index 457528594..81373c75b 100644 --- a/libs/apprise/__init__.py +++ b/libs/apprise/__init__.py @@ -27,7 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. __title__ = 'Apprise' -__version__ = '1.8.0' +__version__ = '1.7.6' __author__ = 'Chris Caron' __license__ = 'BSD' __copywrite__ = 'Copyright (C) 2024 Chris Caron ' @@ -49,17 +49,16 @@ from .common import CONTENT_INCLUDE_MODES from .common import ContentLocation from .common import CONTENT_LOCATIONS -from .url import URLBase -from .url import PrivacyMode -from .plugins.base import NotifyBase -from .config.base import ConfigBase -from .attachment.base import AttachBase +from .URLBase import URLBase +from .URLBase import PrivacyMode +from .plugins.NotifyBase import NotifyBase +from .config.ConfigBase import ConfigBase +from .attachment.AttachBase import AttachBase -from .apprise import Apprise -from .locale import AppriseLocale -from .asset import AppriseAsset -from .apprise_config import AppriseConfig -from .apprise_attachment import AppriseAttachment +from .Apprise import Apprise +from .AppriseAsset import AppriseAsset +from .AppriseConfig import AppriseConfig +from .AppriseAttachment import AppriseAttachment from . import decorators @@ -74,7 +73,7 @@ logging.getLogger(__name__).addHandler(logging.NullHandler()) __all__ = [ # Core 'Apprise', 'AppriseAsset', 'AppriseConfig', 'AppriseAttachment', 'URLBase', - 'NotifyBase', 'ConfigBase', 'AttachBase', 'AppriseLocale', + 'NotifyBase', 'ConfigBase', 'AttachBase', # Reference 'NotifyType', 'NotifyImageSize', 'NotifyFormat', 'OverflowMode', diff --git a/libs/apprise/attachment/base.py b/libs/apprise/attachment/AttachBase.py similarity index 98% rename from libs/apprise/attachment/base.py rename to libs/apprise/attachment/AttachBase.py index 71e3a4d0d..8cb6bd5cb 100644 --- a/libs/apprise/attachment/base.py +++ b/libs/apprise/attachment/AttachBase.py @@ -29,10 +29,10 @@ import os import time import mimetypes -from ..url import URLBase +from ..URLBase import URLBase from ..utils import parse_bool from ..common import ContentLocation -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class AttachBase(URLBase): @@ -315,7 +315,7 @@ class AttachBase(URLBase): "download() is implimented by the child class.") @staticmethod - def parse_url(url, verify_host=True, mimetype_db=None, sanitize=True): + def parse_url(url, verify_host=True, mimetype_db=None): """Parses the URL and returns it broken apart into a dictionary. This is very specific and customized for Apprise. @@ -333,8 +333,7 @@ class AttachBase(URLBase): successful, otherwise None is returned. """ - results = URLBase.parse_url( - url, verify_host=verify_host, sanitize=sanitize) + results = URLBase.parse_url(url, verify_host=verify_host) if not results: # We're done; we failed to parse our url diff --git a/libs/apprise/attachment/base.pyi b/libs/apprise/attachment/AttachBase.pyi similarity index 100% rename from libs/apprise/attachment/base.pyi rename to libs/apprise/attachment/AttachBase.pyi diff --git a/libs/apprise/attachment/file.py b/libs/apprise/attachment/AttachFile.py similarity index 98% rename from libs/apprise/attachment/file.py rename to libs/apprise/attachment/AttachFile.py index c48a707ae..4c9c8f136 100644 --- a/libs/apprise/attachment/file.py +++ b/libs/apprise/attachment/AttachFile.py @@ -28,9 +28,9 @@ import re import os -from .base import AttachBase +from .AttachBase import AttachBase from ..common import ContentLocation -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class AttachFile(AttachBase): diff --git a/libs/apprise/attachment/http.py b/libs/apprise/attachment/AttachHTTP.py similarity index 98% rename from libs/apprise/attachment/http.py rename to libs/apprise/attachment/AttachHTTP.py index aa075d671..5a3af9467 100644 --- a/libs/apprise/attachment/http.py +++ b/libs/apprise/attachment/AttachHTTP.py @@ -31,10 +31,10 @@ import os import requests import threading from tempfile import NamedTemporaryFile -from .base import AttachBase +from .AttachBase import AttachBase from ..common import ContentLocation -from ..url import PrivacyMode -from ..locale import gettext_lazy as _ +from ..URLBase import PrivacyMode +from ..AppriseLocale import gettext_lazy as _ class AttachHTTP(AttachBase): @@ -296,7 +296,8 @@ class AttachHTTP(AttachBase): """ Tidy memory if open """ - self.invalidate() + with self._lock: + self.invalidate() def url(self, privacy=False, *args, **kwargs): """ @@ -362,7 +363,8 @@ class AttachHTTP(AttachBase): us to re-instantiate this object. """ - results = AttachBase.parse_url(url, sanitize=False) + results = AttachBase.parse_url(url) + if not results: # We're done early as we couldn't load the results return results diff --git a/libs/apprise/attachment/__init__.py b/libs/apprise/attachment/__init__.py index c2aef1eec..0a88313d6 100644 --- a/libs/apprise/attachment/__init__.py +++ b/libs/apprise/attachment/__init__.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # Used for testing -from .base import AttachBase -from ..manager_attachment import AttachmentManager +from .AttachBase import AttachBase +from ..AttachmentManager import AttachmentManager # Initalize our Attachment Manager Singleton A_MGR = AttachmentManager() @@ -36,5 +36,4 @@ A_MGR = AttachmentManager() __all__ = [ # Reference 'AttachBase', - 'AttachmentManager', ] diff --git a/libs/apprise/config/base.py b/libs/apprise/config/ConfigBase.py similarity index 99% rename from libs/apprise/config/base.py rename to libs/apprise/config/ConfigBase.py index 953cee394..32e1bde34 100644 --- a/libs/apprise/config/base.py +++ b/libs/apprise/config/ConfigBase.py @@ -33,15 +33,15 @@ import time from .. import plugins from .. import common -from ..asset import AppriseAsset -from ..url import URLBase +from ..AppriseAsset import AppriseAsset +from ..URLBase import URLBase +from ..ConfigurationManager import ConfigurationManager from ..utils import GET_SCHEMA_RE from ..utils import parse_list from ..utils import parse_bool from ..utils import parse_urls from ..utils import cwe312_url -from ..manager_config import ConfigurationManager -from ..manager_plugins import NotificationManager +from ..NotificationManager import NotificationManager # Test whether token is valid or not VALID_TOKEN = re.compile( diff --git a/libs/apprise/config/base.pyi b/libs/apprise/config/ConfigBase.pyi similarity index 100% rename from libs/apprise/config/base.pyi rename to libs/apprise/config/ConfigBase.pyi diff --git a/libs/apprise/config/file.py b/libs/apprise/config/ConfigFile.py similarity index 98% rename from libs/apprise/config/file.py rename to libs/apprise/config/ConfigFile.py index 9f29ca20b..172d699f8 100644 --- a/libs/apprise/config/file.py +++ b/libs/apprise/config/ConfigFile.py @@ -28,10 +28,10 @@ import re import os -from .base import ConfigBase +from .ConfigBase import ConfigBase from ..common import ConfigFormat from ..common import ContentIncludeMode -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class ConfigFile(ConfigBase): diff --git a/libs/apprise/config/http.py b/libs/apprise/config/ConfigHTTP.py similarity index 98% rename from libs/apprise/config/http.py rename to libs/apprise/config/ConfigHTTP.py index 2e2ba299b..f6faba8d4 100644 --- a/libs/apprise/config/http.py +++ b/libs/apprise/config/ConfigHTTP.py @@ -28,11 +28,11 @@ import re import requests -from .base import ConfigBase +from .ConfigBase import ConfigBase from ..common import ConfigFormat from ..common import ContentIncludeMode -from ..url import PrivacyMode -from ..locale import gettext_lazy as _ +from ..URLBase import PrivacyMode +from ..AppriseLocale import gettext_lazy as _ # Support YAML formats # text/yaml diff --git a/libs/apprise/config/memory.py b/libs/apprise/config/ConfigMemory.py similarity index 97% rename from libs/apprise/config/memory.py rename to libs/apprise/config/ConfigMemory.py index 181d76236..413956dfc 100644 --- a/libs/apprise/config/memory.py +++ b/libs/apprise/config/ConfigMemory.py @@ -26,8 +26,8 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .base import ConfigBase -from ..locale import gettext_lazy as _ +from .ConfigBase import ConfigBase +from ..AppriseLocale import gettext_lazy as _ class ConfigMemory(ConfigBase): diff --git a/libs/apprise/config/__init__.py b/libs/apprise/config/__init__.py index 24957e88e..efbace687 100644 --- a/libs/apprise/config/__init__.py +++ b/libs/apprise/config/__init__.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # Used for testing -from .base import ConfigBase -from ..manager_config import ConfigurationManager +from .ConfigBase import ConfigBase +from ..ConfigurationManager import ConfigurationManager # Initalize our Config Manager Singleton C_MGR = ConfigurationManager() @@ -36,5 +36,4 @@ C_MGR = ConfigurationManager() __all__ = [ # Reference 'ConfigBase', - 'ConfigurationManager', ] diff --git a/libs/apprise/conversion.py b/libs/apprise/conversion.py index 5b6d1a941..4d5632f59 100644 --- a/libs/apprise/conversion.py +++ b/libs/apprise/conversion.py @@ -29,7 +29,7 @@ import re from markdown import markdown from .common import NotifyFormat -from .url import URLBase +from .URLBase import URLBase from html.parser import HTMLParser diff --git a/libs/apprise/decorators/base.py b/libs/apprise/decorators/CustomNotifyPlugin.py similarity index 98% rename from libs/apprise/decorators/base.py rename to libs/apprise/decorators/CustomNotifyPlugin.py index 2661db0aa..eb5f17b78 100644 --- a/libs/apprise/decorators/base.py +++ b/libs/apprise/decorators/CustomNotifyPlugin.py @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE.USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from ..plugins.base import NotifyBase -from ..manager_plugins import NotificationManager +from ..plugins.NotifyBase import NotifyBase +from ..NotificationManager import NotificationManager from ..utils import URL_DETAILS_RE from ..utils import parse_url from ..utils import url_assembly @@ -55,9 +55,6 @@ class CustomNotifyPlugin(NotifyBase): # should be treated differently. category = 'custom' - # Support Attachments - attachment_support = True - # Define object templates templates = ( '{schema}://', diff --git a/libs/apprise/decorators/notify.py b/libs/apprise/decorators/notify.py index 892c3adfe..2dd5f5099 100644 --- a/libs/apprise/decorators/notify.py +++ b/libs/apprise/decorators/notify.py @@ -26,7 +26,7 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .base import CustomNotifyPlugin +from .CustomNotifyPlugin import CustomNotifyPlugin def notify(on, name=None): diff --git a/libs/apprise/i18n/en/LC_MESSAGES/apprise.mo b/libs/apprise/i18n/en/LC_MESSAGES/apprise.mo index f108b538df44a22b2a18b2bb6140e8554b0428c1..1d22b89a68a0bcb2532b945c30a51bab4eccad96 100644 GIT binary patch delta 14 Vcmew^_g!v7HaDZu<{WNr762`b1ib(N delta 14 Vcmew^_g!v7HaDa3<{WNr762`h1ik((?!_)[A-Za-z0-9]+))$') - # thread safe loading _lock = threading.Lock() @@ -180,7 +177,7 @@ class PluginManager(metaclass=Singleton): # The .py extension is optional as we support loading directories # too module_re = re.compile( - r'^(?P(?!base|_)[a-z0-9_]+)(\.py)?$', + r'^(?P' + self.fname_prefix + r'[a-z0-9]+)(\.py)?$', re.I) t_start = time.time() @@ -191,6 +188,10 @@ class PluginManager(metaclass=Singleton): # keep going continue + elif match.group('name') == f'{self.fname_prefix}Base': + # keep going + continue + # Store our notification/plugin name: module_name = match.group('name') module_pyname = '{}.{}'.format(module_name_prefix, module_name) @@ -215,47 +216,7 @@ class PluginManager(metaclass=Singleton): # logging found in import_module and not needed here continue - module_class = None - for m_class in [obj for obj in dir(module) - if self.module_filter_re.match(obj)]: - # Get our plugin - plugin = getattr(module, m_class) - if not hasattr(plugin, 'app_id'): - # Filter out non-notification modules - logger.trace( - "(%s) import failed; no app_id defined in %s", - self.name, m_class, os.path.join(module_path, f)) - continue - - # Add our plugin name to our module map - self._module_map[module_name] = { - 'plugin': set([plugin]), - 'module': module, - 'path': '{}.{}'.format( - module_name_prefix, module_name), - 'native': True, - } - - fn = getattr(plugin, 'schemas', None) - schemas = set([]) if not callable(fn) else fn(plugin) - - # map our schema to our plugin - for schema in schemas: - if schema in self._schema_map: - logger.error( - "{} schema ({}) mismatch detected - {} to {}" - .format(self.name, schema, self._schema_map, - plugin)) - continue - - # Assign plugin - self._schema_map[schema] = plugin - - # Store our class - module_class = m_class - break - - if not module_class: + if not hasattr(module, module_name): # Not a library we can load as it doesn't follow the simple # rule that the class must bear the same name as the # notification file itself. @@ -265,6 +226,38 @@ class PluginManager(metaclass=Singleton): self.name, module_name, os.path.join(module_path, f)) continue + # Get our plugin + plugin = getattr(module, module_name) + if not hasattr(plugin, 'app_id'): + # Filter out non-notification modules + logger.trace( + "(%s) import failed; no app_id defined in %s", + self.name, module_name, os.path.join(module_path, f)) + continue + + # Add our plugin name to our module map + self._module_map[module_name] = { + 'plugin': set([plugin]), + 'module': module, + 'path': '{}.{}'.format(module_name_prefix, module_name), + 'native': True, + } + + fn = getattr(plugin, 'schemas', None) + schemas = set([]) if not callable(fn) else fn(plugin) + + # map our schema to our plugin + for schema in schemas: + if schema in self._schema_map: + logger.error( + "{} schema ({}) mismatch detected - {} to {}" + .format(self.name, schema, self._schema_map, + plugin)) + continue + + # Assign plugin + self._schema_map[schema] = plugin + logger.trace( '{} {} loaded in {:.6f}s'.format( self.name, module_name, (time.time() - tl_start))) diff --git a/libs/apprise/plugins/apprise_api.py b/libs/apprise/plugins/NotifyAppriseAPI.py similarity index 99% rename from libs/apprise/plugins/apprise_api.py rename to libs/apprise/plugins/NotifyAppriseAPI.py index fd71236b5..34c34a6d4 100644 --- a/libs/apprise/plugins/apprise_api.py +++ b/libs/apprise/plugins/NotifyAppriseAPI.py @@ -31,12 +31,12 @@ import requests from json import dumps import base64 -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class AppriseAPIMethod: @@ -123,7 +123,7 @@ class NotifyAppriseAPI(NotifyBase): 'type': 'string', 'required': True, 'private': True, - 'regex': (r'^[A-Z0-9_-]{1,128}$', 'i'), + 'regex': (r'^[A-Z0-9_-]{1,32}$', 'i'), }, }) diff --git a/libs/apprise/plugins/aprs.py b/libs/apprise/plugins/NotifyAprs.py similarity index 99% rename from libs/apprise/plugins/aprs.py rename to libs/apprise/plugins/NotifyAprs.py index b8adef5aa..5d8c3c100 100644 --- a/libs/apprise/plugins/aprs.py +++ b/libs/apprise/plugins/NotifyAprs.py @@ -70,9 +70,9 @@ import socket import sys from itertools import chain -from .base import NotifyBase -from ..locale import gettext_lazy as _ -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..AppriseLocale import gettext_lazy as _ +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_call_sign from ..utils import parse_call_sign diff --git a/libs/apprise/plugins/bark.py b/libs/apprise/plugins/NotifyBark.py similarity index 99% rename from libs/apprise/plugins/bark.py rename to libs/apprise/plugins/NotifyBark.py index e2f5bbfb4..781a1515e 100644 --- a/libs/apprise/plugins/bark.py +++ b/libs/apprise/plugins/NotifyBark.py @@ -32,13 +32,13 @@ import requests import json -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Sounds generated off of: https://github.com/Finb/Bark/tree/master/Sounds diff --git a/libs/apprise/plugins/base.py b/libs/apprise/plugins/NotifyBase.py similarity index 99% rename from libs/apprise/plugins/base.py rename to libs/apprise/plugins/NotifyBase.py index 1abc3410e..c29417c60 100644 --- a/libs/apprise/plugins/base.py +++ b/libs/apprise/plugins/NotifyBase.py @@ -30,7 +30,7 @@ import asyncio import re from functools import partial -from ..url import URLBase +from ..URLBase import URLBase from ..common import NotifyType from ..utils import parse_bool from ..common import NOTIFY_TYPES @@ -38,8 +38,8 @@ from ..common import NotifyFormat from ..common import NOTIFY_FORMATS from ..common import OverflowMode from ..common import OVERFLOW_MODES -from ..locale import gettext_lazy as _ -from ..apprise_attachment import AppriseAttachment +from ..AppriseLocale import gettext_lazy as _ +from ..AppriseAttachment import AppriseAttachment class NotifyBase(URLBase): diff --git a/libs/apprise/plugins/base.pyi b/libs/apprise/plugins/NotifyBase.pyi similarity index 100% rename from libs/apprise/plugins/base.pyi rename to libs/apprise/plugins/NotifyBase.pyi diff --git a/libs/apprise/plugins/boxcar.py b/libs/apprise/plugins/NotifyBoxcar.py similarity index 99% rename from libs/apprise/plugins/boxcar.py rename to libs/apprise/plugins/NotifyBoxcar.py index 851cdd3d8..808920ed5 100644 --- a/libs/apprise/plugins/boxcar.py +++ b/libs/apprise/plugins/NotifyBoxcar.py @@ -35,14 +35,14 @@ from hashlib import sha1 from itertools import chain from urllib.parse import urlparse -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex from ..common import NotifyType from ..common import NotifyImageSize -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default to sending to all devices if nothing is specified DEFAULT_TAG = '@all' diff --git a/libs/apprise/plugins/bulksms.py b/libs/apprise/plugins/NotifyBulkSMS.py similarity index 99% rename from libs/apprise/plugins/bulksms.py rename to libs/apprise/plugins/NotifyBulkSMS.py index 29c4d7fac..33664fb00 100644 --- a/libs/apprise/plugins/bulksms.py +++ b/libs/apprise/plugins/NotifyBulkSMS.py @@ -36,13 +36,13 @@ import re import requests import json from itertools import chain -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ IS_GROUP_RE = re.compile( diff --git a/libs/apprise/plugins/bulkvs.py b/libs/apprise/plugins/NotifyBulkVS.py similarity index 99% rename from libs/apprise/plugins/bulkvs.py rename to libs/apprise/plugins/NotifyBulkVS.py index 53a363008..e912dff25 100644 --- a/libs/apprise/plugins/bulkvs.py +++ b/libs/apprise/plugins/NotifyBulkVS.py @@ -35,13 +35,13 @@ # Messaging/post_messageSend import requests import json -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyBulkVS(NotifyBase): diff --git a/libs/apprise/plugins/burstsms.py b/libs/apprise/plugins/NotifyBurstSMS.py similarity index 99% rename from libs/apprise/plugins/burstsms.py rename to libs/apprise/plugins/NotifyBurstSMS.py index eb19df8e4..39606abba 100644 --- a/libs/apprise/plugins/burstsms.py +++ b/libs/apprise/plugins/NotifyBurstSMS.py @@ -33,14 +33,14 @@ # import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class BurstSMSCountryCode: diff --git a/libs/apprise/plugins/chantify.py b/libs/apprise/plugins/NotifyChantify.py similarity index 98% rename from libs/apprise/plugins/chantify.py rename to libs/apprise/plugins/NotifyChantify.py index d549a59fa..d912bd257 100644 --- a/libs/apprise/plugins/chantify.py +++ b/libs/apprise/plugins/NotifyChantify.py @@ -35,10 +35,10 @@ import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyChantify(NotifyBase): diff --git a/libs/apprise/plugins/clicksend.py b/libs/apprise/plugins/NotifyClickSend.py similarity index 94% rename from libs/apprise/plugins/clicksend.py rename to libs/apprise/plugins/NotifyClickSend.py index 9ade1055e..5e345fe10 100644 --- a/libs/apprise/plugins/clicksend.py +++ b/libs/apprise/plugins/NotifyClickSend.py @@ -41,14 +41,15 @@ # import requests from json import dumps +from base64 import b64encode -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages CLICKSEND_HTTP_ERROR_MAP = { @@ -88,7 +89,7 @@ class NotifyClickSend(NotifyBase): # Define object templates templates = ( - '{schema}://{user}:{apikey}@{targets}', + '{schema}://{user}:{password}@{targets}', ) # Define our template tokens @@ -98,12 +99,11 @@ class NotifyClickSend(NotifyBase): 'type': 'string', 'required': True, }, - 'apikey': { - 'name': _('API Key'), + 'password': { + 'name': _('Password'), 'type': 'string', 'private': True, 'required': True, - 'map_to': 'password', }, 'target_phone': { 'name': _('Target Phone No'), @@ -124,9 +124,6 @@ class NotifyClickSend(NotifyBase): 'to': { 'alias_of': 'targets', }, - 'key': { - 'alias_of': 'apikey', - }, 'batch': { 'name': _('Batch Mode'), 'type': 'bool', @@ -177,6 +174,9 @@ class NotifyClickSend(NotifyBase): headers = { 'User-Agent': self.app_id, 'Content-Type': 'application/json; charset=utf-8', + 'Authorization': 'Basic {}'.format( + b64encode('{}:{}'.format( + self.user, self.password).encode('utf-8'))), } # error tracking (used for function return) @@ -208,7 +208,6 @@ class NotifyClickSend(NotifyBase): r = requests.post( self.notify_url, data=dumps(payload), - auth=(self.user, self.password), headers=headers, verify=self.verify_certificate, timeout=self.request_timeout, @@ -323,12 +322,6 @@ class NotifyClickSend(NotifyBase): results['batch'] = \ parse_bool(results['qsd'].get('batch', False)) - # API Key - if 'key' in results['qsd'] and len(results['qsd']['key']): - # Extract the API Key from an argument - results['password'] = \ - NotifyClickSend.unquote(results['qsd']['key']) - # Support the 'to' variable so that we can support rooms this way too # The 'to' makes it easier to use yaml configuration if 'to' in results['qsd'] and len(results['qsd']['to']): diff --git a/libs/apprise/plugins/d7networks.py b/libs/apprise/plugins/NotifyD7Networks.py similarity index 99% rename from libs/apprise/plugins/d7networks.py rename to libs/apprise/plugins/NotifyD7Networks.py index ad55e2197..906ec2fb9 100644 --- a/libs/apprise/plugins/d7networks.py +++ b/libs/apprise/plugins/NotifyD7Networks.py @@ -39,13 +39,13 @@ import requests from json import dumps from json import loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages D7NETWORKS_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/dbus.py b/libs/apprise/plugins/NotifyDBus.py similarity index 99% rename from libs/apprise/plugins/dbus.py rename to libs/apprise/plugins/NotifyDBus.py index f2361fd62..52e119813 100644 --- a/libs/apprise/plugins/dbus.py +++ b/libs/apprise/plugins/NotifyDBus.py @@ -27,11 +27,11 @@ # POSSIBILITY OF SUCH DAMAGE. import sys -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_DBUS_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/dapnet.py b/libs/apprise/plugins/NotifyDapnet.py similarity index 99% rename from libs/apprise/plugins/dapnet.py rename to libs/apprise/plugins/NotifyDapnet.py index 60a18acd4..ae7199c94 100644 --- a/libs/apprise/plugins/dapnet.py +++ b/libs/apprise/plugins/NotifyDapnet.py @@ -51,9 +51,9 @@ from json import dumps import requests from requests.auth import HTTPBasicAuth -from .base import NotifyBase -from ..locale import gettext_lazy as _ -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..AppriseLocale import gettext_lazy as _ +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_call_sign from ..utils import parse_call_sign diff --git a/libs/apprise/plugins/dingtalk.py b/libs/apprise/plugins/NotifyDingTalk.py similarity index 99% rename from libs/apprise/plugins/dingtalk.py rename to libs/apprise/plugins/NotifyDingTalk.py index 2ca1bc55b..d4a492fc7 100644 --- a/libs/apprise/plugins/dingtalk.py +++ b/libs/apprise/plugins/NotifyDingTalk.py @@ -34,13 +34,13 @@ import base64 import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Register at https://dingtalk.com # - Download their PC based software as it is the only way you can create diff --git a/libs/apprise/plugins/discord.py b/libs/apprise/plugins/NotifyDiscord.py similarity index 99% rename from libs/apprise/plugins/discord.py rename to libs/apprise/plugins/NotifyDiscord.py index 14c6152ba..82d764f50 100644 --- a/libs/apprise/plugins/discord.py +++ b/libs/apprise/plugins/NotifyDiscord.py @@ -50,14 +50,14 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase # Used to detect user/role IDs diff --git a/libs/apprise/plugins/email.py b/libs/apprise/plugins/NotifyEmail.py similarity index 98% rename from libs/apprise/plugins/email.py rename to libs/apprise/plugins/NotifyEmail.py index 142c93cfb..80f88bf61 100644 --- a/libs/apprise/plugins/email.py +++ b/libs/apprise/plugins/NotifyEmail.py @@ -41,12 +41,12 @@ from socket import error as SocketError from datetime import datetime from datetime import timezone -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat, NotifyType from ..conversion import convert_between -from ..utils import is_ipaddr, is_email, parse_emails, is_hostname -from ..locale import gettext_lazy as _ +from ..utils import is_email, parse_emails, is_hostname +from ..AppriseLocale import gettext_lazy as _ from ..logger import logger # Globally Default encoding mode set to Quoted Printable. @@ -1053,12 +1053,8 @@ class NotifyEmail(NotifyBase): # Prepare our target lists results['targets'] = [] - if is_ipaddr(results['host']): - # Silently move on and do not disrupt any configuration - pass - - elif not is_hostname(results['host'], ipv4=False, ipv6=False, - underscore=False): + if not is_hostname(results['host'], ipv4=False, ipv6=False, + underscore=False): if is_email(NotifyEmail.unquote(results['host'])): # Don't lose defined email addresses diff --git a/libs/apprise/plugins/emby.py b/libs/apprise/plugins/NotifyEmby.py similarity index 99% rename from libs/apprise/plugins/emby.py rename to libs/apprise/plugins/NotifyEmby.py index 5e4e0b89f..ce96553a2 100644 --- a/libs/apprise/plugins/emby.py +++ b/libs/apprise/plugins/NotifyEmby.py @@ -34,12 +34,12 @@ import hashlib from json import dumps from json import loads -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..utils import parse_bool from ..common import NotifyType from .. import __version__ as VERSION -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyEmby(NotifyBase): diff --git a/libs/apprise/plugins/enigma2.py b/libs/apprise/plugins/NotifyEnigma2.py similarity index 98% rename from libs/apprise/plugins/enigma2.py rename to libs/apprise/plugins/NotifyEnigma2.py index 8b1fff687..313149993 100644 --- a/libs/apprise/plugins/enigma2.py +++ b/libs/apprise/plugins/NotifyEnigma2.py @@ -37,10 +37,10 @@ import requests from json import loads -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class Enigma2MessageType: diff --git a/libs/apprise/plugins/fcm/__init__.py b/libs/apprise/plugins/NotifyFCM/__init__.py similarity index 99% rename from libs/apprise/plugins/fcm/__init__.py rename to libs/apprise/plugins/NotifyFCM/__init__.py index 9dc0679f1..54b6c9cc7 100644 --- a/libs/apprise/plugins/fcm/__init__.py +++ b/libs/apprise/plugins/NotifyFCM/__init__.py @@ -50,15 +50,15 @@ # You will need this in order to send an apprise messag import requests from json import dumps -from ..base import NotifyBase +from ..NotifyBase import NotifyBase from ...common import NotifyType from ...utils import validate_regex from ...utils import parse_list from ...utils import parse_bool from ...utils import dict_full_update from ...common import NotifyImageSize -from ...apprise_attachment import AppriseAttachment -from ...locale import gettext_lazy as _ +from ...AppriseAttachment import AppriseAttachment +from ...AppriseLocale import gettext_lazy as _ from .common import (FCMMode, FCM_MODES) from .priority import (FCM_PRIORITIES, FCMPriorityManager) from .color import FCMColorManager diff --git a/libs/apprise/plugins/fcm/color.py b/libs/apprise/plugins/NotifyFCM/color.py similarity index 99% rename from libs/apprise/plugins/fcm/color.py rename to libs/apprise/plugins/NotifyFCM/color.py index 20149eedd..c7da209a7 100644 --- a/libs/apprise/plugins/fcm/color.py +++ b/libs/apprise/plugins/NotifyFCM/color.py @@ -36,7 +36,7 @@ import re from ...utils import parse_bool from ...common import NotifyType -from ...asset import AppriseAsset +from ...AppriseAsset import AppriseAsset class FCMColorManager: diff --git a/libs/apprise/plugins/fcm/common.py b/libs/apprise/plugins/NotifyFCM/common.py similarity index 100% rename from libs/apprise/plugins/fcm/common.py rename to libs/apprise/plugins/NotifyFCM/common.py diff --git a/libs/apprise/plugins/fcm/oauth.py b/libs/apprise/plugins/NotifyFCM/oauth.py similarity index 100% rename from libs/apprise/plugins/fcm/oauth.py rename to libs/apprise/plugins/NotifyFCM/oauth.py diff --git a/libs/apprise/plugins/fcm/priority.py b/libs/apprise/plugins/NotifyFCM/priority.py similarity index 100% rename from libs/apprise/plugins/fcm/priority.py rename to libs/apprise/plugins/NotifyFCM/priority.py diff --git a/libs/apprise/plugins/feishu.py b/libs/apprise/plugins/NotifyFeishu.py similarity index 98% rename from libs/apprise/plugins/feishu.py rename to libs/apprise/plugins/NotifyFeishu.py index 961523bab..e6988333c 100644 --- a/libs/apprise/plugins/feishu.py +++ b/libs/apprise/plugins/NotifyFeishu.py @@ -36,10 +36,10 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyFeishu(NotifyBase): diff --git a/libs/apprise/plugins/flock.py b/libs/apprise/plugins/NotifyFlock.py similarity index 99% rename from libs/apprise/plugins/flock.py rename to libs/apprise/plugins/NotifyFlock.py index bf2cd131d..f1d12067e 100644 --- a/libs/apprise/plugins/flock.py +++ b/libs/apprise/plugins/NotifyFlock.py @@ -44,14 +44,14 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..common import NotifyImageSize from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages diff --git a/libs/apprise/plugins/custom_form.py b/libs/apprise/plugins/NotifyForm.py similarity index 99% rename from libs/apprise/plugins/custom_form.py rename to libs/apprise/plugins/NotifyForm.py index 0f36643f7..9690cd4f5 100644 --- a/libs/apprise/plugins/custom_form.py +++ b/libs/apprise/plugins/NotifyForm.py @@ -29,11 +29,11 @@ import re import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class FORMPayloadField: diff --git a/libs/apprise/plugins/freemobile.py b/libs/apprise/plugins/NotifyFreeMobile.py similarity index 98% rename from libs/apprise/plugins/freemobile.py rename to libs/apprise/plugins/NotifyFreeMobile.py index 4ff3d4822..4aad8db3d 100644 --- a/libs/apprise/plugins/freemobile.py +++ b/libs/apprise/plugins/NotifyFreeMobile.py @@ -36,9 +36,9 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyFreeMobile(NotifyBase): @@ -126,7 +126,6 @@ class NotifyFreeMobile(NotifyBase): # prepare our headers headers = { 'User-Agent': self.app_id, - 'Content-Type': 'application/json', } # Prepare our payload diff --git a/libs/apprise/plugins/gnome.py b/libs/apprise/plugins/NotifyGnome.py similarity index 99% rename from libs/apprise/plugins/gnome.py rename to libs/apprise/plugins/NotifyGnome.py index b64b51304..67129216d 100644 --- a/libs/apprise/plugins/gnome.py +++ b/libs/apprise/plugins/NotifyGnome.py @@ -26,11 +26,11 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_GNOME_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/google_chat.py b/libs/apprise/plugins/NotifyGoogleChat.py similarity index 99% rename from libs/apprise/plugins/google_chat.py rename to libs/apprise/plugins/NotifyGoogleChat.py index f30cdae49..d2a6cc8a8 100644 --- a/libs/apprise/plugins/google_chat.py +++ b/libs/apprise/plugins/NotifyGoogleChat.py @@ -58,11 +58,11 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyGoogleChat(NotifyBase): diff --git a/libs/apprise/plugins/gotify.py b/libs/apprise/plugins/NotifyGotify.py similarity index 99% rename from libs/apprise/plugins/gotify.py rename to libs/apprise/plugins/NotifyGotify.py index bf6c1b281..3f4ce132d 100644 --- a/libs/apprise/plugins/gotify.py +++ b/libs/apprise/plugins/NotifyGotify.py @@ -34,10 +34,10 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType, NotifyFormat from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Priorities diff --git a/libs/apprise/plugins/growl.py b/libs/apprise/plugins/NotifyGrowl.py similarity index 99% rename from libs/apprise/plugins/growl.py rename to libs/apprise/plugins/NotifyGrowl.py index 0b3672188..0b42e3bec 100644 --- a/libs/apprise/plugins/growl.py +++ b/libs/apprise/plugins/NotifyGrowl.py @@ -26,12 +26,12 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_GROWL_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/guilded.py b/libs/apprise/plugins/NotifyGuilded.py similarity index 96% rename from libs/apprise/plugins/guilded.py rename to libs/apprise/plugins/NotifyGuilded.py index 0ee07018e..0ea36d9f8 100644 --- a/libs/apprise/plugins/guilded.py +++ b/libs/apprise/plugins/NotifyGuilded.py @@ -45,11 +45,10 @@ # import re -# Import namespace so the class won't conflict with the actual Notify object -from . import discord +from .NotifyDiscord import NotifyDiscord -class NotifyGuilded(discord.NotifyDiscord): +class NotifyGuilded(NotifyDiscord): """ A wrapper to Guilded Notifications diff --git a/libs/apprise/plugins/home_assistant.py b/libs/apprise/plugins/NotifyHomeAssistant.py similarity index 98% rename from libs/apprise/plugins/home_assistant.py rename to libs/apprise/plugins/NotifyHomeAssistant.py index b0ffcaa64..0829381b9 100644 --- a/libs/apprise/plugins/home_assistant.py +++ b/libs/apprise/plugins/NotifyHomeAssistant.py @@ -34,11 +34,11 @@ from json import dumps from uuid import uuid4 -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyHomeAssistant(NotifyBase): diff --git a/libs/apprise/plugins/httpsms.py b/libs/apprise/plugins/NotifyHttpSMS.py similarity index 99% rename from libs/apprise/plugins/httpsms.py rename to libs/apprise/plugins/NotifyHttpSMS.py index b36e286d7..647100949 100644 --- a/libs/apprise/plugins/httpsms.py +++ b/libs/apprise/plugins/NotifyHttpSMS.py @@ -31,12 +31,12 @@ # https://httpsms.com import requests import json -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyHttpSMS(NotifyBase): diff --git a/libs/apprise/plugins/ifttt.py b/libs/apprise/plugins/NotifyIFTTT.py similarity index 99% rename from libs/apprise/plugins/ifttt.py rename to libs/apprise/plugins/NotifyIFTTT.py index 9d89b1464..9174640d0 100644 --- a/libs/apprise/plugins/ifttt.py +++ b/libs/apprise/plugins/NotifyIFTTT.py @@ -44,11 +44,11 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyIFTTT(NotifyBase): diff --git a/libs/apprise/plugins/custom_json.py b/libs/apprise/plugins/NotifyJSON.py similarity index 99% rename from libs/apprise/plugins/custom_json.py rename to libs/apprise/plugins/NotifyJSON.py index e0d7a6753..182ff77cf 100644 --- a/libs/apprise/plugins/custom_json.py +++ b/libs/apprise/plugins/NotifyJSON.py @@ -30,11 +30,11 @@ import requests import base64 from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class JSONPayloadField: diff --git a/libs/apprise/plugins/join.py b/libs/apprise/plugins/NotifyJoin.py similarity index 99% rename from libs/apprise/plugins/join.py rename to libs/apprise/plugins/NotifyJoin.py index b92bb37a3..c6b0d91e9 100644 --- a/libs/apprise/plugins/join.py +++ b/libs/apprise/plugins/NotifyJoin.py @@ -39,13 +39,13 @@ import re import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages JOIN_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/kavenegar.py b/libs/apprise/plugins/NotifyKavenegar.py similarity index 99% rename from libs/apprise/plugins/kavenegar.py rename to libs/apprise/plugins/NotifyKavenegar.py index e4963f409..2a9c169d7 100644 --- a/libs/apprise/plugins/kavenegar.py +++ b/libs/apprise/plugins/NotifyKavenegar.py @@ -38,12 +38,12 @@ import requests from json import loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages # Based on https://kavenegar.com/rest.html diff --git a/libs/apprise/plugins/kumulos.py b/libs/apprise/plugins/NotifyKumulos.py similarity index 99% rename from libs/apprise/plugins/kumulos.py rename to libs/apprise/plugins/NotifyKumulos.py index 504dcc379..da372e773 100644 --- a/libs/apprise/plugins/kumulos.py +++ b/libs/apprise/plugins/NotifyKumulos.py @@ -39,10 +39,10 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages KUMULOS_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/lametric.py b/libs/apprise/plugins/NotifyLametric.py similarity index 99% rename from libs/apprise/plugins/lametric.py rename to libs/apprise/plugins/NotifyLametric.py index 411b9ea21..5825d9176 100644 --- a/libs/apprise/plugins/lametric.py +++ b/libs/apprise/plugins/NotifyLametric.py @@ -90,10 +90,10 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from ..utils import is_hostname from ..utils import is_ipaddr diff --git a/libs/apprise/plugins/line.py b/libs/apprise/plugins/NotifyLine.py similarity index 98% rename from libs/apprise/plugins/line.py rename to libs/apprise/plugins/NotifyLine.py index 07a01e769..61e4f3703 100644 --- a/libs/apprise/plugins/line.py +++ b/libs/apprise/plugins/NotifyLine.py @@ -33,14 +33,14 @@ import requests import re from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_list from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Used to break path apart into list of streams diff --git a/libs/apprise/plugins/lunasea.py b/libs/apprise/plugins/NotifyLunaSea.py similarity index 99% rename from libs/apprise/plugins/lunasea.py rename to libs/apprise/plugins/NotifyLunaSea.py index 2af519179..51d820915 100644 --- a/libs/apprise/plugins/lunasea.py +++ b/libs/apprise/plugins/NotifyLunaSea.py @@ -33,15 +33,15 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..utils import parse_list from ..utils import is_hostname from ..utils import is_ipaddr from ..utils import parse_bool -from ..locale import gettext_lazy as _ -from ..url import PrivacyMode +from ..AppriseLocale import gettext_lazy as _ +from ..URLBase import PrivacyMode class LunaSeaMode: diff --git a/libs/apprise/plugins/mqtt.py b/libs/apprise/plugins/NotifyMQTT.py similarity index 99% rename from libs/apprise/plugins/mqtt.py rename to libs/apprise/plugins/NotifyMQTT.py index 1e09cd147..49380d330 100644 --- a/libs/apprise/plugins/mqtt.py +++ b/libs/apprise/plugins/NotifyMQTT.py @@ -38,12 +38,12 @@ import re from time import sleep from datetime import datetime from os.path import isfile -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_MQTT_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/msg91.py b/libs/apprise/plugins/NotifyMSG91.py similarity index 99% rename from libs/apprise/plugins/msg91.py rename to libs/apprise/plugins/NotifyMSG91.py index 28a5bf183..a7bd9c473 100644 --- a/libs/apprise/plugins/msg91.py +++ b/libs/apprise/plugins/NotifyMSG91.py @@ -38,12 +38,12 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no, parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class MSG91PayloadField: diff --git a/libs/apprise/plugins/msteams.py b/libs/apprise/plugins/NotifyMSTeams.py similarity index 99% rename from libs/apprise/plugins/msteams.py rename to libs/apprise/plugins/NotifyMSTeams.py index 2e0957f32..06572c3e6 100644 --- a/libs/apprise/plugins/msteams.py +++ b/libs/apprise/plugins/NotifyMSTeams.py @@ -81,7 +81,7 @@ import requests import json from json.decoder import JSONDecodeError -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..common import NotifyFormat @@ -89,8 +89,8 @@ from ..utils import parse_bool from ..utils import validate_regex from ..utils import apply_template from ..utils import TemplateType -from ..apprise_attachment import AppriseAttachment -from ..locale import gettext_lazy as _ +from ..AppriseAttachment import AppriseAttachment +from ..AppriseLocale import gettext_lazy as _ class NotifyMSTeams(NotifyBase): diff --git a/libs/apprise/plugins/macosx.py b/libs/apprise/plugins/NotifyMacOSX.py similarity index 99% rename from libs/apprise/plugins/macosx.py rename to libs/apprise/plugins/NotifyMacOSX.py index 31b7101bd..dd53369fe 100644 --- a/libs/apprise/plugins/macosx.py +++ b/libs/apprise/plugins/NotifyMacOSX.py @@ -30,11 +30,11 @@ import platform import subprocess import os -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_MACOSX_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/mailgun.py b/libs/apprise/plugins/NotifyMailgun.py similarity index 99% rename from libs/apprise/plugins/mailgun.py rename to libs/apprise/plugins/NotifyMailgun.py index 69ab72dda..82cf970bf 100644 --- a/libs/apprise/plugins/mailgun.py +++ b/libs/apprise/plugins/NotifyMailgun.py @@ -56,7 +56,7 @@ # import requests from email.utils import formataddr -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import parse_emails @@ -64,7 +64,7 @@ from ..utils import parse_bool from ..utils import is_email from ..utils import validate_regex from ..logger import logger -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Provide some known codes Mailgun uses and what they translate to: # Based on https://documentation.mailgun.com/en/latest/api-intro.html#errors diff --git a/libs/apprise/plugins/mastodon.py b/libs/apprise/plugins/NotifyMastodon.py similarity index 99% rename from libs/apprise/plugins/mastodon.py rename to libs/apprise/plugins/NotifyMastodon.py index b6e451ad1..0d2f27df3 100644 --- a/libs/apprise/plugins/mastodon.py +++ b/libs/apprise/plugins/NotifyMastodon.py @@ -33,16 +33,16 @@ from json import dumps, loads from datetime import datetime from datetime import timezone -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase # Accept: # - @username diff --git a/libs/apprise/plugins/matrix.py b/libs/apprise/plugins/NotifyMatrix.py similarity index 99% rename from libs/apprise/plugins/matrix.py rename to libs/apprise/plugins/NotifyMatrix.py index c1401f238..594274761 100644 --- a/libs/apprise/plugins/matrix.py +++ b/libs/apprise/plugins/NotifyMatrix.py @@ -37,8 +37,8 @@ from json import dumps from json import loads from time import time -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..common import NotifyFormat @@ -46,7 +46,7 @@ from ..utils import parse_bool from ..utils import parse_list from ..utils import is_hostname from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Define default path MATRIX_V1_WEBHOOK_PATH = '/api/v1/matrix/hook' diff --git a/libs/apprise/plugins/mattermost.py b/libs/apprise/plugins/NotifyMattermost.py similarity index 99% rename from libs/apprise/plugins/mattermost.py rename to libs/apprise/plugins/NotifyMattermost.py index 481a9b852..dbb5f0dd3 100644 --- a/libs/apprise/plugins/mattermost.py +++ b/libs/apprise/plugins/NotifyMattermost.py @@ -39,13 +39,13 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Some Reference Locations: # - https://docs.mattermost.com/developer/webhooks-incoming.html diff --git a/libs/apprise/plugins/messagebird.py b/libs/apprise/plugins/NotifyMessageBird.py similarity index 99% rename from libs/apprise/plugins/messagebird.py rename to libs/apprise/plugins/NotifyMessageBird.py index c496d347e..42d880acd 100644 --- a/libs/apprise/plugins/messagebird.py +++ b/libs/apprise/plugins/NotifyMessageBird.py @@ -34,12 +34,12 @@ import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyMessageBird(NotifyBase): diff --git a/libs/apprise/plugins/misskey.py b/libs/apprise/plugins/NotifyMisskey.py similarity index 99% rename from libs/apprise/plugins/misskey.py rename to libs/apprise/plugins/NotifyMisskey.py index 73b8f7c6e..8965a0f7b 100644 --- a/libs/apprise/plugins/misskey.py +++ b/libs/apprise/plugins/NotifyMisskey.py @@ -47,10 +47,10 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class MisskeyVisibility: diff --git a/libs/apprise/plugins/nextcloud.py b/libs/apprise/plugins/NotifyNextcloud.py similarity index 99% rename from libs/apprise/plugins/nextcloud.py rename to libs/apprise/plugins/NotifyNextcloud.py index 9acfc43d5..fd471d9eb 100644 --- a/libs/apprise/plugins/nextcloud.py +++ b/libs/apprise/plugins/NotifyNextcloud.py @@ -28,11 +28,11 @@ import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyNextcloud(NotifyBase): diff --git a/libs/apprise/plugins/nextcloudtalk.py b/libs/apprise/plugins/NotifyNextcloudTalk.py similarity index 98% rename from libs/apprise/plugins/nextcloudtalk.py rename to libs/apprise/plugins/NotifyNextcloudTalk.py index b1b014770..4bfced282 100644 --- a/libs/apprise/plugins/nextcloudtalk.py +++ b/libs/apprise/plugins/NotifyNextcloudTalk.py @@ -29,11 +29,11 @@ import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyNextcloudTalk(NotifyBase): diff --git a/libs/apprise/plugins/notica.py b/libs/apprise/plugins/NotifyNotica.py similarity index 99% rename from libs/apprise/plugins/notica.py rename to libs/apprise/plugins/NotifyNotica.py index 661fde1d1..33a94fc96 100644 --- a/libs/apprise/plugins/notica.py +++ b/libs/apprise/plugins/NotifyNotica.py @@ -43,11 +43,11 @@ import re import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NoticaMode: diff --git a/libs/apprise/plugins/notifiarr.py b/libs/apprise/plugins/NotifyNotifiarr.py similarity index 99% rename from libs/apprise/plugins/notifiarr.py rename to libs/apprise/plugins/NotifyNotifiarr.py index cc13e1a56..e195cbd32 100644 --- a/libs/apprise/plugins/notifiarr.py +++ b/libs/apprise/plugins/NotifyNotifiarr.py @@ -31,9 +31,9 @@ import requests from json import dumps from itertools import chain -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from ..common import NotifyImageSize from ..utils import parse_list, parse_bool from ..utils import validate_regex diff --git a/libs/apprise/plugins/notifico.py b/libs/apprise/plugins/NotifyNotifico.py similarity index 99% rename from libs/apprise/plugins/notifico.py rename to libs/apprise/plugins/NotifyNotifico.py index 5cb0d666e..27ce29a6e 100644 --- a/libs/apprise/plugins/notifico.py +++ b/libs/apprise/plugins/NotifyNotifico.py @@ -44,11 +44,11 @@ import re import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotificoFormat: diff --git a/libs/apprise/plugins/ntfy.py b/libs/apprise/plugins/NotifyNtfy.py similarity index 99% rename from libs/apprise/plugins/ntfy.py rename to libs/apprise/plugins/NotifyNtfy.py index 805b87260..40834eece 100644 --- a/libs/apprise/plugins/ntfy.py +++ b/libs/apprise/plugins/NotifyNtfy.py @@ -41,18 +41,18 @@ from json import loads from json import dumps from os.path import basename -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..common import NotifyImageSize -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from ..utils import parse_list from ..utils import parse_bool from ..utils import is_hostname from ..utils import is_ipaddr from ..utils import validate_regex -from ..url import PrivacyMode -from ..attachment.base import AttachBase +from ..URLBase import PrivacyMode +from ..attachment.AttachBase import AttachBase class NtfyMode: diff --git a/libs/apprise/plugins/office365.py b/libs/apprise/plugins/NotifyOffice365.py similarity index 99% rename from libs/apprise/plugins/office365.py rename to libs/apprise/plugins/NotifyOffice365.py index b04f7a037..0c62279f9 100644 --- a/libs/apprise/plugins/office365.py +++ b/libs/apprise/plugins/NotifyOffice365.py @@ -64,14 +64,14 @@ from datetime import datetime from datetime import timedelta from json import loads from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import is_email from ..utils import parse_emails from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyOffice365(NotifyBase): diff --git a/libs/apprise/plugins/one_signal.py b/libs/apprise/plugins/NotifyOneSignal.py similarity index 99% rename from libs/apprise/plugins/one_signal.py rename to libs/apprise/plugins/NotifyOneSignal.py index 76ec212f4..eb1e10f7a 100644 --- a/libs/apprise/plugins/one_signal.py +++ b/libs/apprise/plugins/NotifyOneSignal.py @@ -37,14 +37,14 @@ import requests from json import dumps from itertools import chain -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_list from ..utils import parse_bool from ..utils import is_email -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class OneSignalCategory: diff --git a/libs/apprise/plugins/opsgenie.py b/libs/apprise/plugins/NotifyOpsgenie.py similarity index 99% rename from libs/apprise/plugins/opsgenie.py rename to libs/apprise/plugins/NotifyOpsgenie.py index 5327ec803..c2dfed232 100644 --- a/libs/apprise/plugins/opsgenie.py +++ b/libs/apprise/plugins/NotifyOpsgenie.py @@ -49,13 +49,13 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex from ..utils import is_uuid from ..utils import parse_list from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class OpsgenieCategory(NotifyBase): diff --git a/libs/apprise/plugins/pagerduty.py b/libs/apprise/plugins/NotifyPagerDuty.py similarity index 99% rename from libs/apprise/plugins/pagerduty.py rename to libs/apprise/plugins/NotifyPagerDuty.py index c9d555527..0896b41b1 100644 --- a/libs/apprise/plugins/pagerduty.py +++ b/libs/apprise/plugins/NotifyPagerDuty.py @@ -34,13 +34,13 @@ import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import validate_regex from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class PagerDutySeverity: diff --git a/libs/apprise/plugins/pagertree.py b/libs/apprise/plugins/NotifyPagerTree.py similarity index 99% rename from libs/apprise/plugins/pagertree.py rename to libs/apprise/plugins/NotifyPagerTree.py index 8a041a358..c9290f2f7 100644 --- a/libs/apprise/plugins/pagertree.py +++ b/libs/apprise/plugins/NotifyPagerTree.py @@ -31,11 +31,11 @@ from json import dumps from uuid import uuid4 -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Actions diff --git a/libs/apprise/plugins/parseplatform.py b/libs/apprise/plugins/NotifyParsePlatform.py similarity index 99% rename from libs/apprise/plugins/parseplatform.py rename to libs/apprise/plugins/NotifyParsePlatform.py index cd59d0575..2a182ed31 100644 --- a/libs/apprise/plugins/parseplatform.py +++ b/libs/apprise/plugins/NotifyParsePlatform.py @@ -30,10 +30,10 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Used to break path apart into list of targets TARGET_LIST_DELIM = re.compile(r'[ \t\r\n,\\/]+') diff --git a/libs/apprise/plugins/popcorn_notify.py b/libs/apprise/plugins/NotifyPopcornNotify.py similarity index 99% rename from libs/apprise/plugins/popcorn_notify.py rename to libs/apprise/plugins/NotifyPopcornNotify.py index 388aa2199..0ecd8af68 100644 --- a/libs/apprise/plugins/popcorn_notify.py +++ b/libs/apprise/plugins/NotifyPopcornNotify.py @@ -28,14 +28,14 @@ import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_email from ..utils import is_phone_no from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyPopcornNotify(NotifyBase): diff --git a/libs/apprise/plugins/prowl.py b/libs/apprise/plugins/NotifyProwl.py similarity index 99% rename from libs/apprise/plugins/prowl.py rename to libs/apprise/plugins/NotifyProwl.py index c174615c0..e5c07bf4e 100644 --- a/libs/apprise/plugins/prowl.py +++ b/libs/apprise/plugins/NotifyProwl.py @@ -28,10 +28,10 @@ import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Priorities diff --git a/libs/apprise/plugins/pushbullet.py b/libs/apprise/plugins/NotifyPushBullet.py similarity index 99% rename from libs/apprise/plugins/pushbullet.py rename to libs/apprise/plugins/NotifyPushBullet.py index 8e006db1d..5e9c43fbf 100644 --- a/libs/apprise/plugins/pushbullet.py +++ b/libs/apprise/plugins/NotifyPushBullet.py @@ -30,13 +30,13 @@ import requests from json import dumps from json import loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..utils import is_email from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase # Flag used as a placeholder to sending to all devices PUSHBULLET_SEND_TO_ALL = 'ALL_DEVICES' diff --git a/libs/apprise/plugins/pushdeer.py b/libs/apprise/plugins/NotifyPushDeer.py similarity index 98% rename from libs/apprise/plugins/pushdeer.py rename to libs/apprise/plugins/NotifyPushDeer.py index fa888b159..6766dc7ff 100644 --- a/libs/apprise/plugins/pushdeer.py +++ b/libs/apprise/plugins/NotifyPushDeer.py @@ -29,9 +29,9 @@ import requests from ..common import NotifyType -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Syntax: # schan://{key}/ diff --git a/libs/apprise/plugins/pushme.py b/libs/apprise/plugins/NotifyPushMe.py similarity index 98% rename from libs/apprise/plugins/pushme.py rename to libs/apprise/plugins/NotifyPushMe.py index abbed7944..30889f868 100644 --- a/libs/apprise/plugins/pushme.py +++ b/libs/apprise/plugins/NotifyPushMe.py @@ -28,12 +28,12 @@ import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import validate_regex from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyPushMe(NotifyBase): diff --git a/libs/apprise/plugins/pushsafer.py b/libs/apprise/plugins/NotifyPushSafer.py similarity index 99% rename from libs/apprise/plugins/pushsafer.py rename to libs/apprise/plugins/NotifyPushSafer.py index 7bdca7a69..32cfa82fe 100644 --- a/libs/apprise/plugins/pushsafer.py +++ b/libs/apprise/plugins/NotifyPushSafer.py @@ -30,11 +30,11 @@ import base64 import requests from json import loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class PushSaferSound: diff --git a/libs/apprise/plugins/pushed.py b/libs/apprise/plugins/NotifyPushed.py similarity index 99% rename from libs/apprise/plugins/pushed.py rename to libs/apprise/plugins/NotifyPushed.py index 1ed83b9e4..a50970f99 100644 --- a/libs/apprise/plugins/pushed.py +++ b/libs/apprise/plugins/NotifyPushed.py @@ -31,12 +31,12 @@ import requests from json import dumps from itertools import chain -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Used to detect and parse channels IS_CHANNEL = re.compile(r'^#?(?P[A-Za-z0-9]+)$') diff --git a/libs/apprise/plugins/pushjet.py b/libs/apprise/plugins/NotifyPushjet.py similarity index 98% rename from libs/apprise/plugins/pushjet.py rename to libs/apprise/plugins/NotifyPushjet.py index f8dcfdf31..253ac6818 100644 --- a/libs/apprise/plugins/pushjet.py +++ b/libs/apprise/plugins/NotifyPushjet.py @@ -29,11 +29,11 @@ import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyPushjet(NotifyBase): diff --git a/libs/apprise/plugins/pushover.py b/libs/apprise/plugins/NotifyPushover.py similarity index 99% rename from libs/apprise/plugins/pushover.py rename to libs/apprise/plugins/NotifyPushover.py index 954e7dd0f..be6ada289 100644 --- a/libs/apprise/plugins/pushover.py +++ b/libs/apprise/plugins/NotifyPushover.py @@ -30,14 +30,14 @@ import re import requests from itertools import chain -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..conversion import convert_between from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase # Flag used as a placeholder to sending to all devices PUSHOVER_SEND_TO_ALL = 'ALL_DEVICES' diff --git a/libs/apprise/plugins/pushy.py b/libs/apprise/plugins/NotifyPushy.py similarity index 99% rename from libs/apprise/plugins/pushy.py rename to libs/apprise/plugins/NotifyPushy.py index bb2a24ecd..097017dac 100644 --- a/libs/apprise/plugins/pushy.py +++ b/libs/apprise/plugins/NotifyPushy.py @@ -32,11 +32,11 @@ import requests from itertools import chain from json import dumps, loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Used to detect a Device and Topic VALIDATE_DEVICE = re.compile(r'^@(?P[a-z0-9]+)$', re.I) diff --git a/libs/apprise/plugins/rsyslog.py b/libs/apprise/plugins/NotifyRSyslog.py similarity index 99% rename from libs/apprise/plugins/rsyslog.py rename to libs/apprise/plugins/NotifyRSyslog.py index 9631c72fd..e39744627 100644 --- a/libs/apprise/plugins/rsyslog.py +++ b/libs/apprise/plugins/NotifyRSyslog.py @@ -29,10 +29,10 @@ import os import socket -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class syslog: diff --git a/libs/apprise/plugins/reddit.py b/libs/apprise/plugins/NotifyReddit.py similarity index 99% rename from libs/apprise/plugins/reddit.py rename to libs/apprise/plugins/NotifyReddit.py index 3a60b5e03..022a0a50d 100644 --- a/libs/apprise/plugins/reddit.py +++ b/libs/apprise/plugins/NotifyReddit.py @@ -53,14 +53,14 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from .. import __title__, __version__ # Extend HTTP Error Messages diff --git a/libs/apprise/plugins/revolt.py b/libs/apprise/plugins/NotifyRevolt.py similarity index 99% rename from libs/apprise/plugins/revolt.py rename to libs/apprise/plugins/NotifyRevolt.py index 1f518540b..ae0a43b10 100644 --- a/libs/apprise/plugins/revolt.py +++ b/libs/apprise/plugins/NotifyRevolt.py @@ -42,13 +42,13 @@ from datetime import timedelta from datetime import datetime from datetime import timezone -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import validate_regex from ..utils import parse_list -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyRevolt(NotifyBase): diff --git a/libs/apprise/plugins/rocketchat.py b/libs/apprise/plugins/NotifyRocketChat.py similarity index 99% rename from libs/apprise/plugins/rocketchat.py rename to libs/apprise/plugins/NotifyRocketChat.py index 973651e30..9011a5e71 100644 --- a/libs/apprise/plugins/rocketchat.py +++ b/libs/apprise/plugins/NotifyRocketChat.py @@ -32,14 +32,14 @@ from json import loads from json import dumps from itertools import chain -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ IS_CHANNEL = re.compile(r'^#(?P[A-Za-z0-9_-]+)$') IS_USER = re.compile(r'^@(?P[A-Za-z0-9._-]+)$') diff --git a/libs/apprise/plugins/ryver.py b/libs/apprise/plugins/NotifyRyver.py similarity index 99% rename from libs/apprise/plugins/ryver.py rename to libs/apprise/plugins/NotifyRyver.py index 114dc6a0c..0872f3e52 100644 --- a/libs/apprise/plugins/ryver.py +++ b/libs/apprise/plugins/NotifyRyver.py @@ -38,12 +38,12 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class RyverWebhookMode: diff --git a/libs/apprise/plugins/ses.py b/libs/apprise/plugins/NotifySES.py similarity index 99% rename from libs/apprise/plugins/ses.py rename to libs/apprise/plugins/NotifySES.py index 5a2c047a7..b580b14d6 100644 --- a/libs/apprise/plugins/ses.py +++ b/libs/apprise/plugins/NotifySES.py @@ -95,13 +95,13 @@ from email.utils import formataddr from email.header import Header from urllib.parse import quote -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_emails from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from ..utils import is_email # Our Regin Identifier diff --git a/libs/apprise/plugins/smseagle.py b/libs/apprise/plugins/NotifySMSEagle.py similarity index 99% rename from libs/apprise/plugins/smseagle.py rename to libs/apprise/plugins/NotifySMSEagle.py index 8eddca588..33b4af752 100644 --- a/libs/apprise/plugins/smseagle.py +++ b/libs/apprise/plugins/NotifySMSEagle.py @@ -32,14 +32,14 @@ from json import dumps, loads import base64 from itertools import chain -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..url import PrivacyMode -from ..locale import gettext_lazy as _ +from ..URLBase import PrivacyMode +from ..AppriseLocale import gettext_lazy as _ GROUP_REGEX = re.compile( diff --git a/libs/apprise/plugins/smsmanager.py b/libs/apprise/plugins/NotifySMSManager.py similarity index 99% rename from libs/apprise/plugins/smsmanager.py rename to libs/apprise/plugins/NotifySMSManager.py index 1d352daf2..efc158b62 100644 --- a/libs/apprise/plugins/smsmanager.py +++ b/libs/apprise/plugins/NotifySMSManager.py @@ -35,13 +35,13 @@ # 2. Generate an API key in web administration. import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class SMSManagerGateway(object): diff --git a/libs/apprise/plugins/smtp2go.py b/libs/apprise/plugins/NotifySMTP2Go.py similarity index 99% rename from libs/apprise/plugins/smtp2go.py rename to libs/apprise/plugins/NotifySMTP2Go.py index 017da8111..a34492d05 100644 --- a/libs/apprise/plugins/smtp2go.py +++ b/libs/apprise/plugins/NotifySMTP2Go.py @@ -49,14 +49,14 @@ import base64 import requests from json import dumps from email.utils import formataddr -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import parse_emails from ..utils import parse_bool from ..utils import is_email from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ SMTP2GO_HTTP_ERROR_MAP = { 429: 'To many requests.', diff --git a/libs/apprise/plugins/sns.py b/libs/apprise/plugins/NotifySNS.py similarity index 99% rename from libs/apprise/plugins/sns.py rename to libs/apprise/plugins/NotifySNS.py index cc6e83076..5a287e37e 100644 --- a/libs/apprise/plugins/sns.py +++ b/libs/apprise/plugins/NotifySNS.py @@ -36,13 +36,13 @@ from collections import OrderedDict from xml.etree import ElementTree from itertools import chain -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Topic Detection # Summary: 256 Characters max, only alpha/numeric plus underscore (_) and diff --git a/libs/apprise/plugins/sendgrid.py b/libs/apprise/plugins/NotifySendGrid.py similarity index 99% rename from libs/apprise/plugins/sendgrid.py rename to libs/apprise/plugins/NotifySendGrid.py index d50839f17..b82e3e60d 100644 --- a/libs/apprise/plugins/sendgrid.py +++ b/libs/apprise/plugins/NotifySendGrid.py @@ -49,13 +49,13 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import is_email from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages SENDGRID_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/serverchan.py b/libs/apprise/plugins/NotifyServerChan.py similarity index 98% rename from libs/apprise/plugins/serverchan.py rename to libs/apprise/plugins/NotifyServerChan.py index e3abaa354..cf250cf5b 100644 --- a/libs/apprise/plugins/serverchan.py +++ b/libs/apprise/plugins/NotifyServerChan.py @@ -30,9 +30,9 @@ import re import requests from ..common import NotifyType -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Register at https://sct.ftqq.com/ diff --git a/libs/apprise/plugins/signal_api.py b/libs/apprise/plugins/NotifySignalAPI.py similarity index 99% rename from libs/apprise/plugins/signal_api.py rename to libs/apprise/plugins/NotifySignalAPI.py index 7e557133c..b35b4989e 100644 --- a/libs/apprise/plugins/signal_api.py +++ b/libs/apprise/plugins/NotifySignalAPI.py @@ -31,13 +31,13 @@ import requests from json import dumps import base64 -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import parse_bool -from ..url import PrivacyMode -from ..locale import gettext_lazy as _ +from ..URLBase import PrivacyMode +from ..AppriseLocale import gettext_lazy as _ GROUP_REGEX = re.compile( diff --git a/libs/apprise/plugins/simplepush.py b/libs/apprise/plugins/NotifySimplePush.py similarity index 98% rename from libs/apprise/plugins/simplepush.py rename to libs/apprise/plugins/NotifySimplePush.py index 10b01b0f8..3851e1e3c 100644 --- a/libs/apprise/plugins/simplepush.py +++ b/libs/apprise/plugins/NotifySimplePush.py @@ -30,11 +30,11 @@ from os import urandom from json import loads import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ from base64 import urlsafe_b64encode import hashlib diff --git a/libs/apprise/plugins/sinch.py b/libs/apprise/plugins/NotifySinch.py similarity index 99% rename from libs/apprise/plugins/sinch.py rename to libs/apprise/plugins/NotifySinch.py index 06bd5b1e7..74b3c452a 100644 --- a/libs/apprise/plugins/sinch.py +++ b/libs/apprise/plugins/NotifySinch.py @@ -39,13 +39,13 @@ import requests import json -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class SinchRegion: diff --git a/libs/apprise/plugins/slack.py b/libs/apprise/plugins/NotifySlack.py similarity index 99% rename from libs/apprise/plugins/slack.py rename to libs/apprise/plugins/NotifySlack.py index b929dfb46..b66fe99f4 100644 --- a/libs/apprise/plugins/slack.py +++ b/libs/apprise/plugins/NotifySlack.py @@ -78,7 +78,7 @@ from json import dumps from json import loads from time import time -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..common import NotifyFormat @@ -86,7 +86,7 @@ from ..utils import is_email from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages SLACK_HTTP_ERROR_MAP = { diff --git a/libs/apprise/plugins/sparkpost.py b/libs/apprise/plugins/NotifySparkPost.py similarity index 99% rename from libs/apprise/plugins/sparkpost.py rename to libs/apprise/plugins/NotifySparkPost.py index b873d6b0e..255db0709 100644 --- a/libs/apprise/plugins/sparkpost.py +++ b/libs/apprise/plugins/NotifySparkPost.py @@ -58,7 +58,7 @@ import requests import base64 from json import loads from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import is_email @@ -66,7 +66,7 @@ from email.utils import formataddr from ..utils import validate_regex from ..utils import parse_emails from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Provide some known codes SparkPost uses and what they translate to: # Based on https://www.sparkpost.com/docs/tech-resources/extended-error-codes/ diff --git a/libs/apprise/plugins/streamlabs.py b/libs/apprise/plugins/NotifyStreamlabs.py similarity index 99% rename from libs/apprise/plugins/streamlabs.py rename to libs/apprise/plugins/NotifyStreamlabs.py index c534f1ba6..d1e4186a6 100644 --- a/libs/apprise/plugins/streamlabs.py +++ b/libs/apprise/plugins/NotifyStreamlabs.py @@ -38,10 +38,10 @@ # import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # calls diff --git a/libs/apprise/plugins/synology.py b/libs/apprise/plugins/NotifySynology.py similarity index 98% rename from libs/apprise/plugins/synology.py rename to libs/apprise/plugins/NotifySynology.py index ed85f80c0..be58c0643 100644 --- a/libs/apprise/plugins/synology.py +++ b/libs/apprise/plugins/NotifySynology.py @@ -29,10 +29,10 @@ import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # For API Details see: # https://kb.synology.com/en-au/DSM/help/Chat/chat_integration diff --git a/libs/apprise/plugins/syslog.py b/libs/apprise/plugins/NotifySyslog.py similarity index 99% rename from libs/apprise/plugins/syslog.py rename to libs/apprise/plugins/NotifySyslog.py index 935111eea..5540fc758 100644 --- a/libs/apprise/plugins/syslog.py +++ b/libs/apprise/plugins/NotifySyslog.py @@ -28,10 +28,10 @@ import syslog -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class SyslogFacility: diff --git a/libs/apprise/plugins/techuluspush.py b/libs/apprise/plugins/NotifyTechulusPush.py similarity index 98% rename from libs/apprise/plugins/techuluspush.py rename to libs/apprise/plugins/NotifyTechulusPush.py index 682bf088e..4d0b99348 100644 --- a/libs/apprise/plugins/techuluspush.py +++ b/libs/apprise/plugins/NotifyTechulusPush.py @@ -53,10 +53,10 @@ import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Token required as part of the API request # Used to prepare our UUID regex matching diff --git a/libs/apprise/plugins/telegram.py b/libs/apprise/plugins/NotifyTelegram.py similarity index 99% rename from libs/apprise/plugins/telegram.py rename to libs/apprise/plugins/NotifyTelegram.py index 6b997f574..cce8af625 100644 --- a/libs/apprise/plugins/telegram.py +++ b/libs/apprise/plugins/NotifyTelegram.py @@ -59,15 +59,15 @@ import os from json import loads from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyImageSize from ..common import NotifyFormat from ..utils import parse_bool from ..utils import parse_list from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase TELEGRAM_IMAGE_XY = NotifyImageSize.XY_256 diff --git a/libs/apprise/plugins/threema.py b/libs/apprise/plugins/NotifyThreema.py similarity index 99% rename from libs/apprise/plugins/threema.py rename to libs/apprise/plugins/NotifyThreema.py index 423c23124..c2ad82e2e 100644 --- a/libs/apprise/plugins/threema.py +++ b/libs/apprise/plugins/NotifyThreema.py @@ -35,14 +35,14 @@ import requests from itertools import chain -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import validate_regex from ..utils import is_email -from ..url import PrivacyMode +from ..URLBase import PrivacyMode from ..utils import parse_list -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class ThreemaRecipientTypes: diff --git a/libs/apprise/plugins/twilio.py b/libs/apprise/plugins/NotifyTwilio.py similarity index 99% rename from libs/apprise/plugins/twilio.py rename to libs/apprise/plugins/NotifyTwilio.py index 4205e37f0..863b09a94 100644 --- a/libs/apprise/plugins/twilio.py +++ b/libs/apprise/plugins/NotifyTwilio.py @@ -46,13 +46,13 @@ import requests from json import loads -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyTwilio(NotifyBase): diff --git a/libs/apprise/plugins/twist.py b/libs/apprise/plugins/NotifyTwist.py similarity index 99% rename from libs/apprise/plugins/twist.py rename to libs/apprise/plugins/NotifyTwist.py index 62d729f4f..fa26feb84 100644 --- a/libs/apprise/plugins/twist.py +++ b/libs/apprise/plugins/NotifyTwist.py @@ -35,13 +35,13 @@ import requests from json import loads from itertools import chain -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyFormat from ..common import NotifyType from ..utils import parse_list from ..utils import is_email -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # A workspace can also be interpreted as a team name too! diff --git a/libs/apprise/plugins/twitter.py b/libs/apprise/plugins/NotifyTwitter.py similarity index 99% rename from libs/apprise/plugins/twitter.py rename to libs/apprise/plugins/NotifyTwitter.py index 8000a8151..7a0813c1f 100644 --- a/libs/apprise/plugins/twitter.py +++ b/libs/apprise/plugins/NotifyTwitter.py @@ -36,14 +36,14 @@ from datetime import timezone from requests_oauthlib import OAuth1 from json import dumps from json import loads -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import parse_list from ..utils import parse_bool from ..utils import validate_regex -from ..locale import gettext_lazy as _ -from ..attachment.base import AttachBase +from ..AppriseLocale import gettext_lazy as _ +from ..attachment.AttachBase import AttachBase IS_USER = re.compile(r'^\s*@?(?P[A-Z0-9_]+)$', re.I) diff --git a/libs/apprise/plugins/voipms.py b/libs/apprise/plugins/NotifyVoipms.py similarity index 99% rename from libs/apprise/plugins/voipms.py rename to libs/apprise/plugins/NotifyVoipms.py index 3a4e6d25f..a023589a7 100644 --- a/libs/apprise/plugins/voipms.py +++ b/libs/apprise/plugins/NotifyVoipms.py @@ -37,12 +37,12 @@ import requests from json import loads -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import is_email from ..utils import parse_phone_no -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyVoipms(NotifyBase): diff --git a/libs/apprise/plugins/vonage.py b/libs/apprise/plugins/NotifyVonage.py similarity index 99% rename from libs/apprise/plugins/vonage.py rename to libs/apprise/plugins/NotifyVonage.py index 441a6ba6b..e9b1422ad 100644 --- a/libs/apprise/plugins/vonage.py +++ b/libs/apprise/plugins/NotifyVonage.py @@ -33,13 +33,13 @@ # import requests -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyVonage(NotifyBase): diff --git a/libs/apprise/plugins/wecombot.py b/libs/apprise/plugins/NotifyWeComBot.py similarity index 99% rename from libs/apprise/plugins/wecombot.py rename to libs/apprise/plugins/NotifyWeComBot.py index ab6341718..4289b39e9 100644 --- a/libs/apprise/plugins/wecombot.py +++ b/libs/apprise/plugins/NotifyWeComBot.py @@ -59,10 +59,10 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyWeComBot(NotifyBase): diff --git a/libs/apprise/plugins/webexteams.py b/libs/apprise/plugins/NotifyWebexTeams.py similarity index 99% rename from libs/apprise/plugins/webexteams.py rename to libs/apprise/plugins/NotifyWebexTeams.py index bd0bdb577..c91864bad 100644 --- a/libs/apprise/plugins/webexteams.py +++ b/libs/apprise/plugins/NotifyWebexTeams.py @@ -63,11 +63,11 @@ import re import requests from json import dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..common import NotifyFormat from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Extend HTTP Error Messages # Based on: https://developer.webex.com/docs/api/basics/rate-limiting diff --git a/libs/apprise/plugins/whatsapp.py b/libs/apprise/plugins/NotifyWhatsApp.py similarity index 99% rename from libs/apprise/plugins/whatsapp.py rename to libs/apprise/plugins/NotifyWhatsApp.py index 7120d736c..4ccbcbdaf 100644 --- a/libs/apprise/plugins/whatsapp.py +++ b/libs/apprise/plugins/NotifyWhatsApp.py @@ -44,12 +44,12 @@ import re import requests from json import loads, dumps -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import is_phone_no from ..utils import parse_phone_no from ..utils import validate_regex -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyWhatsApp(NotifyBase): diff --git a/libs/apprise/plugins/windows.py b/libs/apprise/plugins/NotifyWindows.py similarity index 99% rename from libs/apprise/plugins/windows.py rename to libs/apprise/plugins/NotifyWindows.py index 746fcd1dd..207e0f221 100644 --- a/libs/apprise/plugins/windows.py +++ b/libs/apprise/plugins/NotifyWindows.py @@ -28,11 +28,11 @@ from time import sleep -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NotifyType from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # Default our global support flag NOTIFY_WINDOWS_SUPPORT_ENABLED = False diff --git a/libs/apprise/plugins/xbmc.py b/libs/apprise/plugins/NotifyXBMC.py similarity index 99% rename from libs/apprise/plugins/xbmc.py rename to libs/apprise/plugins/NotifyXBMC.py index 8006e1005..7d4462e41 100644 --- a/libs/apprise/plugins/xbmc.py +++ b/libs/apprise/plugins/NotifyXBMC.py @@ -29,12 +29,12 @@ import requests from json import dumps -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyType from ..common import NotifyImageSize from ..utils import parse_bool -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class NotifyXBMC(NotifyBase): diff --git a/libs/apprise/plugins/custom_xml.py b/libs/apprise/plugins/NotifyXML.py similarity index 99% rename from libs/apprise/plugins/custom_xml.py rename to libs/apprise/plugins/NotifyXML.py index b7928fceb..21ccb79d3 100644 --- a/libs/apprise/plugins/custom_xml.py +++ b/libs/apprise/plugins/NotifyXML.py @@ -30,11 +30,11 @@ import re import requests import base64 -from .base import NotifyBase -from ..url import PrivacyMode +from .NotifyBase import NotifyBase +from ..URLBase import PrivacyMode from ..common import NotifyImageSize from ..common import NotifyType -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ class XMLPayloadField: diff --git a/libs/apprise/plugins/zulip.py b/libs/apprise/plugins/NotifyZulip.py similarity index 99% rename from libs/apprise/plugins/zulip.py rename to libs/apprise/plugins/NotifyZulip.py index e829e6f6d..54fe2d062 100644 --- a/libs/apprise/plugins/zulip.py +++ b/libs/apprise/plugins/NotifyZulip.py @@ -61,13 +61,13 @@ import re import requests -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyType from ..utils import parse_list from ..utils import validate_regex from ..utils import is_email from ..utils import remove_suffix -from ..locale import gettext_lazy as _ +from ..AppriseLocale import gettext_lazy as _ # A Valid Bot Name VALIDATE_BOTNAME = re.compile(r'(?P[A-Z0-9_-]{1,32})', re.I) diff --git a/libs/apprise/plugins/__init__.py b/libs/apprise/plugins/__init__.py index bfce14371..72cb08fbf 100644 --- a/libs/apprise/plugins/__init__.py +++ b/libs/apprise/plugins/__init__.py @@ -30,7 +30,7 @@ import os import copy # Used for testing -from .base import NotifyBase +from .NotifyBase import NotifyBase from ..common import NotifyImageSize from ..common import NOTIFY_IMAGE_SIZES @@ -40,9 +40,9 @@ from ..utils import parse_list from ..utils import cwe312_url from ..utils import GET_SCHEMA_RE from ..logger import logger -from ..locale import gettext_lazy as _ -from ..locale import LazyTranslation -from ..manager_plugins import NotificationManager +from ..AppriseLocale import gettext_lazy as _ +from ..AppriseLocale import LazyTranslation +from ..NotificationManager import NotificationManager # Grant access to our Notification Manager Singleton diff --git a/libs/apprise/utils.py b/libs/apprise/utils.py index b33ec0749..e1881f314 100644 --- a/libs/apprise/utils.py +++ b/libs/apprise/utils.py @@ -541,7 +541,7 @@ def tidy_path(path): return path -def parse_qsd(qs, simple=False, plus_to_space=False, sanitize=True): +def parse_qsd(qs, simple=False, plus_to_space=False): """ Query String Dictionary Builder @@ -568,8 +568,6 @@ def parse_qsd(qs, simple=False, plus_to_space=False, sanitize=True): per normal URL Encoded defininition. Normal URL parsing applies this, but `+` is very actively used character with passwords, api keys, tokens, etc. So Apprise does not do this by default. - - if sanitize is set to False, then kwargs are not placed into lowercase """ # Our return result set: @@ -610,7 +608,7 @@ def parse_qsd(qs, simple=False, plus_to_space=False, sanitize=True): # Always Query String Dictionary (qsd) for every entry we have # content is always made lowercase for easy indexing - result['qsd'][key.lower().strip() if sanitize else key] = val + result['qsd'][key.lower().strip()] = val if simple: # move along @@ -638,7 +636,7 @@ def parse_qsd(qs, simple=False, plus_to_space=False, sanitize=True): def parse_url(url, default_schema='http', verify_host=True, strict_port=False, - simple=False, plus_to_space=False, sanitize=True): + simple=False, plus_to_space=False): """A function that greatly simplifies the parsing of a url specified by the end user. @@ -693,8 +691,6 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False, If the URL can't be parsed then None is returned - If sanitize is set to False, then kwargs are not placed in lowercase - and wrapping whitespace is not removed """ if not isinstance(url, str): @@ -754,8 +750,7 @@ def parse_url(url, default_schema='http', verify_host=True, strict_port=False, # while ensuring that all keys are lowercase if qsdata: result.update(parse_qsd( - qsdata, simple=simple, plus_to_space=plus_to_space, - sanitize=sanitize)) + qsdata, simple=simple, plus_to_space=plus_to_space)) # Now do a proper extraction of data; http:// is just substitued in place # to allow urlparse() to function as expected, we'll swap this back to the diff --git a/libs/version.txt b/libs/version.txt index 8ac12d74d..ccabf65cc 100644 --- a/libs/version.txt +++ b/libs/version.txt @@ -2,7 +2,7 @@ alembic==1.13.1 aniso8601==9.0.1 argparse==1.4.0 -apprise==1.8.0 +apprise==1.7.6 apscheduler<=3.10.4 attrs==23.2.0 blinker==1.7.0 From 0abf56191c730309c4fb5d0cbd0f4f666b0adc70 Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 25 May 2024 00:18:49 -0400 Subject: [PATCH 069/129] no log: Delete libs/apprise/apprise.py --- libs/apprise/apprise.py | 887 ---------------------------------------- 1 file changed, 887 deletions(-) delete mode 100644 libs/apprise/apprise.py diff --git a/libs/apprise/apprise.py b/libs/apprise/apprise.py deleted file mode 100644 index 05a2ee3cc..000000000 --- a/libs/apprise/apprise.py +++ /dev/null @@ -1,887 +0,0 @@ -# -*- coding: utf-8 -*- -# BSD 2-Clause License -# -# Apprise - Push Notification Library. -# Copyright (c) 2024, Chris Caron -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import asyncio -import concurrent.futures as cf -import os -from itertools import chain -from . import common -from .conversion import convert_between -from .utils import is_exclusive_match -from .manager_plugins import NotificationManager -from .utils import parse_list -from .utils import parse_urls -from .utils import cwe312_url -from .emojis import apply_emojis -from .logger import logger -from .asset import AppriseAsset -from .apprise_config import AppriseConfig -from .apprise_attachment import AppriseAttachment -from .locale import AppriseLocale -from .config.base import ConfigBase -from .plugins.base import NotifyBase - -from . import plugins -from . import __version__ - -# Grant access to our Notification Manager Singleton -N_MGR = NotificationManager() - - -class Apprise: - """ - Our Notification Manager - - """ - - def __init__(self, servers=None, asset=None, location=None, debug=False): - """ - Loads a set of server urls while applying the Asset() module to each - if specified. - - If no asset is provided, then the default asset is used. - - Optionally specify a global ContentLocation for a more strict means - of handling Attachments. - """ - - # Initialize a server list of URLs - self.servers = list() - - # Assigns an central asset object that will be later passed into each - # notification plugin. Assets contain information such as the local - # directory images can be found in. It can also identify remote - # URL paths that contain the images you want to present to the end - # user. If no asset is specified, then the default one is used. - self.asset = \ - asset if isinstance(asset, AppriseAsset) else AppriseAsset() - - if servers: - self.add(servers) - - # Initialize our locale object - self.locale = AppriseLocale() - - # Set our debug flag - self.debug = debug - - # Store our hosting location for optional strict rule handling - # of Attachments. Setting this to None removes any attachment - # restrictions. - self.location = location - - @staticmethod - def instantiate(url, asset=None, tag=None, suppress_exceptions=True): - """ - Returns the instance of a instantiated plugin based on the provided - Server URL. If the url fails to be parsed, then None is returned. - - The specified url can be either a string (the URL itself) or a - dictionary containing all of the components needed to istantiate - the notification service. If identifying a dictionary, at the bare - minimum, one must specify the schema. - - An example of a url dictionary object might look like: - { - schema: 'mailto', - host: 'google.com', - user: 'myuser', - password: 'mypassword', - } - - Alternatively the string is much easier to specify: - mailto://user:mypassword@google.com - - The dictionary works well for people who are calling details() to - extract the components they need to build the URL manually. - """ - - # Initialize our result set - results = None - - # Prepare our Asset Object - asset = asset if isinstance(asset, AppriseAsset) else AppriseAsset() - - if isinstance(url, str): - # Acquire our url tokens - results = plugins.url_to_dict( - url, secure_logging=asset.secure_logging) - - if results is None: - # Failed to parse the server URL; detailed logging handled - # inside url_to_dict - nothing to report here. - return None - - elif isinstance(url, dict): - # We already have our result set - results = url - - if results.get('schema') not in N_MGR: - # schema is a mandatory dictionary item as it is the only way - # we can index into our loaded plugins - logger.error('Dictionary does not include a "schema" entry.') - logger.trace( - 'Invalid dictionary unpacked as:{}{}'.format( - os.linesep, os.linesep.join( - ['{}="{}"'.format(k, v) - for k, v in results.items()]))) - return None - - logger.trace( - 'Dictionary unpacked as:{}{}'.format( - os.linesep, os.linesep.join( - ['{}="{}"'.format(k, v) for k, v in results.items()]))) - - # Otherwise we handle the invalid input specified - else: - logger.error( - 'An invalid URL type (%s) was specified for instantiation', - type(url)) - return None - - if not N_MGR[results['schema']].enabled: - # - # First Plugin Enable Check (Pre Initialization) - # - - # Plugin has been disabled at a global level - logger.error( - '%s:// is disabled on this system.', results['schema']) - return None - - # Build a list of tags to associate with the newly added notifications - results['tag'] = set(parse_list(tag)) - - # Set our Asset Object - results['asset'] = asset - - if suppress_exceptions: - try: - # Attempt to create an instance of our plugin using the parsed - # URL information - plugin = N_MGR[results['schema']](**results) - - # Create log entry of loaded URL - logger.debug( - 'Loaded {} URL: {}'.format( - N_MGR[results['schema']].service_name, - plugin.url(privacy=asset.secure_logging))) - - except Exception: - # CWE-312 (Secure Logging) Handling - loggable_url = url if not asset.secure_logging \ - else cwe312_url(url) - - # the arguments are invalid or can not be used. - logger.error( - 'Could not load {} URL: {}'.format( - N_MGR[results['schema']].service_name, - loggable_url)) - return None - - else: - # Attempt to create an instance of our plugin using the parsed - # URL information but don't wrap it in a try catch - plugin = N_MGR[results['schema']](**results) - - if not plugin.enabled: - # - # Second Plugin Enable Check (Post Initialization) - # - - # Service/Plugin is disabled (on a more local level). This is a - # case where the plugin was initially enabled but then after the - # __init__() was called under the hood something pre-determined - # that it could no longer be used. - - # The only downside to doing it this way is services are - # initialized prior to returning the details() if 3rd party tools - # are polling what is available. These services that become - # disabled thereafter are shown initially that they can be used. - logger.error( - '%s:// has become disabled on this system.', results['schema']) - return None - - return plugin - - def add(self, servers, asset=None, tag=None): - """ - Adds one or more server URLs into our list. - - You can override the global asset if you wish by including it with the - server(s) that you add. - - The tag allows you to associate 1 or more tag values to the server(s) - being added. tagging a service allows you to exclusively access them - when calling the notify() function. - """ - - # Initialize our return status - return_status = True - - if asset is None: - # prepare default asset - asset = self.asset - - if isinstance(servers, str): - # build our server list - servers = parse_urls(servers) - if len(servers) == 0: - return False - - elif isinstance(servers, dict): - # no problem, we support kwargs, convert it to a list - servers = [servers] - - elif isinstance(servers, (ConfigBase, NotifyBase, AppriseConfig)): - # Go ahead and just add our plugin into our list - self.servers.append(servers) - return True - - elif not isinstance(servers, (tuple, set, list)): - logger.error( - "An invalid notification (type={}) was specified.".format( - type(servers))) - return False - - for _server in servers: - - if isinstance(_server, (ConfigBase, NotifyBase, AppriseConfig)): - # Go ahead and just add our plugin into our list - self.servers.append(_server) - continue - - elif not isinstance(_server, (str, dict)): - logger.error( - "An invalid notification (type={}) was specified.".format( - type(_server))) - return_status = False - continue - - # Instantiate ourselves an object, this function throws or - # returns None if it fails - instance = Apprise.instantiate(_server, asset=asset, tag=tag) - if not isinstance(instance, NotifyBase): - # No logging is required as instantiate() handles failure - # and/or success reasons for us - return_status = False - continue - - # Add our initialized plugin to our server listings - self.servers.append(instance) - - # Return our status - return return_status - - def clear(self): - """ - Empties our server list - - """ - self.servers[:] = [] - - def find(self, tag=common.MATCH_ALL_TAG, match_always=True): - """ - Returns a list of all servers matching against the tag specified. - - """ - - # Build our tag setup - # - top level entries are treated as an 'or' - # - second level (or more) entries are treated as 'and' - # - # examples: - # tag="tagA, tagB" = tagA or tagB - # tag=['tagA', 'tagB'] = tagA or tagB - # tag=[('tagA', 'tagC'), 'tagB'] = (tagA and tagC) or tagB - # tag=[('tagB', 'tagC')] = tagB and tagC - - # A match_always flag allows us to pick up on our 'any' keyword - # and notify these services under all circumstances - match_always = common.MATCH_ALWAYS_TAG if match_always else None - - # Iterate over our loaded plugins - for entry in self.servers: - - if isinstance(entry, (ConfigBase, AppriseConfig)): - # load our servers - servers = entry.servers() - - else: - servers = [entry, ] - - for server in servers: - # Apply our tag matching based on our defined logic - if is_exclusive_match( - logic=tag, data=server.tags, - match_all=common.MATCH_ALL_TAG, - match_always=match_always): - yield server - return - - def notify(self, body, title='', notify_type=common.NotifyType.INFO, - body_format=None, tag=common.MATCH_ALL_TAG, match_always=True, - attach=None, interpret_escapes=None): - """ - Send a notification to all the plugins previously loaded. - - If the body_format specified is NotifyFormat.MARKDOWN, it will - be converted to HTML if the Notification type expects this. - - if the tag is specified (either a string or a set/list/tuple - of strings), then only the notifications flagged with that - tagged value are notified. By default, all added services - are notified (tag=MATCH_ALL_TAG) - - This function returns True if all notifications were successfully - sent, False if even just one of them fails, and None if no - notifications were sent at all as a result of tag filtering and/or - simply having empty configuration files that were read. - - Attach can contain a list of attachment URLs. attach can also be - represented by an AttachBase() (or list of) object(s). This - identifies the products you wish to notify - - Set interpret_escapes to True if you want to pre-escape a string - such as turning a \n into an actual new line, etc. - """ - - try: - # Process arguments and build synchronous and asynchronous calls - # (this step can throw internal errors). - sequential_calls, parallel_calls = self._create_notify_calls( - body, title, - notify_type=notify_type, body_format=body_format, - tag=tag, match_always=match_always, attach=attach, - interpret_escapes=interpret_escapes, - ) - - except TypeError: - # No notifications sent, and there was an internal error. - return False - - if not sequential_calls and not parallel_calls: - # Nothing to send - return None - - sequential_result = Apprise._notify_sequential(*sequential_calls) - parallel_result = Apprise._notify_parallel_threadpool(*parallel_calls) - return sequential_result and parallel_result - - async def async_notify(self, *args, **kwargs): - """ - Send a notification to all the plugins previously loaded, for - asynchronous callers. - - The arguments are identical to those of Apprise.notify(). - - """ - try: - # Process arguments and build synchronous and asynchronous calls - # (this step can throw internal errors). - sequential_calls, parallel_calls = self._create_notify_calls( - *args, **kwargs) - - except TypeError: - # No notifications sent, and there was an internal error. - return False - - if not sequential_calls and not parallel_calls: - # Nothing to send - return None - - sequential_result = Apprise._notify_sequential(*sequential_calls) - parallel_result = \ - await Apprise._notify_parallel_asyncio(*parallel_calls) - return sequential_result and parallel_result - - def _create_notify_calls(self, *args, **kwargs): - """ - Creates notifications for all the plugins loaded. - - Returns a list of (server, notify() kwargs) tuples for plugins with - parallelism disabled and another list for plugins with parallelism - enabled. - """ - - all_calls = list(self._create_notify_gen(*args, **kwargs)) - - # Split into sequential and parallel notify() calls. - sequential, parallel = [], [] - for (server, notify_kwargs) in all_calls: - if server.asset.async_mode: - parallel.append((server, notify_kwargs)) - else: - sequential.append((server, notify_kwargs)) - - return sequential, parallel - - def _create_notify_gen(self, body, title='', - notify_type=common.NotifyType.INFO, - body_format=None, tag=common.MATCH_ALL_TAG, - match_always=True, attach=None, - interpret_escapes=None): - """ - Internal generator function for _create_notify_calls(). - """ - - if len(self) == 0: - # Nothing to notify - msg = "There are no service(s) to notify" - logger.error(msg) - raise TypeError(msg) - - if not (title or body or attach): - msg = "No message content specified to deliver" - logger.error(msg) - raise TypeError(msg) - - try: - if title and isinstance(title, bytes): - title = title.decode(self.asset.encoding) - - if body and isinstance(body, bytes): - body = body.decode(self.asset.encoding) - - except UnicodeDecodeError: - msg = 'The content passed into Apprise was not of encoding ' \ - 'type: {}'.format(self.asset.encoding) - logger.error(msg) - raise TypeError(msg) - - # Tracks conversions - conversion_body_map = dict() - conversion_title_map = dict() - - # Prepare attachments if required - if attach is not None and not isinstance(attach, AppriseAttachment): - attach = AppriseAttachment( - attach, asset=self.asset, location=self.location) - - # Allow Asset default value - body_format = self.asset.body_format \ - if body_format is None else body_format - - # Allow Asset default value - interpret_escapes = self.asset.interpret_escapes \ - if interpret_escapes is None else interpret_escapes - - # Iterate over our loaded plugins - for server in self.find(tag, match_always=match_always): - # If our code reaches here, we either did not define a tag (it - # was set to None), or we did define a tag and the logic above - # determined we need to notify the service it's associated with - - # First we need to generate a key we will use to determine if we - # need to build our data out. Entries without are merged with - # the body at this stage. - key = server.notify_format if server.title_maxlen > 0\ - else f'_{server.notify_format}' - - if server.interpret_emojis: - # alter our key slightly to handle emojis since their value is - # pulled out of the notification - key += "-emojis" - - if key not in conversion_title_map: - - # Prepare our title - conversion_title_map[key] = '' if not title else title - - # Conversion of title only occurs for services where the title - # is blended with the body (title_maxlen <= 0) - if conversion_title_map[key] and server.title_maxlen <= 0: - conversion_title_map[key] = convert_between( - body_format, server.notify_format, - content=conversion_title_map[key]) - - # Our body is always converted no matter what - conversion_body_map[key] = \ - convert_between( - body_format, server.notify_format, content=body) - - if interpret_escapes: - # - # Escape our content - # - - try: - # Added overhead required due to Python 3 Encoding Bug - # identified here: https://bugs.python.org/issue21331 - conversion_body_map[key] = \ - conversion_body_map[key]\ - .encode('ascii', 'backslashreplace')\ - .decode('unicode-escape') - - conversion_title_map[key] = \ - conversion_title_map[key]\ - .encode('ascii', 'backslashreplace')\ - .decode('unicode-escape') - - except AttributeError: - # Must be of string type - msg = 'Failed to escape message body' - logger.error(msg) - raise TypeError(msg) - - if server.interpret_emojis: - # - # Convert our :emoji: definitions - # - - conversion_body_map[key] = \ - apply_emojis(conversion_body_map[key]) - conversion_title_map[key] = \ - apply_emojis(conversion_title_map[key]) - - kwargs = dict( - body=conversion_body_map[key], - title=conversion_title_map[key], - notify_type=notify_type, - attach=attach, - body_format=body_format - ) - yield (server, kwargs) - - @staticmethod - def _notify_sequential(*servers_kwargs): - """ - Process a list of notify() calls sequentially and synchronously. - """ - - success = True - - for (server, kwargs) in servers_kwargs: - try: - # Send notification - result = server.notify(**kwargs) - success = success and result - - except TypeError: - # These are our internally thrown notifications. - success = False - - except Exception: - # A catch all so we don't have to abort early - # just because one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - success = False - - return success - - @staticmethod - def _notify_parallel_threadpool(*servers_kwargs): - """ - Process a list of notify() calls in parallel and synchronously. - """ - - n_calls = len(servers_kwargs) - - # 0-length case - if n_calls == 0: - return True - - # There's no need to use a thread pool for just a single notification - if n_calls == 1: - return Apprise._notify_sequential(servers_kwargs[0]) - - # Create log entry - logger.info( - 'Notifying %d service(s) with threads.', len(servers_kwargs)) - - with cf.ThreadPoolExecutor() as executor: - success = True - futures = [executor.submit(server.notify, **kwargs) - for (server, kwargs) in servers_kwargs] - - for future in cf.as_completed(futures): - try: - result = future.result() - success = success and result - - except TypeError: - # These are our internally thrown notifications. - success = False - - except Exception: - # A catch all so we don't have to abort early - # just because one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - success = False - - return success - - @staticmethod - async def _notify_parallel_asyncio(*servers_kwargs): - """ - Process a list of async_notify() calls in parallel and asynchronously. - """ - - n_calls = len(servers_kwargs) - - # 0-length case - if n_calls == 0: - return True - - # (Unlike with the thread pool, we don't optimize for the single- - # notification case because asyncio can do useful work while waiting - # for that thread to complete) - - # Create log entry - logger.info( - 'Notifying %d service(s) asynchronously.', len(servers_kwargs)) - - async def do_call(server, kwargs): - return await server.async_notify(**kwargs) - - cors = (do_call(server, kwargs) for (server, kwargs) in servers_kwargs) - results = await asyncio.gather(*cors, return_exceptions=True) - - if any(isinstance(status, Exception) - and not isinstance(status, TypeError) for status in results): - # A catch all so we don't have to abort early just because - # one of our plugins has a bug in it. - logger.exception("Unhandled Notification Exception") - return False - - if any(isinstance(status, TypeError) for status in results): - # These are our internally thrown notifications. - return False - - return all(results) - - def details(self, lang=None, show_requirements=False, show_disabled=False): - """ - Returns the details associated with the Apprise object - - """ - - # general object returned - response = { - # Defines the current version of Apprise - 'version': __version__, - # Lists all of the currently supported Notifications - 'schemas': [], - # Includes the configured asset details - 'asset': self.asset.details(), - } - - for plugin in N_MGR.plugins(): - # Iterate over our hashed plugins and dynamically build details on - # their status: - - content = { - 'service_name': getattr(plugin, 'service_name', None), - 'service_url': getattr(plugin, 'service_url', None), - 'setup_url': getattr(plugin, 'setup_url', None), - # Placeholder - populated below - 'details': None, - - # Let upstream service know of the plugins that support - # attachments - 'attachment_support': getattr( - plugin, 'attachment_support', False), - - # Differentiat between what is a custom loaded plugin and - # which is native. - 'category': getattr(plugin, 'category', None) - } - - # Standard protocol(s) should be None or a tuple - enabled = getattr(plugin, 'enabled', True) - if not show_disabled and not enabled: - # Do not show inactive plugins - continue - - elif show_disabled: - # Add current state to response - content['enabled'] = enabled - - # Standard protocol(s) should be None or a tuple - protocols = getattr(plugin, 'protocol', None) - if isinstance(protocols, str): - protocols = (protocols, ) - - # Secure protocol(s) should be None or a tuple - secure_protocols = getattr(plugin, 'secure_protocol', None) - if isinstance(secure_protocols, str): - secure_protocols = (secure_protocols, ) - - # Add our protocol details to our content - content.update({ - 'protocols': protocols, - 'secure_protocols': secure_protocols, - }) - - if not lang: - # Simply return our results - content['details'] = plugins.details(plugin) - if show_requirements: - content['requirements'] = plugins.requirements(plugin) - - else: - # Emulate the specified language when returning our results - with self.locale.lang_at(lang): - content['details'] = plugins.details(plugin) - if show_requirements: - content['requirements'] = plugins.requirements(plugin) - - # Build our response object - response['schemas'].append(content) - - return response - - def urls(self, privacy=False): - """ - Returns all of the loaded URLs defined in this apprise object. - """ - return [x.url(privacy=privacy) for x in self.servers] - - def pop(self, index): - """ - Removes an indexed Notification Service from the stack and returns it. - - The thing is we can never pop AppriseConfig() entries, only what was - loaded within them. So pop needs to carefully iterate over our list - and only track actual entries. - """ - - # Tracking variables - prev_offset = -1 - offset = prev_offset - - for idx, s in enumerate(self.servers): - if isinstance(s, (ConfigBase, AppriseConfig)): - servers = s.servers() - if len(servers) > 0: - # Acquire a new maximum offset to work with - offset = prev_offset + len(servers) - - if offset >= index: - # we can pop an element from our config stack - fn = s.pop if isinstance(s, ConfigBase) \ - else s.server_pop - - return fn(index if prev_offset == -1 - else (index - prev_offset - 1)) - - else: - offset = prev_offset + 1 - if offset == index: - return self.servers.pop(idx) - - # Update our old offset - prev_offset = offset - - # If we reach here, then we indexed out of range - raise IndexError('list index out of range') - - def __getitem__(self, index): - """ - Returns the indexed server entry of a loaded notification server - """ - # Tracking variables - prev_offset = -1 - offset = prev_offset - - for idx, s in enumerate(self.servers): - if isinstance(s, (ConfigBase, AppriseConfig)): - # Get our list of servers associate with our config object - servers = s.servers() - if len(servers) > 0: - # Acquire a new maximum offset to work with - offset = prev_offset + len(servers) - - if offset >= index: - return servers[index if prev_offset == -1 - else (index - prev_offset - 1)] - - else: - offset = prev_offset + 1 - if offset == index: - return self.servers[idx] - - # Update our old offset - prev_offset = offset - - # If we reach here, then we indexed out of range - raise IndexError('list index out of range') - - def __getstate__(self): - """ - Pickle Support dumps() - """ - attributes = { - 'asset': self.asset, - # Prepare our URL list as we need to extract the associated tags - # and asset details associated with it - 'urls': [{ - 'url': server.url(privacy=False), - 'tag': server.tags if server.tags else None, - 'asset': server.asset} for server in self.servers], - 'locale': self.locale, - 'debug': self.debug, - 'location': self.location, - } - - return attributes - - def __setstate__(self, state): - """ - Pickle Support loads() - """ - self.servers = list() - self.asset = state['asset'] - self.locale = state['locale'] - self.location = state['location'] - for entry in state['urls']: - self.add(entry['url'], asset=entry['asset'], tag=entry['tag']) - - def __bool__(self): - """ - Allows the Apprise object to be wrapped in an 'if statement'. - True is returned if at least one service has been loaded. - """ - return len(self) > 0 - - def __iter__(self): - """ - Returns an iterator to each of our servers loaded. This includes those - found inside configuration. - """ - return chain(*[[s] if not isinstance(s, (ConfigBase, AppriseConfig)) - else iter(s.servers()) for s in self.servers]) - - def __len__(self): - """ - Returns the number of servers loaded; this includes those found within - loaded configuration. This funtion nnever actually counts the - Config entry themselves (if they exist), only what they contain. - """ - return sum([1 if not isinstance(s, (ConfigBase, AppriseConfig)) - else len(s.servers()) for s in self.servers]) From ebb0cc16b16363e91100eebf243a657abd2011cf Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Sat, 25 May 2024 00:19:06 -0400 Subject: [PATCH 070/129] no log: Delete libs/apprise/apprise.pyi --- libs/apprise/apprise.pyi | 62 ---------------------------------------- 1 file changed, 62 deletions(-) delete mode 100644 libs/apprise/apprise.pyi diff --git a/libs/apprise/apprise.pyi b/libs/apprise/apprise.pyi deleted file mode 100644 index 5a34c9c65..000000000 --- a/libs/apprise/apprise.pyi +++ /dev/null @@ -1,62 +0,0 @@ -from typing import Any, Dict, List, Iterable, Iterator, Optional - -from . import (AppriseAsset, AppriseAttachment, AppriseConfig, ConfigBase, - NotifyBase, NotifyFormat, NotifyType) -from .common import ContentLocation - -_Server = Union[str, ConfigBase, NotifyBase, AppriseConfig] -_Servers = Union[_Server, Dict[Any, _Server], Iterable[_Server]] -# Can't define this recursively as mypy doesn't support recursive types: -# https://github.com/python/mypy/issues/731 -_Tag = Union[str, Iterable[Union[str, Iterable[str]]]] - -class Apprise: - def __init__( - self, - servers: _Servers = ..., - asset: Optional[AppriseAsset] = ..., - location: Optional[ContentLocation] = ..., - debug: bool = ... - ) -> None: ... - @staticmethod - def instantiate( - url: Union[str, Dict[str, NotifyBase]], - asset: Optional[AppriseAsset] = ..., - tag: Optional[_Tag] = ..., - suppress_exceptions: bool = ... - ) -> NotifyBase: ... - def add( - self, - servers: _Servers = ..., - asset: Optional[AppriseAsset] = ..., - tag: Optional[_Tag] = ... - ) -> bool: ... - def clear(self) -> None: ... - def find(self, tag: str = ...) -> Iterator[Apprise]: ... - def notify( - self, - body: str, - title: str = ..., - notify_type: NotifyType = ..., - body_format: NotifyFormat = ..., - tag: _Tag = ..., - attach: Optional[AppriseAttachment] = ..., - interpret_escapes: Optional[bool] = ... - ) -> bool: ... - async def async_notify( - self, - body: str, - title: str = ..., - notify_type: NotifyType = ..., - body_format: NotifyFormat = ..., - tag: _Tag = ..., - attach: Optional[AppriseAttachment] = ..., - interpret_escapes: Optional[bool] = ... - ) -> bool: ... - def details(self, lang: Optional[str] = ...) -> Dict[str, Any]: ... - def urls(self, privacy: bool = ...) -> Iterable[str]: ... - def pop(self, index: int) -> ConfigBase: ... - def __getitem__(self, index: int) -> ConfigBase: ... - def __bool__(self) -> bool: ... - def __iter__(self) -> Iterator[ConfigBase]: ... - def __len__(self) -> int: ... \ No newline at end of file From 0e183c428b1509e4cde77c53c4a47a6393c7a54e Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Mon, 27 May 2024 20:58:29 -0400 Subject: [PATCH 071/129] Fixed subdivx series search process. #2499 --- .../subliminal_patch/providers/subdivx.py | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/custom_libs/subliminal_patch/providers/subdivx.py b/custom_libs/subliminal_patch/providers/subdivx.py index ae8625f47..a19ae1b5e 100644 --- a/custom_libs/subliminal_patch/providers/subdivx.py +++ b/custom_libs/subliminal_patch/providers/subdivx.py @@ -126,7 +126,7 @@ class SubdivxSubtitlesProvider(Provider): titles = [video.series if episode else video.title] try: - titles.extend(video.alternative_titles) + titles.extend(video.alternative_series if episode else video.alternative_titles) except: pass else: @@ -138,6 +138,7 @@ class SubdivxSubtitlesProvider(Provider): # TODO: cache pack queries (TV SHOW S01). # Too many redundant server calls. for title in titles: + title = _series_sanitizer(title) for query in ( f"{title} S{video.season:02}E{video.episode:02}", f"{title} S{video.season:02}", @@ -297,20 +298,31 @@ def _check_episode(video, title): ) and season_num == video.season series_title = _SERIES_RE.sub("", title).strip() + series_title = _series_sanitizer(series_title) - distance = abs(len(series_title) - len(video.series)) + for video_series_title in [video.series] + video.alternative_series: + video_series_title = _series_sanitizer(video_series_title) + distance = abs(len(series_title) - len(video_series_title)) - series_matched = distance < 4 and ep_matches + series_matched = (distance < 4 or video_series_title in series_title) and ep_matches + + logger.debug( + "Series matched? %s [%s -> %s] [title distance: %d]", + series_matched, + video_series_title, + series_title, + distance, + ) + + if series_matched: + return True + return False - logger.debug( - "Series matched? %s [%s -> %s] [title distance: %d]", - series_matched, - video, - title, - distance, - ) - return series_matched +def _series_sanitizer(title): + title = re.sub(r"\'|\.+", '', title) # remove single quote and dot + title = re.sub(r"\W+", ' ', title) # replace by a space anything other than a letter, digit or underscore + return re.sub(r"([A-Z])\s(?=[A-Z]\b)", '', title).strip() # Marvels Agent of S.H.I.E.L.D def _check_movie(video, title): From 884200441bec801eba56a4ac08328f8227ad3bed Mon Sep 17 00:00:00 2001 From: JayZed Date: Mon, 27 May 2024 21:18:45 -0400 Subject: [PATCH 072/129] Fix for case insensitive filesystem upates This fix was made necessary when a library changed the case of one of its files, but kept the name the same. When the file was updated in place, the case did not change. The solution is to delete the file first before extracting the new one from the zip file with the changed case. --- bazarr/app/check_update.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bazarr/app/check_update.py b/bazarr/app/check_update.py index 97617c75b..54aeefcce 100644 --- a/bazarr/app/check_update.py +++ b/bazarr/app/check_update.py @@ -165,6 +165,9 @@ def apply_update(): parent_dir = os.path.dirname(file_path) os.makedirs(parent_dir, exist_ok=True) if not os.path.isdir(file_path): + if os.path.exists(file_path): + # remove the file first to handle case-insensitive file systems + os.remove(file_path) with open(file_path, 'wb+') as f: f.write(archive.read(file)) except Exception: From b7e6de71ffe977a5b2fc71d3b61545226af83395 Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Fri, 31 May 2024 11:08:29 +0900 Subject: [PATCH 073/129] Fixed bazarr restart traceback exception --- bazarr/utilities/central.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bazarr/utilities/central.py b/bazarr/utilities/central.py index 009c42e1d..29fe898bc 100644 --- a/bazarr/utilities/central.py +++ b/bazarr/utilities/central.py @@ -3,6 +3,7 @@ # only methods can be specified here that do not cause other moudules to be loaded # for other methods that use settings, etc., use utilities/helper.py +import contextlib import logging import os from pathlib import Path @@ -53,4 +54,8 @@ def restart_bazarr(): except Exception as e: logging.error(f'BAZARR Cannot create restart file: {repr(e)}') logging.info('Bazarr is being restarted...') - raise SystemExit(EXIT_NORMAL) + + # Wrap the SystemExit for a graceful restart. The SystemExit still performs the cleanup but the traceback is omitted + # preventing to throw the exception to the caller but still terminates the Python process with the desired Exit Code + with contextlib.suppress(SystemExit): + raise SystemExit(EXIT_NORMAL) From 77302fad218a3c14c91c3d28074f30e02ffe9b77 Mon Sep 17 00:00:00 2001 From: Alex Meyer Date: Thu, 30 May 2024 22:16:24 -0400 Subject: [PATCH 074/129] Fixed throttled_providers.dat reset --- bazarr/app/get_providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bazarr/app/get_providers.py b/bazarr/app/get_providers.py index d7a61326f..bc251a4e1 100644 --- a/bazarr/app/get_providers.py +++ b/bazarr/app/get_providers.py @@ -496,7 +496,7 @@ def get_throttled_providers(): except Exception: # set empty content in throttled_providers.dat logging.error("Invalid content in throttled_providers.dat. Resetting") - set_throttled_providers(providers) + set_throttled_providers(str(providers)) finally: return providers From bb8233b599fa0bd8133b092897a4fbcfd736c8cd Mon Sep 17 00:00:00 2001 From: morpheus65535 Date: Thu, 6 Jun 2024 06:17:13 -0400 Subject: [PATCH 075/129] Fixed external subtitles indexing on some platforms where filesystem encoding may be using a different UTF8 normalization form. --- custom_libs/subliminal_patch/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_libs/subliminal_patch/core.py b/custom_libs/subliminal_patch/core.py index 872896e9c..c1629496b 100644 --- a/custom_libs/subliminal_patch/core.py +++ b/custom_libs/subliminal_patch/core.py @@ -946,8 +946,8 @@ def _search_external_subtitles(path, languages=None, only_one=False, match_stric lambda m: "" if str(m.group(1)).lower() in FULL_LANGUAGE_LIST else m.group(0), p_root) p_root_lower = p_root_bare.lower() - - filename_matches = p_root_lower == fn_no_ext_lower + # comparing to both unicode normalization forms to prevent broking stuff and improve indexing on some platforms. + filename_matches = fn_no_ext_lower in [p_root_lower, unicodedata.normalize('NFC', p_root_lower)] filename_contains = p_root_lower in fn_no_ext_lower if not filename_matches: From be8f2d6d183e5206f932be1113e0da7db2a2277a Mon Sep 17 00:00:00 2001 From: Anderson Shindy Oki Date: Fri, 7 Jun 2024 12:00:42 +0900 Subject: [PATCH 076/129] Upgraded mantine to v7.x --- frontend/package-lock.json | 747 +++++++----------- frontend/package.json | 14 +- frontend/postcss.config.cjs | 14 + frontend/src/App/Header.module.scss | 9 + frontend/src/App/Header.tsx | 67 +- frontend/src/App/Navbar.module.scss | 56 ++ frontend/src/App/Navbar.tsx | 114 +-- frontend/src/App/ThemeLoader.tsx | 39 + frontend/src/App/ThemeProvider.tsx | 61 ++ frontend/src/App/index.tsx | 18 +- frontend/src/App/theme.tsx | 87 -- frontend/src/Router/index.tsx | 4 +- frontend/src/assets/_bazarr.scss | 40 + frontend/src/assets/_mantine.scss | 61 ++ frontend/src/assets/_variables.module.scss | 18 + frontend/src/assets/action_icon.module.scss | 14 + frontend/src/assets/app_shell.module.scss | 5 + frontend/src/assets/badge.module.scss | 8 + frontend/src/assets/button.module.scss | 12 + frontend/src/components/Search.module.scss | 9 + frontend/src/components/Search.tsx | 80 +- frontend/src/components/StateIcon.tsx | 12 +- frontend/src/components/SubtitleToolsMenu.tsx | 6 +- frontend/src/components/bazarr/AudioList.tsx | 2 +- .../src/components/bazarr/Language.test.tsx | 18 +- frontend/src/components/bazarr/Language.tsx | 2 +- .../src/components/forms/FrameRateForm.tsx | 8 +- .../src/components/forms/ItemEditForm.tsx | 2 +- .../forms/MovieUploadForm.module.scss | 0 .../src/components/forms/MovieUploadForm.tsx | 22 +- .../forms/ProfileEditForm.module.scss | 5 + .../src/components/forms/ProfileEditForm.tsx | 16 +- .../src/components/forms/SeriesUploadForm.tsx | 22 +- .../src/components/forms/SyncSubtitleForm.tsx | 45 +- .../src/components/forms/TimeOffsetForm.tsx | 2 +- frontend/src/components/index.tsx | 2 +- .../src/components/inputs/Action.test.tsx | 8 +- .../src/components/inputs/ChipInput.test.tsx | 10 +- frontend/src/components/inputs/ChipInput.tsx | 50 +- .../components/inputs/DropContent.module.scss | 4 + .../src/components/inputs/DropContent.tsx | 20 +- .../src/components/inputs/FileBrowser.tsx | 27 +- .../src/components/inputs/Selector.test.tsx | 35 +- frontend/src/components/inputs/Selector.tsx | 38 +- .../components/modals/ManualSearchModal.tsx | 24 +- .../components/tables/BaseTable.module.scss | 9 + frontend/src/components/tables/BaseTable.tsx | 61 +- frontend/src/components/tables/GroupTable.tsx | 22 +- .../src/components/tables/PageControl.tsx | 2 +- frontend/src/components/toolbox/Button.tsx | 2 +- .../components/toolbox/Toolbox.module.scss | 9 + .../toolbox/{index.tsx => Toolbox.tsx} | 15 +- frontend/src/constants.ts | 8 - frontend/src/modules/socketio/reducer.ts | 2 +- frontend/src/modules/task/index.ts | 2 +- frontend/src/modules/task/notification.ts | 14 +- frontend/src/pages/Authentication.tsx | 2 +- frontend/src/pages/Blacklist/Movies/table.tsx | 4 +- frontend/src/pages/Blacklist/Series/table.tsx | 4 +- frontend/src/pages/Episodes/index.tsx | 4 +- frontend/src/pages/Episodes/table.tsx | 11 +- frontend/src/pages/History/Movies/index.tsx | 4 +- frontend/src/pages/History/Series/index.tsx | 7 +- .../Statistics/HistoryStats.module.scss | 9 + .../{index.tsx => HistoryStats.tsx} | 41 +- frontend/src/pages/History/history.test.tsx | 2 +- frontend/src/pages/Movies/Details/index.tsx | 8 +- frontend/src/pages/Movies/Details/table.tsx | 11 +- frontend/src/pages/Movies/index.tsx | 4 +- frontend/src/pages/Series/index.tsx | 19 +- frontend/src/pages/Settings/General/index.tsx | 17 +- .../src/pages/Settings/Languages/equals.tsx | 2 +- .../src/pages/Settings/Languages/table.tsx | 5 +- .../Settings/Notifications/components.tsx | 2 +- .../pages/Settings/Providers/components.tsx | 30 +- frontend/src/pages/Settings/Radarr/index.tsx | 2 +- frontend/src/pages/Settings/Sonarr/index.tsx | 2 +- .../src/pages/Settings/Subtitles/index.tsx | 2 +- .../Settings/components/Card.module.scss | 9 + .../src/pages/Settings/components/Card.tsx | 33 +- .../src/pages/Settings/components/Layout.tsx | 2 +- .../pages/Settings/components/LayoutModal.tsx | 2 +- .../src/pages/Settings/components/Message.tsx | 2 +- .../Settings/components/Section.test.tsx | 8 +- .../src/pages/Settings/components/Section.tsx | 2 +- .../pages/Settings/components/collapse.tsx | 2 +- .../pages/Settings/components/forms.test.tsx | 4 +- .../src/pages/Settings/components/forms.tsx | 5 + .../src/pages/Settings/components/index.tsx | 4 +- .../pages/Settings/components/pathMapper.tsx | 2 +- .../src/pages/System/Announcements/table.tsx | 7 +- frontend/src/pages/System/Backups/table.tsx | 7 +- frontend/src/pages/System/Logs/index.tsx | 4 +- frontend/src/pages/System/Releases/index.tsx | 4 +- frontend/src/pages/System/Status/index.tsx | 9 +- frontend/src/pages/System/Status/table.tsx | 7 +- frontend/src/pages/System/Tasks/table.tsx | 7 +- frontend/src/pages/Wanted/Movies/index.tsx | 2 +- frontend/src/pages/Wanted/Series/index.tsx | 6 +- frontend/src/pages/errors/UIError.tsx | 6 +- frontend/src/pages/views/ItemOverview.tsx | 57 +- frontend/src/pages/views/MassEditor.tsx | 10 +- frontend/src/providers.tsx | 2 +- frontend/src/styles/index.ts | 1 - frontend/src/styles/table.ts | 19 - frontend/vite.config.ts | 10 + 106 files changed, 1187 insertions(+), 1241 deletions(-) create mode 100644 frontend/postcss.config.cjs create mode 100644 frontend/src/App/Header.module.scss create mode 100644 frontend/src/App/Navbar.module.scss create mode 100644 frontend/src/App/ThemeLoader.tsx create mode 100644 frontend/src/App/ThemeProvider.tsx delete mode 100644 frontend/src/App/theme.tsx create mode 100644 frontend/src/assets/_bazarr.scss create mode 100644 frontend/src/assets/_mantine.scss create mode 100644 frontend/src/assets/_variables.module.scss create mode 100644 frontend/src/assets/action_icon.module.scss create mode 100644 frontend/src/assets/app_shell.module.scss create mode 100644 frontend/src/assets/badge.module.scss create mode 100644 frontend/src/assets/button.module.scss create mode 100644 frontend/src/components/Search.module.scss create mode 100644 frontend/src/components/forms/MovieUploadForm.module.scss create mode 100644 frontend/src/components/forms/ProfileEditForm.module.scss create mode 100644 frontend/src/components/inputs/DropContent.module.scss create mode 100644 frontend/src/components/tables/BaseTable.module.scss create mode 100644 frontend/src/components/toolbox/Toolbox.module.scss rename frontend/src/components/toolbox/{index.tsx => Toolbox.tsx} (58%) create mode 100644 frontend/src/pages/History/Statistics/HistoryStats.module.scss rename frontend/src/pages/History/Statistics/{index.tsx => HistoryStats.tsx} (79%) create mode 100644 frontend/src/pages/Settings/components/Card.module.scss delete mode 100644 frontend/src/styles/index.ts delete mode 100644 frontend/src/styles/table.ts diff --git a/frontend/package-lock.json b/frontend/package-lock.json index bfc24c17f..f19cbab4d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,12 +9,12 @@ "version": "1.0.0", "license": "GPL-3", "dependencies": { - "@mantine/core": "^6.0.21", - "@mantine/dropzone": "^6.0.21", - "@mantine/form": "^6.0.21", - "@mantine/hooks": "^6.0.21", - "@mantine/modals": "^6.0.21", - "@mantine/notifications": "^6.0.21", + "@mantine/core": "^7.10.1", + "@mantine/dropzone": "^7.10.1", + "@mantine/form": "^7.10.1", + "@mantine/hooks": "^7.10.1", + "@mantine/modals": "^7.10.1", + "@mantine/notifications": "^7.10.1", "axios": "^1.6.8", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -49,6 +49,8 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", + "postcss-preset-mantine": "^1.14.4", + "postcss-simple-vars": "^7.0.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", @@ -94,6 +96,7 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, "dependencies": { "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" @@ -335,6 +338,7 @@ "version": "7.24.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, "dependencies": { "@babel/types": "^7.24.0" }, @@ -456,6 +460,7 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -464,6 +469,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -509,6 +515,7 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -523,6 +530,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -534,6 +542,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -547,6 +556,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -554,12 +564,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -568,6 +580,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -576,6 +589,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -2193,6 +2207,7 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -2208,120 +2223,6 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "peer": true, - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==", - "peer": true - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", - "peer": true - }, - "node_modules/@emotion/react": { - "version": "11.11.4", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - }, - "peerDependencies": { - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@emotion/serialize": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", - "peer": true, - "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==", - "peer": true - }, - "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", - "peer": true - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "peer": true, - "peerDependencies": { - "react": ">=16.8.0" - } - }, - "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==", - "peer": true - }, - "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==", - "peer": true - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", @@ -2791,13 +2692,13 @@ } }, "node_modules/@floating-ui/react": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.19.2.tgz", - "integrity": "sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w==", + "version": "0.26.12", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.12.tgz", + "integrity": "sha512-D09o62HrWdIkstF2kGekIKAC0/N/Dl6wo3CQsnLcOmO3LkW6Ik8uIb3kw8JYkwxNCcg+uJ2bpWUiIijTBep05w==", "dependencies": { - "@floating-ui/react-dom": "^1.3.0", - "aria-hidden": "^1.1.3", - "tabbable": "^6.0.1" + "@floating-ui/react-dom": "^2.0.0", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -2805,11 +2706,11 @@ } }, "node_modules/@floating-ui/react-dom": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-1.3.0.tgz", - "integrity": "sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", "dependencies": { - "@floating-ui/dom": "^1.2.1" + "@floating-ui/dom": "^1.6.1" }, "peerDependencies": { "react": ">=16.8.0", @@ -3034,120 +2935,107 @@ } }, "node_modules/@mantine/core": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-6.0.21.tgz", - "integrity": "sha512-Kx4RrRfv0I+cOCIcsq/UA2aWcYLyXgW3aluAuW870OdXnbII6qg7RW28D+r9D76SHPxWFKwIKwmcucAG08Divg==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.10.1.tgz", + "integrity": "sha512-l9ypojKN3PjwO1CSLIsqxi7mA25+7w+xc71Q+JuCCREI0tuGwkZsKbIOpuTATIJOjPh8ycLiW7QxX1LYsRTq6w==", + "license": "MIT", "dependencies": { - "@floating-ui/react": "^0.19.1", - "@mantine/styles": "6.0.21", - "@mantine/utils": "6.0.21", - "@radix-ui/react-scroll-area": "1.0.2", - "react-remove-scroll": "^2.5.5", - "react-textarea-autosize": "8.3.4" + "@floating-ui/react": "^0.26.9", + "clsx": "^2.1.1", + "react-number-format": "^5.3.1", + "react-remove-scroll": "^2.5.7", + "react-textarea-autosize": "8.5.3", + "type-fest": "^4.12.0" }, "peerDependencies": { - "@mantine/hooks": "6.0.21", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "@mantine/hooks": "7.10.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.15.0.tgz", + "integrity": "sha512-tB9lu0pQpX5KJq54g+oHOLumOx+pMep4RaM6liXh2PKmVRFF+/vAtUP0ZaJ0kOySfVNjF6doBWPHhBhISKdlIA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@mantine/dropzone": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-6.0.21.tgz", - "integrity": "sha512-v63tL4x7R1CvBNnxJVaVPhBVnQcfROQvyOV0xK/v0ZGNAzFxjJoiCRMGdlBjxnEawM0dRhNs/46ItpBgjQIr6g==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/dropzone/-/dropzone-7.10.1.tgz", + "integrity": "sha512-WzOLwMf8RJtakivPzSYBy3ZLuCtsZEbkJpAXNmM5PdsA8s9MKmY3jYA1MuZis1/NStAGm1d4twofH1KjGCMphg==", + "license": "MIT", "dependencies": { - "@mantine/utils": "6.0.21", - "react-dropzone": "14.2.3" + "react-dropzone-esm": "15.0.1" }, "peerDependencies": { - "@mantine/core": "6.0.21", - "@mantine/hooks": "6.0.21", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "@mantine/core": "7.10.1", + "@mantine/hooks": "7.10.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" } }, "node_modules/@mantine/form": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/form/-/form-6.0.21.tgz", - "integrity": "sha512-d4tlxyZic7MSDnaPx/WliCX1sRFDkUd2nxx4MxxO2T4OSek0YDqTlSBCxeoveu60P+vrQQN5rbbsVsaOJBe4SQ==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/form/-/form-7.10.1.tgz", + "integrity": "sha512-mZwzg4GEWKEDKEIZu9FmSpGFzYYhFD2YArVOXUM0MMciUqX7yxSCon1PaPJxrV8ldc6FE+JLVI2+G2KVxJ3ZXA==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", - "klona": "^2.0.5" + "klona": "^2.0.6" }, "peerDependencies": { - "react": ">=16.8.0" + "react": "^18.2.0" } }, "node_modules/@mantine/hooks": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-6.0.21.tgz", - "integrity": "sha512-sYwt5wai25W6VnqHbS5eamey30/HD5dNXaZuaVEAJ2i2bBv8C0cCiczygMDpAFiSYdXoSMRr/SZ2CrrPTzeNew==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.10.1.tgz", + "integrity": "sha512-0EH9WBWUdtQLGU3Ak+csQ77EtUxI6pPNfwZdRJQWcaA3f8SFOLo9h9CGxiikFExerhvuCeUlaTf3s+TB9Op/rw==", + "license": "MIT", "peerDependencies": { - "react": ">=16.8.0" + "react": "^18.2.0" } }, "node_modules/@mantine/modals": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-6.0.21.tgz", - "integrity": "sha512-Gx2D/ZHMUuYF197JKMWey4K9FeGP9rxYp4lmAEXUrjXiST2fEhLZOdiD75KuOHXd1/sYAU9NcNRo9wXrlF/gUA==", - "dependencies": { - "@mantine/utils": "6.0.21" - }, + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-7.10.1.tgz", + "integrity": "sha512-2riQSNpVV7f0baizlqcggz9hx9/+y6SQTnW3zEkl/RIkuyK9dpeMFUG6M+M8ntwP79b7x9n7Em9PMWxRbgi28A==", + "license": "MIT", "peerDependencies": { - "@mantine/core": "6.0.21", - "@mantine/hooks": "6.0.21", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "@mantine/core": "7.10.1", + "@mantine/hooks": "7.10.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" } }, "node_modules/@mantine/notifications": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-6.0.21.tgz", - "integrity": "sha512-qsrqxuJHK8b67sf9Pfk+xyhvpf9jMsivW8vchfnJfjv7yz1lLvezjytMFp4fMDoYhjHnDPOEc/YFockK4muhOw==", + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-7.10.1.tgz", + "integrity": "sha512-cx3JR3BJzEzH6t2EF1ysrWVY/rdJk0WbSBQo/qFamJd2sbU+8XAHriI8Cx6hNo7uRGCwd8VGAj7Cf3aWK2VC5A==", + "license": "MIT", "dependencies": { - "@mantine/utils": "6.0.21", - "react-transition-group": "4.4.2" + "@mantine/store": "7.10.1", + "react-transition-group": "4.4.5" }, "peerDependencies": { - "@mantine/core": "6.0.21", - "@mantine/hooks": "6.0.21", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@mantine/styles": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-6.0.21.tgz", - "integrity": "sha512-PVtL7XHUiD/B5/kZ/QvZOZZQQOj12QcRs3Q6nPoqaoPcOX5+S7bMZLMH0iLtcGq5OODYk0uxlvuJkOZGoPj8Mg==", - "dependencies": { - "clsx": "1.1.1", - "csstype": "3.0.9" - }, - "peerDependencies": { - "@emotion/react": ">=11.9.0", - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@mantine/styles/node_modules/clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", - "engines": { - "node": ">=6" + "@mantine/core": "7.10.1", + "@mantine/hooks": "7.10.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" } }, - "node_modules/@mantine/styles/node_modules/csstype": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.9.tgz", - "integrity": "sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==" - }, - "node_modules/@mantine/utils": { - "version": "6.0.21", - "resolved": "https://registry.npmjs.org/@mantine/utils/-/utils-6.0.21.tgz", - "integrity": "sha512-33RVDRop5jiWFao3HKd3Yp7A9mEq4HAJxJPTuYm1NkdqX6aTKOQK7wT8v8itVodBp+sb4cJK6ZVdD1UurK/txQ==", + "node_modules/@mantine/store": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-7.10.1.tgz", + "integrity": "sha512-KrGBsSoMsfrYeLxPwf5rFv0s2Nl/4wf+AaF/U1SpQrMgPI8vYokPXx52Wp3jCmlo12NCZnCIG+/6YHAdTWH1qQ==", + "license": "MIT", "peerDependencies": { - "react": ">=16.8.0" + "react": "^18.2.0" } }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { @@ -3222,137 +3110,6 @@ "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, - "node_modules/@radix-ui/number": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.0.tgz", - "integrity": "sha512-Ofwh/1HX69ZfJRiRBMTy7rgjAzHmwe4kW9C9Y99HTRUcYLUuVT0KESFj15rPjRgKJs20GPq8Bm5aEDJ8DuA3vA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@radix-ui/primitive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz", - "integrity": "sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz", - "integrity": "sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.0.tgz", - "integrity": "sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.0.tgz", - "integrity": "sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.0.tgz", - "integrity": "sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-use-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.1.tgz", - "integrity": "sha512-fHbmislWVkZaIdeF6GZxF0A/NH/3BjrGIYj+Ae6eTmTCr7EB0RQAAVEiqsXK6p3/JcRqVSBQoceZroj30Jj3XA==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.1" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-scroll-area": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.0.2.tgz", - "integrity": "sha512-k8VseTxI26kcKJaX0HPwkvlNBPTs56JRdYzcZ/vzrNUkDlvXBy8sMc7WvCpYzZkHgb+hd72VW9MqkqecGtuNgg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.0", - "@radix-ui/primitive": "1.0.0", - "@radix-ui/react-compose-refs": "1.0.0", - "@radix-ui/react-context": "1.0.0", - "@radix-ui/react-direction": "1.0.0", - "@radix-ui/react-presence": "1.0.0", - "@radix-ui/react-primitive": "1.0.1", - "@radix-ui/react-use-callback-ref": "1.0.0", - "@radix-ui/react-use-layout-effect": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.1.tgz", - "integrity": "sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.0" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz", - "integrity": "sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz", - "integrity": "sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "react": "^16.8 || ^17.0 || ^18.0" - } - }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -3899,7 +3656,8 @@ "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true }, "node_modules/@types/prop-types": { "version": "15.7.12", @@ -4640,17 +4398,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -4848,14 +4595,6 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "node_modules/attr-accept": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", - "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", - "engines": { - "node": ">=4" - } - }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4903,6 +4642,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -5107,10 +4847,20 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { "node": ">=6" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001607", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz", @@ -5229,10 +4979,10 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", - "dev": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5286,12 +5036,6 @@ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", "dev": true }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "peer": true - }, "node_modules/core-js-compat": { "version": "3.36.1", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", @@ -5309,6 +5053,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -5324,6 +5069,7 @@ "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, "engines": { "node": ">= 6" } @@ -5348,6 +5094,18 @@ "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", "dev": true }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/cssstyle": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", @@ -5779,6 +5537,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -5992,6 +5751,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, "engines": { "node": ">=10" }, @@ -6606,17 +6366,6 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/file-selector": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz", - "integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==", - "dependencies": { - "tslib": "^2.4.0" - }, - "engines": { - "node": ">= 12" - } - }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -6629,12 +6378,6 @@ "node": ">=8" } }, - "node_modules/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", - "peer": true - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -6758,6 +6501,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -7035,6 +6779,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -7042,15 +6787,6 @@ "node": ">= 0.4" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "peer": true, - "dependencies": { - "react-is": "^16.7.0" - } - }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -7150,6 +6886,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -7243,7 +6980,8 @@ "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, "node_modules/is-async-function": { "version": "2.0.0", @@ -7316,6 +7054,7 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -7934,7 +7673,8 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -8053,7 +7793,8 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true }, "node_modules/local-pkg": { "version": "0.5.0", @@ -8621,6 +8362,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -8632,6 +8374,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -8686,12 +8429,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "engines": { "node": ">=8" } @@ -8714,7 +8459,8 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", @@ -8776,6 +8522,108 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-mixins": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-9.0.4.tgz", + "integrity": "sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "postcss-js": "^4.0.0", + "postcss-simple-vars": "^7.0.0", + "sugarss": "^4.0.1" + }, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-preset-mantine": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.14.4.tgz", + "integrity": "sha512-T1K3MVhU1hA9mJWfqoGvMcK5WKcHpVi4JUX6AYTbESvp78WneB/KFONUi+eXDG9Lpw62W/KNxEYl1ic3Dpm88w==", + "dev": true, + "dependencies": { + "postcss-mixins": "^9.0.4", + "postcss-nested": "^6.0.1" + }, + "peerDependencies": { + "postcss": ">=8.0.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-simple-vars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", + "dev": true, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -8967,13 +8815,11 @@ "react": "^18.2.0" } }, - "node_modules/react-dropzone": { - "version": "14.2.3", - "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.3.tgz", - "integrity": "sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==", + "node_modules/react-dropzone-esm": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/react-dropzone-esm/-/react-dropzone-esm-15.0.1.tgz", + "integrity": "sha512-RdeGpqwHnoV/IlDFpQji7t7pTtlC2O1i/Br0LWkRZ9hYtLyce814S71h5NolnCZXsIN5wrZId6+8eQj2EBnEzg==", "dependencies": { - "attr-accept": "^2.2.2", - "file-selector": "^0.6.0", "prop-types": "^15.8.1" }, "engines": { @@ -8988,6 +8834,18 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-number-format": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.4.tgz", + "integrity": "sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-query": { "version": "3.39.3", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", @@ -9112,22 +8970,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-smooth/node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", @@ -9164,11 +9006,11 @@ } }, "node_modules/react-textarea-autosize": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz", - "integrity": "sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", "dependencies": { - "@babel/runtime": "^7.10.2", + "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, @@ -9180,9 +9022,9 @@ } }, "node_modules/react-transition-group": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", - "integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -9375,6 +9217,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -9391,6 +9234,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, "engines": { "node": ">=4" } @@ -9701,15 +9545,6 @@ "node": ">=10.0.0" } }, - "node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -9905,11 +9740,21 @@ "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", "dev": true }, - "node_modules/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", - "peer": true + "node_modules/sugarss": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz", + "integrity": "sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } }, "node_modules/supports-color": { "version": "7.2.0", @@ -9927,6 +9772,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -9999,6 +9845,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, "engines": { "node": ">=4" } @@ -10432,6 +10279,12 @@ } } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index eadb26496..971065e4d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,12 +13,12 @@ }, "private": true, "dependencies": { - "@mantine/core": "^6.0.21", - "@mantine/dropzone": "^6.0.21", - "@mantine/form": "^6.0.21", - "@mantine/hooks": "^6.0.21", - "@mantine/modals": "^6.0.21", - "@mantine/notifications": "^6.0.21", + "@mantine/core": "^7.10.1", + "@mantine/dropzone": "^7.10.1", + "@mantine/form": "^7.10.1", + "@mantine/hooks": "^7.10.1", + "@mantine/modals": "^7.10.1", + "@mantine/notifications": "^7.10.1", "axios": "^1.6.8", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -53,6 +53,8 @@ "husky": "^9.0.11", "jsdom": "^24.0.0", "lodash": "^4.17.21", + "postcss-preset-mantine": "^1.14.4", + "postcss-simple-vars": "^7.0.1", "prettier": "^3.2.5", "prettier-plugin-organize-imports": "^3.2.4", "pretty-quick": "^4.0.0", diff --git a/frontend/postcss.config.cjs b/frontend/postcss.config.cjs new file mode 100644 index 000000000..e817f567b --- /dev/null +++ b/frontend/postcss.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + plugins: { + "postcss-preset-mantine": {}, + "postcss-simple-vars": { + variables: { + "mantine-breakpoint-xs": "36em", + "mantine-breakpoint-sm": "48em", + "mantine-breakpoint-md": "62em", + "mantine-breakpoint-lg": "75em", + "mantine-breakpoint-xl": "88em", + }, + }, + }, +}; diff --git a/frontend/src/App/Header.module.scss b/frontend/src/App/Header.module.scss new file mode 100644 index 000000000..85b3661a9 --- /dev/null +++ b/frontend/src/App/Header.module.scss @@ -0,0 +1,9 @@ +.header { + @include light { + color: var(--mantine-color-gray-0); + } + + @include dark { + color: var(--mantine-color-dark-0); + } +} diff --git a/frontend/src/App/Header.tsx b/frontend/src/App/Header.tsx index c15071045..6bb47f5b1 100644 --- a/frontend/src/App/Header.tsx +++ b/frontend/src/App/Header.tsx @@ -1,6 +1,5 @@ import { useSystem, useSystemSettings } from "@/apis/hooks"; import { Action, Search } from "@/components"; -import { Layout } from "@/constants"; import { useNavbar } from "@/contexts/Navbar"; import { useIsOnline } from "@/contexts/Online"; import { Environment, useGotoHomepage } from "@/utilities"; @@ -12,27 +11,16 @@ import { import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Anchor, + AppShell, Avatar, Badge, Burger, Divider, Group, - Header, - MediaQuery, Menu, - createStyles, } from "@mantine/core"; import { FunctionComponent } from "react"; - -const useStyles = createStyles((theme) => { - const headerBackgroundColor = - theme.colorScheme === "light" ? theme.colors.gray[0] : theme.colors.dark[4]; - return { - header: { - backgroundColor: headerBackgroundColor, - }, - }; -}); +import styles from "./Header.module.scss"; const AppHeader: FunctionComponent = () => { const { data: settings } = useSystemSettings(); @@ -47,39 +35,28 @@ const AppHeader: FunctionComponent = () => { const goHome = useGotoHomepage(); - const { classes } = useStyles(); - return ( -
- - - - - - - - - show(!showed)} - size="sm" - > - + + + + + + + show(!showed)} + size="sm" + hiddenFrom="sm" + > Bazarr - + @@ -95,13 +72,13 @@ const AppHeader: FunctionComponent = () => { } + leftSection={} onClick={() => restart()} > Restart } + leftSection={} onClick={() => shutdown()} > Shutdown @@ -114,7 +91,7 @@ const AppHeader: FunctionComponent = () => { -
+ ); }; diff --git a/frontend/src/App/Navbar.module.scss b/frontend/src/App/Navbar.module.scss new file mode 100644 index 000000000..ddb444e4d --- /dev/null +++ b/frontend/src/App/Navbar.module.scss @@ -0,0 +1,56 @@ +.anchor { + border-color: var(--mantine-color-gray-5); + text-decoration: none; + + @include dark { + border-color: var(--mantine-color-dark-5); + } + + &.active { + border-left: 2px solid $color-brand-4; + background-color: var(--mantine-color-gray-1); + + @include dark { + border-left: 2px solid $color-brand-8; + background-color: var(--mantine-color-dark-8); + } + } + + &.hover { + background-color: var(--mantine-color-gray-0); + + @include dark { + background-color: var(--mantine-color-dark-7); + } + } +} + +.badge { + margin-left: auto; + text-decoration: none; + box-shadow: var(--mantine-shadow-xs); +} + +.icon { + width: 1.4rem; + margin-right: var(--mantine-spacing-xs); +} + +.nav { + background-color: var(--mantine-color-gray-2); + + @include dark { + background-color: var(--mantine-color-dark-8); + } +} + +.text { + display: inline-flex; + align-items: center; + width: 100%; + color: var(--mantine-color-gray-8); + + @include dark { + color: var(--mantine-color-gray-5); + } +} diff --git a/frontend/src/App/Navbar.tsx b/frontend/src/App/Navbar.tsx index c626dc257..8254f2fc0 100644 --- a/frontend/src/App/Navbar.tsx +++ b/frontend/src/App/Navbar.tsx @@ -1,5 +1,4 @@ import { Action } from "@/components"; -import { Layout } from "@/constants"; import { useNavbar } from "@/contexts/Navbar"; import { useRouteItems } from "@/Router"; import { CustomRouteObject, Route } from "@/Router/type"; @@ -14,19 +13,19 @@ import { import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Anchor, + AppShell, Badge, Collapse, - createStyles, Divider, Group, - Navbar as MantineNavbar, Stack, Text, + useComputedColorScheme, useMantineColorScheme, } from "@mantine/core"; import { useHover } from "@mantine/hooks"; import clsx from "clsx"; -import { +import React, { createContext, FunctionComponent, useContext, @@ -35,6 +34,7 @@ import { useState, } from "react"; import { matchPath, NavLink, RouteObject, useLocation } from "react-router-dom"; +import styles from "./Navbar.module.scss"; const Selection = createContext<{ selection: string | null; @@ -97,11 +97,12 @@ function useIsActive(parent: string, route: RouteObject) { } const AppNavbar: FunctionComponent = () => { - const { showed } = useNavbar(); const [selection, select] = useState(null); - const { colorScheme, toggleColorScheme } = useMantineColorScheme(); - const dark = colorScheme === "dark"; + const { toggleColorScheme } = useMantineColorScheme(); + const computedColorScheme = useComputedColorScheme("light"); + + const dark = computedColorScheme === "dark"; const routes = useRouteItems(); @@ -111,23 +112,10 @@ const AppNavbar: FunctionComponent = () => { }, [pathname]); return ( - + ); }; @@ -186,7 +174,7 @@ const RouteItem: FunctionComponent<{ if (children !== undefined) { const elements = ( - + {children.map((child, idx) => ( + { - const borderColor = - theme.colorScheme === "light" ? theme.colors.gray[5] : theme.colors.dark[4]; - - const activeBorderColor = - theme.colorScheme === "light" - ? theme.colors.brand[4] - : theme.colors.brand[8]; - - const activeBackgroundColor = - theme.colorScheme === "light" ? theme.colors.gray[1] : theme.colors.dark[8]; - - const hoverBackgroundColor = - theme.colorScheme === "light" ? theme.colors.gray[0] : theme.colors.dark[7]; - - const textColor = - theme.colorScheme === "light" ? theme.colors.gray[8] : theme.colors.gray[5]; - - return { - text: { - display: "inline-flex", - alignItems: "center", - width: "100%", - color: textColor, - }, - anchor: { - textDecoration: "none", - borderLeft: `2px solid ${borderColor}`, - }, - active: { - backgroundColor: activeBackgroundColor, - borderLeft: `2px solid ${activeBorderColor}`, - boxShadow: theme.shadows.xs, - }, - hover: { - backgroundColor: hoverBackgroundColor, - }, - icon: { width: "1.4rem", marginRight: theme.spacing.xs }, - badge: { - marginLeft: "auto", - textDecoration: "none", - boxShadow: theme.shadows.xs, - color: textColor, - }, - }; -}); - interface NavbarItemProps { name: string; link: string; @@ -308,8 +249,6 @@ const NavbarItem: FunctionComponent = ({ onClick, primary = false, }) => { - const { classes } = useStyles(); - const { show } = useNavbar(); const { ref, hovered } = useHover(); @@ -335,9 +274,9 @@ const NavbarItem: FunctionComponent = ({ }} className={({ isActive }) => clsx( - clsx(classes.anchor, { - [classes.active]: isActive, - [classes.hover]: hovered, + clsx(styles.anchor, { + [styles.active]: isActive, + [styles.hover]: hovered, }), ) } @@ -347,18 +286,19 @@ const NavbarItem: FunctionComponent = ({ inline p="xs" size="sm" - weight={primary ? "bold" : "normal"} - className={classes.text} + fw={primary ? "bold" : "normal"} + className={styles.text} + span > {icon && ( )} {name} - {shouldHideBadge === false && ( - + {!shouldHideBadge && ( + {badge} )} diff --git a/frontend/src/App/ThemeLoader.tsx b/frontend/src/App/ThemeLoader.tsx new file mode 100644 index 000000000..2bc7e4005 --- /dev/null +++ b/frontend/src/App/ThemeLoader.tsx @@ -0,0 +1,39 @@ +import { useCallback, useEffect, useState } from "react"; +import { MantineColorScheme, useMantineColorScheme } from "@mantine/core"; +import { useSystemSettings } from "@/apis/hooks"; + +const ThemeProvider = () => { + const [localScheme, setLocalScheme] = useState( + null, + ); + const { setColorScheme } = useMantineColorScheme(); + + const settings = useSystemSettings(); + + const settingsColorScheme = settings.data?.general + .theme as MantineColorScheme; + + const setScheme = useCallback( + (colorScheme: MantineColorScheme) => { + setColorScheme(colorScheme); + }, + [setColorScheme], + ); + + useEffect(() => { + if (!settingsColorScheme) { + return; + } + + if (localScheme === settingsColorScheme) { + return; + } + + setScheme(settingsColorScheme); + setLocalScheme(settingsColorScheme); + }, [settingsColorScheme, setScheme, localScheme]); + + return <>; +}; + +export default ThemeProvider; diff --git a/frontend/src/App/ThemeProvider.tsx b/frontend/src/App/ThemeProvider.tsx new file mode 100644 index 000000000..d3a39cdb0 --- /dev/null +++ b/frontend/src/App/ThemeProvider.tsx @@ -0,0 +1,61 @@ +import { + ActionIcon, + AppShell, + Badge, + Button, + createTheme, + MantineProvider, +} from "@mantine/core"; +import { FunctionComponent, PropsWithChildren } from "react"; +import ThemeLoader from "@/App/ThemeLoader"; +import "@mantine/core/styles.layer.css"; +import "@mantine/notifications/styles.layer.css"; +import styleVars from "@/assets/_variables.module.scss"; +import buttonClasses from "@/assets/button.module.scss"; +import actionIconClasses from "@/assets/action_icon.module.scss"; +import appShellClasses from "@/assets/app_shell.module.scss"; +import badgeClasses from "@/assets/badge.module.scss"; + +const themeProvider = createTheme({ + fontFamily: "Roboto, open sans, Helvetica Neue, Helvetica, Arial, sans-serif", + colors: { + brand: [ + styleVars.colorBrand0, + styleVars.colorBrand1, + styleVars.colorBrand2, + styleVars.colorBrand3, + styleVars.colorBrand4, + styleVars.colorBrand5, + styleVars.colorBrand6, + styleVars.colorBrand7, + styleVars.colorBrand8, + styleVars.colorBrand9, + ], + }, + primaryColor: "brand", + components: { + ActionIcon: ActionIcon.extend({ + classNames: actionIconClasses, + }), + AppShell: AppShell.extend({ + classNames: appShellClasses, + }), + Badge: Badge.extend({ + classNames: badgeClasses, + }), + Button: Button.extend({ + classNames: buttonClasses, + }), + }, +}); + +const ThemeProvider: FunctionComponent = ({ children }) => { + return ( + + + {children} + + ); +}; + +export default ThemeProvider; diff --git a/frontend/src/App/index.tsx b/frontend/src/App/index.tsx index 4e09a97da..1b27734f4 100644 --- a/frontend/src/App/index.tsx +++ b/frontend/src/App/index.tsx @@ -1,7 +1,6 @@ import AppNavbar from "@/App/Navbar"; import { RouterNames } from "@/Router/RouterNames"; import ErrorBoundary from "@/components/ErrorBoundary"; -import { Layout } from "@/constants"; import NavbarProvider from "@/contexts/Navbar"; import OnlineProvider from "@/contexts/Online"; import { notification } from "@/modules/task"; @@ -13,6 +12,7 @@ import { showNotification } from "@mantine/notifications"; import { FunctionComponent, useEffect, useState } from "react"; import { Outlet, useNavigate } from "react-router-dom"; import AppHeader from "./Header"; +import styleVars from "@/assets/_variables.module.scss"; const App: FunctionComponent = () => { const navigate = useNavigate(); @@ -55,13 +55,19 @@ const App: FunctionComponent = () => { } - navbar={} + navbar={{ + width: styleVars.navBarWidth, + breakpoint: "sm", + collapsed: { mobile: !navbar }, + }} + header={{ height: { base: styleVars.headerHeight } }} padding={0} - fixed > - + + + + + diff --git a/frontend/src/App/theme.tsx b/frontend/src/App/theme.tsx deleted file mode 100644 index 947b4f7a8..000000000 --- a/frontend/src/App/theme.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { useSystemSettings } from "@/apis/hooks"; -import { - ColorScheme, - ColorSchemeProvider, - createEmotionCache, - MantineProvider, - MantineThemeOverride, -} from "@mantine/core"; -import { useColorScheme } from "@mantine/hooks"; -import { - FunctionComponent, - PropsWithChildren, - useCallback, - useEffect, - useState, -} from "react"; - -const theme: MantineThemeOverride = { - fontFamily: "Roboto, open sans, Helvetica Neue, Helvetica, Arial, sans-serif", - colors: { - brand: [ - "#F8F0FC", - "#F3D9FA", - "#EEBEFA", - "#E599F7", - "#DA77F2", - "#CC5DE8", - "#BE4BDB", - "#AE3EC9", - "#9C36B5", - "#862E9C", - ], - }, - primaryColor: "brand", -}; - -function useAutoColorScheme() { - const settings = useSystemSettings(); - const settingsColorScheme = settings.data?.general.theme; - - let preferredColorScheme: ColorScheme = useColorScheme(); - switch (settingsColorScheme) { - case "light": - preferredColorScheme = "light" as ColorScheme; - break; - case "dark": - preferredColorScheme = "dark" as ColorScheme; - break; - } - - const [colorScheme, setColorScheme] = useState(preferredColorScheme); - - // automatically switch dark/light theme - useEffect(() => { - setColorScheme(preferredColorScheme); - }, [preferredColorScheme]); - - const toggleColorScheme = useCallback((value?: ColorScheme) => { - setColorScheme((scheme) => value || (scheme === "dark" ? "light" : "dark")); - }, []); - - return { colorScheme, setColorScheme, toggleColorScheme }; -} - -const emotionCache = createEmotionCache({ key: "bazarr" }); - -const ThemeProvider: FunctionComponent = ({ children }) => { - const { colorScheme, toggleColorScheme } = useAutoColorScheme(); - - return ( - - - {children} - - - ); -}; - -export default ThemeProvider; diff --git a/frontend/src/Router/index.tsx b/frontend/src/Router/index.tsx index 335cf2d75..efed2edb2 100644 --- a/frontend/src/Router/index.tsx +++ b/frontend/src/Router/index.tsx @@ -53,7 +53,9 @@ import Redirector from "./Redirector"; import { RouterNames } from "./RouterNames"; import { CustomRouteObject } from "./type"; -const HistoryStats = lazy(() => import("@/pages/History/Statistics")); +const HistoryStats = lazy( + () => import("@/pages/History/Statistics/HistoryStats"), +); const SystemStatusView = lazy(() => import("@/pages/System/Status")); function useRoutes(): CustomRouteObject[] { diff --git a/frontend/src/assets/_bazarr.scss b/frontend/src/assets/_bazarr.scss new file mode 100644 index 000000000..6c23aac1a --- /dev/null +++ b/frontend/src/assets/_bazarr.scss @@ -0,0 +1,40 @@ +$color-brand-0: #f8f0fc; +$color-brand-1: #f3d9fa; +$color-brand-2: #eebefa; +$color-brand-3: #e599f7; +$color-brand-4: #da77f2; +$color-brand-5: #cc5de8; +$color-brand-6: #be4bdb; +$color-brand-7: #ae3ec9; +$color-brand-8: #9c36b5; +$color-brand-9: #862e9c; + +$header-height: 64px; + +:global { + .table-long-break { + overflow-wrap: anywhere; + } + + .table-primary { + display: inline-block; + + font-size: var(--mantine-font-size-sm); + + @include smaller-than($mantine-breakpoint-sm) { + min-width: 12rem; + } + } + + .table-no-wrap { + white-space: nowrap; + } + + .table-select { + display: inline-block; + + @include smaller-than($mantine-breakpoint-sm) { + min-width: 10rem; + } + } +} diff --git a/frontend/src/assets/_mantine.scss b/frontend/src/assets/_mantine.scss new file mode 100644 index 000000000..93412c636 --- /dev/null +++ b/frontend/src/assets/_mantine.scss @@ -0,0 +1,61 @@ +@use "sass:math"; + +$mantine-breakpoint-xs: "36em"; +$mantine-breakpoint-sm: "48em"; +$mantine-breakpoint-md: "62em"; +$mantine-breakpoint-lg: "75em"; +$mantine-breakpoint-xl: "88em"; + +@function rem($value) { + @return #{math.div(math.div($value, $value * 0 + 1), 16)}rem; +} + +@mixin light { + [data-mantine-color-scheme="light"] & { + @content; + } +} + +@mixin dark { + [data-mantine-color-scheme="dark"] & { + @content; + } +} + +@mixin hover { + @media (hover: hover) { + &:hover { + @content; + } + } + + @media (hover: none) { + &:active { + @content; + } + } +} + +@mixin smaller-than($breakpoint) { + @media (max-width: $breakpoint) { + @content; + } +} + +@mixin larger-than($breakpoint) { + @media (min-width: $breakpoint) { + @content; + } +} + +@mixin rtl { + [dir="rtl"] & { + @content; + } +} + +@mixin ltr { + [dir="ltr"] & { + @content; + } +} diff --git a/frontend/src/assets/_variables.module.scss b/frontend/src/assets/_variables.module.scss new file mode 100644 index 000000000..262d285b2 --- /dev/null +++ b/frontend/src/assets/_variables.module.scss @@ -0,0 +1,18 @@ +$navbar-width: 200; + +:export { + colorBrand0: $color-brand-0; + colorBrand1: $color-brand-1; + colorBrand2: $color-brand-2; + colorBrand3: $color-brand-3; + colorBrand4: $color-brand-4; + colorBrand5: $color-brand-5; + colorBrand6: $color-brand-6; + colorBrand7: $color-brand-7; + colorBrand8: $color-brand-8; + colorBrand9: $color-brand-9; + + headerHeight: $header-height; + + navBarWidth: $navbar-width; +} diff --git a/frontend/src/assets/action_icon.module.scss b/frontend/src/assets/action_icon.module.scss new file mode 100644 index 000000000..c4bf2eefa --- /dev/null +++ b/frontend/src/assets/action_icon.module.scss @@ -0,0 +1,14 @@ +@layer mantine { + .root { + &[data-variant="light"] { + color: var(--mantine-color-dark-0); + } + + @include light { + &[data-variant="light"] { + background-color: var(--mantine-color-gray-1); + color: var(--mantine-color-dark-2); + } + } + } +} diff --git a/frontend/src/assets/app_shell.module.scss b/frontend/src/assets/app_shell.module.scss new file mode 100644 index 000000000..b027c771a --- /dev/null +++ b/frontend/src/assets/app_shell.module.scss @@ -0,0 +1,5 @@ +.main { + @include dark { + background-color: rgb(26, 27, 30); + } +} diff --git a/frontend/src/assets/badge.module.scss b/frontend/src/assets/badge.module.scss new file mode 100644 index 000000000..830da2927 --- /dev/null +++ b/frontend/src/assets/badge.module.scss @@ -0,0 +1,8 @@ +.root { + background-color: var(--mantine-color-grape-light); + + @include light { + color: var(--mantine-color-dark-filled); + background-color: var(--mantine-color-grape-light); + } +} diff --git a/frontend/src/assets/button.module.scss b/frontend/src/assets/button.module.scss new file mode 100644 index 000000000..4ef306883 --- /dev/null +++ b/frontend/src/assets/button.module.scss @@ -0,0 +1,12 @@ +@layer mantine { + .root { + @include dark { + color: var(--mantine-color-dark-0); + } + + &[data-variant="danger"] { + background-color: var(--mantine-color-red-9); + color: var(--mantine-color-red-0); + } + } +} diff --git a/frontend/src/components/Search.module.scss b/frontend/src/components/Search.module.scss new file mode 100644 index 000000000..2c42098eb --- /dev/null +++ b/frontend/src/components/Search.module.scss @@ -0,0 +1,9 @@ +.result { + @include light { + color: var(--mantine-color-dark-8); + } + + @include dark { + color: var(--mantine-color-gray-1); + } +} diff --git a/frontend/src/components/Search.tsx b/frontend/src/components/Search.tsx index bc4a9f8d3..75341e0a8 100644 --- a/frontend/src/components/Search.tsx +++ b/frontend/src/components/Search.tsx @@ -5,11 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Anchor, Autocomplete, - createStyles, - SelectItemProps, + ComboboxItem, + OptionsFilter, } from "@mantine/core"; -import { forwardRef, FunctionComponent, useMemo, useState } from "react"; +import { FunctionComponent, useMemo, useState } from "react"; import { Link } from "react-router-dom"; +import styles from "./Search.module.scss"; type SearchResultItem = { value: string; @@ -41,36 +42,35 @@ function useSearch(query: string) { ); } -const useStyles = createStyles((theme) => { - return { - result: { - color: - theme.colorScheme === "light" - ? theme.colors.dark[8] - : theme.colors.gray[1], - }, - }; -}); - -type ResultCompProps = SelectItemProps & SearchResultItem; - -const ResultComponent = forwardRef( - ({ link, value }, ref) => { - const styles = useStyles(); +const optionsFilter: OptionsFilter = ({ options, search }) => { + const lowercaseSearch = search.toLowerCase(); + const trimmedSearch = search.trim(); + return (options as ComboboxItem[]).filter((option) => { return ( - - {value} - + option.value.toLowerCase().includes(lowercaseSearch) || + option.value + .normalize("NFD") + .replace(/[\u0300-\u036f]/g, "") + .toLowerCase() + .includes(trimmedSearch) ); - }, -); + }); +}; + +const ResultComponent = ({ name, link }: { name: string; link: string }) => { + return ( + + {name} + + ); +}; const Search: FunctionComponent = () => { const [query, setQuery] = useState(""); @@ -79,22 +79,22 @@ const Search: FunctionComponent = () => { return ( } - itemComponent={ResultComponent} + leftSection={} + renderOption={(input) => ( + a.value === input.option.value)?.link || "/" + } + /> + )} placeholder="Search" size="sm" data={results} value={query} onChange={setQuery} onBlur={() => setQuery("")} - filter={(value, item) => - item.value.toLowerCase().includes(value.toLowerCase().trim()) || - item.value - .normalize("NFD") - .replace(/[\u0300-\u036f]/g, "") - .toLowerCase() - .includes(value.trim()) - } + filter={optionsFilter} > ); }; diff --git a/frontend/src/components/StateIcon.tsx b/frontend/src/components/StateIcon.tsx index f9683f63a..73700a679 100644 --- a/frontend/src/components/StateIcon.tsx +++ b/frontend/src/components/StateIcon.tsx @@ -31,7 +31,7 @@ const StateIcon: FunctionComponent = ({ return ; } else { return ( - + @@ -48,9 +48,9 @@ const StateIcon: FunctionComponent = ({ - - - + + + @@ -59,8 +59,8 @@ const StateIcon: FunctionComponent = ({ ))} - - + + diff --git a/frontend/src/components/SubtitleToolsMenu.tsx b/frontend/src/components/SubtitleToolsMenu.tsx index e36a1e9e1..bdadb5cb4 100644 --- a/frontend/src/components/SubtitleToolsMenu.tsx +++ b/frontend/src/components/SubtitleToolsMenu.tsx @@ -148,7 +148,7 @@ const SubtitleToolsMenu: FunctionComponent = ({ } + leftSection={} onClick={() => { if (tool.modal) { modals.openContextModal(tool.modal, { selections }); @@ -164,7 +164,7 @@ const SubtitleToolsMenu: FunctionComponent = ({ Actions } + leftSection={} onClick={() => { onAction?.("search"); }} @@ -174,7 +174,7 @@ const SubtitleToolsMenu: FunctionComponent = ({ } + leftSection={} onClick={() => { modals.openConfirmModal({ title: "The following subtitles will be deleted", diff --git a/frontend/src/components/bazarr/AudioList.tsx b/frontend/src/components/bazarr/AudioList.tsx index ac9cce743..b73f71331 100644 --- a/frontend/src/components/bazarr/AudioList.tsx +++ b/frontend/src/components/bazarr/AudioList.tsx @@ -13,7 +13,7 @@ const AudioList: FunctionComponent = ({ ...group }) => { return ( - + {audios.map((audio, idx) => ( {audio.name} diff --git a/frontend/src/components/bazarr/Language.test.tsx b/frontend/src/components/bazarr/Language.test.tsx index 9e0e0fab8..e4e3b42b8 100644 --- a/frontend/src/components/bazarr/Language.test.tsx +++ b/frontend/src/components/bazarr/Language.test.tsx @@ -1,4 +1,4 @@ -import { rawRender, screen } from "@/tests"; +import { render, screen } from "@/tests"; import { describe, it } from "vitest"; import { Language } from "."; @@ -9,13 +9,13 @@ describe("Language text", () => { }; it("should show short text", () => { - rawRender(); + render(); expect(screen.getByText(testLanguage.code2)).toBeDefined(); }); it("should show long text", () => { - rawRender(); + render(); expect(screen.getByText(testLanguage.name)).toBeDefined(); }); @@ -23,7 +23,7 @@ describe("Language text", () => { const testLanguageWithHi: Language.Info = { ...testLanguage, hi: true }; it("should show short text with HI", () => { - rawRender(); + render(); const expectedText = `${testLanguageWithHi.code2}:HI`; @@ -31,7 +31,7 @@ describe("Language text", () => { }); it("should show long text with HI", () => { - rawRender(); + render(); const expectedText = `${testLanguageWithHi.name} HI`; @@ -44,7 +44,7 @@ describe("Language text", () => { }; it("should show short text with Forced", () => { - rawRender(); + render(); const expectedText = `${testLanguageWithHi.code2}:Forced`; @@ -52,9 +52,7 @@ describe("Language text", () => { }); it("should show long text with Forced", () => { - rawRender( - , - ); + render(); const expectedText = `${testLanguageWithHi.name} Forced`; @@ -75,7 +73,7 @@ describe("Language list", () => { ]; it("should show all languages", () => { - rawRender(); + render(); elements.forEach((value) => { expect(screen.getByText(value.name)).toBeDefined(); diff --git a/frontend/src/components/bazarr/Language.tsx b/frontend/src/components/bazarr/Language.tsx index e5627c82e..ba1a884e6 100644 --- a/frontend/src/components/bazarr/Language.tsx +++ b/frontend/src/components/bazarr/Language.tsx @@ -49,7 +49,7 @@ type LanguageListProps = { const LanguageList: FunctionComponent = ({ value }) => { return ( - + {value.map((v) => ( {v.name} ))} diff --git a/frontend/src/components/forms/FrameRateForm.tsx b/frontend/src/components/forms/FrameRateForm.tsx index 7e7eca24c..aa1723183 100644 --- a/frontend/src/components/forms/FrameRateForm.tsx +++ b/frontend/src/components/forms/FrameRateForm.tsx @@ -55,15 +55,17 @@ const FrameRateForm: FunctionComponent = ({ selections, onSubmit }) => { })} > - + diff --git a/frontend/src/components/forms/ItemEditForm.tsx b/frontend/src/components/forms/ItemEditForm.tsx index 9f3856d54..64f87fb36 100644 --- a/frontend/src/components/forms/ItemEditForm.tsx +++ b/frontend/src/components/forms/ItemEditForm.tsx @@ -80,7 +80,7 @@ const ItemEditForm: FunctionComponent = ({ label="Languages Profile" > - + diff --git a/frontend/src/components/forms/ProfileEditForm.module.scss b/frontend/src/components/forms/ProfileEditForm.module.scss new file mode 100644 index 000000000..d98b850ff --- /dev/null +++ b/frontend/src/components/forms/ProfileEditForm.module.scss @@ -0,0 +1,5 @@ +.content { + @include smaller-than($mantine-breakpoint-md) { + padding: 0; + } +} diff --git a/frontend/src/components/forms/ProfileEditForm.tsx b/frontend/src/components/forms/ProfileEditForm.tsx index eecacd73e..93f09d8b8 100644 --- a/frontend/src/components/forms/ProfileEditForm.tsx +++ b/frontend/src/components/forms/ProfileEditForm.tsx @@ -1,6 +1,5 @@ import { Action, Selector, SelectorOption, SimpleTable } from "@/components"; import { useModals, withModal } from "@/modules/modals"; -import { useTableStyles } from "@/styles"; import { useArrayAction, useSelectorOptions } from "@/utilities"; import { LOG } from "@/utilities/console"; import FormUtils from "@/utilities/form"; @@ -19,6 +18,7 @@ import { useForm } from "@mantine/form"; import { FunctionComponent, useCallback, useMemo } from "react"; import { Column } from "react-table"; import ChipInput from "../inputs/ChipInput"; +import styles from "./ProfileEditForm.module.scss"; export const anyCutoff = 65535; @@ -162,12 +162,10 @@ const ProfileEditForm: FunctionComponent = ({ [code], ); - const { classes } = useTableStyles(); - return ( { if (value) { @@ -260,13 +258,7 @@ const ProfileEditForm: FunctionComponent = ({ multiple chevronPosition="right" defaultValue={["Languages"]} - styles={(theme) => ({ - content: { - [theme.fn.smallerThan("md")]: { - padding: 0, - }, - }, - })} + className={styles.content} > @@ -275,7 +267,7 @@ const ProfileEditForm: FunctionComponent = ({ columns={columns} data={form.values.items} > - { - return { - wrapper: { - overflowWrap: "anywhere", - }, - }; -}); - const SeriesUploadForm: FunctionComponent = ({ series, files, onComplete, }) => { const modals = useModals(); - const { classes } = useStyles(); const episodes = useEpisodesBySeriesId(series.sonarrSeriesId); const episodeOptions = useSelectorOptions( episodes.data ?? [], @@ -225,8 +214,7 @@ const SeriesUploadForm: FunctionComponent = ({ id: "filename", accessor: "file", Cell: ({ value: { name } }) => { - const { classes } = useTableStyles(); - return {name}; + return {name}; }, }, { @@ -283,11 +271,10 @@ const SeriesUploadForm: FunctionComponent = ({ ), accessor: "language", Cell: ({ row: { original, index }, value }) => { - const { classes } = useTableStyles(); return ( { action.mutate(index, { ...original, language: item }); @@ -301,12 +288,11 @@ const SeriesUploadForm: FunctionComponent = ({ Header: "Episode", accessor: "episode", Cell: ({ value, row }) => { - const { classes } = useTableStyles(); return ( { action.mutate(row.index, { ...row.original, episode: item }); @@ -368,7 +354,7 @@ const SeriesUploadForm: FunctionComponent = ({ modals.closeSelf(); })} > - + diff --git a/frontend/src/components/forms/SyncSubtitleForm.tsx b/frontend/src/components/forms/SyncSubtitleForm.tsx index b5136fc85..c2e4ae9a5 100644 --- a/frontend/src/components/forms/SyncSubtitleForm.tsx +++ b/frontend/src/components/forms/SyncSubtitleForm.tsx @@ -14,10 +14,15 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Alert, Button, Checkbox, Divider, Stack, Text } from "@mantine/core"; import { useForm } from "@mantine/form"; import { FunctionComponent } from "react"; -import { Selector, SelectorOption } from "../inputs"; +import { GroupedSelector, Selector } from "../inputs"; const TaskName = "Syncing Subtitle"; +interface SelectOptions { + group: string; + items: { value: string; label: string }[]; +} + function useReferencedSubtitles( mediaType: "episode" | "movie", mediaId: number, @@ -37,15 +42,21 @@ function useReferencedSubtitles( const mediaData = mediaType === "episode" ? episodeData : movieData; - const subtitles: { group: string; value: string; label: string }[] = []; + const subtitles: SelectOptions[] = []; if (!mediaData.data) { return []; } else { if (mediaData.data.audio_tracks.length > 0) { + const embeddedAudioGroup: SelectOptions = { + group: "Embedded audio tracks", + items: [], + }; + + subtitles.push(embeddedAudioGroup); + mediaData.data.audio_tracks.forEach((item) => { - subtitles.push({ - group: "Embedded audio tracks", + embeddedAudioGroup.items.push({ value: item.stream, label: `${item.name || item.language} (${item.stream})`, }); @@ -53,9 +64,15 @@ function useReferencedSubtitles( } if (mediaData.data.embedded_subtitles_tracks.length > 0) { + const embeddedSubtitlesTrackGroup: SelectOptions = { + group: "Embedded subtitles tracks", + items: [], + }; + + subtitles.push(embeddedSubtitlesTrackGroup); + mediaData.data.embedded_subtitles_tracks.forEach((item) => { - subtitles.push({ - group: "Embedded subtitles tracks", + embeddedSubtitlesTrackGroup.items.push({ value: item.stream, label: `${item.name || item.language} (${item.stream})`, }); @@ -63,10 +80,16 @@ function useReferencedSubtitles( } if (mediaData.data.external_subtitles_tracks.length > 0) { + const externalSubtitlesFilesGroup: SelectOptions = { + group: "External Subtitles files", + items: [], + }; + + subtitles.push(externalSubtitlesFilesGroup); + mediaData.data.external_subtitles_tracks.forEach((item) => { if (item) { - subtitles.push({ - group: "External Subtitles files", + externalSubtitlesFilesGroup.items.push({ value: item.path, label: item.name, }); @@ -105,7 +128,7 @@ const SyncSubtitleForm: FunctionComponent = ({ const mediaId = selections[0].id; const subtitlesPath = selections[0].path; - const subtitles: SelectorOption[] = useReferencedSubtitles( + const subtitles: SelectOptions[] = useReferencedSubtitles( mediaType, mediaId, subtitlesPath, @@ -145,14 +168,14 @@ const SyncSubtitleForm: FunctionComponent = ({ > {selections.length} subtitles selected - + > = ({ selections, onSubmit }) => { })} > - +