From ec85f6e172cc004138d875f1980dd8d06dffa7a2 Mon Sep 17 00:00:00 2001 From: JayZed Date: Sat, 16 Mar 2024 22:10:50 -0400 Subject: [PATCH 01/58] 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 02/58] 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 03/58] 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 04/58] 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 05/58] 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 06/58] 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 07/58] 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 08/58] 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 09/58] 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 10/58] 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 11/58] 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 12/58] 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 13/58] 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 14/58] 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 15/58] 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 16/58] 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 17/58] 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 18/58] 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 19/58] 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 20/58] 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 21/58] 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 22/58] 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 23/58] 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 24/58] 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 25/58] 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 26/58] 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 27/58] 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 28/58] 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 29/58] 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 30/58] 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 31/58] 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 32/58] 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 33/58] 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 34/58] 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 35/58] 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 36/58] 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 37/58] 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 38/58] 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 39/58] 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 40/58] 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 41/58] 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 42/58] 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 43/58] 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 44/58] 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 45/58] 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 46/58] 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 47/58] 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 48/58] 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 49/58] 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 50/58] 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 51/58] 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 52/58] 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 53/58] 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 54/58] 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 55/58] 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 56/58] 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 57/58] 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 58/58] 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()