From 6fe4fe55f308b06c7975b61ba6e1bb63535a7bc2 Mon Sep 17 00:00:00 2001 From: LimeDrive Date: Tue, 9 Jul 2024 04:13:29 +0200 Subject: [PATCH] init --- .dockerignore | 150 + .github/ISSUE_TEMPLATE/bug_report.md | 30 + .github/ISSUE_TEMPLATE/feature_request.md | 20 + .github/workflows/docker-develop.yml | 38 + .gitignore | 148 + Dockerfile | 18 + LICENSE | 21 + README.md | 165 ++ deploy/docker-compose.yml | 1 + poetry.lock | 2463 +++++++++++++++++ poetry.toml | 3 + pyproject.toml | 34 + stream_fusion/__init__.py | 1 + stream_fusion/__main__.py | 36 + stream_fusion/constants.py | 87 + stream_fusion/gunicorn_runner.py | 86 + stream_fusion/logging_config.py | 109 + stream_fusion/services/redis/redis_config.py | 26 + .../services/security_db/__init__.py | 4 + .../services/security_db/_sqlite_access.py | 181 ++ stream_fusion/settings.py | 70 + stream_fusion/static/docs/redoc.standalone.js | 1804 ++++++++++++ .../static/docs/swagger-ui-bundle.js | 3 + stream_fusion/static/docs/swagger-ui.css | 3 + stream_fusion/templates/config.js | 270 ++ .../templates/docs/redoc.standalone.js | 1804 ++++++++++++ .../templates/docs/swagger-ui-bundle.js | 3 + stream_fusion/templates/docs/swagger-ui.css | 3 + stream_fusion/templates/index.html | 643 +++++ stream_fusion/utils/cache/__init__.py | 4 + stream_fusion/utils/cache/cache.py | 142 + stream_fusion/utils/cache/cache_base.py | 85 + stream_fusion/utils/cache/local_redis.py | 219 ++ stream_fusion/utils/debrid/__init__.py | 9 + stream_fusion/utils/debrid/alldebrid.py | 126 + stream_fusion/utils/debrid/base_debrid.py | 61 + .../utils/debrid/get_debrid_service.py | 19 + stream_fusion/utils/debrid/premiumize.py | 120 + stream_fusion/utils/debrid/realdebrid.py | 285 ++ stream_fusion/utils/detection.py | 28 + stream_fusion/utils/filter/base_filter.py | 15 + stream_fusion/utils/filter/language_filter.py | 52 + stream_fusion/utils/filter/max_size_filter.py | 23 + .../utils/filter/quality_exclusion_filter.py | 43 + .../filter/results_per_quality_filter.py | 33 + .../utils/filter/title_exclusion_filter.py | 32 + stream_fusion/utils/filter_results.py | 196 ++ stream_fusion/utils/general.py | 42 + stream_fusion/utils/jackett/__init__.py | 6 + .../utils/jackett/jackett_indexer.py | 9 + stream_fusion/utils/jackett/jackett_result.py | 61 + .../utils/jackett/jackett_service.py | 279 ++ stream_fusion/utils/metdata/cinemeta.py | 35 + .../utils/metdata/metadata_provider_base.py | 35 + stream_fusion/utils/metdata/tmdb.py | 43 + stream_fusion/utils/models/media.py | 6 + stream_fusion/utils/models/movie.py | 7 + stream_fusion/utils/models/series.py | 9 + stream_fusion/utils/parse_config.py | 16 + stream_fusion/utils/security/__init__.py | 5 + .../utils/security/security_api_key.py | 43 + .../utils/security/security_secret.py | 99 + stream_fusion/utils/string_encoding.py | 9 + stream_fusion/utils/torrent/torrent_item.py | 93 + .../utils/torrent/torrent_service.py | 195 ++ .../utils/torrent/torrent_smart_container.py | 212 ++ stream_fusion/utils/zilean/zilean_result.py | 63 + stream_fusion/utils/zilean/zilean_service.py | 99 + stream_fusion/version.py | 12 + stream_fusion/videos/nocache.mp4 | Bin 0 -> 240125 bytes stream_fusion/web/__init__.py | 1 + stream_fusion/web/api/__init__.py | 0 stream_fusion/web/api/auth/__init__.py | 4 + stream_fusion/web/api/auth/schemas.py | 17 + stream_fusion/web/api/auth/views.py | 92 + stream_fusion/web/api/docs/__init__.py | 4 + stream_fusion/web/api/docs/views.py | 53 + stream_fusion/web/api/monitoring/__init__.py | 4 + stream_fusion/web/api/monitoring/views.py | 12 + stream_fusion/web/api/router.py | 7 + stream_fusion/web/application.py | 64 + stream_fusion/web/lifetime.py | 44 + stream_fusion/web/playback/__init__.py | 0 stream_fusion/web/playback/router.py | 6 + stream_fusion/web/playback/stream/__init__.py | 3 + stream_fusion/web/playback/stream/schemas.py | 13 + stream_fusion/web/playback/stream/views.py | 169 ++ stream_fusion/web/root/__init__.py | 3 + stream_fusion/web/root/config/__init__.py | 3 + stream_fusion/web/root/config/schemas.py | 22 + stream_fusion/web/root/config/views.py | 78 + stream_fusion/web/root/router.py | 8 + stream_fusion/web/root/search/__init__.py | 3 + stream_fusion/web/root/search/schemas.py | 13 + .../web/root/search/stremio_parser.py | 141 + stream_fusion/web/root/search/views.py | 255 ++ 96 files changed, 12113 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/workflows/docker-develop.yml create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100644 deploy/docker-compose.yml create mode 100644 poetry.lock create mode 100644 poetry.toml create mode 100644 pyproject.toml create mode 100644 stream_fusion/__init__.py create mode 100644 stream_fusion/__main__.py create mode 100644 stream_fusion/constants.py create mode 100644 stream_fusion/gunicorn_runner.py create mode 100644 stream_fusion/logging_config.py create mode 100644 stream_fusion/services/redis/redis_config.py create mode 100644 stream_fusion/services/security_db/__init__.py create mode 100644 stream_fusion/services/security_db/_sqlite_access.py create mode 100644 stream_fusion/settings.py create mode 100644 stream_fusion/static/docs/redoc.standalone.js create mode 100644 stream_fusion/static/docs/swagger-ui-bundle.js create mode 100644 stream_fusion/static/docs/swagger-ui.css create mode 100644 stream_fusion/templates/config.js create mode 100644 stream_fusion/templates/docs/redoc.standalone.js create mode 100644 stream_fusion/templates/docs/swagger-ui-bundle.js create mode 100644 stream_fusion/templates/docs/swagger-ui.css create mode 100644 stream_fusion/templates/index.html create mode 100644 stream_fusion/utils/cache/__init__.py create mode 100644 stream_fusion/utils/cache/cache.py create mode 100644 stream_fusion/utils/cache/cache_base.py create mode 100644 stream_fusion/utils/cache/local_redis.py create mode 100644 stream_fusion/utils/debrid/__init__.py create mode 100644 stream_fusion/utils/debrid/alldebrid.py create mode 100644 stream_fusion/utils/debrid/base_debrid.py create mode 100644 stream_fusion/utils/debrid/get_debrid_service.py create mode 100644 stream_fusion/utils/debrid/premiumize.py create mode 100644 stream_fusion/utils/debrid/realdebrid.py create mode 100644 stream_fusion/utils/detection.py create mode 100644 stream_fusion/utils/filter/base_filter.py create mode 100644 stream_fusion/utils/filter/language_filter.py create mode 100644 stream_fusion/utils/filter/max_size_filter.py create mode 100644 stream_fusion/utils/filter/quality_exclusion_filter.py create mode 100644 stream_fusion/utils/filter/results_per_quality_filter.py create mode 100644 stream_fusion/utils/filter/title_exclusion_filter.py create mode 100644 stream_fusion/utils/filter_results.py create mode 100644 stream_fusion/utils/general.py create mode 100644 stream_fusion/utils/jackett/__init__.py create mode 100644 stream_fusion/utils/jackett/jackett_indexer.py create mode 100644 stream_fusion/utils/jackett/jackett_result.py create mode 100644 stream_fusion/utils/jackett/jackett_service.py create mode 100644 stream_fusion/utils/metdata/cinemeta.py create mode 100644 stream_fusion/utils/metdata/metadata_provider_base.py create mode 100644 stream_fusion/utils/metdata/tmdb.py create mode 100644 stream_fusion/utils/models/media.py create mode 100644 stream_fusion/utils/models/movie.py create mode 100644 stream_fusion/utils/models/series.py create mode 100644 stream_fusion/utils/parse_config.py create mode 100644 stream_fusion/utils/security/__init__.py create mode 100644 stream_fusion/utils/security/security_api_key.py create mode 100644 stream_fusion/utils/security/security_secret.py create mode 100644 stream_fusion/utils/string_encoding.py create mode 100644 stream_fusion/utils/torrent/torrent_item.py create mode 100644 stream_fusion/utils/torrent/torrent_service.py create mode 100644 stream_fusion/utils/torrent/torrent_smart_container.py create mode 100644 stream_fusion/utils/zilean/zilean_result.py create mode 100644 stream_fusion/utils/zilean/zilean_service.py create mode 100644 stream_fusion/version.py create mode 100644 stream_fusion/videos/nocache.mp4 create mode 100644 stream_fusion/web/__init__.py create mode 100644 stream_fusion/web/api/__init__.py create mode 100644 stream_fusion/web/api/auth/__init__.py create mode 100644 stream_fusion/web/api/auth/schemas.py create mode 100644 stream_fusion/web/api/auth/views.py create mode 100644 stream_fusion/web/api/docs/__init__.py create mode 100644 stream_fusion/web/api/docs/views.py create mode 100644 stream_fusion/web/api/monitoring/__init__.py create mode 100644 stream_fusion/web/api/monitoring/views.py create mode 100644 stream_fusion/web/api/router.py create mode 100644 stream_fusion/web/application.py create mode 100644 stream_fusion/web/lifetime.py create mode 100644 stream_fusion/web/playback/__init__.py create mode 100644 stream_fusion/web/playback/router.py create mode 100644 stream_fusion/web/playback/stream/__init__.py create mode 100644 stream_fusion/web/playback/stream/schemas.py create mode 100644 stream_fusion/web/playback/stream/views.py create mode 100644 stream_fusion/web/root/__init__.py create mode 100644 stream_fusion/web/root/config/__init__.py create mode 100644 stream_fusion/web/root/config/schemas.py create mode 100644 stream_fusion/web/root/config/views.py create mode 100644 stream_fusion/web/root/router.py create mode 100644 stream_fusion/web/root/search/__init__.py create mode 100644 stream_fusion/web/root/search/schemas.py create mode 100644 stream_fusion/web/root/search/stremio_parser.py create mode 100644 stream_fusion/web/root/search/views.py diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f536d28 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,150 @@ +### Python template + +deploy/ +.idea/ +.vscode/ +.git/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# database +*.db + +# poetry +poetry.toml diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..16c88e7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[Bug]" +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: + + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Your system info (please complete the following information):** + - OS: [e.g. Ubuntu] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + - Docker config ? + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..6269982 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: "[Feature]" +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/docker-develop.yml b/.github/workflows/docker-develop.yml new file mode 100644 index 0000000..2668531 --- /dev/null +++ b/.github/workflows/docker-develop.yml @@ -0,0 +1,38 @@ +name: Build and Push Docker image to GitHub Packages on Dev Branch + +on: + push: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: all + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GHCR_PAT }} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: source/. + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ghcr.io/limedrive/stremio-jackett:develop diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6813cf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,148 @@ +### Python template +.idea/ +.vscode/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +*.log.zip +local_settings.py +*.sqlite3 +*.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# database +*.db + +# apple +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..59b25c6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.11.9-slim-bullseye + +RUN pip install poetry +WORKDIR /app + +COPY pyproject.toml poetry.lock ./ + +RUN poetry config virtualenvs.create false \ + && poetry install --only main --no-interaction --no-ansi --no-root + +COPY . . + +ARG GUNICORN_PORT=8080 +ENV EXPOSE_PORT=${GUNICORN_PORT} + +EXPOSE ${EXPOSE_PORT} + +CMD ["python", "-m", "stream_fusion"] \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c4b890b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 LimeCat on the Hub's + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..10d6017 --- /dev/null +++ b/README.md @@ -0,0 +1,165 @@ +# StreamFusion + +## Description + +StreamFusion is an advanced plugin for Stremio that significantly enhances its streaming capabilities. It leverages torrent indexers and Jackett to integrate cached torrent sources from popular debrid services, offering a smooth and comprehensive streaming experience. This application acts as a bridge between Stremio, torrent indexers, and debrid services, providing users with a wide range of content options. + +## Key Features + +- **Torrent Indexer Integration**: Utilizes various torrent indexers and Jackett to source content. +- **Debrid Service Integration**: Adds cached torrents from RealDebrid, AllDebrid, and Premiumize as sources for Stremio. +- **Direct Streaming**: Uses indexers for direct streaming when cached torrents are unavailable. +- **Advanced Cache Management**: + - Utilizes Redis for efficient caching. + - Community cache for sharing cached torrent searches. +- **Zilean API**: Integration of the DMM Zilean API to obtain cached torrent hashes. +- **Stream Proxification**: All streams originate from a single IP address. +- **Download Management**: Capable of managing torrent downloads on debrid services. + +## Installation + +[Installation instructions to be added] + +## Configuration + +[Configuration details to be added] + +## Usage + +[Usage guide to be added] + +## Contributing + +Contributions are welcome! Please refer to our contribution guidelines for more details. + +```bash +tree -I '*.pyc' -I '__pycache__' +. +├── Dockerfile +├── README.md +├── deploy +│   └── docker-compose.yml +├── poetry.lock +├── poetry.toml +├── pyproject.toml +└── stream_fusion + ├── __init__.py + ├── __main__.py + ├── constants.py + ├── gunicorn_runner.py + ├── logging_config.py + ├── services + │   ├── redis + │   │   └── redis_config.py + │   └── security_db + │   ├── __init__.py + │   └── _sqlite_access.py + ├── settings.py + ├── static + ├── templates + │   ├── config.js + │   └── index.html + ├── utils + │   ├── cache + │   │   ├── cache_base.py + │   │   └── local_redis.py + │   ├── cache.py + │   ├── debrid + │   │   ├── alldebrid.py + │   │   ├── base_debrid.py + │   │   ├── get_debrid_service.py + │   │   ├── premiumize.py + │   │   └── realdebrid.py + │   ├── detection.py + │   ├── filter + │   │   ├── base_filter.py + │   │   ├── language_filter.py + │   │   ├── max_size_filter.py + │   │   ├── quality_exclusion_filter.py + │   │   ├── results_per_quality_filter.py + │   │   └── title_exclusion_filter.py + │   ├── filter_results.py + │   ├── general.py + │   ├── jackett + │   │   ├── jackett_indexer.py + │   │   ├── jackett_result.py + │   │   └── jackett_service.py + │   ├── metdata + │   │   ├── cinemeta.py + │   │   ├── metadata_provider_base.py + │   │   └── tmdb.py + │   ├── models + │   │   ├── media.py + │   │   ├── movie.py + │   │   └── series.py + │   ├── parse_config.py + │   ├── security + │   │   ├── __init__.py + │   │   ├── security_api_key.py + │   │   └── security_secret.py + │   ├── string_encoding.py + │   ├── torrent + │   │   ├── torrent_item.py + │   │   ├── torrent_service.py + │   │   └── torrent_smart_container.py + │   └── zilean + │   ├── zilean_result.py + │   └── zilean_service.py + ├── version.py + ├── videos + │   └── nocache.mp4 + └── web + ├── __init__.py + ├── api + │   ├── __init__.py + │   ├── auth + │   │   ├── __init__.py + │   │   ├── schemas.py + │   │   └── views.py + │   ├── docs + │   │   ├── __init__.py + │   │   └── views.py + │   ├── monitoring + │   │   ├── __init__.py + │   │   └── views.py + │   └── router.py + ├── application.py + ├── lifetime.py + ├── playback + │   ├── __init__.py + │   ├── router.py + │   └── stream + │   ├── __init__.py + │   ├── schemas.py + │   └── views.py + └── root + ├── __init__.py + ├── config + │   ├── __init__.py + │   ├── schemas.py + │   └── views.py + ├── router.py + └── search + ├── __init__.py + ├── schemas.py + ├── stremio_parser.py + └── views.py + +29 directories, 81 files +``` + +## License + +[License information to be added] + +## Disclaimer + +This project is intended for educational and research purposes only. Users are responsible for their use of the software and must comply with local copyright and intellectual property laws. + +## Contact + +[Contact information to be added] + +--- + +StreamFusion is a project derived from Stremio-Jackett, rewritten and improved to offer a better user experience and extended functionalities. \ No newline at end of file diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 0000000..2fed88f --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1 @@ +# TODO: \ No newline at end of file diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..7308bf7 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2463 @@ +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. + +[[package]] +name = "aiocron" +version = "1.8" +description = "Crontabs for asyncio" +optional = false +python-versions = "*" +files = [ + {file = "aiocron-1.8-py3-none-any.whl", hash = "sha256:b6313214c311b62aa2220e872b94139b648631b3103d062ef29e5d3230ddce6d"}, + {file = "aiocron-1.8.tar.gz", hash = "sha256:48546513faf2eb7901e65a64eba7b653c80106ed00ed9ca3419c3d10b6555a01"}, +] + +[package.dependencies] +croniter = "*" +tzlocal = "*" + +[package.extras] +test = ["coverage"] + +[[package]] +name = "aiohttp" +version = "3.9.5" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fcde4c397f673fdec23e6b05ebf8d4751314fa7c24f93334bf1f1364c1c69ac7"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d6b3f1fabe465e819aed2c421a6743d8debbde79b6a8600739300630a01bf2c"}, + {file = "aiohttp-3.9.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ae79c1bc12c34082d92bf9422764f799aee4746fd7a392db46b7fd357d4a17a"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d3ebb9e1316ec74277d19c5f482f98cc65a73ccd5430540d6d11682cd857430"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84dabd95154f43a2ea80deffec9cb44d2e301e38a0c9d331cc4aa0166fe28ae3"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c8a02fbeca6f63cb1f0475c799679057fc9268b77075ab7cf3f1c600e81dd46b"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c26959ca7b75ff768e2776d8055bf9582a6267e24556bb7f7bd29e677932be72"}, + {file = "aiohttp-3.9.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:714d4e5231fed4ba2762ed489b4aec07b2b9953cf4ee31e9871caac895a839c0"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7a6a8354f1b62e15d48e04350f13e726fa08b62c3d7b8401c0a1314f02e3558"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c413016880e03e69d166efb5a1a95d40f83d5a3a648d16486592c49ffb76d0db"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ff84aeb864e0fac81f676be9f4685f0527b660f1efdc40dcede3c251ef1e867f"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ad7f2919d7dac062f24d6f5fe95d401597fbb015a25771f85e692d043c9d7832"}, + {file = "aiohttp-3.9.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:702e2c7c187c1a498a4e2b03155d52658fdd6fda882d3d7fbb891a5cf108bb10"}, + {file = "aiohttp-3.9.5-cp310-cp310-win32.whl", hash = "sha256:67c3119f5ddc7261d47163ed86d760ddf0e625cd6246b4ed852e82159617b5fb"}, + {file = "aiohttp-3.9.5-cp310-cp310-win_amd64.whl", hash = "sha256:471f0ef53ccedec9995287f02caf0c068732f026455f07db3f01a46e49d76bbb"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e0ae53e33ee7476dd3d1132f932eeb39bf6125083820049d06edcdca4381f342"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c088c4d70d21f8ca5c0b8b5403fe84a7bc8e024161febdd4ef04575ef35d474d"}, + {file = "aiohttp-3.9.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:639d0042b7670222f33b0028de6b4e2fad6451462ce7df2af8aee37dcac55424"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f26383adb94da5e7fb388d441bf09c61e5e35f455a3217bfd790c6b6bc64b2ee"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66331d00fb28dc90aa606d9a54304af76b335ae204d1836f65797d6fe27f1ca2"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ff550491f5492ab5ed3533e76b8567f4b37bd2995e780a1f46bca2024223233"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f22eb3a6c1080d862befa0a89c380b4dafce29dc6cd56083f630073d102eb595"}, + {file = "aiohttp-3.9.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a81b1143d42b66ffc40a441379387076243ef7b51019204fd3ec36b9f69e77d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f64fd07515dad67f24b6ea4a66ae2876c01031de91c93075b8093f07c0a2d93d"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:93e22add827447d2e26d67c9ac0161756007f152fdc5210277d00a85f6c92323"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:55b39c8684a46e56ef8c8d24faf02de4a2b2ac60d26cee93bc595651ff545de9"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4715a9b778f4293b9f8ae7a0a7cef9829f02ff8d6277a39d7f40565c737d3771"}, + {file = "aiohttp-3.9.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:afc52b8d969eff14e069a710057d15ab9ac17cd4b6753042c407dcea0e40bf75"}, + {file = "aiohttp-3.9.5-cp311-cp311-win32.whl", hash = "sha256:b3df71da99c98534be076196791adca8819761f0bf6e08e07fd7da25127150d6"}, + {file = "aiohttp-3.9.5-cp311-cp311-win_amd64.whl", hash = "sha256:88e311d98cc0bf45b62fc46c66753a83445f5ab20038bcc1b8a1cc05666f428a"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c7a4b7a6cf5b6eb11e109a9755fd4fda7d57395f8c575e166d363b9fc3ec4678"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0a158704edf0abcac8ac371fbb54044f3270bdbc93e254a82b6c82be1ef08f3c"}, + {file = "aiohttp-3.9.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d153f652a687a8e95ad367a86a61e8d53d528b0530ef382ec5aaf533140ed00f"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82a6a97d9771cb48ae16979c3a3a9a18b600a8505b1115cfe354dfb2054468b4"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60cdbd56f4cad9f69c35eaac0fbbdf1f77b0ff9456cebd4902f3dd1cf096464c"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8676e8fd73141ded15ea586de0b7cda1542960a7b9ad89b2b06428e97125d4fa"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da00da442a0e31f1c69d26d224e1efd3a1ca5bcbf210978a2ca7426dfcae9f58"}, + {file = "aiohttp-3.9.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18f634d540dd099c262e9f887c8bbacc959847cfe5da7a0e2e1cf3f14dbf2daf"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:320e8618eda64e19d11bdb3bd04ccc0a816c17eaecb7e4945d01deee2a22f95f"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:2faa61a904b83142747fc6a6d7ad8fccff898c849123030f8e75d5d967fd4a81"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:8c64a6dc3fe5db7b1b4d2b5cb84c4f677768bdc340611eca673afb7cf416ef5a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:393c7aba2b55559ef7ab791c94b44f7482a07bf7640d17b341b79081f5e5cd1a"}, + {file = "aiohttp-3.9.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c671dc117c2c21a1ca10c116cfcd6e3e44da7fcde37bf83b2be485ab377b25da"}, + {file = "aiohttp-3.9.5-cp312-cp312-win32.whl", hash = "sha256:5a7ee16aab26e76add4afc45e8f8206c95d1d75540f1039b84a03c3b3800dd59"}, + {file = "aiohttp-3.9.5-cp312-cp312-win_amd64.whl", hash = "sha256:5ca51eadbd67045396bc92a4345d1790b7301c14d1848feaac1d6a6c9289e888"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:694d828b5c41255e54bc2dddb51a9f5150b4eefa9886e38b52605a05d96566e8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0605cc2c0088fcaae79f01c913a38611ad09ba68ff482402d3410bf59039bfb8"}, + {file = "aiohttp-3.9.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4558e5012ee03d2638c681e156461d37b7a113fe13970d438d95d10173d25f78"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dbc053ac75ccc63dc3a3cc547b98c7258ec35a215a92bd9f983e0aac95d3d5b"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4109adee842b90671f1b689901b948f347325045c15f46b39797ae1bf17019de"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6ea1a5b409a85477fd8e5ee6ad8f0e40bf2844c270955e09360418cfd09abac"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3c2890ca8c59ee683fd09adf32321a40fe1cf164e3387799efb2acebf090c11"}, + {file = "aiohttp-3.9.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3916c8692dbd9d55c523374a3b8213e628424d19116ac4308e434dbf6d95bbdd"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8d1964eb7617907c792ca00b341b5ec3e01ae8c280825deadbbd678447b127e1"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5ab8e1f6bee051a4bf6195e38a5c13e5e161cb7bad83d8854524798bd9fcd6e"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:52c27110f3862a1afbcb2af4281fc9fdc40327fa286c4625dfee247c3ba90156"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7f64cbd44443e80094309875d4f9c71d0401e966d191c3d469cde4642bc2e031"}, + {file = "aiohttp-3.9.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8b4f72fbb66279624bfe83fd5eb6aea0022dad8eec62b71e7bf63ee1caadeafe"}, + {file = "aiohttp-3.9.5-cp38-cp38-win32.whl", hash = "sha256:6380c039ec52866c06d69b5c7aad5478b24ed11696f0e72f6b807cfb261453da"}, + {file = "aiohttp-3.9.5-cp38-cp38-win_amd64.whl", hash = "sha256:da22dab31d7180f8c3ac7c7635f3bcd53808f374f6aa333fe0b0b9e14b01f91a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1732102949ff6087589408d76cd6dea656b93c896b011ecafff418c9661dc4ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c6021d296318cb6f9414b48e6a439a7f5d1f665464da507e8ff640848ee2a58a"}, + {file = "aiohttp-3.9.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:239f975589a944eeb1bad26b8b140a59a3a320067fb3cd10b75c3092405a1372"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b7b30258348082826d274504fbc7c849959f1989d86c29bc355107accec6cfb"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2adf5c87ff6d8b277814a28a535b59e20bfea40a101db6b3bdca7e9926bc24"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a3d838441bebcf5cf442700e3963f58b5c33f015341f9ea86dcd7d503c07e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3a1ae66e3d0c17cf65c08968a5ee3180c5a95920ec2731f53343fac9bad106"}, + {file = "aiohttp-3.9.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c69e77370cce2d6df5d12b4e12bdcca60c47ba13d1cbbc8645dd005a20b738b"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0cbf56238f4bbf49dab8c2dc2e6b1b68502b1e88d335bea59b3f5b9f4c001475"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d1469f228cd9ffddd396d9948b8c9cd8022b6d1bf1e40c6f25b0fb90b4f893ed"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:45731330e754f5811c314901cebdf19dd776a44b31927fa4b4dbecab9e457b0c"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3fcb4046d2904378e3aeea1df51f697b0467f2aac55d232c87ba162709478c46"}, + {file = "aiohttp-3.9.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8cf142aa6c1a751fcb364158fd710b8a9be874b81889c2bd13aa8893197455e2"}, + {file = "aiohttp-3.9.5-cp39-cp39-win32.whl", hash = "sha256:7b179eea70833c8dee51ec42f3b4097bd6370892fa93f510f76762105568cf09"}, + {file = "aiohttp-3.9.5-cp39-cp39-win_amd64.whl", hash = "sha256:38d80498e2e169bc61418ff36170e0aad0cd268da8b38a17c4cf29d254a8b3f1"}, + {file = "aiohttp-3.9.5.tar.gz", hash = "sha256:edea7d15772ceeb29db4aff55e482d4bcfb6ae160ce144f2682de02f6d693551"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "brotlicffi"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "aiosqlite" +version = "0.20.0" +description = "asyncio bridge to the standard sqlite3 module" +optional = false +python-versions = ">=3.8" +files = [ + {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, + {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, +] + +[package.dependencies] +typing_extensions = ">=4.0" + +[package.extras] +dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.4.0" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.8" +files = [ + {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, + {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] + +[[package]] +name = "arrow" +version = "1.3.0" +description = "Better dates & times for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, + {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, +] + +[package.dependencies] +python-dateutil = ">=2.7.0" +types-python-dateutil = ">=2.8.10" + +[package.extras] +doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] +test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, +] + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] + +[[package]] +name = "bencode-py" +version = "4.0.0" +description = "Simple bencode parser (for Python 2, Python 3 and PyPy)" +optional = false +python-versions = "*" +files = [ + {file = "bencode.py-4.0.0-py2.py3-none-any.whl", hash = "sha256:99c06a55764e85ffe81622fdf9ee78bd737bad3ea61d119784a54bb28860d962"}, + {file = "bencode.py-4.0.0.tar.gz", hash = "sha256:2a24ccda1725a51a650893d0b63260138359eaa299bb6e7a09961350a2a6e05c"}, +] + +[[package]] +name = "cachetools" +version = "5.3.3" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, + {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, +] + +[[package]] +name = "certifi" +version = "2024.7.4" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, + {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "croniter" +version = "2.0.5" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6" +files = [ + {file = "croniter-2.0.5-py2.py3-none-any.whl", hash = "sha256:fdbb44920944045cc323db54599b321325141d82d14fa7453bc0699826bbe9ed"}, + {file = "croniter-2.0.5.tar.gz", hash = "sha256:f1f8ca0af64212fbe99b1bee125ee5a1b53a9c1b433968d8bca8817b79d237f3"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "dnspython" +version = "2.6.1" +description = "DNS toolkit" +optional = false +python-versions = ">=3.8" +files = [ + {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, + {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, +] + +[package.extras] +dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] +dnssec = ["cryptography (>=41)"] +doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] +doq = ["aioquic (>=0.9.25)"] +idna = ["idna (>=3.6)"] +trio = ["trio (>=0.23)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "email-validator" +version = "2.2.0" +description = "A robust email address syntax and deliverability validation library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631"}, + {file = "email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7"}, +] + +[package.dependencies] +dnspython = ">=2.0.0" +idna = ">=2.0.0" + +[[package]] +name = "fastapi" +version = "0.111.0" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi-0.111.0-py3-none-any.whl", hash = "sha256:97ecbf994be0bcbdadedf88c3150252bed7b2087075ac99735403b1b76cc8fc0"}, + {file = "fastapi-0.111.0.tar.gz", hash = "sha256:b9db9dd147c91cb8b769f7183535773d8741dd46f9dc6676cd82eab510228cd7"}, +] + +[package.dependencies] +email_validator = ">=2.0.0" +fastapi-cli = ">=0.0.2" +httpx = ">=0.23.0" +jinja2 = ">=2.11.2" +orjson = ">=3.2.1" +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +python-multipart = ">=0.0.7" +starlette = ">=0.37.2,<0.38.0" +typing-extensions = ">=4.8.0" +ujson = ">=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0" +uvicorn = {version = ">=0.12.0", extras = ["standard"]} + +[package.extras] +all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastapi-cli" +version = "0.0.4" +description = "Run and manage FastAPI apps from the command line with FastAPI CLI. 🚀" +optional = false +python-versions = ">=3.8" +files = [ + {file = "fastapi_cli-0.0.4-py3-none-any.whl", hash = "sha256:a2552f3a7ae64058cdbb530be6fa6dbfc975dc165e4fa66d224c3d396e25e809"}, + {file = "fastapi_cli-0.0.4.tar.gz", hash = "sha256:e2e9ffaffc1f7767f488d6da34b6f5a377751c996f397902eb6abb99a67bde32"}, +] + +[package.dependencies] +typer = ">=0.12.3" + +[package.extras] +standard = ["fastapi", "uvicorn[standard] (>=0.15.0)"] + +[[package]] +name = "frozenlist" +version = "1.4.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.8" +files = [ + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, + {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, + {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, + {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, + {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, + {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, + {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, + {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, + {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, + {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, + {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, + {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, + {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, + {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, + {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, + {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, + {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, + {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, + {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, + {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, + {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, + {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, + {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, + {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, + {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, + {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, + {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, + {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, +] + +[[package]] +name = "greenlet" +version = "3.0.3" +description = "Lightweight in-process concurrent programming" +optional = false +python-versions = ">=3.7" +files = [ + {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, + {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, + {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, + {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, + {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, + {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, + {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, + {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, + {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, + {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, + {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, + {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, + {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, + {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, + {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, + {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, + {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, + {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, + {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, + {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, + {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, + {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, + {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, + {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, + {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, + {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, + {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, + {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, +] + +[package.extras] +docs = ["Sphinx", "furo"] +test = ["objgraph", "psutil"] + +[[package]] +name = "gunicorn" +version = "22.0.0" +description = "WSGI HTTP Server for UNIX" +optional = false +python-versions = ">=3.7" +files = [ + {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, + {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, +] + +[package.dependencies] +packaging = "*" + +[package.extras] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] +gevent = ["gevent (>=1.4.0)"] +setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] +tornado = ["tornado (>=0.2)"] + +[[package]] +name = "h11" +version = "0.14.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.7" +files = [ + {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, + {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, +] + +[[package]] +name = "httpcore" +version = "1.0.5" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, + {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.26.0)"] + +[[package]] +name = "httptools" +version = "0.6.1" +description = "A collection of framework independent HTTP protocol utils." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, + {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, + {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, + {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, + {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, + {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, + {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, + {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, + {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, + {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, + {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, + {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, + {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, + {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, + {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, + {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, + {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, + {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, + {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, + {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, + {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, + {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, +] + +[package.extras] +test = ["Cython (>=0.29.24,<0.30.0)"] + +[[package]] +name = "httpx" +version = "0.27.0" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[[package]] +name = "idna" +version = "3.7" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, + {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jsonpickle" +version = "3.2.2" +description = "Python library for serializing arbitrary object graphs into JSON" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonpickle-3.2.2-py3-none-any.whl", hash = "sha256:87cd82d237fd72c5a34970e7222dddc0accc13fddf49af84111887ed9a9445aa"}, + {file = "jsonpickle-3.2.2.tar.gz", hash = "sha256:d425fd2b8afe9f5d7d57205153403fbf897782204437882a477e8eed60930f8c"}, +] + +[package.extras] +docs = ["furo", "rst.linker (>=1.9)", "sphinx"] +packaging = ["build", "twine"] +testing = ["bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=3.5,!=3.7.3)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy", "scipy (>=1.9.3)", "simplejson", "sqlalchemy", "ujson"] + +[[package]] +name = "levenshtein" +version = "0.25.1" +description = "Python extension for computing string edit distances and similarities." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:eb4d1ec9f2dcbde1757c4b7fb65b8682bc2de45b9552e201988f287548b7abdf"}, + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b4d9fa3affef48a7e727cdbd0d9502cd060da86f34d8b3627edd769d347570e2"}, + {file = "Levenshtein-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c1b6cd186e58196ff8b402565317e9346b408d0c04fa0ed12ce4868c0fcb6d03"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82637ef5428384dd1812849dd7328992819bf0c4a20bff0a3b3ee806821af7ed"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e73656da6cc3e32a6e4bcd48562fcb64599ef124997f2c91f5320d7f1532c069"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5abff796f92cdfba69b9cbf6527afae918d0e95cbfac000bd84017f74e0bd427"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38827d82f2ca9cb755da6f03e686866f2f411280db005f4304272378412b4cba"}, + {file = "Levenshtein-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b989df1e3231261a87d68dfa001a2070771e178b09650f9cf99a20e3d3abc28"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2011d3b3897d438a2f88ef7aed7747f28739cae8538ec7c18c33dd989930c7a0"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6c375b33ec7acc1c6855e8ee8c7c8ac6262576ffed484ff5c556695527f49686"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ce0cb9dd012ef1bf4d5b9d40603e7709b6581aec5acd32fcea9b371b294ca7aa"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:9da9ecb81bae67d784defed7274f894011259b038ec31f2339c4958157970115"}, + {file = "Levenshtein-0.25.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3bd7be5dbe5f4a1b691f381e39512927b39d1e195bd0ad61f9bf217a25bf36c9"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win32.whl", hash = "sha256:f6abb9ced98261de67eb495b95e1d2325fa42b0344ed5763f7c0f36ee2e2bdba"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:97581af3e0a6d359af85c6cf06e51f77f4d635f7109ff7f8ed7fd634d8d8c923"}, + {file = "Levenshtein-0.25.1-cp310-cp310-win_arm64.whl", hash = "sha256:9ba008f490788c6d8d5a10735fcf83559965be97e4ef0812db388a84b1cc736a"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f57d9cf06dac55c2d2f01f0d06e32acc074ab9a902921dc8fddccfb385053ad5"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:22b60c6d791f4ca67a3686b557ddb2a48de203dae5214f220f9dddaab17f44bb"}, + {file = "Levenshtein-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d0444ee62eccf1e6cedc7c5bc01a9face6ff70cc8afa3f3ca9340e4e16f601a4"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e8758be8221a274c83924bae8dd8f42041792565a3c3bdd3c10e3f9b4a5f94e"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:147221cfb1d03ed81d22fdd2a4c7fc2112062941b689e027a30d2b75bbced4a3"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a454d5bc4f4a289f5471418788517cc122fcc00d5a8aba78c54d7984840655a2"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c25f3778bbac78286bef2df0ca80f50517b42b951af0a5ddaec514412f79fac"}, + {file = "Levenshtein-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:181486cf465aff934694cc9a19f3898a1d28025a9a5f80fc1608217e7cd1c799"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8db9f672a5d150706648b37b044dba61f36ab7216c6a121cebbb2899d7dfaa3"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f2a69fe5ddea586d439f9a50d0c51952982f6c0db0e3573b167aa17e6d1dfc48"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:3b684675a3bd35efa6997856e73f36c8a41ef62519e0267dcbeefd15e26cae71"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:cc707ef7edb71f6bf8339198b929ead87c022c78040e41668a4db68360129cef"}, + {file = "Levenshtein-0.25.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:41512c436b8c691326e2d07786d906cba0e92b5e3f455bf338befb302a0ca76d"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win32.whl", hash = "sha256:2a3830175c01ade832ba0736091283f14a6506a06ffe8c846f66d9fbca91562f"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:9e0af4e6e023e0c8f79af1d1ca5f289094eb91201f08ad90f426d71e4ae84052"}, + {file = "Levenshtein-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:38e5d9a1d737d7b49fa17d6a4c03a0359288154bf46dc93b29403a9dd0cd1a7d"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4a40fa16ecd0bf9e557db67131aabeea957f82fe3e8df342aa413994c710c34e"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4f7d2045d5927cffa65a0ac671c263edbfb17d880fdce2d358cd0bda9bcf2b6d"}, + {file = "Levenshtein-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f96590539f9815be70e330b4d2efcce0219db31db5a22fffe99565192f5662"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d78512dd25b572046ff86d8903bec283c373063349f8243430866b6a9946425"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c161f24a1b216e8555c874c7dd70c1a0d98f783f252a16c9face920a8b8a6f3e"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:06ebbfd010a00490795f478d18d7fa2ffc79c9c03fc03b678081f31764d16bab"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaa9ec0a4489ebfb25a9ec2cba064ed68d0d2485b8bc8b7203f84a7874755e0f"}, + {file = "Levenshtein-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26408938a6db7b252824a701545d50dc9cdd7a3e4c7ee70834cca17953b76ad8"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:330ec2faff957281f4e6a1a8c88286d1453e1d73ee273ea0f937e0c9281c2156"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9115d1b08626dfdea6f3955cb49ba5a578f7223205f80ead0038d6fc0442ce13"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:bbd602edab758e93a5c67bf0d8322f374a47765f1cdb6babaf593a64dc9633ad"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b930b4df32cd3aabbed0e9f0c4fdd1ea4090a5c022ba9f1ae4ab70ccf1cf897a"}, + {file = "Levenshtein-0.25.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd66fb51f88a3f73a802e1ff19a14978ddc9fbcb7ce3a667ca34f95ef54e0e44"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win32.whl", hash = "sha256:386de94bd1937a16ae3c8f8b7dd2eff1b733994ecf56ce4d05dfdd0e776d0261"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:9ee1902153d47886c9787598a4a5c324ce7fde44d44daa34fcf3652ac0de21bc"}, + {file = "Levenshtein-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:b56a7e7676093c3aee50402226f4079b15bd21b5b8f1820f9d6d63fe99dc4927"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6b5dfdf6a0e2f35fd155d4c26b03398499c24aba7bc5db40245789c46ad35c04"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355ff797f704459ddd8b95354d699d0d0642348636c92d5e67b49be4b0e6112b"}, + {file = "Levenshtein-0.25.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:933b827a3b721210fff522f3dca9572f9f374a0e88fa3a6c7ee3164406ae7794"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be1da669a240f272d904ab452ad0a1603452e190f4e03e886e6b3a9904152b89"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:265cbd78962503a26f2bea096258a3b70b279bb1a74a525c671d3ee43a190f9c"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63cc4d53a35e673b12b721a58b197b4a65734688fb72aa1987ce63ed612dca96"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75fee0c471b8799c70dad9d0d5b70f1f820249257f9617601c71b6c1b37bee92"}, + {file = "Levenshtein-0.25.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:045d6b0db124fbd37379b2b91f6d0786c2d9220e7a848e2dd31b99509a321240"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:db7a2e9c51ac9cc2fd5679484f1eac6e0ab2085cb181240445f7fbf10df73230"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c379c588aa0d93d4607db7eb225fd683263d49669b1bbe49e28c978aa6a4305d"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:966dd00424df7f69b78da02a29b530fbb6c1728e9002a2925ed7edf26b231924"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:09daa6b068709cc1e68b670a706d928ed8f0b179a26161dd04b3911d9f757525"}, + {file = "Levenshtein-0.25.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d6bed0792635081accf70a7e11cfece986f744fddf46ce26808cd8bfc067e430"}, + {file = "Levenshtein-0.25.1-cp38-cp38-win32.whl", hash = "sha256:28e7b7faf5a745a690d1b1706ab82a76bbe9fa6b729d826f0cfdd24fd7c19740"}, + {file = "Levenshtein-0.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:8ca0cc9b9e07316b5904f158d5cfa340d55b4a3566ac98eaac9f087c6efb9a1a"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:45682cdb3ac4a5465c01b2dce483bdaa1d5dcd1a1359fab37d26165b027d3de2"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f8dc3e63c4cd746ec162a4cd744c6dde857e84aaf8c397daa46359c3d54e6219"}, + {file = "Levenshtein-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:01ad1eb09933a499a49923e74e05b1428ca4ef37fed32965fef23f1334a11563"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbb4e8c4b8b7bbe0e1aa64710b806b6c3f31d93cb14969ae2c0eff0f3a592db8"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48d1fe224b365975002e3e2ea947cbb91d2936a16297859b71c4abe8a39932c"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a164df16d876aab0a400f72aeac870ea97947ea44777c89330e9a16c7dc5cc0e"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:995d3bcedcf64be6ceca423f6cfe29184a36d7c4cbac199fdc9a0a5ec7196cf5"}, + {file = "Levenshtein-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdaf62d637bef6711d6f3457e2684faab53b2db2ed53c05bc0dc856464c74742"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:af9de3b5f8f5f3530cfd97daab9ab480d1b121ef34d8c0aa5bab0c645eae219e"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:78fba73c352383b356a30c4674e39f086ffef7122fa625e7550b98be2392d387"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:9e0df0dcea3943321398f72e330c089b5d5447318310db6f17f5421642f3ade6"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:387f768bb201b9bc45f0f49557e2fb9a3774d9d087457bab972162dcd4fd352b"}, + {file = "Levenshtein-0.25.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5dcf931b64311039b43495715e9b795fbd97ab44ba3dd6bf24360b15e4e87649"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win32.whl", hash = "sha256:2449f8668c0bd62a2b305a5e797348984c06ac20903b38b3bab74e55671ddd51"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:28803fd6ec7b58065621f5ec0d24e44e2a7dc4842b64dcab690cb0a7ea545210"}, + {file = "Levenshtein-0.25.1-cp39-cp39-win_arm64.whl", hash = "sha256:0b074d452dff8ee86b5bdb6031aa32bb2ed3c8469a56718af5e010b9bb5124dc"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e9e060ef3925a68aeb12276f0e524fb1264592803d562ec0306c7c3f5c68eae0"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f84b84049318d44722db307c448f9dcb8d27c73525a378e901189a94889ba61"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e23fdf330cb185a0c7913ca5bd73a189dfd1742eae3a82e31ed8688b191800"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06958e4a81ea0f0b2b7768a2ad05bcd50a9ad04c4d521dd37d5730ff12decdc"}, + {file = "Levenshtein-0.25.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2ea7c34ec22b2fce21299b0caa6dde6bdebafcc2970e265853c9cfea8d1186da"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fddc0ccbdd94f57aa32e2eb3ac8310d08df2e175943dc20b3e1fc7a115850af4"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d52249cb3448bfe661d3d7db3a6673e835c7f37b30b0aeac499a1601bae873d"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8dd4c201b15f8c1e612f9074335392c8208ac147acbce09aff04e3974bf9b16"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23a4d95ce9d44161c7aa87ab76ad6056bc1093c461c60c097054a46dc957991f"}, + {file = "Levenshtein-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:65eea8a9c33037b23069dca4b3bc310e3c28ca53f60ec0c958d15c0952ba39fa"}, + {file = "Levenshtein-0.25.1.tar.gz", hash = "sha256:2df14471c778c75ffbd59cb64bbecfd4b0ef320ef9f80e4804764be7d5678980"}, +] + +[package.dependencies] +rapidfuzz = ">=3.8.0,<4.0.0" + +[[package]] +name = "loguru" +version = "0.7.2" +description = "Python logging made (stupidly) simple" +optional = false +python-versions = ">=3.5" +files = [ + {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"}, + {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"}, +] + +[package.dependencies] +colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} +win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} + +[package.extras] +dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "multidict" +version = "6.0.5" +description = "multidict implementation" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, + {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, + {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, + {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, + {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, + {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, + {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, + {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, + {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, + {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, + {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, + {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, + {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, + {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, + {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, + {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, + {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, + {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, + {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, + {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, + {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, + {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, + {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, + {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, + {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, + {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, + {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, + {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, + {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, + {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, + {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, + {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, + {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, +] + +[[package]] +name = "orjson" +version = "3.10.6" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, + {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, + {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, + {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, + {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, + {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, + {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, + {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, + {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, + {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, + {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, + {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, + {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, + {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, + {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, + {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, + {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, + {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, + {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, + {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, + {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, + {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, + {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, + {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, + {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, + {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, +] + +[[package]] +name = "packaging" +version = "24.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, +] + +[[package]] +name = "parse-torrent-title" +version = "2.8.1" +description = "Extract media information from torrent-like filename" +optional = false +python-versions = "*" +files = [ + {file = "parse-torrent-title-2.8.1.tar.gz", hash = "sha256:939647c4bfacd2b3b878503a7bf79fcdbb11e74a895a4550eb84406d62742457"}, + {file = "parse_torrent_title-2.8.1-py2-none-any.whl", hash = "sha256:0467809b5004065e29c60dfe120499f44832c0be49dc0e7583a1325dd81bbc88"}, + {file = "parse_torrent_title-2.8.1-py3-none-any.whl", hash = "sha256:b96ee593b25fbf07150066d9d8de488b17c15c92b8b05745f984ff47db7f9fcb"}, +] + +[[package]] +name = "parsett" +version = "0.2.4" +description = "PTT" +optional = false +python-versions = "<4.0,>=3.11" +files = [ + {file = "parsett-0.2.4-py3-none-any.whl", hash = "sha256:2a47634f2616fffe6e0fc574439692c3d938442daaea51320ecabe002e995aae"}, + {file = "parsett-0.2.4.tar.gz", hash = "sha256:aa609f253841fd8586414428965c4fcaae436a3ca655c0a572359f921b56f108"}, +] + +[package.dependencies] +arrow = ">=1.3.0,<2.0.0" +regex = ">=2023.12.25,<2024.0.0" + +[[package]] +name = "pydantic" +version = "2.8.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic-2.8.2-py3-none-any.whl", hash = "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"}, + {file = "pydantic-2.8.2.tar.gz", hash = "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.20.1" +typing-extensions = [ + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, + {version = ">=4.6.1", markers = "python_version < \"3.13\""}, +] + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[[package]] +name = "pydantic-core" +version = "2.20.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98"}, + {file = "pydantic_core-2.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a"}, + {file = "pydantic_core-2.20.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840"}, + {file = "pydantic_core-2.20.1-cp310-none-win32.whl", hash = "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250"}, + {file = "pydantic_core-2.20.1-cp310-none-win_amd64.whl", hash = "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312"}, + {file = "pydantic_core-2.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1"}, + {file = "pydantic_core-2.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"}, + {file = "pydantic_core-2.20.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b"}, + {file = "pydantic_core-2.20.1-cp311-none-win32.whl", hash = "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a"}, + {file = "pydantic_core-2.20.1-cp311-none-win_amd64.whl", hash = "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231"}, + {file = "pydantic_core-2.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e"}, + {file = "pydantic_core-2.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1"}, + {file = "pydantic_core-2.20.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd"}, + {file = "pydantic_core-2.20.1-cp312-none-win32.whl", hash = "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688"}, + {file = "pydantic_core-2.20.1-cp312-none-win_amd64.whl", hash = "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686"}, + {file = "pydantic_core-2.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c"}, + {file = "pydantic_core-2.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203"}, + {file = "pydantic_core-2.20.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0"}, + {file = "pydantic_core-2.20.1-cp313-none-win32.whl", hash = "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e"}, + {file = "pydantic_core-2.20.1-cp313-none-win_amd64.whl", hash = "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91"}, + {file = "pydantic_core-2.20.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598"}, + {file = "pydantic_core-2.20.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa"}, + {file = "pydantic_core-2.20.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987"}, + {file = "pydantic_core-2.20.1-cp38-none-win32.whl", hash = "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a"}, + {file = "pydantic_core-2.20.1-cp38-none-win_amd64.whl", hash = "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c"}, + {file = "pydantic_core-2.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006"}, + {file = "pydantic_core-2.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09"}, + {file = "pydantic_core-2.20.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab"}, + {file = "pydantic_core-2.20.1-cp39-none-win32.whl", hash = "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2"}, + {file = "pydantic_core-2.20.1-cp39-none-win_amd64.whl", hash = "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99"}, + {file = "pydantic_core-2.20.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a"}, + {file = "pydantic_core-2.20.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7"}, + {file = "pydantic_core-2.20.1.tar.gz", hash = "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[[package]] +name = "pydantic-settings" +version = "2.3.4" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_settings-2.3.4-py3-none-any.whl", hash = "sha256:11ad8bacb68a045f00e4f862c7a718c8a9ec766aa8fd4c32e39a0594b207b53a"}, + {file = "pydantic_settings-2.3.4.tar.gz", hash = "sha256:c5802e3d62b78e82522319bbc9b8f8ffb28ad1c988a99311d04f2a6051fca0a7"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" + +[package.extras] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.0.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, + {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.9" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"}, + {file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"}, +] + +[package.extras] +dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] + +[[package]] +name = "pytz" +version = "2024.1" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, + {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.1" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, + {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, + {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, + {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, + {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, + {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, + {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, + {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, + {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, + {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, + {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, + {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, + {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, + {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, + {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, + {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, + {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, + {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, + {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, + {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, + {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, + {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, + {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, + {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, +] + +[[package]] +name = "rank-torrent-name" +version = "0.2.23" +description = "Parse Torrents using PTN and Rank them according to your preferences!" +optional = false +python-versions = "<4.0,>=3.11" +files = [ + {file = "rank_torrent_name-0.2.23-py3-none-any.whl", hash = "sha256:61b96db32e6d37dec4101c86654d266bde04628c3ac983fe71b99c4614b4d9eb"}, + {file = "rank_torrent_name-0.2.23.tar.gz", hash = "sha256:50a66c789ef74c9d0f7bdb84a3921e9bcf6b3d2f5ae3ffcce44d3dfac2590c00"}, +] + +[package.dependencies] +levenshtein = ">=0.25.0,<0.26.0" +parse-torrent-title = ">=2.8.1,<3.0.0" +parsett = ">=0.2.4,<0.3.0" +pydantic = ">=2.6.3,<3.0.0" +regex = ">=2023.12.25,<2024.0.0" + +[[package]] +name = "rapidfuzz" +version = "3.9.4" +description = "rapid fuzzy string matching" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rapidfuzz-3.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c9b9793c19bdf38656c8eaefbcf4549d798572dadd70581379e666035c9df781"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:015b5080b999404fe06ec2cb4f40b0be62f0710c926ab41e82dfbc28e80675b4"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc5ceca9c1e1663f3e6c23fb89a311f69b7615a40ddd7645e3435bf3082688a"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1424e238bc3f20e1759db1e0afb48a988a9ece183724bef91ea2a291c0b92a95"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed01378f605aa1f449bee82cd9c83772883120d6483e90aa6c5a4ce95dc5c3aa"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb26d412271e5a76cdee1c2d6bf9881310665d3fe43b882d0ed24edfcb891a84"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f37e9e1f17be193c41a31c864ad4cd3ebd2b40780db11cd5c04abf2bcf4201b"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d070ec5cf96b927c4dc5133c598c7ff6db3b833b363b2919b13417f1002560bc"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:10e61bb7bc807968cef09a0e32ce253711a2d450a4dce7841d21d45330ffdb24"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:31a2fc60bb2c7face4140010a7aeeafed18b4f9cdfa495cc644a68a8c60d1ff7"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fbebf1791a71a2e89f5c12b78abddc018354d5859e305ec3372fdae14f80a826"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:aee9fc9e3bb488d040afc590c0a7904597bf4ccd50d1491c3f4a5e7e67e6cd2c"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win32.whl", hash = "sha256:005a02688a51c7d2451a2d41c79d737aa326ff54167211b78a383fc2aace2c2c"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:3a2e75e41ee3274754d3b2163cc6c82cd95b892a85ab031f57112e09da36455f"}, + {file = "rapidfuzz-3.9.4-cp310-cp310-win_arm64.whl", hash = "sha256:2c99d355f37f2b289e978e761f2f8efeedc2b14f4751d9ff7ee344a9a5ca98d9"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:07141aa6099e39d48637ce72a25b893fc1e433c50b3e837c75d8edf99e0c63e1"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db1664eaff5d7d0f2542dd9c25d272478deaf2c8412e4ad93770e2e2d828e175"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc01a223f6605737bec3202e94dcb1a449b6c76d46082cfc4aa980f2a60fd40e"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1869c42e73e2a8910b479be204fa736418741b63ea2325f9cc583c30f2ded41a"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62ea7007941fb2795fff305ac858f3521ec694c829d5126e8f52a3e92ae75526"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:698e992436bf7f0afc750690c301215a36ff952a6dcd62882ec13b9a1ebf7a39"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b76f611935f15a209d3730c360c56b6df8911a9e81e6a38022efbfb96e433bab"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129627d730db2e11f76169344a032f4e3883d34f20829419916df31d6d1338b1"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:90a82143c14e9a14b723a118c9ef8d1bbc0c5a16b1ac622a1e6c916caff44dd8"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ded58612fe3b0e0d06e935eaeaf5a9fd27da8ba9ed3e2596307f40351923bf72"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f16f5d1c4f02fab18366f2d703391fcdbd87c944ea10736ca1dc3d70d8bd2d8b"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:26aa7eece23e0df55fb75fbc2a8fb678322e07c77d1fd0e9540496e6e2b5f03e"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win32.whl", hash = "sha256:f187a9c3b940ce1ee324710626daf72c05599946bd6748abe9e289f1daa9a077"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8e9130fe5d7c9182990b366ad78fd632f744097e753e08ace573877d67c32f8"}, + {file = "rapidfuzz-3.9.4-cp311-cp311-win_arm64.whl", hash = "sha256:40419e98b10cd6a00ce26e4837a67362f658fc3cd7a71bd8bd25c99f7ee8fea5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b5d5072b548db1b313a07d62d88fe0b037bd2783c16607c647e01b070f6cf9e5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cf5bcf22e1f0fd273354462631d443ef78d677f7d2fc292de2aec72ae1473e66"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c8fc973adde8ed52810f590410e03fb6f0b541bbaeb04c38d77e63442b2df4c"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2464bb120f135293e9a712e342c43695d3d83168907df05f8c4ead1612310c7"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d9d58689aca22057cf1a5851677b8a3ccc9b535ca008c7ed06dc6e1899f7844"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167e745f98baa0f3034c13583e6302fb69249a01239f1483d68c27abb841e0a1"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db0bf0663b4b6da1507869722420ea9356b6195aa907228d6201303e69837af9"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd6ac61b74fdb9e23f04d5f068e6cf554f47e77228ca28aa2347a6ca8903972f"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:60ff67c690acecf381759c16cb06c878328fe2361ddf77b25d0e434ea48a29da"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:cb934363380c60f3a57d14af94325125cd8cded9822611a9f78220444034e36e"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fe833493fb5cc5682c823ea3e2f7066b07612ee8f61ecdf03e1268f262106cdd"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2797fb847d89e04040d281cb1902cbeffbc4b5131a5c53fc0db490fd76b2a547"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win32.whl", hash = "sha256:52e3d89377744dae68ed7c84ad0ddd3f5e891c82d48d26423b9e066fc835cc7c"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:c76da20481c906e08400ee9be230f9e611d5931a33707d9df40337c2655c84b5"}, + {file = "rapidfuzz-3.9.4-cp312-cp312-win_arm64.whl", hash = "sha256:f2d2846f3980445864c7e8b8818a29707fcaff2f0261159ef6b7bd27ba139296"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:355fc4a268ffa07bab88d9adee173783ec8d20136059e028d2a9135c623c44e6"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4d81a78f90269190b568a8353d4ea86015289c36d7e525cd4d43176c88eff429"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e618625ffc4660b26dc8e56225f8b966d5842fa190e70c60db6cd393e25b86e"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b712336ad6f2bacdbc9f1452556e8942269ef71f60a9e6883ef1726b52d9228a"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc1ee19fdad05770c897e793836c002344524301501d71ef2e832847425707"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1950f8597890c0c707cb7e0416c62a1cf03dcdb0384bc0b2dbda7e05efe738ec"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a6c35f272ec9c430568dc8c1c30cb873f6bc96be2c79795e0bce6db4e0e101d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:1df0f9e9239132a231c86ae4f545ec2b55409fa44470692fcfb36b1bd00157ad"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d2c51955329bfccf99ae26f63d5928bf5be9fcfcd9f458f6847fd4b7e2b8986c"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:3c522f462d9fc504f2ea8d82e44aa580e60566acc754422c829ad75c752fbf8d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:d8a52fc50ded60d81117d7647f262c529659fb21d23e14ebfd0b35efa4f1b83d"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:04dbdfb0f0bfd3f99cf1e9e24fadc6ded2736d7933f32f1151b0f2abb38f9a25"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-win32.whl", hash = "sha256:4968c8bd1df84b42f382549e6226710ad3476f976389839168db3e68fd373298"}, + {file = "rapidfuzz-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:3fe4545f89f8d6c27b6bbbabfe40839624873c08bd6700f63ac36970a179f8f5"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f256c8fb8f3125574c8c0c919ab0a1f75d7cba4d053dda2e762dcc36357969d"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5fdc09cf6e9d8eac3ce48a4615b3a3ee332ea84ac9657dbbefef913b13e632f"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d395d46b80063d3b5d13c0af43d2c2cedf3ab48c6a0c2aeec715aa5455b0c632"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fa714fb96ce9e70c37e64c83b62fe8307030081a0bfae74a76fac7ba0f91715"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc1a0f29f9119be7a8d3c720f1d2068317ae532e39e4f7f948607c3a6de8396"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6022674aa1747d6300f699cd7c54d7dae89bfe1f84556de699c4ac5df0838082"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb72e5f9762fd469701a7e12e94b924af9004954f8c739f925cb19c00862e38"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad04ae301129f0eb5b350a333accd375ce155a0c1cec85ab0ec01f770214e2e4"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f46a22506f17c0433e349f2d1dc11907c393d9b3601b91d4e334fa9a439a6a4d"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:01b42a8728c36011718da409aa86b84984396bf0ca3bfb6e62624f2014f6022c"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e590d5d5443cf56f83a51d3c4867bd1f6be8ef8cfcc44279522bcef3845b2a51"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4c72078b5fdce34ba5753f9299ae304e282420e6455e043ad08e4488ca13a2b0"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win32.whl", hash = "sha256:f75639277304e9b75e6a7b3c07042d2264e16740a11e449645689ed28e9c2124"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:e81e27e8c32a1e1278a4bb1ce31401bfaa8c2cc697a053b985a6f8d013df83ec"}, + {file = "rapidfuzz-3.9.4-cp39-cp39-win_arm64.whl", hash = "sha256:15bc397ee9a3ed1210b629b9f5f1da809244adc51ce620c504138c6e7095b7bd"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:20488ade4e1ddba3cfad04f400da7a9c1b91eff5b7bd3d1c50b385d78b587f4f"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:e61b03509b1a6eb31bc5582694f6df837d340535da7eba7bedb8ae42a2fcd0b9"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:098d231d4e51644d421a641f4a5f2f151f856f53c252b03516e01389b2bfef99"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17ab8b7d10fde8dd763ad428aa961c0f30a1b44426e675186af8903b5d134fb0"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e272df61bee0a056a3daf99f9b1bd82cf73ace7d668894788139c868fdf37d6f"}, + {file = "rapidfuzz-3.9.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d6481e099ff8c4edda85b8b9b5174c200540fd23c8f38120016c765a86fa01f5"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ad61676e9bdae677d577fe80ec1c2cea1d150c86be647e652551dcfe505b1113"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:af65020c0dd48d0d8ae405e7e69b9d8ae306eb9b6249ca8bf511a13f465fad85"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d38b4e026fcd580e0bda6c0ae941e0e9a52c6bc66cdce0b8b0da61e1959f5f8"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f74ed072c2b9dc6743fb19994319d443a4330b0e64aeba0aa9105406c7c5b9c2"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee5f6b8321f90615c184bd8a4c676e9becda69b8e4e451a90923db719d6857c"}, + {file = "rapidfuzz-3.9.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3a555e3c841d6efa350f862204bb0a3fea0c006b8acc9b152b374fa36518a1c6"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0772150d37bf018110351c01d032bf9ab25127b966a29830faa8ad69b7e2f651"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:addcdd3c3deef1bd54075bd7aba0a6ea9f1d01764a08620074b7a7b1e5447cb9"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fe86b82b776554add8f900b6af202b74eb5efe8f25acdb8680a5c977608727f"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0fc91ac59f4414d8542454dfd6287a154b8e6f1256718c898f695bdbb993467"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a944e546a296a5fdcaabb537b01459f1b14d66f74e584cb2a91448bffadc3c1"}, + {file = "rapidfuzz-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fb96ba96d58c668a17a06b5b5e8340fedc26188e87b0d229d38104556f30cd8"}, + {file = "rapidfuzz-3.9.4.tar.gz", hash = "sha256:366bf8947b84e37f2f4cf31aaf5f37c39f620d8c0eddb8b633e6ba0129ca4a0a"}, +] + +[package.extras] +full = ["numpy"] + +[[package]] +name = "redis" +version = "5.0.7" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.7-py3-none-any.whl", hash = "sha256:0e479e24da960c690be5d9b96d21f7b918a98c0cf49af3b6fafaa0753f93a0db"}, + {file = "redis-5.0.7.tar.gz", hash = "sha256:8f611490b93c8109b50adc317b31bfd84fff31def3475b92e7e80bf39f48175b"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "shellingham" +version = "1.5.4" +description = "Tool to Detect Surrounding Shell" +optional = false +python-versions = ">=3.7" +files = [ + {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, + {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.31" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"}, + {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"}, + {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"}, + {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"}, + {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"}, + {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"}, + {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"}, + {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"}, + {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + +[[package]] +name = "stackprinter" +version = "0.2.12" +description = "Debug-friendly stack traces, with variable values and semantic highlighting" +optional = false +python-versions = ">=3.4" +files = [ + {file = "stackprinter-0.2.12-py3-none-any.whl", hash = "sha256:0a0623d46a5babd7a8a9787f605f4dd4a42d6ff7aee140541d5e9291a506e8d9"}, + {file = "stackprinter-0.2.12.tar.gz", hash = "sha256:271efc75ebdcc1554e58168ea7779f98066d54a325f57c7dc19f10fa998ef01e"}, +] + +[[package]] +name = "starlette" +version = "0.37.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.8" +files = [ + {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, + {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, +] + +[package.dependencies] +anyio = ">=3.4.0,<5" + +[package.extras] +full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, +] + +[[package]] +name = "typer" +version = "0.12.3" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +optional = false +python-versions = ">=3.7" +files = [ + {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, + {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, +] + +[package.dependencies] +click = ">=8.0.0" +rich = ">=10.11.0" +shellingham = ">=1.3.0" +typing-extensions = ">=3.7.4.3" + +[[package]] +name = "types-python-dateutil" +version = "2.9.0.20240316" +description = "Typing stubs for python-dateutil" +optional = false +python-versions = ">=3.8" +files = [ + {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, + {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +files = [ + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, +] + +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + +[[package]] +name = "ujson" +version = "5.10.0" +description = "Ultra fast JSON encoder and decoder for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, + {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, + {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, + {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, + {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, + {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, + {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, + {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, + {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, + {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, + {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, + {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, + {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, + {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, + {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, + {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, + {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, + {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, + {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, + {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, + {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, + {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, + {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, + {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, + {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, + {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, + {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, + {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, + {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, + {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, + {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, + {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, + {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, + {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, + {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, +] + +[[package]] +name = "urllib3" +version = "2.2.2" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.30.1" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.8" +files = [ + {file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, + {file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} +h11 = ">=0.8" +httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} +python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} +uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} +watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} +websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "uvloop" +version = "0.19.0" +description = "Fast implementation of asyncio event loop on top of libuv" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, + {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, + {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, + {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, + {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, + {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, + {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, + {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, + {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, + {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, + {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, + {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, + {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, + {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, + {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, + {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, + {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, +] + +[package.extras] +docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] + +[[package]] +name = "watchfiles" +version = "0.22.0" +description = "Simple, modern and high performance file watching and code reload in python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchfiles-0.22.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:da1e0a8caebf17976e2ffd00fa15f258e14749db5e014660f53114b676e68538"}, + {file = "watchfiles-0.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:61af9efa0733dc4ca462347becb82e8ef4945aba5135b1638bfc20fad64d4f0e"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d9188979a58a096b6f8090e816ccc3f255f137a009dd4bbec628e27696d67c1"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2bdadf6b90c099ca079d468f976fd50062905d61fae183f769637cb0f68ba59a"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:067dea90c43bf837d41e72e546196e674f68c23702d3ef80e4e816937b0a3ffd"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbf8a20266136507abf88b0df2328e6a9a7c7309e8daff124dda3803306a9fdb"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1235c11510ea557fe21be5d0e354bae2c655a8ee6519c94617fe63e05bca4171"}, + {file = "watchfiles-0.22.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2444dc7cb9d8cc5ab88ebe792a8d75709d96eeef47f4c8fccb6df7c7bc5be71"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c5af2347d17ab0bd59366db8752d9e037982e259cacb2ba06f2c41c08af02c39"}, + {file = "watchfiles-0.22.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9624a68b96c878c10437199d9a8b7d7e542feddda8d5ecff58fdc8e67b460848"}, + {file = "watchfiles-0.22.0-cp310-none-win32.whl", hash = "sha256:4b9f2a128a32a2c273d63eb1fdbf49ad64852fc38d15b34eaa3f7ca2f0d2b797"}, + {file = "watchfiles-0.22.0-cp310-none-win_amd64.whl", hash = "sha256:2627a91e8110b8de2406d8b2474427c86f5a62bf7d9ab3654f541f319ef22bcb"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8c39987a1397a877217be1ac0fb1d8b9f662c6077b90ff3de2c05f235e6a8f96"}, + {file = "watchfiles-0.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a927b3034d0672f62fb2ef7ea3c9fc76d063c4b15ea852d1db2dc75fe2c09696"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052d668a167e9fc345c24203b104c313c86654dd6c0feb4b8a6dfc2462239249"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e45fb0d70dda1623a7045bd00c9e036e6f1f6a85e4ef2c8ae602b1dfadf7550"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c49b76a78c156979759d759339fb62eb0549515acfe4fd18bb151cc07366629c"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4a65474fd2b4c63e2c18ac67a0c6c66b82f4e73e2e4d940f837ed3d2fd9d4da"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc0cba54f47c660d9fa3218158b8963c517ed23bd9f45fe463f08262a4adae1"}, + {file = "watchfiles-0.22.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94ebe84a035993bb7668f58a0ebf998174fb723a39e4ef9fce95baabb42b787f"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e0f0a874231e2839abbf473256efffe577d6ee2e3bfa5b540479e892e47c172d"}, + {file = "watchfiles-0.22.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:213792c2cd3150b903e6e7884d40660e0bcec4465e00563a5fc03f30ea9c166c"}, + {file = "watchfiles-0.22.0-cp311-none-win32.whl", hash = "sha256:b44b70850f0073b5fcc0b31ede8b4e736860d70e2dbf55701e05d3227a154a67"}, + {file = "watchfiles-0.22.0-cp311-none-win_amd64.whl", hash = "sha256:00f39592cdd124b4ec5ed0b1edfae091567c72c7da1487ae645426d1b0ffcad1"}, + {file = "watchfiles-0.22.0-cp311-none-win_arm64.whl", hash = "sha256:3218a6f908f6a276941422b035b511b6d0d8328edd89a53ae8c65be139073f84"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:c7b978c384e29d6c7372209cbf421d82286a807bbcdeb315427687f8371c340a"}, + {file = "watchfiles-0.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bd4c06100bce70a20c4b81e599e5886cf504c9532951df65ad1133e508bf20be"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:425440e55cd735386ec7925f64d5dde392e69979d4c8459f6bb4e920210407f2"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:68fe0c4d22332d7ce53ad094622b27e67440dacefbaedd29e0794d26e247280c"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8a31bfd98f846c3c284ba694c6365620b637debdd36e46e1859c897123aa232"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc2e8fe41f3cac0660197d95216c42910c2b7e9c70d48e6d84e22f577d106fc1"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b7cc10261c2786c41d9207193a85c1db1b725cf87936df40972aab466179b6"}, + {file = "watchfiles-0.22.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28585744c931576e535860eaf3f2c0ec7deb68e3b9c5a85ca566d69d36d8dd27"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:00095dd368f73f8f1c3a7982a9801190cc88a2f3582dd395b289294f8975172b"}, + {file = "watchfiles-0.22.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:52fc9b0dbf54d43301a19b236b4a4614e610605f95e8c3f0f65c3a456ffd7d35"}, + {file = "watchfiles-0.22.0-cp312-none-win32.whl", hash = "sha256:581f0a051ba7bafd03e17127735d92f4d286af941dacf94bcf823b101366249e"}, + {file = "watchfiles-0.22.0-cp312-none-win_amd64.whl", hash = "sha256:aec83c3ba24c723eac14225194b862af176d52292d271c98820199110e31141e"}, + {file = "watchfiles-0.22.0-cp312-none-win_arm64.whl", hash = "sha256:c668228833c5619f6618699a2c12be057711b0ea6396aeaece4ded94184304ea"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d47e9ef1a94cc7a536039e46738e17cce058ac1593b2eccdede8bf72e45f372a"}, + {file = "watchfiles-0.22.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28f393c1194b6eaadcdd8f941307fc9bbd7eb567995232c830f6aef38e8a6e88"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd64f3a4db121bc161644c9e10a9acdb836853155a108c2446db2f5ae1778c3d"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2abeb79209630da981f8ebca30a2c84b4c3516a214451bfc5f106723c5f45843"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cc382083afba7918e32d5ef12321421ef43d685b9a67cc452a6e6e18920890e"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d048ad5d25b363ba1d19f92dcf29023988524bee6f9d952130b316c5802069cb"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:103622865599f8082f03af4214eaff90e2426edff5e8522c8f9e93dc17caee13"}, + {file = "watchfiles-0.22.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e1f3cf81f1f823e7874ae563457828e940d75573c8fbf0ee66818c8b6a9099"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8597b6f9dc410bdafc8bb362dac1cbc9b4684a8310e16b1ff5eee8725d13dcd6"}, + {file = "watchfiles-0.22.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0b04a2cbc30e110303baa6d3ddce8ca3664bc3403be0f0ad513d1843a41c97d1"}, + {file = "watchfiles-0.22.0-cp38-none-win32.whl", hash = "sha256:b610fb5e27825b570554d01cec427b6620ce9bd21ff8ab775fc3a32f28bba63e"}, + {file = "watchfiles-0.22.0-cp38-none-win_amd64.whl", hash = "sha256:fe82d13461418ca5e5a808a9e40f79c1879351fcaeddbede094028e74d836e86"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3973145235a38f73c61474d56ad6199124e7488822f3a4fc97c72009751ae3b0"}, + {file = "watchfiles-0.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:280a4afbc607cdfc9571b9904b03a478fc9f08bbeec382d648181c695648202f"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a0d883351a34c01bd53cfa75cd0292e3f7e268bacf2f9e33af4ecede7e21d1d"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9165bcab15f2b6d90eedc5c20a7f8a03156b3773e5fb06a790b54ccecdb73385"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc1b9b56f051209be458b87edb6856a449ad3f803315d87b2da4c93b43a6fe72"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc1fc25a1dedf2dd952909c8e5cb210791e5f2d9bc5e0e8ebc28dd42fed7562"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc92d2d2706d2b862ce0568b24987eba51e17e14b79a1abcd2edc39e48e743c8"}, + {file = "watchfiles-0.22.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97b94e14b88409c58cdf4a8eaf0e67dfd3ece7e9ce7140ea6ff48b0407a593ec"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96eec15e5ea7c0b6eb5bfffe990fc7c6bd833acf7e26704eb18387fb2f5fd087"}, + {file = "watchfiles-0.22.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:28324d6b28bcb8d7c1041648d7b63be07a16db5510bea923fc80b91a2a6cbed6"}, + {file = "watchfiles-0.22.0-cp39-none-win32.whl", hash = "sha256:8c3e3675e6e39dc59b8fe5c914a19d30029e36e9f99468dddffd432d8a7b1c93"}, + {file = "watchfiles-0.22.0-cp39-none-win_amd64.whl", hash = "sha256:25c817ff2a86bc3de3ed2df1703e3d24ce03479b27bb4527c57e722f8554d971"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b810a2c7878cbdecca12feae2c2ae8af59bea016a78bc353c184fa1e09f76b68"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7e1f9c5d1160d03b93fc4b68a0aeb82fe25563e12fbcdc8507f8434ab6f823c"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:030bc4e68d14bcad2294ff68c1ed87215fbd9a10d9dea74e7cfe8a17869785ab"}, + {file = "watchfiles-0.22.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ace7d060432acde5532e26863e897ee684780337afb775107c0a90ae8dbccfd2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5834e1f8b71476a26df97d121c0c0ed3549d869124ed2433e02491553cb468c2"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0bc3b2f93a140df6806c8467c7f51ed5e55a931b031b5c2d7ff6132292e803d6"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fdebb655bb1ba0122402352b0a4254812717a017d2dc49372a1d47e24073795"}, + {file = "watchfiles-0.22.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c8e0aa0e8cc2a43561e0184c0513e291ca891db13a269d8d47cb9841ced7c71"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2f350cbaa4bb812314af5dab0eb8d538481e2e2279472890864547f3fe2281ed"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7a74436c415843af2a769b36bf043b6ccbc0f8d784814ba3d42fc961cdb0a9dc"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00ad0bcd399503a84cc688590cdffbe7a991691314dde5b57b3ed50a41319a31"}, + {file = "watchfiles-0.22.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a44e9481afc7a5ee3291b09c419abab93b7e9c306c9ef9108cb76728ca58d2"}, + {file = "watchfiles-0.22.0.tar.gz", hash = "sha256:988e981aaab4f3955209e7e28c7794acdb690be1efa7f16f8ea5aba7ffdadacb"}, +] + +[package.dependencies] +anyio = ">=3.0.0" + +[[package]] +name = "websockets" +version = "12.0" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, + {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, + {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, + {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, + {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, + {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, + {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, + {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, + {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, + {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, + {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, + {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, + {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, + {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, + {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, + {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, + {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, + {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, + {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, + {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, + {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, + {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, + {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, + {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, + {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, + {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, + {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, + {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, + {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, + {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, + {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, + {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, + {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, + {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, + {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, + {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, + {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, + {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, + {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, + {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, + {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, + {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, +] + +[[package]] +name = "win32-setctime" +version = "1.1.0" +description = "A small Python utility to set file creation time on Windows" +optional = false +python-versions = ">=3.5" +files = [ + {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"}, + {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"}, +] + +[package.extras] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] + +[[package]] +name = "yarl" +version = "1.9.4" +description = "Yet another URL library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, + {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, + {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, + {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, + {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, + {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, + {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, + {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, + {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, + {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, + {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, + {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, + {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, + {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, + {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, + {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, + {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, + {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, + {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, + {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, + {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, + {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, + {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, + {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, + {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, + {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, + {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, + {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, + {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, + {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, + {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, + {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, + {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[metadata] +lock-version = "2.0" +python-versions = "^3.11" +content-hash = "ee9c95e5603a23661a82e8d4df62fce700e7249e0cd145453c22c44625cf3359" diff --git a/poetry.toml b/poetry.toml new file mode 100644 index 0000000..62e2dff --- /dev/null +++ b/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +create = true diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ad57b1a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,34 @@ +[tool.poetry] +name = "stream-fusion" +version = "v0.1.0" +description = "StreamFusion is an advanced plugin for Stremio that significantly enhances its streaming capabilities with debrid service." +authors = ["LimeDrive "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.11" +fastapi = "^0.111.0" +uvicorn = "^0.30.1" +starlette = "^0.37.2" +requests = "^2.32.3" +bencode-py = "^4.0.0" +jinja2 = "^3.1.4" +aiocron = "^1.8" +python-dotenv = "^1.0.1" +rank-torrent-name = "^0.2.21" +cachetools = "^5.3.3" +redis = "^5.0.7" +aiohttp = "^3.9.5" +gunicorn = "^22.0.0" +loguru = "^0.7.2" +stackprinter = "^0.2.12" +pydantic-settings = "^2.3.4" +sqlalchemy = "^2.0.31" +aiosqlite = "^0.20.0" +toml = "^0.10.2" +jsonpickle = "^3.2.2" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/stream_fusion/__init__.py b/stream_fusion/__init__.py new file mode 100644 index 0000000..7bf6164 --- /dev/null +++ b/stream_fusion/__init__.py @@ -0,0 +1 @@ +"""stream_fusion package.""" diff --git a/stream_fusion/__main__.py b/stream_fusion/__main__.py new file mode 100644 index 0000000..d375e45 --- /dev/null +++ b/stream_fusion/__main__.py @@ -0,0 +1,36 @@ +import uvicorn + +from stream_fusion.gunicorn_runner import GunicornApplication +from stream_fusion.settings import settings + + +def main() -> None: + """Entrypoint of the application.""" + if settings.reload: + uvicorn.run( + "stream_fusion.web.application:get_app", + workers=settings.workers_count, + host=settings.host, + port=settings.port, + reload=settings.reload, + log_level=settings.log_level.value.lower(), + factory=True, + ) + else: + # We choose gunicorn only if reload + # option is not used, because reload + # feature doen't work with GUvicorn workers. + GunicornApplication( + "stream_fusion.web.application:get_app", + host=settings.host, + port=settings.port, + workers=settings.workers_count, + factory=True, + accesslog="-", + loglevel=settings.log_level.value.lower(), + access_log_format='%r "-" %s "-" %Tf', + ).run() + + +if __name__ == "__main__": + main() diff --git a/stream_fusion/constants.py b/stream_fusion/constants.py new file mode 100644 index 0000000..7ff0150 --- /dev/null +++ b/stream_fusion/constants.py @@ -0,0 +1,87 @@ +NO_CONFIG = {'streams': [{'url': "#", 'title': "No configuration found"}]} +JACKETT_ERROR = {'streams': [{'url': "#", 'title': "An error occured"}]} + +CACHER_URL = "https://stremio-jackett-cacher.elfhosted.com/" + +DMM_API_URL = "http://135.181.110.62:8181" + +NO_CACHE_VIDEO_URL = "https://github.com/aymene69/stremio-jackett/raw/main/source/videos/nocache.mp4" + +EXCLUDED_TRACKERS = ['0day.kiev', '1ptbar', '2 Fast 4 You', '2xFree', '3ChangTrai', '3D Torrents', '3Wmg', '4thD', + '52PT', '720pier', 'Abnormal', 'ABtorrents', 'Acid-Lounge', 'Across The Tasman', 'Aftershock', + 'AGSVPT', 'Aidoru!Online', 'Aither (API)', 'AlphaRatio', 'Amigos Share Club', 'AniDUB', + 'Anime-Free', 'AnimeBytes', 'AnimeLayer', 'AnimeTorrents', 'AnimeTorrents.ro', 'AnimeWorld (API)', + 'AniToons', 'Anthelion (API)', 'ArabaFenice', 'ArabP2P', 'ArabTorrents', 'ArenaBG', 'AsianCinema', + 'AsianDVDClub', 'Audiences', 'AudioNews', 'Aussierul.es', 'AvistaZ', 'Azusa', 'Back-ups', 'BakaBT', + 'BeiTai', 'Beload', 'Best-Core', 'Beyond-HD (API)', 'Bibliotik', 'Bit-Bázis', 'BIT-HDTV', 'Bitded', + 'Bithorlo', 'BitHUmen', 'BitPorn', 'Bitspyder', 'Bittorrentfiles', 'BiTTuRK', 'BJ-Share', + 'BlueBird', 'Blutopia (API)', 'BookTracker', 'BootyTape', 'Borgzelle', 'Boxing Torrents', + 'BrasilTracker', 'BroadcasTheNet', 'BroadCity', 'BrokenStones', 'BrSociety (API)', 'BTArg', + 'BTNext', 'BTSCHOOL', 'BwTorrents', 'BYRBT', 'Carp-Hunter', 'Carpathians', 'CarPT', 'CartoonChaos', + 'Cathode-Ray.Tube', 'Catorrent', 'Central Torrent', 'CeskeForum', 'CGPeers', 'CHDBits', 'cheggit', + 'ChileBT', 'Cinemageddon', 'CinemaMovieS_ZT', 'Cinematik', 'CinemaZ', 'Classix', 'Coastal-Crew', + 'Concertos', 'CrazySpirits', 'CrnaBerza', 'CRT2FA', 'Dajiao', 'DanishBytes (API)', 'Dark-Shadow', + 'DataScene (API)', 'Deildu', 'Demonoid', 'DesiTorrents (API)', 'Devil-Torrents', 'Diablo Torrent', + 'DICMusic', 'DigitalCore', 'DimeADozen', 'DiscFan', 'DivTeam', 'DocsPedia', 'Dream Tracker', + 'DreamingTree', 'Drugari', 'DXP', 'Ebooks-Shares', 'Electro-Torrent', 'Empornium', 'Empornium2FA', + 'EniaHD', 'Enthralled', 'Enthralled2FA', 'Erai-Raws', 'eShareNet', 'eStone', 'Ex-torrenty', + 'exitorrent.org', 'ExKinoRay', 'ExoticaZ', 'ExtremeBits', 'ExtremlymTorrents', + 'Falkon Vision Team', 'FANO.IN', 'Fantastiko', 'Fappaizuri', 'FastScene', 'FearNoPeer', + 'Femdomcult', 'File-Tracker', 'FileList', 'FinElite', 'FinVip', 'Flux-Zone', 'Free Farm', 'FSM', + 'FunFile', 'FunkyTorrents', 'FutureTorrent', 'Fuzer', 'Gamera', 'Gay-Torrents.net', + 'gay-torrents.org', 'GAYtorrent.ru', 'GazelleGames', 'GazelleGames (API)', 'Generation-Free (API)', + 'Genesis-Movement', 'GigaTorrents', 'GimmePeers', 'Girotorrent', 'GreatPosterWall', 'GreekDiamond', + 'HaiDan', 'Haitang', 'HappyFappy', 'Hares Club', 'hawke-uno', 'HD-CLUB', 'HD-CzTorrent', + 'HD-Forever', 'HD-Olimpo (API)', 'HD-Only', 'HD-Space', 'HD-Torrents', 'HD-UNiT3D (API)', + 'HD4FANS', 'HDArea', 'HDAtmos', 'HDBits (API)', 'HDC', 'HDDolby', 'HDFans', 'HDFun', 'HDGalaKtik', + 'HDHome', 'HDMaYi', 'HDPT', 'HDRoute', 'HDSky', 'HDtime', 'HDTorrents.it', 'HDTurk', 'HDU', + 'hdvbits', 'HDVIDEO', 'Hebits', 'HellasHut', 'HellTorrents', 'HHanClub', 'HomePornTorrents', + 'House of Devil', 'HQMusic', 'HunTorrent', 'iAnon', 'ICC2022', 'Il Corsaro Blu', 'ilDraGoNeRo', + 'ImmortalSeed', 'Immortuos', 'Indietorrents', 'Infire', 'Insane Tracker', 'IPTorrents', + 'ItaTorrents', 'JME-REUNIT3D (API)', 'JoyHD', 'JPopsuki', 'JPTV (API)', 'KamePT', 'Karagarga', + 'Keep Friends', 'KIMOJI', 'Kinorun', 'Kinozal', 'Kinozal (M)', 'Korsar', 'KrazyZone', 'Kufei', + 'Kufirc', 'LaidBackManor (API)', 'Last Digital Underground', 'LastFiles', 'Lat-Team (API)', + 'Le-Cinephile', 'LearnBits', 'LearnFlakes', 'leech24', 'Les-Cinephiles', 'LeSaloon', 'Lesbians4u', + 'Libble', 'LibraNet', 'LinkoManija', 'Locadora', 'LosslessClub', 'LostFilm.tv', 'LST', + 'M-Team - TP', 'M-Team - TP (2FA)', 'MaDs Revolution', 'Magnetico (Local DHT)', 'Majomparádé', + 'Making Off', 'Marine Tracker', 'Masters-TB', 'Mazepa', 'MDAN', 'MegamixTracker', + 'Mendigos da WEB', 'MeseVilág', 'Metal Tracker', 'MetalGuru', 'Milkie', 'MIRCrew', 'MMA-torrents', + 'MNV', 'MOJBLiNK', 'MonikaDesign (API)', 'MoreThanTV (API)', 'MouseBits', 'Movie-Torrentz', + 'MovieWorld', 'MuseBootlegs', 'MVGroup Forum', 'MVGroup Main', 'MyAnonamouse', 'MySpleen', 'nCore', + 'NebulanceAPI', 'NetHD', 'NewStudioL', 'NicePT', 'NoNaMe ClubL', 'NorBits', 'NORDiCHD', + 'Ntelogo (API)', 'OKPT', 'Old Toons World', 'OnlyEncodes (API)', 'OpenCD', 'Orpheus', 'OshenPT', + 'Ostwiki', 'OurBits', 'P2PBG', 'Panda', 'Party-Tracker', 'PassThePopcorn', 'Peeratiko', 'Peers.FM', + 'PigNetwork', 'PixelCove', 'PixelCove2FA', 'PiXELHD', 'PolishSource', 'PolishTracker (API)', + 'Pornbay', 'PornoLab', 'Portugas (API)', 'PotUK', 'PreToMe', 'PrivateHD', 'ProAudioTorrents', + 'PTCafe', 'PTChina', 'PTerClub', 'PTFiles', 'PThome', 'PTLSP', 'PTSBAO', 'PTTime', 'PT分享站', + "Punk's Horror Tracker", 'PuntoTorrent', 'PussyTorrents', 'PuTao', 'PWTorrents', 'R3V WTF!', + 'Racing4Everyone (API)', 'RacingForMe', 'Rainbow Tracker', 'RareShare2 (API)', 'Red Leaves', + 'Red Star Torrent', 'Redacted', 'RedBits (API)', 'ReelFLiX (API)', 'Resurrect The Net', + 'RetroFlix', 'RevolutionTT', 'RGFootball', 'RinTor', 'RiperAM', 'RM-HD', 'RockBox', + 'Romanian Metal Torrents', 'Rousi', 'RPTScene', 'RUDUB', 'Rustorka', 'RuTracker', 'SATClubbing', + 'SceneHD', 'SceneLinks', 'SceneRush', 'SceneTime', 'Secret Cinema', 'SeedFile', 'seleZen', + 'Shadowflow', 'Shareisland (API)', 'Sharewood', 'Sharewood API', 'SharkPT', 'Shazbat', 'SiamBIT', + 'SkipTheCommercials (API)', 'SkipTheTrailers', 'SkTorrent', 'SkTorrent-org', 'slosoul', 'SnowPT', + 'SoulVoice', 'Speed.cd', 'SpeedApp', 'Speedmaster HD', 'SpeedTorrent Reloaded', + 'Spirit of Revolution', 'SportsCult', 'SpringSunday', 'SugoiMusic', 'Superbits', 'Swarmazon (API)', + 'Tapochek', 'Tasmanit', 'Team CT Game', 'TeamHD', 'TeamOS', 'TEKNO3D', 'teracod', 'The Crazy Ones', + 'The Empire', 'The Falling Angels', 'The Geeks', 'The New Retro', 'The Occult', + 'The Old School (API)', 'The Place', 'The Shinning (API)', 'The Show', 'The Vault', 'The-New-Fun', + 'TheLeachZone', 'themixingbowl', 'TheRebels (API)', 'TheScenePlace', "Thor's Land", 'TJUPT', + 'TLFBits', 'TmGHuB', 'Toca Share', 'Toloka.to', 'ToonsForMe', 'Tornado', 'Torrent Heaven', + 'Torrent Network', 'Torrent Sector Crew', 'Torrent Trader', 'Torrent-Explosiv', 'Torrent-Syndikat', + 'TOrrent-tuRK', 'Torrent.LT', 'TorrentBD', 'TorrentBytes', 'TorrentCCF', 'TorrentDay', 'TorrentDD', + 'Torrenteros (API)', 'TorrentHeaven', 'TorrentHR', 'Torrenting', 'Torrentland', + 'Torrentland (API)', 'TorrentLeech', 'Torrentleech.pl', 'TorrentMasters', 'Torrents-Local', + 'TorrentSeeds (API)', 'TotallyKids', 'ToTheGlory', 'ToTheGloryCookie', 'TrackerMK', + 'TranceTraffic', 'Trellas', 'TreZzoR', 'TreZzoRCookie', 'TribalMixes', 'TurkTorrent', 'TV Store', + 'TVChaosUK', 'TvRoad', 'Twisted-Music', 'U2', 'UBits', 'UHDBits', 'UltraHD', 'Union Fansub', + 'UnionGang', 'UniOtaku', 'Universal-Torrents', 'Unlimitz', 'upload.cx', 'UTOPIA', 'WDT', + 'White Angel', 'WinterSakura', 'World-In-HD', 'World-of-Tomorrow', 'Wukong', 'x-ite.me', + 'XbytesV2', 'Xider-Torrent', 'XSpeeds', 'Xthor (API)', 'xTorrenty', 'Xtreme Bytes', 'XWT-Classics', + 'XWtorrents', 'YDYPT', 'YGGcookie', 'YGGtorrent', 'Zamunda.net', 'Zelka.org', 'ZmPT (织梦)', + 'ZOMB', 'ZonaQ', 'Ztracker'] + +REDIS_HOST = 'redis' + +REDIS_PORT = 6379 \ No newline at end of file diff --git a/stream_fusion/gunicorn_runner.py b/stream_fusion/gunicorn_runner.py new file mode 100644 index 0000000..52b9348 --- /dev/null +++ b/stream_fusion/gunicorn_runner.py @@ -0,0 +1,86 @@ +import os + +from typing import Any + +from gunicorn.app.base import BaseApplication +from gunicorn.util import import_app +from uvicorn.workers import UvicornWorker as BaseUvicornWorker +from stream_fusion.logging_config import configure_logging + +try: + import uvloop # (Found nested import) +except ImportError: + uvloop = None # type: ignore # (variables overlap) + + +class UvicornWorker(BaseUvicornWorker): + """ + Configuration for uvicorn workers. + + This class is subclassing UvicornWorker and defines + some parameters class-wide, because it's impossible, + to pass these parameters through gunicorn. + """ + + CONFIG_KWARGS = { # (upper-case constant in a class) # noqa: RUF012 + "loop": "uvloop" if uvloop is not None else "asyncio", + "http": "httptools", + "lifespan": "on", + "factory": True, + "proxy_headers": False, + } + + +class GunicornApplication(BaseApplication): + """ + Custom gunicorn application. + + This class is used to start guncicorn + with custom uvicorn workers. + """ + + def __init__(self, app: str, host: str, port: int, workers: int, **kwargs: Any): + self.options = { + "bind": f"{host}:{port}", + "workers": workers, + "worker_class": "stream_fusion.gunicorn_runner.UvicornWorker", + "logconfig_dict": { + 'version': 1, + 'disable_existing_loggers': False, + }, + **kwargs, + } + self.app = app + super().__init__() + + def load(self) -> str: + os.environ["RUNNING_UNDER_GUNICORN"] = "1" + return import_app(self.app) + + def when_ready(self, server): + configure_logging() + + def load_config(self) -> None: + """ + Load config for web server. + + This function is used to set parameters to gunicorn + main process. It only sets parameters that + gunicorn can handle. If you pass unknown + parameter to it, it crash with error. + """ + for key, value in self.options.items(): + if key in self.cfg.settings and value is not None: + self.cfg.set(key.lower(), value) + + def load(self) -> str: + """ + Load actual application. + + Gunicorn loads application based on this + function's returns. We return python's path to + the app's factory. + + :returns: python path to app factory. + """ + return import_app(self.app) diff --git a/stream_fusion/logging_config.py b/stream_fusion/logging_config.py new file mode 100644 index 0000000..966b043 --- /dev/null +++ b/stream_fusion/logging_config.py @@ -0,0 +1,109 @@ +# logging_config.py +from loguru import logger +from stream_fusion.settings import settings +import sys +import logging +import inspect +import re +import stackprinter + +REDACTED = settings.log_redacted +patterns = [ + r"/ey.*?/", +] + +class SecretFilter: + def __init__(self, patterns): + self._patterns = patterns + + def __call__(self, record): + record["message"] = self.redact(record["message"]) + if "stack" in record["extra"]: + record["extra"]["stack"] = self.redact(record["extra"]["stack"]) + return record + + def redact(self, message): + for pattern in self._patterns: + message = re.sub(pattern, "****", message) + return message + + +def format(record): + format_ = "{time} {level} {function} {message}\n" + pats = [ + r"/ey.*?/", + ] + if record["exception"] is not None: + stack = stackprinter.format( + record["exception"], + suppressed_vars=[ + r".*ygg_playload.*", + ], + ) + if REDACTED: + for pat in pats: + stack = re.sub(pat, "/****/", stack) + record["extra"]["stack"] = stack + format_ += "{extra[stack]}\n" + return format_ + +class InterceptHandler(logging.Handler): + def emit(self, record: logging.LogRecord) -> None: + level: str | int + try: + level = logger.level(record.levelname).name + except ValueError: + level = record.levelno + + frame, depth = inspect.currentframe(), 0 + while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__): + frame = frame.f_back + depth += 1 + + logger.opt(depth=depth, exception=record.exc_info).log( + level, record.getMessage() + ) + +def is_running_under_gunicorn(): + return "gunicorn" in sys.modules + +def configure_logging(): + + intercept_handler = InterceptHandler() + + logging.basicConfig(handlers=[intercept_handler], level=logging.NOTSET) + + if is_running_under_gunicorn(): + # Configuration spécifique pour Gunicorn + gunicorn_logger = logging.getLogger('gunicorn.error') + logger.remove() + logger.add(gunicorn_logger.handlers[0], format=format, level=gunicorn_logger.level) + + for logger_name in logging.root.manager.loggerDict: + if logger_name.startswith("uvicorn."): + logging.getLogger(logger_name).handlers = [] + + # change handler for default uvicorn logger + logging.getLogger("uvicorn").handlers = [intercept_handler] + logging.getLogger("uvicorn.access").handlers = [intercept_handler] + + logger.remove() + + logger.add( + sys.stdout, + format=format, + level=settings.log_level.value, + colorize=True, + filter=SecretFilter(patterns) if REDACTED else None, + ) + + logger.add( + settings.log_path, + format=format, + level="DEBUG", + rotation="2 MB", + retention="5 days", + compression="zip", + enqueue=True, + filter=SecretFilter(patterns) if REDACTED else None, + ) diff --git a/stream_fusion/services/redis/redis_config.py b/stream_fusion/services/redis/redis_config.py new file mode 100644 index 0000000..9174a14 --- /dev/null +++ b/stream_fusion/services/redis/redis_config.py @@ -0,0 +1,26 @@ +from functools import lru_cache +from fastapi import Depends +from stream_fusion.settings import settings +from stream_fusion.utils.cache.local_redis import RedisCache + +@lru_cache() +def get_redis_config(): + return { + "redisHost": settings.redis_host, + "redisPort": settings.redis_port, + "redisExpiration": settings.redis_expiration, + } + +@lru_cache() +def create_redis_cache(): + return RedisCache(get_redis_config()) + +def get_redis_cache(): + return create_redis_cache() + +async def get_redis_cache_dependency(): + redis_cache = get_redis_cache() + try: + yield redis_cache + finally: + redis_cache.close() \ No newline at end of file diff --git a/stream_fusion/services/security_db/__init__.py b/stream_fusion/services/security_db/__init__.py new file mode 100644 index 0000000..e9229e6 --- /dev/null +++ b/stream_fusion/services/security_db/__init__.py @@ -0,0 +1,4 @@ +"""Security Database Service""" +from stream_fusion.services.security_db._sqlite_access import security_db_access + +__all__ = ["security_db_access"] diff --git a/stream_fusion/services/security_db/_sqlite_access.py b/stream_fusion/services/security_db/_sqlite_access.py new file mode 100644 index 0000000..3254b05 --- /dev/null +++ b/stream_fusion/services/security_db/_sqlite_access.py @@ -0,0 +1,181 @@ +"""Interaction with SQLite database.""" + +import sqlite3 +import threading +import uuid +from datetime import datetime, timedelta +from typing import List, Optional, Tuple + +from fastapi import HTTPException +from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY + +from stream_fusion.logging_config import logger +from stream_fusion.settings import settings + + +class SQLiteAccess: + """Class handling SQLite connection and operations.""" + + def __init__(self): + self.db_location = settings.db_path + self.expiration_limit = getattr(settings, "security_api_key_expiration", 15) + if self.expiration_limit == 15: + logger.warning( + "Security API key expiration not found or invalid in settings. Using default value of 15 days." + ) + self.init_db() + + def init_db(self): + """Initialize the database and perform necessary migrations.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + """ + CREATE TABLE IF NOT EXISTS stream_fusion_security ( + api_key TEXT PRIMARY KEY, + is_active INTEGER, + never_expire INTEGER, + expiration_date TEXT, + latest_query_date TEXT, + total_queries INTEGER, + name TEXT + ) + """ + ) + conn.commit() + + def create_key(self, name: str, never_expire: bool) -> str: + """Create a new API key.""" + api_key = str(uuid.uuid4()) + expiration_date = ( + (datetime.utcnow() + timedelta(days=self.expiration_limit)).isoformat( + timespec="seconds" + ) + if not never_expire + else None + ) + + with sqlite3.connect(self.db_location) as conn: + conn.execute( + """ + INSERT INTO stream_fusion_security ( + api_key, + is_active, + never_expire, + expiration_date, + latest_query_date, + total_queries, name) VALUES (?, 1, ?, ?, NULL, 0, ?) + """, + (api_key, int(never_expire), expiration_date, name), + ) + return api_key + + def renew_key(self, api_key: str, new_expiration_date: Optional[str] = None) -> str: + """Renew an existing API key.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT is_active, never_expire FROM stream_fusion_security WHERE api_key = ?", + (api_key,), + ) + result = cursor.fetchone() + + if not result: + raise HTTPException( + status_code=HTTP_404_NOT_FOUND, detail="API key not found" + ) + + is_active, never_expire = result + response_lines = [] + + if not is_active: + response_lines.append( + "This API key was revoked and has been reactivated." + ) + + if not never_expire: + if not new_expiration_date: + new_expiration_date = ( + datetime.utcnow() + timedelta(days=self.expiration_limit) + ).isoformat(timespec="seconds") + else: + try: + new_expiration_date = datetime.fromisoformat( + new_expiration_date + ).isoformat(timespec="seconds") + except ValueError: + raise HTTPException( + status_code=HTTP_422_UNPROCESSABLE_ENTITY, + detail="The expiration date could not be parsed. Please use ISO 8601 format.", + ) + + cursor.execute( + "UPDATE stream_fusion_security SET expiration_date = ?, is_active = 1 WHERE api_key = ?", + (new_expiration_date, api_key), + ) + response_lines.append( + f"The new expiration date for the API key is {new_expiration_date}" + ) + + return " ".join(response_lines) + + def revoke_key(self, api_key: str): + """Revoke an API key.""" + with sqlite3.connect(self.db_location) as conn: + conn.execute( + "UPDATE stream_fusion_security SET is_active = 0 WHERE api_key = ?", + (api_key,), + ) + + def check_key(self, api_key: str) -> bool: + """Check if an API key is valid.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + "SELECT is_active, total_queries, expiration_date, never_expire FROM stream_fusion_security WHERE api_key = ?", + (api_key,), + ) + result = cursor.fetchone() + + if ( + not result + or not result[0] + or ( + not result[3] + and datetime.fromisoformat(result[2]) < datetime.utcnow() + ) + ): + return False + + threading.Thread( + target=self._update_usage, args=(api_key, result[1]) + ).start() + return True + + def _update_usage(self, api_key: str, usage_count: int): + """Update usage statistics for an API key.""" + with sqlite3.connect(self.db_location) as conn: + conn.execute( + "UPDATE stream_fusion_security SET total_queries = ?, latest_query_date = ? WHERE api_key = ?", + ( + usage_count + 1, + datetime.utcnow().isoformat(timespec="seconds"), + api_key, + ), + ) + + def get_usage_stats(self) -> List[Tuple[str, bool, bool, str, str, int, str]]: + """Return usage statistics for all API keys.""" + with sqlite3.connect(self.db_location) as conn: + cursor = conn.cursor() + cursor.execute( + """ + SELECT api_key, is_active, never_expire, expiration_date, latest_query_date, total_queries, name + FROM stream_fusion_security + ORDER BY latest_query_date DESC + """ + ) + return cursor.fetchall() + + +security_db_access = SQLiteAccess() diff --git a/stream_fusion/settings.py b/stream_fusion/settings.py new file mode 100644 index 0000000..0a9a645 --- /dev/null +++ b/stream_fusion/settings.py @@ -0,0 +1,70 @@ +import enum +from pydantic import ValidationError +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class LogLevel(str, enum.Enum): + """Possible log levels.""" + + NOTSET = "NOTSET" + DEBUG = "DEBUG" + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" + FATAL = "FATAL" + + +class Settings(BaseSettings): + """Settings for the application""" + + # STREAM-FUSION + workers_count: int = 4 + port: int = 8080 + host: str = "0.0.0.0" + timeout: int = 180 + reload: bool = True + # LOGGING + log_level: LogLevel = LogLevel.INFO + log_path: str = "/app/config/logs/stream-fusion.log" + log_redacted: bool = True + # SECUIRITY + secret_api_key: str = "superkey_that_can_be_changed" + security_hide_docs: bool = True + # SQLITE + db_path: str = "/app/config/stream-fusion.db" + db_echo: bool = False + db_timeout: int = 15 + # REDIS + redis_host: str = "redis" + redis_port: int = 6379 + redis_expiration: int = 604800 + # TMDB + tmdb_api_key: str = "tmdb_api_key" + # FLARESOLVERR + flaresolverr_host: str = "localhost" + flaresolverr_shema: str = "http" + flaresolverr_port: int = 8191 + # JACKETT + jackett_host: str = "localhost" + jackett_shema: str = "http" + jackett_port: int = 9117 + jackett_api_key: str = "jackett_api_key" + # ZILEAN DMM API + zilean_dmm_api_key: str = "zilean_dmm_api_key" # TODO: check to protéct Zilane API with APIKEY + zilean_url: str = "https://zilean.io/api/v1/dmm/search" + # DEVELOPMENT + debug: bool = True + dev_host: str = "0.0.0.0" + dev_port: int = 8080 + # VERSION + version_path: str = "/app/pyproject.toml" + + model_config = SettingsConfigDict( + env_file=".env", secrets_dir="/run/secrets", env_file_encoding="utf-8" + ) + + +try: + settings = Settings() +except ValidationError as e: + raise RuntimeError(f"Configuration validation error: {e}") diff --git a/stream_fusion/static/docs/redoc.standalone.js b/stream_fusion/static/docs/redoc.standalone.js new file mode 100644 index 0000000..6cd53ac --- /dev/null +++ b/stream_fusion/static/docs/redoc.standalone.js @@ -0,0 +1,1804 @@ +/*! For license information please see redoc.standalone.js.LICENSE.txt */!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("null")):"function"==typeof define&&define.amd?define(["null"],t):"object"==typeof exports?exports.Redoc=t(require("null")):e.Redoc=t(e.null)}(this,(function(e){return function(){var t={5499:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;let n=r(3325),i=r(6479),o=r(5522),a=r(1603),s=["/properties"],l="http://json-schema.org/draft-07/schema";class c extends n.default{_addVocabularies(){super._addVocabularies(),i.default.forEach((e=>this.addVocabulary(e))),this.opts.discriminator&&this.addKeyword(o.default)}_addDefaultMetaSchema(){if(super._addDefaultMetaSchema(),!this.opts.meta)return;let e=this.opts.$data?this.$dataMetaSchema(a,s):a;this.addMetaSchema(e,l,!1),this.refs["http://json-schema.org/schema"]=l}defaultMeta(){return this.opts.defaultMeta=super.defaultMeta()||(this.getSchema(l)?l:void 0)}}e.exports=t=c,Object.defineProperty(t,"__esModule",{value:!0}),t.default=c;var u=r(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return u.KeywordCxt}});var p=r(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return p._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return p.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return p.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return p.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return p.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return p.CodeGen}})},4667:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexpCode=t.getProperty=t.safeStringify=t.stringify=t.strConcat=t.addCodeArg=t.str=t._=t.nil=t._Code=t.Name=t.IDENTIFIER=t._CodeOrName=void 0;class r{}t._CodeOrName=r,t.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;class n extends r{constructor(e){if(super(),!t.IDENTIFIER.test(e))throw Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}}t.Name=n;class i extends r{constructor(e){super(),this._items="string"==typeof e?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;let e=this._items[0];return""===e||'""'===e}get str(){var e;return null!==(e=this._str)&&void 0!==e?e:this._str=this._items.reduce(((e,t)=>`${e}${t}`),"")}get names(){var e;return null!==(e=this._names)&&void 0!==e?e:this._names=this._items.reduce(((e,t)=>(t instanceof n&&(e[t.str]=(e[t.str]||0)+1),e)),{})}}function o(e,...t){let r=[e[0]],n=0;for(;n"),GTE:new n._Code(">="),LT:new n._Code("<"),LTE:new n._Code("<="),EQ:new n._Code("==="),NEQ:new n._Code("!=="),NOT:new n._Code("!"),OR:new n._Code("||"),AND:new n._Code("&&"),ADD:new n._Code("+")};class s{optimizeNodes(){return this}optimizeNames(e,t){return this}}class l extends s{constructor(e,t,r){super(),this.varKind=e,this.name=t,this.rhs=r}render({es5:e,_n:t}){let r=e?i.varKinds.var:this.varKind,n=void 0===this.rhs?"":` = ${this.rhs}`;return`${r} ${this.name}${n};`+t}optimizeNames(e,t){if(e[this.name.str])return this.rhs&&(this.rhs=C(this.rhs,e,t)),this}get names(){return this.rhs instanceof n._CodeOrName?this.rhs.names:{}}}class c extends s{constructor(e,t,r){super(),this.lhs=e,this.rhs=t,this.sideEffects=r}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,t){if(!(this.lhs instanceof n.Name)||e[this.lhs.str]||this.sideEffects)return this.rhs=C(this.rhs,e,t),this}get names(){return $(this.lhs instanceof n.Name?{}:{...this.lhs.names},this.rhs)}}class u extends c{constructor(e,t,r,n){super(e,r,n),this.op=t}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}}class p extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}}class d extends s{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}}class f extends s{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}}class h extends s{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,t){return this.code=C(this.code,e,t),this}get names(){return this.code instanceof n._CodeOrName?this.code.names:{}}}class m extends s{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce(((t,r)=>t+r.render(e)),"")}optimizeNodes(){let{nodes:e}=this,t=e.length;for(;t--;){let r=e[t].optimizeNodes();Array.isArray(r)?e.splice(t,1,...r):r?e[t]=r:e.splice(t,1)}return e.length>0?this:void 0}optimizeNames(e,t){let{nodes:r}=this,n=r.length;for(;n--;){let i=r[n];i.optimizeNames(e,t)||(R(e,i.names),r.splice(n,1))}return r.length>0?this:void 0}get names(){return this.nodes.reduce(((e,t)=>A(e,t.names)),{})}}class g extends m{render(e){return"{"+e._n+super.render(e)+"}"+e._n}}class y extends g{}y.kind="else";class v extends g{constructor(e,t){super(t),this.condition=e}render(e){let t=`if(${this.condition})`+super.render(e);return this.else&&(t+="else "+this.else.render(e)),t}optimizeNodes(){super.optimizeNodes();let e=this.condition;if(!0===e)return this.nodes;let t=this.else;if(t){let e=t.optimizeNodes();t=this.else=Array.isArray(e)?new y(e):e}return t?!1===e?t instanceof v?t:t.nodes:this.nodes.length?this:new v(j(e),t instanceof v?[t]:t.nodes):!1!==e&&this.nodes.length?this:void 0}optimizeNames(e,t){var r;if(this.else=null===(r=this.else)||void 0===r?void 0:r.optimizeNames(e,t),super.optimizeNames(e,t)||this.else)return this.condition=C(this.condition,e,t),this}get names(){let e=super.names;return $(e,this.condition),this.else&&A(e,this.else.names),e}}v.kind="if";class b extends g{}b.kind="for";class x extends b{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iteration=C(this.iteration,e,t),this}get names(){return A(super.names,this.iteration.names)}}class w extends b{constructor(e,t,r,n){super(),this.varKind=e,this.name=t,this.from=r,this.to=n}render(e){let t=e.es5?i.varKinds.var:this.varKind,{name:r,from:n,to:o}=this;return`for(${t} ${r}=${n}; ${r}<${o}; ${r}++)`+super.render(e)}get names(){let e=$(super.names,this.from);return $(e,this.to)}}class k extends b{constructor(e,t,r,n){super(),this.loop=e,this.varKind=t,this.name=r,this.iterable=n}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,t){if(super.optimizeNames(e,t))return this.iterable=C(this.iterable,e,t),this}get names(){return A(super.names,this.iterable.names)}}class _ extends g{constructor(e,t,r){super(),this.name=e,this.args=t,this.async=r}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}}_.kind="func";class O extends m{render(e){return"return "+super.render(e)}}O.kind="return";class S extends g{render(e){let t="try"+super.render(e);return this.catch&&(t+=this.catch.render(e)),this.finally&&(t+=this.finally.render(e)),t}optimizeNodes(){var e,t;return super.optimizeNodes(),null===(e=this.catch)||void 0===e||e.optimizeNodes(),null===(t=this.finally)||void 0===t||t.optimizeNodes(),this}optimizeNames(e,t){var r,n;return super.optimizeNames(e,t),null===(r=this.catch)||void 0===r||r.optimizeNames(e,t),null===(n=this.finally)||void 0===n||n.optimizeNames(e,t),this}get names(){let e=super.names;return this.catch&&A(e,this.catch.names),this.finally&&A(e,this.finally.names),e}}class E extends g{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}}E.kind="catch";class P extends g{render(e){return"finally"+super.render(e)}}function A(e,t){for(let r in t)e[r]=(e[r]||0)+(t[r]||0);return e}function $(e,t){return t instanceof n._CodeOrName?A(e,t.names):e}function C(e,t,r){var i;return e instanceof n.Name?o(e):(i=e)instanceof n._Code&&i._items.some((e=>e instanceof n.Name&&1===t[e.str]&&void 0!==r[e.str]))?new n._Code(e._items.reduce(((e,t)=>(t instanceof n.Name&&(t=o(t)),t instanceof n._Code?e.push(...t._items):e.push(t),e)),[])):e;function o(e){let n=r[e.str];return void 0===n||1!==t[e.str]?e:(delete t[e.str],n)}}function R(e,t){for(let r in t)e[r]=(e[r]||0)-(t[r]||0)}function j(e){return"boolean"==typeof e||"number"==typeof e||null===e?!e:n._`!${L(e)}`}P.kind="finally",t.CodeGen=class{constructor(e,t={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...t,_n:t.lines?"\n":""},this._extScope=e,this._scope=new i.Scope({parent:e}),this._nodes=[new class extends m{}]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,t){let r=this._extScope.value(e,t);return(this._values[r.prefix]||(this._values[r.prefix]=new Set)).add(r),r}getScopeValue(e,t){return this._extScope.getValue(e,t)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,t,r,n){let i=this._scope.toName(t);return void 0!==r&&n&&(this._constants[i.str]=r),this._leafNode(new l(e,i,r)),i}const(e,t,r){return this._def(i.varKinds.const,e,t,r)}let(e,t,r){return this._def(i.varKinds.let,e,t,r)}var(e,t,r){return this._def(i.varKinds.var,e,t,r)}assign(e,t,r){return this._leafNode(new c(e,t,r))}add(e,r){return this._leafNode(new u(e,t.operators.ADD,r))}code(e){return"function"==typeof e?e():e!==n.nil&&this._leafNode(new h(e)),this}object(...e){let t=["{"];for(let[r,i]of e)t.length>1&&t.push(","),t.push(r),(r!==i||this.opts.es5)&&(t.push(":"),n.addCodeArg(t,i));return t.push("}"),new n._Code(t)}if(e,t,r){if(this._blockNode(new v(e)),t&&r)this.code(t).else().code(r).endIf();else if(t)this.code(t).endIf();else if(r)throw Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new v(e))}else(){return this._elseNode(new y)}endIf(){return this._endBlockNode(v,y)}_for(e,t){return this._blockNode(e),t&&this.code(t).endFor(),this}for(e,t){return this._for(new x(e),t)}forRange(e,t,r,n,o=(this.opts.es5?i.varKinds.var:i.varKinds.let)){let a=this._scope.toName(e);return this._for(new w(o,a,t,r),(()=>n(a)))}forOf(e,t,r,o=i.varKinds.const){let a=this._scope.toName(e);if(this.opts.es5){let e=t instanceof n.Name?t:this.var("_arr",t);return this.forRange("_i",0,n._`${e}.length`,(t=>{this.var(a,n._`${e}[${t}]`),r(a)}))}return this._for(new k("of",o,a,t),(()=>r(a)))}forIn(e,t,r,o=(this.opts.es5?i.varKinds.var:i.varKinds.const)){if(this.opts.ownProperties)return this.forOf(e,n._`Object.keys(${t})`,r);let a=this._scope.toName(e);return this._for(new k("in",o,a,t),(()=>r(a)))}endFor(){return this._endBlockNode(b)}label(e){return this._leafNode(new p(e))}break(e){return this._leafNode(new d(e))}return(e){let t=new O;if(this._blockNode(t),this.code(e),1!==t.nodes.length)throw Error('CodeGen: "return" should have one node');return this._endBlockNode(O)}try(e,t,r){if(!t&&!r)throw Error('CodeGen: "try" without "catch" and "finally"');let n=new S;if(this._blockNode(n),this.code(e),t){let e=this.name("e");this._currNode=n.catch=new E(e),t(e)}return r&&(this._currNode=n.finally=new P,this.code(r)),this._endBlockNode(E,P)}throw(e){return this._leafNode(new f(e))}block(e,t){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(t),this}endBlock(e){let t=this._blockStarts.pop();if(void 0===t)throw Error("CodeGen: not in self-balancing block");let r=this._nodes.length-t;if(r<0||void 0!==e&&r!==e)throw Error(`CodeGen: wrong number of nodes: ${r} vs ${e} expected`);return this._nodes.length=t,this}func(e,t=n.nil,r,i){return this._blockNode(new _(e,t,r)),i&&this.code(i).endFunc(),this}endFunc(){return this._endBlockNode(_)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,t){let r=this._currNode;if(r instanceof e||t&&r instanceof t)return this._nodes.pop(),this;throw Error(`CodeGen: not in block "${t?`${e.kind}/${t.kind}`:e.kind}"`)}_elseNode(e){let t=this._currNode;if(!(t instanceof v))throw Error('CodeGen: "else" without "if"');return this._currNode=t.else=e,this}get _root(){return this._nodes[0]}get _currNode(){let e=this._nodes;return e[e.length-1]}set _currNode(e){let t=this._nodes;t[t.length-1]=e}},t.not=j;let T=N(t.operators.AND);t.and=function(...e){return e.reduce(T)};let I=N(t.operators.OR);function N(e){return(t,r)=>t===n.nil?r:r===n.nil?t:n._`${L(t)} ${e} ${L(r)}`}function L(e){return e instanceof n.Name?e:n._`(${e})`}t.or=function(...e){return e.reduce(I)}},7791:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.ValueScope=t.ValueScopeName=t.Scope=t.varKinds=t.UsedValueState=void 0;let o=r(4667);class a extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}}(i=n=t.UsedValueState||(t.UsedValueState={}))[i.Started=0]="Started",i[i.Completed=1]="Completed",t.varKinds={const:new o.Name("const"),let:new o.Name("let"),var:new o.Name("var")};class s{constructor({prefixes:e,parent:t}={}){this._names={},this._prefixes=e,this._parent=t}toName(e){return e instanceof o.Name?e:this.name(e)}name(e){return new o.Name(this._newName(e))}_newName(e){return`${e}${(this._names[e]||this._nameGroup(e)).index++}`}_nameGroup(e){var t,r;if((null===(r=null===(t=this._parent)||void 0===t?void 0:t._prefixes)||void 0===r?void 0:r.has(e))||this._prefixes&&!this._prefixes.has(e))throw Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}}t.Scope=s;class l extends o.Name{constructor(e,t){super(t),this.prefix=e}setValue(e,{property:t,itemIndex:r}){this.value=e,this.scopePath=o._`.${new o.Name(t)}[${r}]`}}t.ValueScopeName=l;let c=o._`\n`;t.ValueScope=class extends s{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?c:o.nil}}get(){return this._scope}name(e){return new l(e,this._newName(e))}value(e,t){var r;if(void 0===t.ref)throw Error("CodeGen: ref must be passed in value");let n=this.toName(e),{prefix:i}=n,o=null!==(r=t.key)&&void 0!==r?r:t.ref,a=this._values[i];if(a){let e=a.get(o);if(e)return e}else a=this._values[i]=new Map;a.set(o,n);let s=this._scope[i]||(this._scope[i]=[]),l=s.length;return s[l]=t.ref,n.setValue(t,{property:i,itemIndex:l}),n}getValue(e,t){let r=this._values[e];if(r)return r.get(t)}scopeRefs(e,t=this._values){return this._reduceValues(t,(t=>{if(void 0===t.scopePath)throw Error(`CodeGen: name "${t}" has no value`);return o._`${e}${t.scopePath}`}))}scopeCode(e=this._values,t,r){return this._reduceValues(e,(e=>{if(void 0===e.value)throw Error(`CodeGen: name "${e}" has no value`);return e.value.code}),t,r)}_reduceValues(e,r,i={},s){let l=o.nil;for(let c in e){let u=e[c];if(!u)continue;let p=i[c]=i[c]||new Map;u.forEach((e=>{if(p.has(e))return;p.set(e,n.Started);let i=r(e);if(i){let r=this.opts.es5?t.varKinds.var:t.varKinds.const;l=o._`${l}${r} ${e} = ${i};${this.opts._n}`}else{if(!(i=null==s?void 0:s(e)))throw new a(e);l=o._`${l}${i}${this.opts._n}`}p.set(e,n.Completed)}))}return l}}},1885:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendErrors=t.resetErrorsCount=t.reportExtraError=t.reportError=t.keyword$DataError=t.keywordError=void 0;let n=r(4475),i=r(6124),o=r(5018);function a(e,t){let r=e.const("err",t);e.if(n._`${o.default.vErrors} === null`,(()=>e.assign(o.default.vErrors,n._`[${r}]`)),n._`${o.default.vErrors}.push(${r})`),e.code(n._`${o.default.errors}++`)}function s(e,t){let{gen:r,validateName:i,schemaEnv:o}=e;o.$async?r.throw(n._`new ${e.ValidationError}(${t})`):(r.assign(n._`${i}.errors`,t),r.return(!1))}t.keywordError={message:({keyword:e})=>n.str`should pass "${e}" keyword validation`},t.keyword$DataError={message:({keyword:e,schemaType:t})=>t?n.str`"${e}" keyword must be ${t} ($data)`:n.str`"${e}" keyword is invalid ($data)`},t.reportError=function(e,r=t.keywordError,i,o){let{it:l}=e,{gen:u,compositeRule:p,allErrors:d}=l,f=c(e,r,i);(null!=o?o:p||d)?a(u,f):s(l,n._`[${f}]`)},t.reportExtraError=function(e,r=t.keywordError,n){let{it:i}=e,{gen:l,compositeRule:u,allErrors:p}=i;a(l,c(e,r,n)),u||p||s(i,o.default.vErrors)},t.resetErrorsCount=function(e,t){e.assign(o.default.errors,t),e.if(n._`${o.default.vErrors} !== null`,(()=>e.if(t,(()=>e.assign(n._`${o.default.vErrors}.length`,t)),(()=>e.assign(o.default.vErrors,null)))))},t.extendErrors=function({gen:e,keyword:t,schemaValue:r,data:i,errsCount:a,it:s}){if(void 0===a)throw Error("ajv implementation error");let l=e.name("err");e.forRange("i",a,o.default.errors,(a=>{e.const(l,n._`${o.default.vErrors}[${a}]`),e.if(n._`${l}.instancePath === undefined`,(()=>e.assign(n._`${l}.instancePath`,n.strConcat(o.default.instancePath,s.errorPath)))),e.assign(n._`${l}.schemaPath`,n.str`${s.errSchemaPath}/${t}`),s.opts.verbose&&(e.assign(n._`${l}.schema`,r),e.assign(n._`${l}.data`,i))}))};let l={keyword:new n.Name("keyword"),schemaPath:new n.Name("schemaPath"),params:new n.Name("params"),propertyName:new n.Name("propertyName"),message:new n.Name("message"),schema:new n.Name("schema"),parentSchema:new n.Name("parentSchema")};function c(e,t,r){let{createErrors:a}=e.it;return!1===a?n._`{}`:function(e,t,r={}){let{gen:a,it:s}=e,c=[function({errorPath:e},{instancePath:t}){let r=t?n.str`${e}${i.getErrorPath(t,i.Type.Str)}`:e;return[o.default.instancePath,n.strConcat(o.default.instancePath,r)]}(s,r),function({keyword:e,it:{errSchemaPath:t}},{schemaPath:r,parentSchema:o}){let a=o?t:n.str`${t}/${e}`;return r&&(a=n.str`${a}${i.getErrorPath(r,i.Type.Str)}`),[l.schemaPath,a]}(e,r)];return function(e,{params:t,message:r},i){let{keyword:a,data:s,schemaValue:c,it:u}=e,{opts:p,propertyName:d,topSchemaRef:f,schemaPath:h}=u;i.push([l.keyword,a],[l.params,"function"==typeof t?t(e):t||n._`{}`]),p.messages&&i.push([l.message,"function"==typeof r?r(e):r]),p.verbose&&i.push([l.schema,c],[l.parentSchema,n._`${f}${h}`],[o.default.data,s]),d&&i.push([l.propertyName,d])}(e,t,c),a.object(...c)}(e,t,r)}},7805:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.resolveSchema=t.getCompilingSchema=t.resolveRef=t.compileSchema=t.SchemaEnv=void 0;let n=r(4475),i=r(8451),o=r(5018),a=r(9826),s=r(6124),l=r(1321),c=r(540);class u{constructor(e){var t;let r;this.refs={},this.dynamicAnchors={},"object"==typeof e.schema&&(r=e.schema),this.schema=e.schema,this.schemaId=e.schemaId,this.root=e.root||this,this.baseId=null!==(t=e.baseId)&&void 0!==t?t:a.normalizeId(null==r?void 0:r[e.schemaId||"$id"]),this.schemaPath=e.schemaPath,this.localRefs=e.localRefs,this.meta=e.meta,this.$async=null==r?void 0:r.$async,this.refs={}}}function p(e){let t=f.call(this,e);if(t)return t;let r,s=a.getFullPath(e.root.baseId),{es5:c,lines:u}=this.opts.code,{ownProperties:p}=this.opts,d=new n.CodeGen(this.scope,{es5:c,lines:u,ownProperties:p});e.$async&&(r=d.scopeValue("Error",{ref:i.default,code:n._`require("ajv/dist/runtime/validation_error").default`}));let h=d.scopeName("validate");e.validateName=h;let m,g={gen:d,allErrors:this.opts.allErrors,data:o.default.data,parentData:o.default.parentData,parentDataProperty:o.default.parentDataProperty,dataNames:[o.default.data],dataPathArr:[n.nil],dataLevel:0,dataTypes:[],definedProperties:new Set,topSchemaRef:d.scopeValue("schema",!0===this.opts.code.source?{ref:e.schema,code:n.stringify(e.schema)}:{ref:e.schema}),validateName:h,ValidationError:r,schema:e.schema,schemaEnv:e,rootId:s,baseId:e.baseId||s,schemaPath:n.nil,errSchemaPath:e.schemaPath||(this.opts.jtd?"":"#"),errorPath:n._`""`,opts:this.opts,self:this};try{this._compilations.add(e),l.validateFunctionCode(g),d.optimize(this.opts.code.optimize);let t=d.toString();m=`const visitedNodesForRef = new WeakMap(); ${d.scopeRefs(o.default.scope)}return ${t}`,this.opts.code.process&&(m=this.opts.code.process(m,e));let r=Function(`${o.default.self}`,`${o.default.scope}`,m)(this,this.scope.get());if(this.scope.value(h,{ref:r}),r.errors=null,r.schema=e.schema,r.schemaEnv=e,e.$async&&(r.$async=!0),!0===this.opts.code.source&&(r.source={validateName:h,validateCode:t,scopeValues:d._values}),this.opts.unevaluated){let{props:e,items:t}=g;r.evaluated={props:e instanceof n.Name?void 0:e,items:t instanceof n.Name?void 0:t,dynamicProps:e instanceof n.Name,dynamicItems:t instanceof n.Name},r.source&&(r.source.evaluated=n.stringify(r.evaluated))}return e.validate=r,e}catch(t){throw delete e.validate,delete e.validateName,m&&this.logger.error("Error compiling schema, function code:",m),t}finally{this._compilations.delete(e)}}function d(e){return a.inlineRef(e.schema,this.opts.inlineRefs)?e.schema:e.validate?e:p.call(this,e)}function f(e){var t,r;for(let n of this._compilations)if(r=e,(t=n).schema===r.schema&&t.root===r.root&&t.baseId===r.baseId)return n}function h(e,t){let r;for(;"string"==typeof(r=this.refs[t]);)t=r;return r||this.schemas[t]||m.call(this,e,t)}function m(e,t){let r=c.parse(t),n=a._getFullPath(r),i=a.getFullPath(e.baseId);if(Object.keys(e.schema).length>0&&n===i)return y.call(this,r,e);let o=a.normalizeId(n),s=this.refs[o]||this.schemas[o];if("string"==typeof s){let t=m.call(this,e,s);if("object"!=typeof(null==t?void 0:t.schema))return;return y.call(this,r,t)}if("object"==typeof(null==s?void 0:s.schema)){if(s.validate||p.call(this,s),o===a.normalizeId(t)){let{schema:t}=s,{schemaId:r}=this.opts,n=t[r];return n&&(i=a.resolveUrl(i,n)),new u({schema:t,schemaId:r,root:e,baseId:i})}return y.call(this,r,s)}}t.SchemaEnv=u,t.compileSchema=p,t.resolveRef=function(e,t,r){var n;let i=a.resolveUrl(t,r),o=e.refs[i];if(o)return o;let s=h.call(this,e,i);if(void 0===s){let r=null===(n=e.localRefs)||void 0===n?void 0:n[i],{schemaId:o}=this.opts;r&&(s=new u({schema:r,schemaId:o,root:e,baseId:t}))}if(void 0===s&&this.opts.loadSchemaSync){let n=this.opts.loadSchemaSync(t,r,i);!n||this.refs[i]||this.schemas[i]||(this.addSchema(n,i,void 0),s=h.call(this,e,i))}return void 0!==s?e.refs[i]=d.call(this,s):void 0},t.getCompilingSchema=f,t.resolveSchema=m;let g=new Set(["properties","patternProperties","enum","dependencies","definitions"]);function y(e,{baseId:t,schema:r,root:n}){var i;if("/"!==(null===(i=e.fragment)||void 0===i?void 0:i[0]))return;for(let n of e.fragment.slice(1).split("/")){if("boolean"==typeof r||void 0===(r=r[s.unescapeFragment(n)]))return;let e="object"==typeof r&&r[this.opts.schemaId];!g.has(n)&&e&&(t=a.resolveUrl(t,e))}let o;if("boolean"!=typeof r&&r.$ref&&!s.schemaHasRulesButRef(r,this.RULES)){let e=a.resolveUrl(t,r.$ref);o=m.call(this,n,e)}let{schemaId:l}=this.opts;return(o=o||new u({schema:r,schemaId:l,root:n,baseId:t})).schema!==o.root.schema?o:void 0}},5018:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i={data:new n.Name("data"),valCxt:new n.Name("valCxt"),instancePath:new n.Name("instancePath"),parentData:new n.Name("parentData"),parentDataProperty:new n.Name("parentDataProperty"),rootData:new n.Name("rootData"),dynamicAnchors:new n.Name("dynamicAnchors"),vErrors:new n.Name("vErrors"),errors:new n.Name("errors"),this:new n.Name("this"),self:new n.Name("self"),scope:new n.Name("scope"),json:new n.Name("json"),jsonPos:new n.Name("jsonPos"),jsonLen:new n.Name("jsonLen"),jsonPart:new n.Name("jsonPart")};t.default=i},4143:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(9826);t.default=class extends Error{constructor(e,t,r){super(r||`can't resolve reference ${t} from id ${e}`),this.missingRef=n.resolveUrl(e,t),this.missingSchema=n.normalizeId(n.getFullPath(this.missingRef))}}},9826:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSchemaRefs=t.resolveUrl=t.normalizeId=t._getFullPath=t.getFullPath=t.inlineRef=void 0;let n=r(6124),i=r(4063),o=r(4029),a=r(540),s=new Set(["type","format","pattern","maxLength","minLength","maxProperties","minProperties","maxItems","minItems","maximum","minimum","uniqueItems","multipleOf","required","enum","const"]);t.inlineRef=function(e,t=!0){return"boolean"==typeof e||(!0===t?!function e(t){for(let r in t){if(l.has(r))return!0;let n=t[r];if(Array.isArray(n)&&n.some(e)||"object"==typeof n&&e(n))return!0}return!1}(e):!!t&&function e(t){let r=0;for(let i in t)if("$ref"===i||(r++,!s.has(i)&&("object"==typeof t[i]&&n.eachItem(t[i],(t=>r+=e(t))),r===1/0)))return 1/0;return r}(e)<=t)};let l=new Set(["$ref","$recursiveRef","$recursiveAnchor","$dynamicRef","$dynamicAnchor"]);function c(e="",t){return!1!==t&&(e=d(e)),u(a.parse(e))}function u(e){return a.serialize(e).split("#")[0]+"#"}t.getFullPath=c,t._getFullPath=u;let p=/#\/?$/;function d(e){return e?e.replace(p,""):""}t.normalizeId=d,t.resolveUrl=function(e,t){return t=d(t),a.resolve(e,t)};let f=/^[a-z_][-a-z0-9._]*$/i;t.getSchemaRefs=function(e){if("boolean"==typeof e)return{};let{schemaId:t}=this.opts,r=d(e[t]),n={"":r},s=c(r,!1),l={},u=new Set;return o(e,{allKeys:!0},((e,r,i,o)=>{if(void 0===o)return;let c=s+r,m=n[o];function g(t){if(t=d(m?a.resolve(m,t):t),u.has(t))throw h(t);u.add(t);let r=this.refs[t];return"string"==typeof r&&(r=this.refs[r]),"object"==typeof r?p(e,r.schema,t):t!==d(c)&&("#"===t[0]?(p(e,l[t],t),l[t]=e):this.refs[t]=c),t}function y(e){if("string"==typeof e){if(!f.test(e))throw Error(`invalid anchor "${e}"`);g.call(this,`#${e}`)}}"string"==typeof e[t]&&(m=g.call(this,e[t])),y.call(this,e.$anchor),y.call(this,e.$dynamicAnchor),n[r]=m})),l;function p(e,t,r){if(void 0!==t&&!i(e,t))throw h(r)}function h(e){return Error(`reference "${e}" resolves to more than one schema`)}}},3664:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getRules=t.isJSONType=void 0;let r=new Set(["string","number","integer","boolean","null","object","array"]);t.isJSONType=function(e){return"string"==typeof e&&r.has(e)},t.getRules=function(){let e={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...e,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},e.number,e.string,e.array,e.object],post:{rules:[]},all:{},keywords:{}}}},6124:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.checkStrictMode=t.getErrorPath=t.Type=t.useFunc=t.setEvaluated=t.evaluatedPropsToName=t.mergeEvaluated=t.eachItem=t.unescapeJsonPointer=t.escapeJsonPointer=t.escapeFragment=t.unescapeFragment=t.schemaRefOrVal=t.schemaHasRulesButRef=t.schemaHasRules=t.checkUnknownRules=t.alwaysValidSchema=t.toHash=void 0;let o=r(4475),a=r(4667);function s(e,t=e.schema){let{opts:r,self:n}=e;if(!r.strictSchema||"boolean"==typeof t)return;let i=n.RULES.keywords;for(let r in t)i[r]||m(e,`unknown keyword: "${r}"`)}function l(e,t){if("boolean"==typeof e)return!e;for(let r in e)if(t[r])return!0;return!1}function c(e){return"number"==typeof e?`${e}`:e.replace(/~/g,"~0").replace(/\//g,"~1")}function u(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function p({mergeNames:e,mergeToName:t,mergeValues:r,resultToName:n}){return(i,a,s,l)=>{let c=void 0===s?a:s instanceof o.Name?(a instanceof o.Name?e(i,a,s):t(i,a,s),s):a instanceof o.Name?(t(i,s,a),a):r(a,s);return l!==o.Name||c instanceof o.Name?c:n(i,c)}}function d(e,t){if(!0===t)return e.var("props",!0);let r=e.var("props",o._`{}`);return void 0!==t&&f(e,r,t),r}function f(e,t,r){Object.keys(r).forEach((r=>e.assign(o._`${t}${o.getProperty(r)}`,!0)))}t.toHash=function(e){let t={};for(let r of e)t[r]=!0;return t},t.alwaysValidSchema=function(e,t){return"boolean"==typeof t?t:0===Object.keys(t).length||(s(e,t),!l(t,e.self.RULES.all))},t.checkUnknownRules=s,t.schemaHasRules=l,t.schemaHasRulesButRef=function(e,t){if("boolean"==typeof e)return!e;for(let r in e)if("$ref"!==r&&t.all[r])return!0;return!1},t.schemaRefOrVal=function({topSchemaRef:e,schemaPath:t},r,n,i){if(!i){if("number"==typeof r||"boolean"==typeof r)return r;if("string"==typeof r)return o._`${r}`}return o._`${e}${t}${o.getProperty(n)}`},t.unescapeFragment=function(e){return u(decodeURIComponent(e))},t.escapeFragment=function(e){return encodeURIComponent(c(e))},t.escapeJsonPointer=c,t.unescapeJsonPointer=u,t.eachItem=function(e,t){if(Array.isArray(e))for(let r of e)t(r);else t(e)},t.mergeEvaluated={props:p({mergeNames:(e,t,r)=>e.if(o._`${r} !== true && ${t} !== undefined`,(()=>{e.if(o._`${t} === true`,(()=>e.assign(r,!0)),(()=>e.assign(r,o._`${r} || {}`).code(o._`Object.assign(${r}, ${t})`)))})),mergeToName:(e,t,r)=>e.if(o._`${r} !== true`,(()=>{!0===t?e.assign(r,!0):(e.assign(r,o._`${r} || {}`),f(e,r,t))})),mergeValues:(e,t)=>!0===e||{...e,...t},resultToName:d}),items:p({mergeNames:(e,t,r)=>e.if(o._`${r} !== true && ${t} !== undefined`,(()=>e.assign(r,o._`${t} === true ? true : ${r} > ${t} ? ${r} : ${t}`))),mergeToName:(e,t,r)=>e.if(o._`${r} !== true`,(()=>e.assign(r,!0===t||o._`${r} > ${t} ? ${r} : ${t}`))),mergeValues:(e,t)=>!0===e||Math.max(e,t),resultToName:(e,t)=>e.var("items",t)})},t.evaluatedPropsToName=d,t.setEvaluated=f;let h={};function m(e,t,r=e.opts.strictSchema){if(r){if(t=`strict mode: ${t}`,!0===r)throw Error(t);e.self.logger.warn(t)}}t.useFunc=function(e,t){return e.scopeValue("func",{ref:t,code:h[t.code]||(h[t.code]=new a._Code(t.code))})},(i=n=t.Type||(t.Type={}))[i.Num=0]="Num",i[i.Str=1]="Str",t.getErrorPath=function(e,t,r){if(e instanceof o.Name){let i=t===n.Num;return r?i?o._`"[" + ${e} + "]"`:o._`"['" + ${e} + "']"`:i?o._`"/" + ${e}`:o._`"/" + ${e}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return r?o.getProperty(e).toString():"/"+c(e)},t.checkStrictMode=m},4566:function(e,t){"use strict";function r(e,t){return t.rules.some((t=>n(e,t)))}function n(e,t){var r;return void 0!==e[t.keyword]||(null===(r=t.definition.implements)||void 0===r?void 0:r.some((t=>void 0!==e[t])))}Object.defineProperty(t,"__esModule",{value:!0}),t.shouldUseRule=t.shouldUseGroup=t.schemaHasRulesForType=void 0,t.schemaHasRulesForType=function({schema:e,self:t},n){let i=t.RULES.types[n];return i&&!0!==i&&r(e,i)},t.shouldUseGroup=r,t.shouldUseRule=n},7627:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.boolOrEmptySchema=t.topBoolOrEmptySchema=void 0;let n=r(1885),i=r(4475),o=r(5018),a={message:"boolean schema is false"};function s(e,t){let{gen:r,data:i}=e;n.reportError({gen:r,keyword:"false schema",data:i,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:e},a,void 0,t)}t.topBoolOrEmptySchema=function(e){let{gen:t,schema:r,validateName:n}=e;!1===r?s(e,!1):"object"==typeof r&&!0===r.$async?t.return(o.default.data):(t.assign(i._`${n}.errors`,null),t.return(!0))},t.boolOrEmptySchema=function(e,t){let{gen:r,schema:n}=e;!1===n?(r.var(t,!1),s(e)):r.var(t,!0)}},7927:function(e,t,r){"use strict";var n,i;Object.defineProperty(t,"__esModule",{value:!0}),t.reportTypeError=t.checkDataTypes=t.checkDataType=t.coerceAndCheckDataType=t.getJSONTypes=t.getSchemaTypes=t.DataType=void 0;let o=r(3664),a=r(4566),s=r(1885),l=r(4475),c=r(6124);function u(e){let t=Array.isArray(e)?e:e?[e]:[];if(t.every(o.isJSONType))return t;throw Error("type must be JSONType or JSONType[]: "+t.join(","))}(i=n=t.DataType||(t.DataType={}))[i.Correct=0]="Correct",i[i.Wrong=1]="Wrong",t.getSchemaTypes=function(e){let t=u(e.type);if(t.includes("null")){if(!1===e.nullable)throw Error("type: null contradicts nullable: false")}else{if(!t.length&&void 0!==e.nullable)throw Error('"nullable" cannot be used without "type"');!0===e.nullable&&t.push("null")}return t},t.getJSONTypes=u,t.coerceAndCheckDataType=function(e,t){var r,i;let{gen:o,data:s,opts:c}=e,u=(r=t,(i=c.coerceTypes)?r.filter((e=>p.has(e)||"array"===i&&"array"===e)):[]),d=t.length>0&&!(0===u.length&&1===t.length&&a.schemaHasRulesForType(e,t[0]));if(d){let r=f(t,s,c.strictNumbers,n.Wrong);o.if(r,(()=>{u.length?function(e,t,r){let{gen:n,data:i,opts:o}=e,a=n.let("dataType",l._`typeof ${i}`),s=n.let("coerced",l._`undefined`);for(let e of("array"===o.coerceTypes&&n.if(l._`${a} == 'object' && Array.isArray(${i}) && ${i}.length == 1`,(()=>n.assign(i,l._`${i}[0]`).assign(a,l._`typeof ${i}`).if(f(t,i,o.strictNumbers),(()=>n.assign(s,i))))),n.if(l._`${s} !== undefined`),r))(p.has(e)||"array"===e&&"array"===o.coerceTypes)&&c(e);function c(e){switch(e){case"string":return void n.elseIf(l._`${a} == "number" || ${a} == "boolean"`).assign(s,l._`"" + ${i}`).elseIf(l._`${i} === null`).assign(s,l._`""`);case"number":return void n.elseIf(l._`${a} == "boolean" || ${i} === null + || (${a} == "string" && ${i} && ${i} == +${i})`).assign(s,l._`+${i}`);case"integer":return void n.elseIf(l._`${a} === "boolean" || ${i} === null + || (${a} === "string" && ${i} && ${i} == +${i} && !(${i} % 1))`).assign(s,l._`+${i}`);case"boolean":return void n.elseIf(l._`${i} === "false" || ${i} === 0 || ${i} === null`).assign(s,!1).elseIf(l._`${i} === "true" || ${i} === 1`).assign(s,!0);case"null":return n.elseIf(l._`${i} === "" || ${i} === 0 || ${i} === false`),void n.assign(s,null);case"array":n.elseIf(l._`${a} === "string" || ${a} === "number" + || ${a} === "boolean" || ${i} === null`).assign(s,l._`[${i}]`)}}n.else(),m(e),n.endIf(),n.if(l._`${s} !== undefined`,(()=>{n.assign(i,s),function({gen:e,parentData:t,parentDataProperty:r},n){e.if(l._`${t} !== undefined`,(()=>e.assign(l._`${t}[${r}]`,n)))}(e,s)}))}(e,t,u):m(e)}))}return d};let p=new Set(["string","number","integer","boolean","null"]);function d(e,t,r,i=n.Correct){let o,a=i===n.Correct?l.operators.EQ:l.operators.NEQ;switch(e){case"null":return l._`${t} ${a} null`;case"array":o=l._`Array.isArray(${t})`;break;case"object":o=l._`${t} && typeof ${t} == "object" && !Array.isArray(${t})`;break;case"integer":o=s(l._`!(${t} % 1) && !isNaN(${t})`);break;case"number":o=s();break;default:return l._`typeof ${t} ${a} ${e}`}return i===n.Correct?o:l.not(o);function s(e=l.nil){return l.and(l._`typeof ${t} == "number"`,e,r?l._`isFinite(${t})`:l.nil)}}function f(e,t,r,n){if(1===e.length)return d(e[0],t,r,n);let i,o=c.toHash(e);if(o.array&&o.object){let e=l._`typeof ${t} != "object"`;i=o.null?e:l._`!${t} || ${e}`,delete o.null,delete o.array,delete o.object}else i=l.nil;for(let e in o.number&&delete o.integer,o)i=l.and(i,d(e,t,r,n));return i}t.checkDataType=d,t.checkDataTypes=f;let h={message:({schema:e})=>`must be ${e}`,params:({schema:e,schemaValue:t})=>"string"==typeof e?l._`{type: ${e}}`:l._`{type: ${t}}`};function m(e){let t=function(e){let{gen:t,data:r,schema:n}=e,i=c.schemaRefOrVal(e,n,"type");return{gen:t,keyword:"type",data:r,schema:n.type,schemaCode:i,schemaValue:i,parentSchema:n,params:{},it:e}}(e);s.reportError(t,h)}t.reportTypeError=m},2537:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.assignDefaults=void 0;let n=r(4475),i=r(6124);function o(e,t,r){let{gen:o,compositeRule:a,data:s,opts:l}=e;if(void 0===r)return;let c=n._`${s}${n.getProperty(t)}`;if(a)return void i.checkStrictMode(e,`default is ignored for: ${c}`);let u=n._`${c} === undefined`;"empty"===l.useDefaults&&(u=n._`${u} || ${c} === null || ${c} === ""`),o.if(u,n._`${c} = ${n.stringify(r)}`)}t.assignDefaults=function(e,t){let{properties:r,items:n}=e.schema;if("object"===t&&r)for(let t in r)o(e,t,r[t].default);else"array"===t&&Array.isArray(n)&&n.forEach(((t,r)=>o(e,r,t.default)))}},1321:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getData=t.KeywordCxt=t.validateFunctionCode=void 0;let n=r(7627),i=r(7927),o=r(4566),a=r(7927),s=r(2537),l=r(6488),c=r(4688),u=r(4475),p=r(5018),d=r(9826),f=r(6124),h=r(1885);function m({gen:e,validateName:t,schema:r,schemaEnv:n,opts:i},o){i.code.es5?e.func(t,u._`${p.default.data}, ${p.default.valCxt}`,n.$async,(()=>{e.code(u._`"use strict"; ${g(r,i)}`),function(e,t){e.if(p.default.valCxt,(()=>{e.var(p.default.instancePath,u._`${p.default.valCxt}.${p.default.instancePath}`),e.var(p.default.parentData,u._`${p.default.valCxt}.${p.default.parentData}`),e.var(p.default.parentDataProperty,u._`${p.default.valCxt}.${p.default.parentDataProperty}`),e.var(p.default.rootData,u._`${p.default.valCxt}.${p.default.rootData}`),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`${p.default.valCxt}.${p.default.dynamicAnchors}`)}),(()=>{e.var(p.default.instancePath,u._`""`),e.var(p.default.parentData,u._`undefined`),e.var(p.default.parentDataProperty,u._`undefined`),e.var(p.default.rootData,p.default.data),t.dynamicRef&&e.var(p.default.dynamicAnchors,u._`{}`)}))}(e,i),e.code(o)})):e.func(t,u._`${p.default.data}, ${u._`{${p.default.instancePath}="", ${p.default.parentData}, ${p.default.parentDataProperty}, ${p.default.rootData}=${p.default.data}${i.dynamicRef?u._`, ${p.default.dynamicAnchors}={}`:u.nil}}={}`}`,n.$async,(()=>e.code(g(r,i)).code(o)))}function g(e,t){let r="object"==typeof e&&e[t.schemaId];return r&&(t.code.source||t.code.process)?u._`/*# sourceURL=${r} */`:u.nil}function y({schema:e,self:t}){if("boolean"==typeof e)return!e;for(let r in e)if(t.RULES.all[r])return!0;return!1}function v(e){return"boolean"!=typeof e.schema}function b(e){f.checkUnknownRules(e),function(e){let{schema:t,errSchemaPath:r,opts:n,self:i}=e;t.$ref&&n.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(t,i.RULES)&&i.logger.warn(`$ref: keywords ignored in schema at path "${r}"`)}(e)}function x(e,t){if(e.opts.jtd)return k(e,[],!1,t);let r=i.getSchemaTypes(e.schema);k(e,r,!i.coerceAndCheckDataType(e,r),t)}function w({gen:e,schemaEnv:t,schema:r,errSchemaPath:n,opts:i}){let o=r.$comment;if(!0===i.$comment)e.code(u._`${p.default.self}.logger.log(${o})`);else if("function"==typeof i.$comment){let r=u.str`${n}/$comment`,i=e.scopeValue("root",{ref:t.root});e.code(u._`${p.default.self}.opts.$comment(${o}, ${r}, ${i}.schema)`)}}function k(e,t,r,n){var i,s,l,c,d,h;let{gen:m,schema:g,data:y,allErrors:v,opts:b,self:x}=e,{RULES:w}=x;function k(i){o.shouldUseGroup(g,i)&&(i.type?(m.if(a.checkDataType(i.type,y,b.strictNumbers)),_(e,i),1===t.length&&t[0]===i.type&&r&&(m.else(),a.reportTypeError(e)),m.endIf()):_(e,i),v||m.if(u._`${p.default.errors} === ${n||0}`))}!g.$ref||!b.ignoreKeywordsWithRef&&f.schemaHasRulesButRef(g,w)?(b.jtd||(s=t,!(i=e).schemaEnv.meta&&i.opts.strictTypes&&(l=i,(c=s).length&&(l.dataTypes.length?(c.forEach((e=>{O(l.dataTypes,e)||S(l,`type "${e}" not allowed by context "${l.dataTypes.join(",")}"`)})),l.dataTypes=l.dataTypes.filter((e=>O(c,e)))):l.dataTypes=c),i.opts.allowUnionTypes||(d=i,(h=s).length>1&&(2!==h.length||!h.includes("null"))&&S(d,"use allowUnionTypes to allow union type keyword")),function(e,t){let r=e.self.RULES.all;for(let n in r){let i=r[n];if("object"==typeof i&&o.shouldUseRule(e.schema,i)){let{type:r}=i.definition;r.length&&!r.some((e=>{var r,n;return n=e,(r=t).includes(n)||"number"===n&&r.includes("integer")}))&&S(e,`missing type "${r.join(",")}" for keyword "${n}"`)}}}(i,i.dataTypes))),m.block((()=>{for(let e of w.rules)k(e);k(w.post)}))):m.block((()=>P(e,"$ref",w.all.$ref.definition)))}function _(e,t){let{gen:r,schema:n,opts:{useDefaults:i}}=e;i&&s.assignDefaults(e,t.type),r.block((()=>{for(let r of t.rules)o.shouldUseRule(n,r)&&P(e,r.keyword,r.definition,t.type)}))}function O(e,t){return e.includes(t)||"integer"===t&&e.includes("number")}function S(e,t){t+=` at "${e.schemaEnv.baseId+e.errSchemaPath}" (strictTypes)`,f.checkStrictMode(e,t,e.opts.strictTypes)}t.validateFunctionCode=function(e){v(e)&&(b(e),y(e))?function(e){let{schema:t,opts:r,gen:n}=e;m(e,(()=>{r.$comment&&t.$comment&&w(e),function(e){let{schema:t,opts:r}=e;void 0!==t.default&&r.useDefaults&&r.strictSchema&&f.checkStrictMode(e,"default is ignored in the schema root")}(e),n.let(p.default.vErrors,null),n.let(p.default.errors,0),r.unevaluated&&function(e){let{gen:t,validateName:r}=e;e.evaluated=t.const("evaluated",u._`${r}.evaluated`),t.if(u._`${e.evaluated}.dynamicProps`,(()=>t.assign(u._`${e.evaluated}.props`,u._`undefined`))),t.if(u._`${e.evaluated}.dynamicItems`,(()=>t.assign(u._`${e.evaluated}.items`,u._`undefined`)))}(e),x(e),function(e){let{gen:t,schemaEnv:r,validateName:n,ValidationError:i,opts:o}=e;r.$async?t.if(u._`${p.default.errors} === 0`,(()=>t.return(p.default.data)),(()=>t.throw(u._`new ${i}(${p.default.vErrors})`))):(t.assign(u._`${n}.errors`,p.default.vErrors),o.unevaluated&&function({gen:e,evaluated:t,props:r,items:n}){r instanceof u.Name&&e.assign(u._`${t}.props`,r),n instanceof u.Name&&e.assign(u._`${t}.items`,n)}(e),t.return(u._`${p.default.errors} === 0`))}(e)}))}(e):m(e,(()=>n.topBoolOrEmptySchema(e)))};class E{constructor(e,t,r){if(l.validateKeywordUsage(e,t,r),this.gen=e.gen,this.allErrors=e.allErrors,this.keyword=r,this.data=e.data,this.schema=e.schema[r],this.$data=t.$data&&e.opts.$data&&this.schema&&this.schema.$data,this.schemaValue=f.schemaRefOrVal(e,this.schema,r,this.$data),this.schemaType=t.schemaType,this.parentSchema=e.schema,this.params={},this.it=e,this.def=t,this.$data)this.schemaCode=e.gen.const("vSchema",C(this.$data,e));else if(this.schemaCode=this.schemaValue,!l.validSchemaType(this.schema,t.schemaType,t.allowUndefined))throw Error(`${r} value must be ${JSON.stringify(t.schemaType)}`);("code"in t?t.trackErrors:!1!==t.errors)&&(this.errsCount=e.gen.const("_errs",p.default.errors))}result(e,t,r){this.gen.if(u.not(e)),r?r():this.error(),t?(this.gen.else(),t(),this.allErrors&&this.gen.endIf()):this.allErrors?this.gen.endIf():this.gen.else()}pass(e,t){this.result(e,void 0,t)}fail(e){if(void 0===e)return this.error(),void(this.allErrors||this.gen.if(!1));this.gen.if(e),this.error(),this.allErrors?this.gen.endIf():this.gen.else()}fail$data(e){if(!this.$data)return this.fail(e);let{schemaCode:t}=this;this.fail(u._`${t} !== undefined && (${u.or(this.invalid$data(),e)})`)}error(e,t,r){if(t)return this.setParams(t),this._error(e,r),void this.setParams({});this._error(e,r)}_error(e,t){(e?h.reportExtraError:h.reportError)(this,this.def.error,t)}$dataError(){h.reportError(this,this.def.$dataError||h.keyword$DataError)}reset(){if(void 0===this.errsCount)throw Error('add "trackErrors" to keyword definition');h.resetErrorsCount(this.gen,this.errsCount)}ok(e){this.allErrors||this.gen.if(e)}setParams(e,t){t?Object.assign(this.params,e):this.params=e}block$data(e,t,r=u.nil){this.gen.block((()=>{this.check$data(e,r),t()}))}check$data(e=u.nil,t=u.nil){if(!this.$data)return;let{gen:r,schemaCode:n,schemaType:i,def:o}=this;r.if(u.or(u._`${n} === undefined`,t)),e!==u.nil&&r.assign(e,!0),(i.length||o.validateSchema)&&(r.elseIf(this.invalid$data()),this.$dataError(),e!==u.nil&&r.assign(e,!1)),r.else()}invalid$data(){let{gen:e,schemaCode:t,schemaType:r,def:n,it:i}=this;return u.or(function(){if(r.length){if(!(t instanceof u.Name))throw Error("ajv implementation error");let e=Array.isArray(r)?r:[r];return u._`${a.checkDataTypes(e,t,i.opts.strictNumbers,a.DataType.Wrong)}`}return u.nil}(),function(){if(n.validateSchema){let r=e.scopeValue("validate$data",{ref:n.validateSchema});return u._`!${r}(${t})`}return u.nil}())}subschema(e,t){var r,i;let o=c.getSubschema(this.it,e);c.extendSubschemaData(o,this.it,e),c.extendSubschemaMode(o,e);let a={...this.it,...o,items:void 0,props:void 0};return i=t,v(r=a)&&(b(r),y(r))?function(e,t){let{schema:r,gen:n,opts:i}=e;i.$comment&&r.$comment&&w(e),function(e){let t=e.schema[e.opts.schemaId];t&&(e.baseId=d.resolveUrl(e.baseId,t))}(e),function(e){if(e.schema.$async&&!e.schemaEnv.$async)throw Error("async schema in sync schema")}(e);let o=n.const("_errs",p.default.errors);x(e,o),n.var(t,u._`${o} === ${p.default.errors}`)}(r,i):n.boolOrEmptySchema(r,i),a}mergeEvaluated(e,t){let{it:r,gen:n}=this;r.opts.unevaluated&&(!0!==r.props&&void 0!==e.props&&(r.props=f.mergeEvaluated.props(n,e.props,r.props,t)),!0!==r.items&&void 0!==e.items&&(r.items=f.mergeEvaluated.items(n,e.items,r.items,t)))}mergeValidEvaluated(e,t){let{it:r,gen:n}=this;if(r.opts.unevaluated&&(!0!==r.props||!0!==r.items))return n.if(t,(()=>this.mergeEvaluated(e,u.Name))),!0}}function P(e,t,r,n){let i=new E(e,r,t);"code"in r?r.code(i,n):i.$data&&r.validate?l.funcKeywordCode(i,r):"macro"in r?l.macroKeywordCode(i,r):(r.compile||r.validate)&&l.funcKeywordCode(i,r)}t.KeywordCxt=E;let A=/^\/(?:[^~]|~0|~1)*$/,$=/^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;function C(e,{dataLevel:t,dataNames:r,dataPathArr:n}){let i,o;if(""===e)return p.default.rootData;if("/"===e[0]){if(!A.test(e))throw Error(`Invalid JSON-pointer: ${e}`);i=e,o=p.default.rootData}else{let a=$.exec(e);if(!a)throw Error(`Invalid JSON-pointer: ${e}`);let s=+a[1];if("#"===(i=a[2])){if(s>=t)throw Error(l("property/index",s));return n[t-s]}if(s>t)throw Error(l("data",s));if(o=r[t-s],!i)return o}let a=o,s=i.split("/");for(let e of s)e&&(a=u._`${a} && ${o=u._`${o}${u.getProperty(f.unescapeJsonPointer(e))}`}`);return a;function l(e,r){return`Cannot access ${e} ${r} levels up, current level is ${t}`}}t.getData=C},6488:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateKeywordUsage=t.validSchemaType=t.funcKeywordCode=t.macroKeywordCode=void 0;let n=r(4475),i=r(5018),o=r(8619),a=r(1885);function s(e){let{gen:t,data:r,it:i}=e;t.if(i.parentData,(()=>t.assign(r,n._`${i.parentData}[${i.parentDataProperty}]`)))}function l(e,t,r){if(void 0===r)throw Error(`keyword "${t}" failed to compile`);return e.scopeValue("keyword","function"==typeof r?{ref:r}:{ref:r,code:n.stringify(r)})}t.macroKeywordCode=function(e,t){let{gen:r,keyword:i,schema:o,parentSchema:a,it:s}=e,c=t.macro.call(s.self,o,a,s),u=l(r,i,c);!1!==s.opts.validateSchema&&s.self.validateSchema(c,!0);let p=r.name("valid");e.subschema({schema:c,schemaPath:n.nil,errSchemaPath:`${s.errSchemaPath}/${i}`,topSchemaRef:u,compositeRule:!0},p),e.pass(p,(()=>e.error(!0)))},t.funcKeywordCode=function(e,t){var r;let{gen:c,keyword:u,schema:p,parentSchema:d,$data:f,it:h}=e;!function({schemaEnv:e},t){if(t.async&&!e.$async)throw Error("async keyword in sync schema")}(h,t);let m=!f&&t.compile?t.compile.call(h.self,p,d,h):t.validate,g=l(c,u,m),y=c.let("valid");function v(r=(t.async?n._`await `:n.nil)){let a=h.opts.passContext?i.default.this:i.default.self,s=!("compile"in t&&!f||!1===t.schema);c.assign(y,n._`${r}${o.callValidateCode(e,g,a,s)}`,t.modifying)}function b(e){var r;c.if(n.not(null!==(r=t.valid)&&void 0!==r?r:y),e)}e.block$data(y,(function(){if(!1===t.errors)v(),t.modifying&&s(e),b((()=>e.error()));else{let r=t.async?function(){let e=c.let("ruleErrs",null);return c.try((()=>v(n._`await `)),(t=>c.assign(y,!1).if(n._`${t} instanceof ${h.ValidationError}`,(()=>c.assign(e,n._`${t}.errors`)),(()=>c.throw(t))))),e}():function(){let e=n._`${g}.errors`;return c.assign(e,null),v(n.nil),e}();t.modifying&&s(e),b((()=>function(e,t){let{gen:r}=e;r.if(n._`Array.isArray(${t})`,(()=>{r.assign(i.default.vErrors,n._`${i.default.vErrors} === null ? ${t} : ${i.default.vErrors}.concat(${t})`).assign(i.default.errors,n._`${i.default.vErrors}.length`),a.extendErrors(e)}),(()=>e.error()))}(e,r)))}})),e.ok(null!==(r=t.valid)&&void 0!==r?r:y)},t.validSchemaType=function(e,t,r=!1){return!t.length||t.some((t=>"array"===t?Array.isArray(e):"object"===t?e&&"object"==typeof e&&!Array.isArray(e):typeof e==t||r&&void 0===e))},t.validateKeywordUsage=function({schema:e,opts:t,self:r,errSchemaPath:n},i,o){if(Array.isArray(i.keyword)?!i.keyword.includes(o):i.keyword!==o)throw Error("ajv implementation error");let a=i.dependencies;if(null==a?void 0:a.some((t=>!Object.prototype.hasOwnProperty.call(e,t))))throw Error(`parent schema must have dependencies of ${o}: ${a.join(",")}`);if(i.validateSchema&&!i.validateSchema(e[o])){let e=`keyword "${o}" value is invalid at path "${n}": `+r.errorsText(i.validateSchema.errors);if("log"!==t.validateSchema)throw Error(e);r.logger.error(e)}}},4688:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.extendSubschemaMode=t.extendSubschemaData=t.getSubschema=void 0;let n=r(4475),i=r(6124);t.getSubschema=function(e,{keyword:t,schemaProp:r,schema:o,schemaPath:a,errSchemaPath:s,topSchemaRef:l}){if(void 0!==t&&void 0!==o)throw Error('both "keyword" and "schema" passed, only one allowed');if(void 0!==t){let o=e.schema[t];return void 0===r?{schema:o,schemaPath:n._`${e.schemaPath}${n.getProperty(t)}`,errSchemaPath:`${e.errSchemaPath}/${t}`}:{schema:o[r],schemaPath:n._`${e.schemaPath}${n.getProperty(t)}${n.getProperty(r)}`,errSchemaPath:`${e.errSchemaPath}/${t}/${i.escapeFragment(r)}`}}if(void 0!==o){if(void 0===a||void 0===s||void 0===l)throw Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"');return{schema:o,schemaPath:a,topSchemaRef:l,errSchemaPath:s}}throw Error('either "keyword" or "schema" must be passed')},t.extendSubschemaData=function(e,t,{dataProp:r,dataPropType:o,data:a,dataTypes:s,propertyName:l}){if(void 0!==a&&void 0!==r)throw Error('both "data" and "dataProp" passed, only one allowed');let{gen:c}=t;if(void 0!==r){let{errorPath:a,dataPathArr:s,opts:l}=t;u(c.let("data",n._`${t.data}${n.getProperty(r)}`,!0)),e.errorPath=n.str`${a}${i.getErrorPath(r,o,l.jsPropertySyntax)}`,e.parentDataProperty=n._`${r}`,e.dataPathArr=[...s,e.parentDataProperty]}function u(r){e.data=r,e.dataLevel=t.dataLevel+1,e.dataTypes=[],t.definedProperties=new Set,e.parentData=t.data,e.dataNames=[...t.dataNames,r]}void 0!==a&&(u(a instanceof n.Name?a:c.let("data",a,!0)),void 0!==l&&(e.propertyName=l)),s&&(e.dataTypes=s)},t.extendSubschemaMode=function(e,{jtdDiscriminator:t,jtdMetadata:r,compositeRule:n,createErrors:i,allErrors:o}){void 0!==n&&(e.compositeRule=n),void 0!==i&&(e.createErrors=i),void 0!==o&&(e.allErrors=o),e.jtdDiscriminator=t,e.jtdMetadata=r}},3325:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CodeGen=t.Name=t.nil=t.stringify=t.str=t._=t.KeywordCxt=void 0;var n=r(1321);Object.defineProperty(t,"KeywordCxt",{enumerable:!0,get:function(){return n.KeywordCxt}});var i=r(4475);Object.defineProperty(t,"_",{enumerable:!0,get:function(){return i._}}),Object.defineProperty(t,"str",{enumerable:!0,get:function(){return i.str}}),Object.defineProperty(t,"stringify",{enumerable:!0,get:function(){return i.stringify}}),Object.defineProperty(t,"nil",{enumerable:!0,get:function(){return i.nil}}),Object.defineProperty(t,"Name",{enumerable:!0,get:function(){return i.Name}}),Object.defineProperty(t,"CodeGen",{enumerable:!0,get:function(){return i.CodeGen}});let o=r(8451),a=r(4143),s=r(3664),l=r(7805),c=r(4475),u=r(9826),p=r(7927),d=r(6124),f=r(425),h=["removeAdditional","useDefaults","coerceTypes"],m=new Set(["validate","serialize","parse","wrapper","root","schema","keyword","pattern","formats","validate$data","func","obj","Error"]);class g{constructor(e={}){this.schemas={},this.refs={},this.formats={},this._compilations=new Set,this._loading={},this._cache=new Map,e=this.opts={...e,...function(e){var t,r,n,i,o,a,s,l,c,u,p,d,f,h,m,g,y,v,b,x,w,k;let _=e.strict,O=null===(t=e.code)||void 0===t?void 0:t.optimize,S=!0===O||void 0===O?1:O||0;return{strictSchema:null===(n=null!==(r=e.strictSchema)&&void 0!==r?r:_)||void 0===n||n,strictNumbers:null===(o=null!==(i=e.strictNumbers)&&void 0!==i?i:_)||void 0===o||o,strictTypes:null!==(s=null!==(a=e.strictTypes)&&void 0!==a?a:_)&&void 0!==s?s:"log",strictTuples:null!==(c=null!==(l=e.strictTuples)&&void 0!==l?l:_)&&void 0!==c?c:"log",strictRequired:null!==(p=null!==(u=e.strictRequired)&&void 0!==u?u:_)&&void 0!==p&&p,code:e.code?{...e.code,optimize:S}:{optimize:S},loopRequired:null!==(d=e.loopRequired)&&void 0!==d?d:200,loopEnum:null!==(f=e.loopEnum)&&void 0!==f?f:200,meta:null===(h=e.meta)||void 0===h||h,messages:null===(m=e.messages)||void 0===m||m,inlineRefs:null===(g=e.inlineRefs)||void 0===g||g,schemaId:null!==(y=e.schemaId)&&void 0!==y?y:"$id",addUsedSchema:null===(v=e.addUsedSchema)||void 0===v||v,validateSchema:null===(b=e.validateSchema)||void 0===b||b,validateFormats:null===(x=e.validateFormats)||void 0===x||x,unicodeRegExp:null===(w=e.unicodeRegExp)||void 0===w||w,int32range:null===(k=e.int32range)||void 0===k||k}}(e)};let{es5:t,lines:r}=this.opts.code;this.scope=new c.ValueScope({scope:{},prefixes:m,es5:t,lines:r}),this.logger=function(e){if(!1===e)return b;if(void 0===e)return console;if(e.log&&e.warn&&e.error)return e;throw Error("logger must implement log, warn and error methods")}(e.logger);let n=e.validateFormats;e.validateFormats=!1,this.RULES=s.getRules(),y.call(this,{errorDataPath:"",format:"`validateFormats: false` can be used instead.",nullable:'"nullable" keyword is supported by default.',jsonPointers:"Deprecated jsPropertySyntax can be used instead.",extendRefs:"Deprecated ignoreKeywordsWithRef can be used instead.",missingRefs:"Pass empty schema with $id that should be ignored to ajv.addSchema.",processCode:"Use option `code: {process: (code, schemaEnv: object) => string}`",sourceCode:"Use option `code: {source: true}`",strictDefaults:"It is default now, see option `strict`.",strictKeywords:"It is default now, see option `strict`.",uniqueItems:'"uniqueItems" keyword is always validated.',unknownFormats:"Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).",cache:"Map is used as cache, schema object as key.",serialize:"Map is used as cache, schema object as key.",ajvErrors:"It is default now."},e,"NOT SUPPORTED"),y.call(this,{ignoreKeywordsWithRef:"",jsPropertySyntax:"",unicode:'"minLength"/"maxLength" account for unicode characters by default.'},e,"DEPRECATED","warn"),this._metaOpts=function(){let e={...this.opts};for(let t of h)delete e[t];return e}.call(this),e.formats&&function(){for(let e in this.opts.formats){let t=this.opts.formats[e];t&&this.addFormat(e,t)}}.call(this),this._addVocabularies(),this._addDefaultMetaSchema(),e.keywords&&function(e){if(Array.isArray(e))this.addVocabulary(e);else for(let t in this.logger.warn("keywords option as map is deprecated, pass array"),e){let r=e[t];r.keyword||(r.keyword=t),this.addKeyword(r)}}.call(this,e.keywords),"object"==typeof e.meta&&this.addMetaSchema(e.meta),function(){let e=this.opts.schemas;if(e)if(Array.isArray(e))this.addSchema(e);else for(let t in e)this.addSchema(e[t],t)}.call(this),e.validateFormats=n}_addVocabularies(){this.addKeyword("$async")}_addDefaultMetaSchema(){let{$data:e,meta:t,schemaId:r}=this.opts,n=f;"id"===r&&((n={...f}).id=n.$id,delete n.$id),t&&e&&this.addMetaSchema(n,n[r],!1)}defaultMeta(){let{meta:e,schemaId:t}=this.opts;return this.opts.defaultMeta="object"==typeof e?e[t]||e:void 0}validate(e,t){let r;if("string"==typeof e){if(!(r=this.getSchema(e)))throw Error(`no schema with key or ref "${e}"`)}else r=this.compile(e);let n=r(t);return"$async"in r||(this.errors=r.errors),n}compile(e,t){let r=this._addSchema(e,t);return r.validate||this._compileSchemaEnv(r)}compileAsync(e,t){if("function"!=typeof this.opts.loadSchema)throw Error("options.loadSchema should be a function");let{loadSchema:r}=this.opts;return n.call(this,e,t);async function n(e,t){await i.call(this,e.$schema);let r=this._addSchema(e,t);return r.validate||o.call(this,r)}async function i(e){e&&!this.getSchema(e)&&await n.call(this,{$ref:e},!0)}async function o(e){try{return this._compileSchemaEnv(e)}catch(t){if(!(t instanceof a.default))throw t;return s.call(this,t),await l.call(this,t.missingSchema),o.call(this,e)}}function s({missingSchema:e,missingRef:t}){if(this.refs[e])throw Error(`AnySchema ${e} is loaded but ${t} cannot be resolved`)}async function l(e){let r=await c.call(this,e);this.refs[e]||await i.call(this,r.$schema),this.refs[e]||this.addSchema(r,e,t)}async function c(e){let t=this._loading[e];if(t)return t;try{return await(this._loading[e]=r(e))}finally{delete this._loading[e]}}}addSchema(e,t,r,n=this.opts.validateSchema){if(Array.isArray(e)){for(let t of e)this.addSchema(t,void 0,r,n);return this}let i;if("object"==typeof e){let{schemaId:t}=this.opts;if(void 0!==(i=e[t])&&"string"!=typeof i)throw Error(`schema ${t} must be string`)}return t=u.normalizeId(t||i),this._checkUnique(t),this.schemas[t]=this._addSchema(e,r,t,n,!0),this}addMetaSchema(e,t,r=this.opts.validateSchema){return this.addSchema(e,t,!0,r),this}validateSchema(e,t){if("boolean"==typeof e)return!0;let r;if(void 0!==(r=e.$schema)&&"string"!=typeof r)throw Error("$schema must be a string");if(!(r=r||this.opts.defaultMeta||this.defaultMeta()))return this.logger.warn("meta-schema not available"),this.errors=null,!0;let n=this.validate(r,e);if(!n&&t){let e="schema is invalid: "+this.errorsText();if("log"!==this.opts.validateSchema)throw Error(e);this.logger.error(e)}return n}getSchema(e){let t;for(;"string"==typeof(t=v.call(this,e));)e=t;if(void 0===t){let{schemaId:r}=this.opts,n=new l.SchemaEnv({schema:{},schemaId:r});if(!(t=l.resolveSchema.call(this,n,e)))return;this.refs[e]=t}return t.validate||this._compileSchemaEnv(t)}removeSchema(e){if(e instanceof RegExp)return this._removeAllSchemas(this.schemas,e),this._removeAllSchemas(this.refs,e),this;switch(typeof e){case"undefined":return this._removeAllSchemas(this.schemas),this._removeAllSchemas(this.refs),this._cache.clear(),this;case"string":{let t=v.call(this,e);return"object"==typeof t&&this._cache.delete(t.schema),delete this.schemas[e],delete this.refs[e],this}case"object":{this._cache.delete(e);let t=e[this.opts.schemaId];return t&&(t=u.normalizeId(t),delete this.schemas[t],delete this.refs[t]),this}default:throw Error("ajv.removeSchema: invalid parameter")}}addVocabulary(e){for(let t of e)this.addKeyword(t);return this}addKeyword(e,t){let r;if("string"==typeof e)r=e,"object"==typeof t&&(this.logger.warn("these parameters are deprecated, see docs for addKeyword"),t.keyword=r);else{if("object"!=typeof e||void 0!==t)throw Error("invalid addKeywords parameters");if(Array.isArray(r=(t=e).keyword)&&!r.length)throw Error("addKeywords: keyword must be string or non-empty array")}if(w.call(this,r,t),!t)return d.eachItem(r,(e=>k.call(this,e))),this;O.call(this,t);let n={...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)};return d.eachItem(r,0===n.type.length?e=>k.call(this,e,n):e=>n.type.forEach((t=>k.call(this,e,n,t)))),this}getKeyword(e){let t=this.RULES.all[e];return"object"==typeof t?t.definition:!!t}removeKeyword(e){let{RULES:t}=this;for(let r of(delete t.keywords[e],delete t.all[e],t.rules)){let t=r.rules.findIndex((t=>t.keyword===e));t>=0&&r.rules.splice(t,1)}return this}addFormat(e,t){return"string"==typeof t&&(t=RegExp(t)),this.formats[e]=t,this}errorsText(e=this.errors,{separator:t=", ",dataVar:r="data"}={}){return e&&0!==e.length?e.map((e=>`${r}${e.instancePath} ${e.message}`)).reduce(((e,r)=>e+t+r)):"No errors"}$dataMetaSchema(e,t){let r=this.RULES.all;for(let n of(e=JSON.parse(JSON.stringify(e)),t)){let t=n.split("/").slice(1),i=e;for(let e of t)i=i[e];for(let e in r){let t=r[e];if("object"!=typeof t)continue;let{$data:n}=t.definition,o=i[e];n&&o&&(i[e]=E(o))}}return e}_removeAllSchemas(e,t){for(let r in e){let n=e[r];t&&!t.test(r)||("string"==typeof n?delete e[r]:n&&!n.meta&&(this._cache.delete(n.schema),delete e[r]))}}_addSchema(e,t,r,n=this.opts.validateSchema,i=this.opts.addUsedSchema){let o,{schemaId:a}=this.opts;if("object"==typeof e)o=e[a];else{if(this.opts.jtd)throw Error("schema must be object");if("boolean"!=typeof e)throw Error("schema must be object or boolean")}let s=this._cache.get(e);if(void 0!==s)return s;let c=u.getSchemaRefs.call(this,e);return r=u.normalizeId(o||r),s=new l.SchemaEnv({schema:e,schemaId:a,meta:t,baseId:r,localRefs:c}),this._cache.set(s.schema,s),i&&!r.startsWith("#")&&(r&&this._checkUnique(r),this.refs[r]=s),n&&this.validateSchema(e,!0),s}_checkUnique(e){if(this.schemas[e]||this.refs[e])throw Error(`schema with key or id "${e}" already exists`)}_compileSchemaEnv(e){if(e.meta?this._compileMetaSchema(e):l.compileSchema.call(this,e),!e.validate)throw Error("ajv implementation error");return e.validate}_compileMetaSchema(e){let t=this.opts;this.opts=this._metaOpts;try{l.compileSchema.call(this,e)}finally{this.opts=t}}}function y(e,t,r,n="error"){for(let i in e){let o=i;o in t&&this.logger[n](`${r}: option ${i}. ${e[o]}`)}}function v(e){return e=u.normalizeId(e),this.schemas[e]||this.refs[e]}t.default=g,g.ValidationError=o.default,g.MissingRefError=a.default;let b={log(){},warn(){},error(){}},x=/^[a-z_$][a-z0-9_$:-]*$/i;function w(e,t){let{RULES:r}=this;if(d.eachItem(e,(e=>{if(r.keywords[e])throw Error(`Keyword ${e} is already defined`);if(!x.test(e))throw Error(`Keyword ${e} has invalid name`)})),t&&t.$data&&!("code"in t)&&!("validate"in t))throw Error('$data keyword must have "code" or "validate" function')}function k(e,t,r){var n;let i=null==t?void 0:t.post;if(r&&i)throw Error('keyword with "post" flag cannot have "type"');let{RULES:o}=this,a=i?o.post:o.rules.find((({type:e})=>e===r));if(a||(a={type:r,rules:[]},o.rules.push(a)),o.keywords[e]=!0,!t)return;let s={keyword:e,definition:{...t,type:p.getJSONTypes(t.type),schemaType:p.getJSONTypes(t.schemaType)}};t.before?_.call(this,a,s,t.before):a.rules.push(s),o.all[e]=s,null===(n=t.implements)||void 0===n||n.forEach((e=>this.addKeyword(e)))}function _(e,t,r){let n=e.rules.findIndex((e=>e.keyword===r));n>=0?e.rules.splice(n,0,t):(e.rules.push(t),this.logger.warn(`rule ${r} is not defined`))}function O(e){let{metaSchema:t}=e;void 0!==t&&(e.$data&&this.opts.$data&&(t=E(t)),e.validateSchema=this.compile(t,!0))}let S={$ref:"https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#"};function E(e){return{anyOf:[e,S]}}},412:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4063);n.code='require("ajv/dist/runtime/equal").default',t.default=n},5872:function(e,t){"use strict";function r(e){let t,r=e.length,n=0,i=0;for(;i=55296&&t<=56319&&i{var a;return a=o,void r.forRange("i",t.length,c,(t=>{e.subschema({keyword:s,dataProp:t,dataPropType:i.Type.Num},a),l.allErrors||r.if(n.not(a),(()=>r.break()))}))})),e.ok(o)}}t.validateAdditionalItems=o,t.default={keyword:"additionalItems",type:"array",schemaType:["boolean","object"],before:"uniqueItems",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){let{parentSchema:t,it:r}=e,{items:n}=t;Array.isArray(n)?o(e,n):i.checkStrictMode(r,'"additionalItems" is ignored when "items" is not an array of schemas')}}},1422:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(5018),a=r(6124);t.default={keyword:"additionalProperties",type:["object"],schemaType:["boolean","object"],allowUndefined:!0,trackErrors:!0,error:{message:"must NOT have additional properties",params:({params:e})=>i._`{additionalProperty: ${e.additionalProperty}}`},code(e){let{gen:t,parentSchema:r,data:s,errsCount:l,it:c}=e,{schema:u=c.opts.defaultAdditionalProperties}=e;if(!l)throw Error("ajv implementation error");let{allErrors:p,opts:d}=c;if(c.props=!0,"all"!==d.removeAdditional&&a.alwaysValidSchema(c,u))return;let f=n.allSchemaProperties(r.properties),h=n.allSchemaProperties(r.patternProperties);function m(e){t.code(i._`delete ${s}[${e}]`)}function g(r){if("all"===d.removeAdditional||d.removeAdditional&&!1===u)m(r);else{if(!1===u)return e.setParams({additionalProperty:r}),e.error(),void(p||t.break());if("object"==typeof u&&!a.alwaysValidSchema(c,u)){let n=t.name("valid");"failing"===d.removeAdditional?(y(r,n,!1),t.if(i.not(n),(()=>{e.reset(),m(r)}))):(y(r,n),p||t.if(i.not(n),(()=>t.break())))}}}function y(t,r,n){let i={keyword:"additionalProperties",dataProp:t,dataPropType:a.Type.Str};!1===n&&Object.assign(i,{compositeRule:!0,createErrors:!1,allErrors:!1}),e.subschema(i,r)}t.forIn("key",s,(o=>{f.length||h.length?t.if(function(o){let s;if(f.length>8){let e=a.schemaRefOrVal(c,r.properties,"properties");s=n.isOwnProperty(t,e,o)}else s=f.length?i.or(...f.map((e=>i._`${o} === ${e}`))):i.nil;return h.length&&(s=i.or(s,...h.map((t=>i._`${n.usePattern(e,t)}.test(${o})`)))),i.not(s)}(o),(()=>g(o))):g(o)})),e.ok(i._`${l} === ${o.default.errors}`)}}},5716:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:"allOf",schemaType:"array",code(e){let{gen:t,schema:r,it:i}=e;if(!Array.isArray(r))throw Error("ajv implementation error");let o=t.name("valid");r.forEach(((t,r)=>{if(n.alwaysValidSchema(i,t))return;let a=e.subschema({keyword:"allOf",schemaProp:r},o);e.ok(o),e.mergeEvaluated(a)}))}}},1668:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n={keyword:"anyOf",schemaType:"array",trackErrors:!0,code:r(8619).validateUnion,error:{message:"must match a schema in anyOf"}};t.default=n},9564:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"contains",type:"array",schemaType:["object","boolean"],before:"uniqueItems",trackErrors:!0,error:{message:({params:{min:e,max:t}})=>void 0===t?n.str`must contain at least ${e} valid item(s)`:n.str`must contain at least ${e} and no more than ${t} valid item(s)`,params:({params:{min:e,max:t}})=>void 0===t?n._`{minContains: ${e}}`:n._`{minContains: ${e}, maxContains: ${t}}`},code(e){let t,r,{gen:o,schema:a,parentSchema:s,data:l,it:c}=e,{minContains:u,maxContains:p}=s;c.opts.next?(t=void 0===u?1:u,r=p):t=1;let d=o.const("len",n._`${l}.length`);if(e.setParams({min:t,max:r}),void 0===r&&0===t)return void i.checkStrictMode(c,'"minContains" == 0 without "maxContains": "contains" keyword ignored');if(void 0!==r&&t>r)return i.checkStrictMode(c,'"minContains" > "maxContains" is always invalid'),void e.fail();if(i.alwaysValidSchema(c,a)){let i=n._`${d} >= ${t}`;return void 0!==r&&(i=n._`${i} && ${d} <= ${r}`),void e.pass(i)}c.items=!0;let f=o.name("valid");if(void 0===r&&1===t)h(f,(()=>o.if(f,(()=>o.break()))));else{o.let(f,!1);let e=o.name("_valid"),i=o.let("count",0);h(e,(()=>o.if(e,(()=>{var e;return e=i,o.code(n._`${e}++`),void(void 0===r?o.if(n._`${e} >= ${t}`,(()=>o.assign(f,!0).break())):(o.if(n._`${e} > ${r}`,(()=>o.assign(f,!1).break())),1===t?o.assign(f,!0):o.if(n._`${e} >= ${t}`,(()=>o.assign(f,!0)))))}))))}function h(t,r){o.forRange("i",0,d,(n=>{e.subschema({keyword:"contains",dataProp:n,dataPropType:i.Type.Num,compositeRule:!0},t),r()}))}e.result(f,(()=>e.reset()))}}},1117:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateSchemaDeps=t.validatePropertyDeps=t.error=void 0;let n=r(4475),i=r(6124),o=r(8619);t.error={message:({params:{property:e,depsCount:t,deps:r}})=>n.str`must have ${1===t?"property":"properties"} ${r} when property ${e} is present`,params:({params:{property:e,depsCount:t,deps:r,missingProperty:i}})=>n._`{property: ${e}, + missingProperty: ${i}, + depsCount: ${t}, + deps: ${r}}`};let a={keyword:"dependencies",type:"object",schemaType:"object",error:t.error,code(e){let[t,r]=function({schema:e}){let t={},r={};for(let n in e)"__proto__"!==n&&((Array.isArray(e[n])?t:r)[n]=e[n]);return[t,r]}(e);s(e,t),l(e,r)}};function s(e,t=e.schema){let{gen:r,data:i,it:a}=e;if(0===Object.keys(t).length)return;let s=r.let("missing");for(let l in t){let c=t[l];if(0===c.length)continue;let u=o.propertyInData(r,i,l,a.opts.ownProperties);e.setParams({property:l,depsCount:c.length,deps:c.join(", ")}),a.allErrors?r.if(u,(()=>{for(let t of c)o.checkReportMissingProp(e,t)})):(r.if(n._`${u} && (${o.checkMissingProp(e,c,s)})`),o.reportMissingProp(e,s),r.else())}}function l(e,t=e.schema){let{gen:r,data:n,keyword:a,it:s}=e,l=r.name("valid");for(let c in t)i.alwaysValidSchema(s,t[c])||(r.if(o.propertyInData(r,n,c,s.opts.ownProperties),(()=>{let t=e.subschema({keyword:a,schemaProp:c},l);e.mergeValidEvaluated(t,l)}),(()=>r.var(l,!0))),e.ok(l))}t.validatePropertyDeps=s,t.validateSchemaDeps=l,t.default=a},5184:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);function o(e,t){let r=e.schema[t];return void 0!==r&&!i.alwaysValidSchema(e,r)}t.default={keyword:"if",schemaType:["object","boolean"],trackErrors:!0,error:{message:({params:e})=>n.str`must match "${e.ifClause}" schema`,params:({params:e})=>n._`{failingKeyword: ${e.ifClause}}`},code(e){let{gen:t,parentSchema:r,it:a}=e;void 0===r.then&&void 0===r.else&&i.checkStrictMode(a,'"if" without "then" and "else" is ignored');let s=o(a,"then"),l=o(a,"else");if(!s&&!l)return;let c=t.let("valid",!0),u=t.name("_valid");if(function(){let t=e.subschema({keyword:"if",compositeRule:!0,createErrors:!1,allErrors:!1},u);e.mergeEvaluated(t)}(),e.reset(),s&&l){let r=t.let("ifClause");e.setParams({ifClause:r}),t.if(u,p("then",r),p("else",r))}else s?t.if(u,p("then")):t.if(n.not(u),p("else"));function p(r,i){return()=>{let o=e.subschema({keyword:r},u);t.assign(c,u),e.mergeValidEvaluated(o,c),i?t.assign(i,n._`${r}`):e.setParams({ifClause:r})}}e.pass(c,(()=>e.error(!0)))}}},9616:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(3074),i=r(6988),o=r(6348),a=r(9822),s=r(9564),l=r(1117),c=r(4002),u=r(1422),p=r(9690),d=r(9883),f=r(8435),h=r(1668),m=r(9684),g=r(5716),y=r(5184),v=r(5642);t.default=function(e=!1){let t=[f.default,h.default,m.default,g.default,y.default,v.default,c.default,u.default,l.default,p.default,d.default];return e?t.push(i.default,a.default):t.push(n.default,o.default),t.push(s.default),t}},6348:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateTuple=void 0;let n=r(4475),i=r(6124),o=r(8619);function a(e,t,r=e.schema){let{gen:o,parentSchema:a,data:s,keyword:l,it:c}=e;!function(e){let{opts:n,errSchemaPath:o}=c,a=r.length,s=a===e.minItems&&(a===e.maxItems||!1===e[t]);if(n.strictTuples&&!s){let e=`"${l}" is ${a}-tuple, but minItems or maxItems/${t} are not specified or different at path "${o}"`;i.checkStrictMode(c,e,n.strictTuples)}}(a),c.opts.unevaluated&&r.length&&!0!==c.items&&(c.items=i.mergeEvaluated.items(o,r.length,c.items));let u=o.name("valid"),p=o.const("len",n._`${s}.length`);r.forEach(((t,r)=>{i.alwaysValidSchema(c,t)||(o.if(n._`${p} > ${r}`,(()=>e.subschema({keyword:l,schemaProp:r,dataProp:r},u))),e.ok(u))}))}t.validateTuple=a,t.default={keyword:"items",type:"array",schemaType:["object","array","boolean"],before:"uniqueItems",code(e){let{schema:t,it:r}=e;if(Array.isArray(t))return a(e,"additionalItems",t);r.items=!0,i.alwaysValidSchema(r,t)||e.ok(o.validateArray(e))}}},9822:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(8619),a=r(3074);t.default={keyword:"items",type:"array",schemaType:["object","boolean"],before:"uniqueItems",error:{message:({params:{len:e}})=>n.str`must NOT have more than ${e} items`,params:({params:{len:e}})=>n._`{limit: ${e}}`},code(e){let{schema:t,parentSchema:r,it:n}=e,{prefixItems:s}=r;n.items=!0,i.alwaysValidSchema(n,t)||(s?a.validateAdditionalItems(e,s):e.ok(o.validateArray(e)))}}},8435:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:"not",schemaType:["object","boolean"],trackErrors:!0,code(e){let{gen:t,schema:r,it:i}=e;if(n.alwaysValidSchema(i,r))return void e.fail();let o=t.name("valid");e.subschema({keyword:"not",compositeRule:!0,createErrors:!1,allErrors:!1},o),e.result(o,(()=>e.error()),(()=>e.reset()))},error:{message:"must NOT be valid"}}},9684:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"oneOf",schemaType:"array",trackErrors:!0,error:{message:"must match exactly one schema in oneOf",params:({params:e})=>n._`{passingSchemas: ${e.passing}}`},code(e){let{gen:t,schema:r,parentSchema:o,it:a}=e;if(!Array.isArray(r))throw Error("ajv implementation error");if(a.opts.discriminator&&o.discriminator)return;let s=r,l=t.let("valid",!1),c=t.let("passing",null),u=t.name("_valid");e.setParams({passing:c}),t.block((function(){s.forEach(((r,o)=>{let s;i.alwaysValidSchema(a,r)?t.var(u,!0):s=e.subschema({keyword:"oneOf",schemaProp:o,compositeRule:!0},u),o>0&&t.if(n._`${u} && ${l}`).assign(l,!1).assign(c,n._`[${c}, ${o}]`).else(),t.if(u,(()=>{t.assign(l,!0),t.assign(c,o),s&&e.mergeEvaluated(s,n.Name)}))}))})),e.result(l,(()=>e.reset()),(()=>e.error(!0)))}}},9883:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(6124),a=r(6124);t.default={keyword:"patternProperties",type:"object",schemaType:"object",code(e){let{gen:t,schema:r,data:s,parentSchema:l,it:c}=e,{opts:u}=c,p=n.allSchemaProperties(r),d=p.filter((e=>o.alwaysValidSchema(c,r[e])));if(0===p.length||d.length===p.length&&(!c.opts.unevaluated||!0===c.props))return;let f=u.strictSchema&&!u.allowMatchingProperties&&l.properties,h=t.name("valid");!0===c.props||c.props instanceof i.Name||(c.props=a.evaluatedPropsToName(t,c.props));let{props:m}=c;function g(e){for(let t in f)RegExp(e).test(t)&&o.checkStrictMode(c,`property ${t} matches pattern ${e} (use allowMatchingProperties)`)}function y(r){t.forIn("key",s,(o=>{t.if(i._`${n.usePattern(e,r)}.test(${o})`,(()=>{let n=d.includes(r);n||e.subschema({keyword:"patternProperties",schemaProp:r,dataProp:o,dataPropType:a.Type.Str},h),c.opts.unevaluated&&!0!==m?t.assign(i._`${m}[${o}]`,!0):n||c.allErrors||t.if(i.not(h),(()=>t.break()))}))}))}!function(){for(let e of p)f&&g(e),c.allErrors?y(e):(t.var(h,!0),y(e),t.if(h))}()}}},6988:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6348);t.default={keyword:"prefixItems",type:"array",schemaType:["array"],before:"uniqueItems",code:e=>n.validateTuple(e,"items")}},9690:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(1321),i=r(8619),o=r(6124),a=r(1422);t.default={keyword:"properties",type:"object",schemaType:"object",code(e){let{gen:t,schema:r,parentSchema:s,data:l,it:c}=e;("all"===c.opts.removeAdditional&&void 0===s.additionalProperties||!1===c.opts.defaultAdditionalProperties)&&a.default.code(new n.KeywordCxt(c,a.default,"additionalProperties"));let u=i.allSchemaProperties(r);for(let e of u)c.definedProperties.add(e);c.opts.unevaluated&&u.length&&!0!==c.props&&(c.props=o.mergeEvaluated.props(t,o.toHash(u),c.props));let p=u.filter((e=>!o.alwaysValidSchema(c,r[e])));if(0===p.length)return;let d=t.name("valid");for(let r of p)f(r)?h(r):(t.if(i.propertyInData(t,l,r,c.opts.ownProperties)),h(r),c.allErrors||t.else().var(d,!0),t.endIf()),e.it.definedProperties.add(r),e.ok(d);function f(e){return c.opts.useDefaults&&!c.compositeRule&&void 0!==r[e].default}function h(t){e.subschema({keyword:"properties",schemaProp:t,dataProp:t},d)}}}},4002:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124);t.default={keyword:"propertyNames",type:"object",schemaType:["object","boolean"],error:{message:"property name must be valid",params:({params:e})=>n._`{propertyName: ${e.propertyName}}`},code(e){let{gen:t,schema:r,data:o,it:a}=e;if(i.alwaysValidSchema(a,r))return;let s=t.name("valid");t.forIn("key",o,(r=>{e.setParams({propertyName:r}),e.subschema({keyword:"propertyNames",data:r,dataTypes:["string"],propertyName:r,compositeRule:!0},s),t.if(n.not(s),(()=>{e.error(!0),a.allErrors||t.break()}))})),e.ok(s)}}},5642:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(6124);t.default={keyword:["then","else"],schemaType:["object","boolean"],code({keyword:e,parentSchema:t,it:r}){void 0===t.if&&n.checkStrictMode(r,`"${e}" without "if" is ignored`)}}},8619:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateUnion=t.validateArray=t.usePattern=t.callValidateCode=t.schemaProperties=t.allSchemaProperties=t.noPropertyInData=t.propertyInData=t.isOwnProperty=t.hasPropFunc=t.reportMissingProp=t.checkMissingProp=t.checkReportMissingProp=void 0;let n=r(4475),i=r(6124),o=r(5018);function a(e){return e.scopeValue("func",{ref:Object.prototype.hasOwnProperty,code:n._`Object.prototype.hasOwnProperty`})}function s(e,t,r){return n._`${a(e)}.call(${t}, ${r})`}function l(e,t,r,i){let o=n._`${t}${n.getProperty(r)} === undefined`;return i?n.or(o,n.not(s(e,t,r))):o}function c(e){return e?Object.keys(e).filter((e=>"__proto__"!==e)):[]}t.checkReportMissingProp=function(e,t){let{gen:r,data:i,it:o}=e;r.if(l(r,i,t,o.opts.ownProperties),(()=>{e.setParams({missingProperty:n._`${t}`},!0),e.error()}))},t.checkMissingProp=function({gen:e,data:t,it:{opts:r}},i,o){return n.or(...i.map((i=>n.and(l(e,t,i,r.ownProperties),n._`${o} = ${i}`))))},t.reportMissingProp=function(e,t){e.setParams({missingProperty:t},!0),e.error()},t.hasPropFunc=a,t.isOwnProperty=s,t.propertyInData=function(e,t,r,i){let o=n._`${t}${n.getProperty(r)} !== undefined`;return i?n._`${o} && ${s(e,t,r)}`:o},t.noPropertyInData=l,t.allSchemaProperties=c,t.schemaProperties=function(e,t){return c(t).filter((r=>!i.alwaysValidSchema(e,t[r])))},t.callValidateCode=function({schemaCode:e,data:t,it:{gen:r,topSchemaRef:i,schemaPath:a,errorPath:s},it:l},c,u,p){let d=p?n._`${e}, ${t}, ${i}${a}`:t,f=[[o.default.instancePath,n.strConcat(o.default.instancePath,s)],[o.default.parentData,l.parentData],[o.default.parentDataProperty,l.parentDataProperty],[o.default.rootData,o.default.rootData]];l.opts.dynamicRef&&f.push([o.default.dynamicAnchors,o.default.dynamicAnchors]);let h=n._`${d}, ${r.object(...f)}`;return u!==n.nil?n._`${c}.call(${u}, ${h})`:n._`${c}(${h})`},t.usePattern=function({gen:e,it:{opts:t}},r){let i=t.unicodeRegExp?"u":"";return e.scopeValue("pattern",{key:r,ref:RegExp(r,i),code:n._`new RegExp(${r}, ${i})`})},t.validateArray=function(e){let{gen:t,data:r,keyword:o,it:a}=e,s=t.name("valid");if(a.allErrors){let e=t.let("valid",!0);return l((()=>t.assign(e,!1))),e}return t.var(s,!0),l((()=>t.break())),s;function l(a){let l=t.const("len",n._`${r}.length`);t.forRange("i",0,l,(r=>{e.subschema({keyword:o,dataProp:r,dataPropType:i.Type.Num},s),t.if(n.not(s),a)}))}},t.validateUnion=function(e){let{gen:t,schema:r,keyword:o,it:a}=e;if(!Array.isArray(r))throw Error("ajv implementation error");if(r.some((e=>i.alwaysValidSchema(a,e)))&&!a.opts.unevaluated)return;let s=t.let("valid",!1),l=t.name("_valid");t.block((()=>r.forEach(((r,i)=>{let a=e.subschema({keyword:o,schemaProp:i,compositeRule:!0},l);t.assign(s,n._`${s} || ${l}`),e.mergeValidEvaluated(a,l)||t.if(n.not(s))})))),e.result(s,(()=>e.reset()),(()=>e.error(!0)))}},5060:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={keyword:"id",code(){throw Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID')}}},8223:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(5060),i=r(4028),o=["$schema","$id","$defs","$vocabulary",{keyword:"$comment"},"definitions",n.default,i.default];t.default=o},4028:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.callRef=t.getValidate=void 0;let n=r(4143),i=r(8619),o=r(4475),a=r(5018),s=r(7805),l=r(6124);function c(e,t){let{gen:r}=e;return t.validate?r.scopeValue("validate",{ref:t.validate}):o._`${r.scopeValue("wrapper",{ref:t})}.validate`}function u(e,t,r,n){let{gen:s,it:c}=e,{allErrors:u,schemaEnv:p,opts:d}=c,f=d.passContext?a.default.this:o.nil;function h(e){let t=o._`${e}.errors`;s.assign(a.default.vErrors,o._`${a.default.vErrors} === null ? ${t} : ${a.default.vErrors}.concat(${t})`),s.assign(a.default.errors,o._`${a.default.vErrors}.length`)}function m(e){var t;if(!c.opts.unevaluated)return;let n=null===(t=null==r?void 0:r.validate)||void 0===t?void 0:t.evaluated;if(!0!==c.props)if(n&&!n.dynamicProps)void 0!==n.props&&(c.props=l.mergeEvaluated.props(s,n.props,c.props));else{let t=s.var("props",o._`${e}.evaluated.props`);c.props=l.mergeEvaluated.props(s,t,c.props,o.Name)}if(!0!==c.items)if(n&&!n.dynamicItems)void 0!==n.items&&(c.items=l.mergeEvaluated.items(s,n.items,c.items));else{let t=s.var("items",o._`${e}.evaluated.items`);c.items=l.mergeEvaluated.items(s,t,c.items,o.Name)}}n?function(){if(!p.$async)throw Error("async schema referenced by sync schema");let r=s.let("valid");s.try((()=>{s.code(o._`await ${i.callValidateCode(e,t,f)}`),m(t),u||s.assign(r,!0)}),(e=>{s.if(o._`!(${e} instanceof ${c.ValidationError})`,(()=>s.throw(e))),h(e),u||s.assign(r,!1)})),e.ok(r)}():function(){let r=s.name("visitedNodes");s.code(o._`const ${r} = visitedNodesForRef.get(${t}) || new Set()`),s.if(o._`!${r}.has(${e.data})`,(()=>{s.code(o._`visitedNodesForRef.set(${t}, ${r})`),s.code(o._`const dataNode = ${e.data}`),s.code(o._`${r}.add(dataNode)`);let n=e.result(i.callValidateCode(e,t,f),(()=>m(t)),(()=>h(t)));return s.code(o._`${r}.delete(dataNode)`),n}))}()}t.getValidate=c,t.callRef=u,t.default={keyword:"$ref",schemaType:"string",code(e){let{gen:t,schema:r,it:i}=e,{baseId:a,schemaEnv:l,validateName:p,opts:d,self:f}=i,{root:h}=l;if(("#"===r||"#/"===r)&&a===h.baseId)return function(){if(l===h)return u(e,p,l,l.$async);let r=t.scopeValue("root",{ref:h});return u(e,o._`${r}.validate`,h,h.$async)}();let m=s.resolveRef.call(f,h,a,r);if(void 0===m)throw new n.default(a,r);return m instanceof s.SchemaEnv?function(t){let r=c(e,t);u(e,r,t,t.$async)}(m):function(n){let i=t.scopeValue("schema",!0===d.code.source?{ref:n,code:o.stringify(n)}:{ref:n}),a=t.name("valid"),s=e.subschema({schema:n,dataTypes:[],schemaPath:o.nil,topSchemaRef:i,errSchemaPath:r},a);e.mergeEvaluated(s),e.ok(a)}(m)}}},5522:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6545);t.default={keyword:"discriminator",type:"object",schemaType:"object",error:{message:({params:{discrError:e,tagName:t}})=>e===i.DiscrError.Tag?`tag "${t}" must be string`:`value of tag "${t}" must be in oneOf`,params:({params:{discrError:e,tag:t,tagName:r}})=>n._`{error: ${e}, tag: ${r}, tagValue: ${t}}`},code(e){let{gen:t,data:r,schema:o,parentSchema:a,it:s}=e,{oneOf:l}=a;if(!s.opts.discriminator)throw Error("discriminator: requires discriminator option");let c=o.propertyName;if("string"!=typeof c)throw Error("discriminator: requires propertyName");if(!l)throw Error("discriminator: requires oneOf keyword");let u=t.let("valid",!1),p=t.const("tag",n._`${r}${n.getProperty(c)}`);function d(r){let i=t.name("valid"),o=e.subschema({keyword:"oneOf",schemaProp:r},i);return e.mergeEvaluated(o,n.Name),i}function f(e){return e.hasOwnProperty("$ref")}t.if(n._`typeof ${p} == "string"`,(()=>function(){let r=function(){var e;let t={},r=i(a),n=!0;for(let t=0;te.error(!1,{discrError:i.DiscrError.Tag,tag:p,tagName:c}))),e.ok(u)}}},6545:function(e,t){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.DiscrError=void 0,(r=t.DiscrError||(t.DiscrError={})).Tag="tag",r.Mapping="mapping"},6479:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8223),i=r(3799),o=r(9616),a=r(3815),s=r(4826),l=[n.default,i.default,o.default(),a.default,s.metadataVocabulary,s.contentVocabulary];t.default=l},157:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:"format",type:["number","string"],schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>n.str`must match format "${e}"`,params:({schemaCode:e})=>n._`{format: ${e}}`},code(e,t){let{gen:r,data:i,$data:o,schema:a,schemaCode:s,it:l}=e,{opts:c,errSchemaPath:u,schemaEnv:p,self:d}=l;c.validateFormats&&(o?function(){let o=r.scopeValue("formats",{ref:d.formats,code:c.code.formats}),a=r.const("fDef",n._`${o}[${s}]`),l=r.let("fType"),u=r.let("format");r.if(n._`typeof ${a} == "object" && !(${a} instanceof RegExp)`,(()=>r.assign(l,n._`${a}.type || "string"`).assign(u,n._`${a}.validate`)),(()=>r.assign(l,n._`"string"`).assign(u,a))),e.fail$data(n.or(!1===c.strictSchema?n.nil:n._`${s} && !${u}`,function(){let e=p.$async?n._`(${a}.async ? await ${u}(${i}) : ${u}(${i}))`:n._`${u}(${i})`,r=n._`(typeof ${u} == "function" ? ${e} : ${u}.test(${i}))`;return n._`${u} && ${u} !== true && ${l} === ${t} && !${r}`}()))}():function(){let o=d.formats[a];if(!o)return void function(){if(!1!==c.strictSchema)throw Error(e());function e(){return`unknown format "${a}" ignored in schema at path "${u}"`}d.logger.warn(e())}();if(!0===o)return;let[s,l,f]=function(e){let t=e instanceof RegExp?n.regexpCode(e):c.code.formats?n._`${c.code.formats}${n.getProperty(a)}`:void 0,i=r.scopeValue("formats",{key:a,ref:e,code:t});return"object"!=typeof e||e instanceof RegExp?["string",e,i]:[e.type||"string",e.validate,n._`${i}.validate`]}(o);s===t&&e.pass(function(){if("object"==typeof o&&!(o instanceof RegExp)&&o.async){if(!p.$async)throw Error("async format in sync schema");return n._`await ${f}(${i})`}return"function"==typeof l?n._`${f}(${i})`:n._`${f}.test(${i})`}())}())}}},3815:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=[r(157).default];t.default=n},4826:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.contentVocabulary=t.metadataVocabulary=void 0,t.metadataVocabulary=["title","description","default","deprecated","readOnly","writeOnly","examples"],t.contentVocabulary=["contentMediaType","contentEncoding","contentSchema"]},7535:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(412);t.default={keyword:"const",$data:!0,error:{message:"must be equal to constant",params:({schemaCode:e})=>n._`{allowedValue: ${e}}`},code(e){let{gen:t,data:r,$data:a,schemaCode:s,schema:l}=e;a||l&&"object"==typeof l?e.fail$data(n._`!${i.useFunc(t,o.default)}(${r}, ${s})`):e.fail(n._`${l} !== ${r}`)}}},4147:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(412);t.default={keyword:"enum",schemaType:"array",$data:!0,error:{message:"must be equal to one of the allowed values",params:({schemaCode:e})=>n._`{allowedValues: ${e}}`},code(e){let{gen:t,data:r,$data:a,schema:s,schemaCode:l,it:c}=e;if(!a&&0===s.length)throw Error("enum must have non-empty array");let u,p=s.length>=c.opts.loopEnum,d=i.useFunc(t,o.default);if(p||a)u=t.let("valid"),e.block$data(u,(function(){t.assign(u,!1),t.forOf("v",l,(e=>t.if(n._`${d}(${r}, ${e})`,(()=>t.assign(u,!0).break()))))}));else{if(!Array.isArray(s))throw Error("ajv implementation error");let e=t.const("vSchema",l);u=n.or(...s.map(((t,i)=>function(e,t){let i=s[t];return"object"==typeof i&&null!==i?n._`${d}(${r}, ${e}[${t}])`:n._`${r} === ${i}`}(e,i))))}e.pass(u)}}},3799:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(9640),i=r(7692),o=r(3765),a=r(8582),s=r(6711),l=r(7835),c=r(8950),u=r(7326),p=r(7535),d=r(4147),f=[n.default,i.default,o.default,a.default,s.default,l.default,c.default,u.default,{keyword:"type",schemaType:["string","array"]},{keyword:"nullable",schemaType:"boolean"},p.default,d.default];t.default=f},8950:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:["maxItems","minItems"],type:"array",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxItems"===e?"more":"fewer"} than ${t} items`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e,o="maxItems"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`${r}.length ${o} ${i}`)}}},3765:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=r(6124),o=r(5872);t.default={keyword:["maxLength","minLength"],type:"string",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxLength"===e?"more":"fewer"} than ${t} characters`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:a,it:s}=e,l="maxLength"===t?n.operators.GT:n.operators.LT,c=!1===s.opts.unicode?n._`${r}.length`:n._`${i.useFunc(e.gen,o.default)}(${r})`;e.fail$data(n._`${c} ${l} ${a}`)}}},9640:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475),i=n.operators,o={maximum:{okStr:"<=",ok:i.LTE,fail:i.GT},minimum:{okStr:">=",ok:i.GTE,fail:i.LT},exclusiveMaximum:{okStr:"<",ok:i.LT,fail:i.GTE},exclusiveMinimum:{okStr:">",ok:i.GT,fail:i.LTE}},a={keyword:Object.keys(o),type:"number",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must be ${o[e].okStr} ${t}`,params:({keyword:e,schemaCode:t})=>n._`{comparison: ${o[e].okStr}, limit: ${t}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e;e.fail$data(n._`${r} ${o[t].fail} ${i} || isNaN(${r})`)}};t.default=a},6711:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:["maxProperties","minProperties"],type:"object",schemaType:"number",$data:!0,error:{message:({keyword:e,schemaCode:t})=>n.str`must NOT have ${"maxProperties"===e?"more":"fewer"} than ${t} items`,params:({schemaCode:e})=>n._`{limit: ${e}}`},code(e){let{keyword:t,data:r,schemaCode:i}=e,o="maxProperties"===t?n.operators.GT:n.operators.LT;e.fail$data(n._`Object.keys(${r}).length ${o} ${i}`)}}},7692:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(4475);t.default={keyword:"multipleOf",type:"number",schemaType:"number",$data:!0,error:{message:({schemaCode:e})=>n.str`must be multiple of ${e}`,params:({schemaCode:e})=>n._`{multipleOf: ${e}}`},code(e){let{gen:t,data:r,schemaCode:i,it:o}=e,a=o.opts.multipleOfPrecision,s=t.let("res"),l=a?n._`Math.abs(Math.round(${s}) - ${s}) > 1e-${a}`:n._`${s} !== parseInt(${s})`;e.fail$data(n._`(${i} === 0 || (${s} = ${r}/${i}, ${l}))`)}}},8582:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475);t.default={keyword:"pattern",type:"string",schemaType:"string",$data:!0,error:{message:({schemaCode:e})=>i.str`must match pattern "${e}"`,params:({schemaCode:e})=>i._`{pattern: ${e}}`},code(e){let{data:t,$data:r,schema:o,schemaCode:a,it:s}=e,l=s.opts.unicodeRegExp?"u":"",c=r?i._`(new RegExp(${a}, ${l}))`:n.usePattern(e,o);e.fail$data(i._`!${c}.test(${t})`)}}},7835:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(8619),i=r(4475),o=r(6124);t.default={keyword:"required",type:"object",schemaType:"array",$data:!0,error:{message:({params:{missingProperty:e}})=>i.str`must have required property '${e}'`,params:({params:{missingProperty:e}})=>i._`{missingProperty: ${e}}`},code(e){let{gen:t,schema:r,schemaCode:a,data:s,$data:l,it:c}=e,{opts:u}=c;if(!l&&0===r.length)return;let p=r.length>=u.loopRequired;if(c.allErrors?function(){if(p||l)e.block$data(i.nil,d);else for(let t of r)n.checkReportMissingProp(e,t)}():function(){let o=t.let("missing");if(p||l){let r=t.let("valid",!0);e.block$data(r,(()=>{var l,c;return l=o,c=r,e.setParams({missingProperty:l}),void t.forOf(l,a,(()=>{t.assign(c,n.propertyInData(t,s,l,u.ownProperties)),t.if(i.not(c),(()=>{e.error(),t.break()}))}),i.nil)})),e.ok(r)}else t.if(n.checkMissingProp(e,r,o)),n.reportMissingProp(e,o),t.else()}(),u.strictRequired){let t=e.parentSchema.properties,{definedProperties:n}=e.it;for(let e of r)if(void 0===(null==t?void 0:t[e])&&!n.has(e)){let t=`required property "${e}" is not defined at "${c.schemaEnv.baseId+c.errSchemaPath}" (strictRequired)`;o.checkStrictMode(c,t,c.opts.strictRequired)}}function d(){t.forOf("prop",a,(r=>{e.setParams({missingProperty:r}),t.if(n.noPropertyInData(t,s,r,u.ownProperties),(()=>e.error()))}))}}}},7326:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});let n=r(7927),i=r(4475),o=r(6124),a=r(412);t.default={keyword:"uniqueItems",type:"array",schemaType:"boolean",$data:!0,error:{message:({params:{i:e,j:t}})=>i.str`must NOT have duplicate items (items ## ${t} and ${e} are identical)`,params:({params:{i:e,j:t}})=>i._`{i: ${e}, j: ${t}}`},code(e){let{gen:t,data:r,$data:s,schema:l,parentSchema:c,schemaCode:u,it:p}=e;if(!s&&!l)return;let d=t.let("valid"),f=c.items?n.getSchemaTypes(c.items):[];function h(o,a){let s=t.name("item"),l=n.checkDataTypes(f,s,p.opts.strictNumbers,n.DataType.Wrong),c=t.const("indices",i._`{}`);t.for(i._`;${o}--;`,(()=>{t.let(s,i._`${r}[${o}]`),t.if(l,i._`continue`),f.length>1&&t.if(i._`typeof ${s} == "string"`,i._`${s} += "_"`),t.if(i._`typeof ${c}[${s}] == "number"`,(()=>{t.assign(a,i._`${c}[${s}]`),e.error(),t.assign(d,!1).break()})).code(i._`${c}[${s}] = ${o}`)}))}function m(n,s){let l=o.useFunc(t,a.default),c=t.name("outer");t.label(c).for(i._`;${n}--;`,(()=>t.for(i._`${s} = ${n}; ${s}--;`,(()=>t.if(i._`${l}(${r}[${n}], ${r}[${s}])`,(()=>{e.error(),t.assign(d,!1).break(c)}))))))}e.block$data(d,(function(){let n=t.let("i",i._`${r}.length`),o=t.let("j");e.setParams({i:n,j:o}),t.assign(d,!0),t.if(i._`${n} > 1`,(()=>(f.length>0&&!f.some((e=>"object"===e||"array"===e))?h:m)(n,o)))}),i._`${u} === false`),e.ok(d)}}},4029:function(e){"use strict";var t=e.exports=function(e,r,n){"function"==typeof r&&(n=r,r={}),function e(r,n,i,o,a,s,l,c,u,p){if(o&&"object"==typeof o&&!Array.isArray(o)){for(var d in n(o,a,s,l,c,u,p),o){var f=o[d];if(Array.isArray(f)){if(d in t.arrayKeywords)for(var h=0;h0;)if(a=s.pop()+(a?`-${a}`:""),!o||!o[a]||d(o[a],e,r))return a;if(!o[a=m.refBaseName(n)+(a?`_${a}`:"")]||d(o[a],e,r))return a;let c=a,u=2;for(;o[a]&&!d(o[a],e,r);)a=`${c}-${u}`,u++;return o[a]||r.report({message:`Two schemas are referenced with the same name but different content. Renamed ${c} to ${a}.`,location:r.location,forceSeverity:"warn"}),a}(r,t,n);return l[t][i]=r.node,e===h.OasMajorVersion.Version3?`#/components/${t}/${i}`:`#/${t}/${i}`}function d(e,t,r){var n;return!(!m.isRef(e)||(null===(n=r.resolve(e).location)||void 0===n?void 0:n.absolutePointer)!==t.location.absolutePointer)||a(e,t.node)}return e===h.OasMajorVersion.Version3&&(c.DiscriminatorMapping={leave(r,n){for(let i of Object.keys(r)){let o=r[i],a=n.resolve({$ref:o});if(!a.location||void 0===a.node)return void y.reportUnresolvedRef(a,n.report,n.location.child(i));let s=_("Schema",e);t?p(s,a,n):r[i]=p(s,a,n)}}}),c}(A,k,O,t,I,E)},...j],C);return f.walkDocument({document:t,rootType:C.DefinitionRoot,normalizedVisitors:N,resolvedRefMap:I,ctx:T}),{bundle:t,problems:T.problems.map((e=>r.addProblemToIgnore(e))),fileDependencies:o.getFiles(),rootType:C.DefinitionRoot,refTypes:T.refTypes,visitorsData:T.visitorsData}}))}function _(e,t){switch(t){case h.OasMajorVersion.Version3:switch(e){case"Schema":return"schemas";case"Parameter":return"parameters";case"Response":return"responses";case"Example":return"examples";case"RequestBody":return"requestBodies";case"Header":return"headers";case"SecuritySchema":return"securitySchemes";case"Link":return"links";case"Callback":return"callbacks";default:return null}case h.OasMajorVersion.Version2:switch(e){case"Schema":return"definitions";case"Parameter":return"parameters";case"Response":return"responses";default:return null}}}(n=i=t.OasVersion||(t.OasVersion={})).Version2="oas2",n.Version3_0="oas3_0",n.Version3_1="oas3_1",t.bundle=function(e){return o(this,void 0,void 0,(function*(){let{ref:t,doc:r,externalRefResolver:n=new s.BaseResolver(e.config.resolve),base:i=null}=e;if(!t&&!r)throw Error("Document or reference is required.\n");let o=void 0!==r?r:yield n.resolveDocument(i,t,!0);if(o instanceof Error)throw o;return k(Object.assign(Object.assign({document:o},e),{config:e.config.lint,externalRefResolver:n}))}))},t.bundleDocument=k,t.mapTypeToComponent=_},6877:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"error","info-contact":"error","info-license":"error","info-license-url":"error","tag-description":"error","tags-alphabetical":"error","parameter-description":"error","no-identical-paths":"error","no-ambiguous-paths":"error","no-path-trailing-slash":"error","path-segment-plural":"error","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"error","operation-2xx-response":"error","operation-4xx-response":"error",assertions:"error","operation-operationId":"error","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"error","operation-security-defined":"error","operation-singular-tag":"error","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"error","paths-kebab-case":"error","no-http-verbs-in-paths":"error","path-excludes-patterns":{severity:"error",patterns:[]},"request-mime-type":"error",spec:"error","no-invalid-schema-examples":"error","no-invalid-parameter-examples":"error","scalar-property-missing-example":"error"},oas3_0Rules:{"no-invalid-media-type-examples":"error","no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"error","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"error","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},6242:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultPlugin=t.builtInConfigs=void 0;let n=r(8057),i=r(6877),o=r(9016),a=r(226),s=r(7523),l=r(226),c=r(7523),u=r(1753),p=r(7060);t.builtInConfigs={recommended:n.default,minimal:o.default,all:i.default,"redocly-registry":{decorators:{"registry-dependencies":"on"}}},t.defaultPlugin={id:"",rules:{oas3:a.rules,oas2:s.rules},preprocessors:{oas3:l.preprocessors,oas2:c.preprocessors},decorators:{oas3:u.decorators,oas2:p.decorators},configs:t.builtInConfigs}},7040:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))},i=this&&this.__rest||function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]])}return r};Object.defineProperty(t,"__esModule",{value:!0}),t.resolvePreset=t.resolveLint=t.resolveApis=t.resolvePlugins=t.resolveConfig=void 0;let o=r(6470),a=r(6212),s=r(7468),l=r(4182),c=r(6242),u=r(2565),p=r(771),d=r(3777);function f(e,t=""){if(!e)return[];let r=require,n=new Map;return e.map((e=>{if(p.isString(e)&&s.isAbsoluteUrl(e))throw Error(a.red("We don't support remote plugins yet."));let i=p.isString(e)?r(o.resolve(o.dirname(t),e)):e,l=i.id;if("string"!=typeof l)throw Error(a.red(`Plugin must define \`id\` property in ${a.blue(e.toString())}.`));if(n.has(l)){let t=n.get(l);throw Error(a.red(`Plugin "id" must be unique. Plugin ${a.blue(e.toString())} uses id "${a.blue(l)}" already seen in ${a.blue(t)}`))}n.set(l,e.toString());let c=Object.assign(Object.assign({id:l},i.configs?{configs:i.configs}:{}),i.typeExtension?{typeExtension:i.typeExtension}:{});if(i.rules){if(!i.rules.oas3&&!i.rules.oas2)throw Error(`Plugin rules must have \`oas3\` or \`oas2\` rules "${e}.`);c.rules={},i.rules.oas3&&(c.rules.oas3=u.prefixRules(i.rules.oas3,l)),i.rules.oas2&&(c.rules.oas2=u.prefixRules(i.rules.oas2,l))}if(i.preprocessors){if(!i.preprocessors.oas3&&!i.preprocessors.oas2)throw Error(`Plugin \`preprocessors\` must have \`oas3\` or \`oas2\` preprocessors "${e}.`);c.preprocessors={},i.preprocessors.oas3&&(c.preprocessors.oas3=u.prefixRules(i.preprocessors.oas3,l)),i.preprocessors.oas2&&(c.preprocessors.oas2=u.prefixRules(i.preprocessors.oas2,l))}if(i.decorators){if(!i.decorators.oas3&&!i.decorators.oas2)throw Error(`Plugin \`decorators\` must have \`oas3\` or \`oas2\` decorators "${e}.`);c.decorators={},i.decorators.oas3&&(c.decorators.oas3=u.prefixRules(i.decorators.oas3,l)),i.decorators.oas2&&(c.decorators.oas2=u.prefixRules(i.decorators.oas2,l))}return c})).filter(p.notUndefined)}function h({rawConfig:e,configPath:t="",resolver:r}){var i,o;return n(this,void 0,void 0,(function*(){let{apis:n={},lint:a={}}=e,s={};for(let[e,l]of Object.entries(n||{})){if(null===(o=null===(i=l.lint)||void 0===i?void 0:i.extends)||void 0===o?void 0:o.some(p.isNotString))throw Error("Error configuration format not detected in extends value must contain strings");let n=y(a,l.lint),c=yield m({lintConfig:n,configPath:t,resolver:r});s[e]=Object.assign(Object.assign({},l),{lint:c})}return s}))}function m(e,t=[],r=[]){return n(this,void 0,void 0,(function*(){let a=yield function e({lintConfig:t,configPath:r="",resolver:a=new l.BaseResolver},d=[],h=[]){var m,y,v;return n(this,void 0,void 0,(function*(){if(d.includes(r))throw Error(`Circular dependency in config file: "${r}"`);let l=u.getUniquePlugins(f([...(null==t?void 0:t.plugins)||[],c.defaultPlugin],r)),b=null===(m=null==t?void 0:t.plugins)||void 0===m?void 0:m.filter(p.isString).map((e=>o.resolve(o.dirname(r),e))),x=s.isAbsoluteUrl(r)?r:r&&o.resolve(r),w=yield Promise.all((null===(y=null==t?void 0:t.extends)||void 0===y?void 0:y.map((t=>n(this,void 0,void 0,(function*(){if(!s.isAbsoluteUrl(t)&&!o.extname(t))return g(t,l);let i=s.isAbsoluteUrl(t)?t:s.isAbsoluteUrl(r)?new URL(t,r).href:o.resolve(o.dirname(r),t),c=yield function(e,t){return n(this,void 0,void 0,(function*(){try{let r=yield t.loadExternalRef(e),n=u.transformConfig(p.parseYaml(r.body));if(!n.lint)throw Error(`Lint configuration format not detected: "${e}"`);return n.lint}catch(t){throw Error(`Failed to load "${e}": ${t.message}`)}}))}(i,a);return yield e({lintConfig:c,configPath:i,resolver:a},[...d,x],h)})))))||[]),k=u.mergeExtends([...w,Object.assign(Object.assign({},t),{plugins:l,extends:void 0,extendPaths:[...d,x],pluginPaths:b})]),{plugins:_=[]}=k,O=i(k,["plugins"]);return Object.assign(Object.assign({},O),{extendPaths:null===(v=O.extendPaths)||void 0===v?void 0:v.filter((e=>e&&!s.isAbsoluteUrl(e))),plugins:u.getUniquePlugins(_),recommendedFallback:null==t?void 0:t.recommendedFallback,doNotResolveExamples:null==t?void 0:t.doNotResolveExamples})}))}(e,t,r);return Object.assign(Object.assign({},a),{rules:a.rules&&function(e){if(!e)return e;let t={},r=[];for(let[n,i]of Object.entries(e))if(n.startsWith("assert/")&&"object"==typeof i&&null!==i){let e=i;r.push(Object.assign(Object.assign({},e),{assertionId:n.replace("assert/","")}))}else t[n]=i;return r.length>0&&(t.assertions=r),t}(a.rules)})}))}function g(e,t){var r;let{pluginId:n,configName:i}=u.parsePresetName(e),o=t.find((e=>e.id===n));if(!o)throw Error(`Invalid config ${a.red(e)}: plugin ${n} is not included.`);let s=null===(r=o.configs)||void 0===r?void 0:r[i];if(!s)throw Error(n?`Invalid config ${a.red(e)}: plugin ${n} doesn't export config with name ${i}.`:`Invalid config ${a.red(e)}: there is no such built-in config.`);return s}function y(e,t){return Object.assign(Object.assign(Object.assign({},e),t),{rules:Object.assign(Object.assign({},null==e?void 0:e.rules),null==t?void 0:t.rules),oas2Rules:Object.assign(Object.assign({},null==e?void 0:e.oas2Rules),null==t?void 0:t.oas2Rules),oas3_0Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Rules),null==t?void 0:t.oas3_0Rules),oas3_1Rules:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Rules),null==t?void 0:t.oas3_1Rules),preprocessors:Object.assign(Object.assign({},null==e?void 0:e.preprocessors),null==t?void 0:t.preprocessors),oas2Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas2Preprocessors),null==t?void 0:t.oas2Preprocessors),oas3_0Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Preprocessors),null==t?void 0:t.oas3_0Preprocessors),oas3_1Preprocessors:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Preprocessors),null==t?void 0:t.oas3_1Preprocessors),decorators:Object.assign(Object.assign({},null==e?void 0:e.decorators),null==t?void 0:t.decorators),oas2Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas2Decorators),null==t?void 0:t.oas2Decorators),oas3_0Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_0Decorators),null==t?void 0:t.oas3_0Decorators),oas3_1Decorators:Object.assign(Object.assign({},null==e?void 0:e.oas3_1Decorators),null==t?void 0:t.oas3_1Decorators),recommendedFallback:!(null==t?void 0:t.extends)&&e.recommendedFallback})}t.resolveConfig=function(e,t){var r,i,o,a,s;return n(this,void 0,void 0,(function*(){if(null===(i=null===(r=e.lint)||void 0===r?void 0:r.extends)||void 0===i?void 0:i.some(p.isNotString))throw Error("Error configuration format not detected in extends value must contain strings");let n=new l.BaseResolver(u.getResolveConfig(e.resolve)),c=null!==(a=null===(o=null==e?void 0:e.lint)||void 0===o?void 0:o.extends)&&void 0!==a?a:["recommended"],f=!(null===(s=null==e?void 0:e.lint)||void 0===s?void 0:s.extends),g=Object.assign(Object.assign({},null==e?void 0:e.lint),{extends:c,recommendedFallback:f}),y=yield h({rawConfig:Object.assign(Object.assign({},e),{lint:g}),configPath:t,resolver:n}),v=yield m({lintConfig:g,configPath:t,resolver:n});return new d.Config(Object.assign(Object.assign({},e),{apis:y,lint:v}),t)}))},t.resolvePlugins=f,t.resolveApis=h,t.resolveLint=m,t.resolvePreset=g},3777:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Config=t.LintConfig=t.AVAILABLE_REGIONS=t.DOMAINS=t.DEFAULT_REGION=t.IGNORE_FILE=t.env=void 0;let n=r(5101),i=r(6470),o=r(5273),a=r(771),s=r(1510),l=r(2565);t.env="undefined"!=typeof process&&{}||{},t.IGNORE_FILE=".redocly.lint-ignore.yaml",t.DEFAULT_REGION="us",t.DOMAINS=function(){let e={us:"redocly.com",eu:"eu.redocly.com"},r=t.env.REDOCLY_DOMAIN;return(null==r?void 0:r.endsWith(".redocly.host"))&&(e[r.split(".")[0]]=r),"redoc.online"===r&&(e[r]=r),e}(),t.AVAILABLE_REGIONS=Object.keys(t.DOMAINS);class c{constructor(e,r){this.rawConfig=e,this.configFile=r,this.ignore={},this._usedRules=new Set,this._usedVersions=new Set,this.plugins=e.plugins||[],this.doNotResolveExamples=!!e.doNotResolveExamples,this.recommendedFallback=e.recommendedFallback||!1,this.rules={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.rules),e.oas2Rules),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.rules),e.oas3_0Rules),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.rules),e.oas3_1Rules)},this.preprocessors={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.preprocessors),e.oas2Preprocessors),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.preprocessors),e.oas3_0Preprocessors),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.preprocessors),e.oas3_1Preprocessors)},this.decorators={[s.OasVersion.Version2]:Object.assign(Object.assign({},e.decorators),e.oas2Decorators),[s.OasVersion.Version3_0]:Object.assign(Object.assign({},e.decorators),e.oas3_0Decorators),[s.OasVersion.Version3_1]:Object.assign(Object.assign({},e.decorators),e.oas3_1Decorators)},this.extendPaths=e.extendPaths||[],this.pluginPaths=e.pluginPaths||[];let a=this.configFile?i.dirname(this.configFile):"undefined"!=typeof process&&process.cwd()||"",l=i.join(a,t.IGNORE_FILE);if(n.hasOwnProperty("existsSync")&&n.existsSync(l))for(let e of(this.ignore=o.parseYaml(n.readFileSync(l,"utf-8"))||{},Object.keys(this.ignore))){for(let t of(this.ignore[i.resolve(i.dirname(l),e)]=this.ignore[e],Object.keys(this.ignore[e])))this.ignore[e][t]=new Set(this.ignore[e][t]);delete this.ignore[e]}}saveIgnore(){let e=this.configFile?i.dirname(this.configFile):process.cwd(),r=i.join(e,t.IGNORE_FILE),s={};for(let t of Object.keys(this.ignore)){let r=s[a.slash(i.relative(e,t))]=this.ignore[t];for(let e of Object.keys(r))r[e]=Array.from(r[e])}n.writeFileSync(r,"# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n# See https://redoc.ly/docs/cli/ for more information.\n"+o.stringifyYaml(s))}addIgnore(e){let t=this.ignore,r=e.location[0];if(void 0===r.pointer)return;let n=t[r.source.absoluteRef]=t[r.source.absoluteRef]||{};(n[e.ruleId]=n[e.ruleId]||new Set).add(r.pointer)}addProblemToIgnore(e){let t=e.location[0];if(void 0===t.pointer)return e;let r=(this.ignore[t.source.absoluteRef]||{})[e.ruleId],n=r&&r.has(t.pointer);return n?Object.assign(Object.assign({},e),{ignored:n}):e}extendTypes(e,t){let r=e;for(let e of this.plugins)if(void 0!==e.typeExtension)switch(t){case s.OasVersion.Version3_0:case s.OasVersion.Version3_1:if(!e.typeExtension.oas3)continue;r=e.typeExtension.oas3(r,t);case s.OasVersion.Version2:if(!e.typeExtension.oas2)continue;r=e.typeExtension.oas2(r,t);default:throw Error("Not implemented")}return r}getRuleSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.rules[t][e]||"off";return"string"==typeof r?{severity:r}:Object.assign({severity:"error"},r)}getPreprocessorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.preprocessors[t][e]||"off";return"string"==typeof r?{severity:"on"===r?"error":r}:Object.assign({severity:"error"},r)}getDecoratorSettings(e,t){this._usedRules.add(e),this._usedVersions.add(t);let r=this.decorators[t][e]||"off";return"string"==typeof r?{severity:"on"===r?"error":r}:Object.assign({severity:"error"},r)}getUnusedRules(){let e=[],t=[],r=[];for(let n of Array.from(this._usedVersions))e.push(...Object.keys(this.rules[n]).filter((e=>!this._usedRules.has(e)))),t.push(...Object.keys(this.decorators[n]).filter((e=>!this._usedRules.has(e)))),r.push(...Object.keys(this.preprocessors[n]).filter((e=>!this._usedRules.has(e))));return{rules:e,preprocessors:r,decorators:t}}getRulesForOasVersion(e){switch(e){case s.OasMajorVersion.Version3:let e=[];return this.plugins.forEach((t=>{var r;return(null===(r=t.preprocessors)||void 0===r?void 0:r.oas3)&&e.push(t.preprocessors.oas3)})),this.plugins.forEach((t=>{var r;return(null===(r=t.rules)||void 0===r?void 0:r.oas3)&&e.push(t.rules.oas3)})),this.plugins.forEach((t=>{var r;return(null===(r=t.decorators)||void 0===r?void 0:r.oas3)&&e.push(t.decorators.oas3)})),e;case s.OasMajorVersion.Version2:let t=[];return this.plugins.forEach((e=>{var r;return(null===(r=e.preprocessors)||void 0===r?void 0:r.oas2)&&t.push(e.preprocessors.oas2)})),this.plugins.forEach((e=>{var r;return(null===(r=e.rules)||void 0===r?void 0:r.oas2)&&t.push(e.rules.oas2)})),this.plugins.forEach((e=>{var r;return(null===(r=e.decorators)||void 0===r?void 0:r.oas2)&&t.push(e.decorators.oas2)})),t}}skipRules(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.rules[e][t]&&(this.rules[e][t]="off")}skipPreprocessors(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.preprocessors[e][t]&&(this.preprocessors[e][t]="off")}skipDecorators(e){for(let t of e||[])for(let e of Object.values(s.OasVersion))this.decorators[e][t]&&(this.decorators[e][t]="off")}}t.LintConfig=c,t.Config=class{constructor(e,t){this.rawConfig=e,this.configFile=t,this.apis=e.apis||{},this.lint=new c(e.lint||{},t),this["features.openapi"]=e["features.openapi"]||{},this["features.mockServer"]=e["features.mockServer"]||{},this.resolve=l.getResolveConfig(null==e?void 0:e.resolve),this.region=e.region,this.organization=e.organization}}},8698:function(e,t,r){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,r,n){void 0===n&&(n=r),Object.defineProperty(e,n,{enumerable:!0,get:function(){return t[r]}})}:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]}),i=this&&this.__exportStar||function(e,t){for(var r in e)"default"===r||Object.prototype.hasOwnProperty.call(t,r)||n(t,e,r)};Object.defineProperty(t,"__esModule",{value:!0}),i(r(3777),t),i(r(3865),t),i(r(5030),t),i(r(6242),t),i(r(9129),t),i(r(2565),t),i(r(7040),t)},9129:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.getConfig=t.findConfig=t.CONFIG_FILE_NAMES=t.loadConfig=void 0;let i=r(5101),o=r(6470),a=r(1094),s=r(771),l=r(3777),c=r(2565),u=r(7040);function p(e){if(!i.hasOwnProperty("existsSync"))return;let r=t.CONFIG_FILE_NAMES.map((t=>e?o.resolve(e,t):t)).filter(i.existsSync);if(r.length>1)throw Error(`\n Multiple configuration files are not allowed. \n Found the following files: ${r.join(", ")}. \n Please use 'redocly.yaml' instead.\n `);return r[0]}function d(e=p()){return n(this,void 0,void 0,(function*(){if(!e)return{};try{let t=(yield s.loadYaml(e))||{};return c.transformConfig(t)}catch(t){throw Error(`Error parsing config file at '${e}': ${t.message}`)}}))}t.loadConfig=function(e=p(),t,r){return n(this,void 0,void 0,(function*(){let i=yield d(e);return"function"==typeof r&&(yield r(i)),yield function({rawConfig:e,customExtends:t,configPath:r}){var i;return n(this,void 0,void 0,(function*(){void 0!==t?(e.lint=e.lint||{},e.lint.extends=t):s.isEmptyObject(e);let n=new a.RedoclyClient,o=yield n.getTokens();if(o.length)for(let t of(e.resolve||(e.resolve={}),e.resolve.http||(e.resolve.http={}),e.resolve.http.headers=[...null!==(i=e.resolve.http.headers)&&void 0!==i?i:[]],o)){let r=l.DOMAINS[t.region];e.resolve.http.headers.push({matches:`https://api.${r}/registry/**`,name:"Authorization",envVariable:void 0,value:t.token},..."us"===t.region?[{matches:"https://api.redoc.ly/registry/**",name:"Authorization",envVariable:void 0,value:t.token}]:[])}return u.resolveConfig(e,r)}))}({rawConfig:i,customExtends:t,configPath:e})}))},t.CONFIG_FILE_NAMES=["redocly.yaml","redocly.yml",".redocly.yaml",".redocly.yml"],t.findConfig=p,t.getConfig=d},9016:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"off","info-license-url":"off","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"warn","no-identical-paths":"warn","no-ambiguous-paths":"warn","path-declaration-must-exist":"warn","path-not-include-query":"warn","path-parameters-defined":"warn","operation-description":"off","operation-2xx-response":"warn","operation-4xx-response":"off",assertions:"warn","operation-operationId":"warn","operation-summary":"warn","operation-operationId-unique":"warn","operation-parameters-unique":"warn","operation-tag-defined":"off","operation-security-defined":"warn","operation-operationId-url-safe":"warn","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"warn","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"warn","no-example-value-and-externalValue":"warn","no-unused-components":"warn","no-undefined-server-variable":"warn","no-servers-empty-enum":"error"}}},8057:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={rules:{"info-description":"warn","info-contact":"off","info-license":"warn","info-license-url":"warn","tag-description":"warn","tags-alphabetical":"off","parameter-description":"off","no-path-trailing-slash":"error","no-identical-paths":"error","no-ambiguous-paths":"warn","path-declaration-must-exist":"error","path-not-include-query":"error","path-parameters-defined":"error","operation-description":"off","operation-2xx-response":"warn",assertions:"warn","operation-4xx-response":"warn","operation-operationId":"warn","operation-summary":"error","operation-operationId-unique":"error","operation-operationId-url-safe":"error","operation-parameters-unique":"error","operation-tag-defined":"off","operation-security-defined":"error","operation-singular-tag":"off","no-unresolved-refs":"error","no-enum-type-mismatch":"error","boolean-parameter-prefixes":"off","paths-kebab-case":"off",spec:"error"},oas3_0Rules:{"no-invalid-media-type-examples":{severity:"warn",disallowAdditionalProperties:!0},"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"},oas3_1Rules:{"no-server-example.com":"warn","no-server-trailing-slash":"error","no-empty-servers":"error","no-example-value-and-externalValue":"error","no-unused-components":"warn","no-undefined-server-variable":"error","no-servers-empty-enum":"error"}}},5030:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.initRules=void 0;let n=r(771);t.initRules=function(e,t,r,i){return e.flatMap((e=>Object.keys(e).map((n=>{let o=e[n],a="rules"===r?t.getRuleSettings(n,i):"preprocessors"===r?t.getPreprocessorSettings(n,i):t.getDecoratorSettings(n,i);if("off"===a.severity)return;let s=o(a);return Array.isArray(s)?s.map((e=>({severity:a.severity,ruleId:n,visitor:e}))):{severity:a.severity,ruleId:n,visitor:s}})))).flatMap((e=>e)).filter(n.notUndefined)}},3865:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},2565:function(e,t,r){"use strict";var n=this&&this.__rest||function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&0>t.indexOf(n)&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);it.indexOf(n[i])&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]])}return r};Object.defineProperty(t,"__esModule",{value:!0}),t.getUniquePlugins=t.getResolveConfig=t.transformConfig=t.getMergedConfig=t.mergeExtends=t.prefixRules=t.transformApiDefinitionsToApis=t.parsePresetName=void 0;let i=r(6212),o=r(771),a=r(3777);function s(e={}){let t={};for(let[r,n]of Object.entries(e))t[r]={root:n};return t}t.parsePresetName=function(e){if(e.indexOf("/")>-1){let[t,r]=e.split("/");return{pluginId:t,configName:r}}return{pluginId:"",configName:e}},t.transformApiDefinitionsToApis=s,t.prefixRules=function(e,t){if(!t)return e;let r={};for(let n of Object.keys(e))r[`${t}/${n}`]=e[n];return r},t.mergeExtends=function(e){let t={rules:{},oas2Rules:{},oas3_0Rules:{},oas3_1Rules:{},preprocessors:{},oas2Preprocessors:{},oas3_0Preprocessors:{},oas3_1Preprocessors:{},decorators:{},oas2Decorators:{},oas3_0Decorators:{},oas3_1Decorators:{},plugins:[],pluginPaths:[],extendPaths:[]};for(let r of e){if(r.extends)throw Error(`\`extends\` is not supported in shared configs yet: ${JSON.stringify(r,null,2)}.`);Object.assign(t.rules,r.rules),Object.assign(t.oas2Rules,r.oas2Rules),o.assignExisting(t.oas2Rules,r.rules||{}),Object.assign(t.oas3_0Rules,r.oas3_0Rules),o.assignExisting(t.oas3_0Rules,r.rules||{}),Object.assign(t.oas3_1Rules,r.oas3_1Rules),o.assignExisting(t.oas3_1Rules,r.rules||{}),Object.assign(t.preprocessors,r.preprocessors),Object.assign(t.oas2Preprocessors,r.oas2Preprocessors),o.assignExisting(t.oas2Preprocessors,r.preprocessors||{}),Object.assign(t.oas3_0Preprocessors,r.oas3_0Preprocessors),o.assignExisting(t.oas3_0Preprocessors,r.preprocessors||{}),Object.assign(t.oas3_1Preprocessors,r.oas3_1Preprocessors),o.assignExisting(t.oas3_1Preprocessors,r.preprocessors||{}),Object.assign(t.decorators,r.decorators),Object.assign(t.oas2Decorators,r.oas2Decorators),o.assignExisting(t.oas2Decorators,r.decorators||{}),Object.assign(t.oas3_0Decorators,r.oas3_0Decorators),o.assignExisting(t.oas3_0Decorators,r.decorators||{}),Object.assign(t.oas3_1Decorators,r.oas3_1Decorators),o.assignExisting(t.oas3_1Decorators,r.decorators||{}),t.plugins.push(...r.plugins||[]),t.pluginPaths.push(...r.pluginPaths||[]),t.extendPaths.push(...new Set(r.extendPaths))}return t},t.getMergedConfig=function(e,t){var r,n,i,o,s,l;let c=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.extendPaths})),null===(n=null===(r=e.rawConfig)||void 0===r?void 0:r.lint)||void 0===n?void 0:n.extendPaths].flat().filter(Boolean),u=[...Object.values(e.apis).map((e=>{var t;return null===(t=null==e?void 0:e.lint)||void 0===t?void 0:t.pluginPaths})),null===(o=null===(i=e.rawConfig)||void 0===i?void 0:i.lint)||void 0===o?void 0:o.pluginPaths].flat().filter(Boolean);return t?new a.Config(Object.assign(Object.assign({},e.rawConfig),{lint:Object.assign(Object.assign({},e.apis[t]?e.apis[t].lint:e.rawConfig.lint),{extendPaths:c,pluginPaths:u}),"features.openapi":Object.assign(Object.assign({},e["features.openapi"]),null===(s=e.apis[t])||void 0===s?void 0:s["features.openapi"]),"features.mockServer":Object.assign(Object.assign({},e["features.mockServer"]),null===(l=e.apis[t])||void 0===l?void 0:l["features.mockServer"])}),e.configFile):e},t.transformConfig=function(e){if(e.apis&&e.apiDefinitions)throw Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");if(e["features.openapi"]&&e.referenceDocs)throw Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");let t=e,{apiDefinitions:r,referenceDocs:o}=t,a=n(t,["apiDefinitions","referenceDocs"]);return r&&process.stderr.write(`The ${i.yellow("apiDefinitions")} field is deprecated. Use ${i.green("apis")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),o&&process.stderr.write(`The ${i.yellow("referenceDocs")} field is deprecated. Use ${i.green("features.openapi")} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`),Object.assign({"features.openapi":o,apis:s(r)},a)},t.getResolveConfig=function(e){var t,r;return{http:{headers:null!==(r=null===(t=null==e?void 0:e.http)||void 0===t?void 0:t.headers)&&void 0!==r?r:[],customFetch:void 0}}},t.getUniquePlugins=function(e){let t=new Set,r=[];for(let n of e)t.has(n.id)?n.id&&process.stderr.write(`Duplicate plugin id "${i.yellow(n.id)}".\n`):(r.push(n),t.add(n.id));return r}},1988:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.checkIfMatchByStrategy=t.filter=void 0;let n=r(7468),i=r(771);function o(e){return Array.isArray(e)?e:[e]}t.filter=function(e,t,r){let{parent:o,key:a}=t,s=!1;if(Array.isArray(e))for(let i=0;ie.includes(t))):"all"===r&&t.every((t=>e.includes(t)))):e===t)}},9244:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterIn=void 0;let n=r(1988);t.FilterIn=({property:e,value:t,matchStrategy:r})=>{let i=r||"any",o=r=>(null==r?void 0:r[e])&&!n.checkIfMatchByStrategy(null==r?void 0:r[e],t,i);return{any:{enter(e,t){n.filter(e,t,o)}}}}},8623:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.FilterOut=void 0;let n=r(1988);t.FilterOut=({property:e,value:t,matchStrategy:r})=>{let i=r||"any",o=r=>n.checkIfMatchByStrategy(null==r?void 0:r[e],t,i);return{any:{enter(e,t){n.filter(e,t,o)}}}}},4555:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescriptionOverride=void 0;let n=r(771);t.InfoDescriptionOverride=({filePath:e})=>({Info:{leave(t,{report:r,location:i}){if(!e)throw Error('Parameter "filePath" is not provided for "info-description-override" rule');try{t.description=n.readFileAsStringSync(e)}catch(e){r({message:`Failed to read markdown override file for "info.description".\n${e.message}`,location:i.child("description")})}}}})},7802:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescriptionOverride=void 0;let n=r(771);t.OperationDescriptionOverride=({operationIds:e})=>({Operation:{leave(t,{report:r,location:i}){if(!t.operationId)return;if(!e)throw Error('Parameter "operationIds" is not provided for "operation-description-override" rule');let o=t.operationId;if(e[o])try{t.description=n.readFileAsStringSync(e[o])}catch(e){r({message:`Failed to read markdown override file for operation "${o}".\n${e.message}`,location:i.child("operationId").key()})}}}})},2287:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryDependencies=void 0;let n=r(1094);t.RegistryDependencies=()=>{let e=new Set;return{DefinitionRoot:{leave(t,r){r.getVisitorData().links=Array.from(e)}},ref(t){if(t.$ref){let r=t.$ref.split("#/")[0];n.isRedoclyRegistryURL(r)&&e.add(r)}}}}},5830:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveXInternal=void 0;let n=r(771),i=r(7468);t.RemoveXInternal=({internalFlagProperty:e})=>{let t=e||"x-internal";return{any:{enter(e,r){!function(e,r){var o,a,s,l;let{parent:c,key:u}=r,p=!1;if(Array.isArray(e))for(let n=0;n({Tag:{leave(t,{report:r}){if(!e)throw Error('Parameter "tagNames" is not provided for "tag-description-override" rule');if(e[t.name])try{t.description=n.readFileAsStringSync(e[t.name])}catch(e){r({message:`Failed to read markdown override file for tag "${t.name}".\n${e.message}`})}}}})},7060:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;let n=r(2287),i=r(7802),o=r(423),a=r(4555),s=r(5830),l=r(9244),c=r(8623);t.decorators={"registry-dependencies":n.RegistryDependencies,"operation-description-override":i.OperationDescriptionOverride,"tag-description-override":o.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},1753:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decorators=void 0;let n=r(2287),i=r(7802),o=r(423),a=r(4555),s=r(5830),l=r(9244),c=r(8623);t.decorators={"registry-dependencies":n.RegistryDependencies,"operation-description-override":i.OperationDescriptionOverride,"tag-description-override":o.TagDescriptionOverride,"info-description-override":a.InfoDescriptionOverride,"remove-x-internal":s.RemoveXInternal,"filter-in":l.FilterIn,"filter-out":c.FilterOut}},5273:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.stringifyYaml=t.parseYaml=void 0;let n=r(3320),i=n.JSON_SCHEMA.extend({implicit:[n.types.merge],explicit:[n.types.binary,n.types.omap,n.types.pairs,n.types.set]});t.parseYaml=(e,t)=>n.load(e,Object.assign({schema:i},t)),t.stringifyYaml=(e,t)=>n.dump(e,t)},1510:function(e,t){"use strict";var r,n,i,o;Object.defineProperty(t,"__esModule",{value:!0}),t.openAPIMajor=t.detectOpenAPI=t.OasMajorVersion=t.OasVersion=void 0,(i=r=t.OasVersion||(t.OasVersion={})).Version2="oas2",i.Version3_0="oas3_0",i.Version3_1="oas3_1",(o=n=t.OasMajorVersion||(t.OasMajorVersion={})).Version2="oas2",o.Version3="oas3",t.detectOpenAPI=function(e){if("object"!=typeof e)throw Error("Document must be JSON object, got "+typeof e);if(!e.openapi&&!e.swagger)throw Error("This doesn’t look like an OpenAPI document.\n");if(e.openapi&&"string"!=typeof e.openapi)throw Error(`Invalid OpenAPI version: should be a string but got "${typeof e.openapi}"`);if(e.openapi&&e.openapi.startsWith("3.0"))return r.Version3_0;if(e.openapi&&e.openapi.startsWith("3.1"))return r.Version3_1;if(e.swagger&&"2.0"===e.swagger)return r.Version2;throw Error(`Unsupported OpenAPI Version: ${e.openapi||e.swagger}`)},t.openAPIMajor=function(e){return e===r.Version2?n.Version2:n.Version3}},1094:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isRedoclyRegistryURL=t.RedoclyClient=void 0;let i=r(2116),o=r(6470),a=r(6918),s=r(8836),l=r(1390),c=r(3777),u=r(771),p=".redocly-config.json";t.RedoclyClient=class{constructor(e){this.accessTokens={},this.region=this.loadRegion(e),this.loadTokens(),this.domain=e?c.DOMAINS[e]:c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION],c.env.REDOCLY_DOMAIN=this.domain,this.registryApi=new l.RegistryApi(this.accessTokens,this.region)}loadRegion(e){if(e&&!c.DOMAINS[e])throw Error(`Invalid argument: region in config file.\nGiven: ${s.green(e)}, choices: "us", "eu".`);return c.env.REDOCLY_DOMAIN?c.AVAILABLE_REGIONS.find((e=>c.DOMAINS[e]===c.env.REDOCLY_DOMAIN))||c.DEFAULT_REGION:e||c.DEFAULT_REGION}getRegion(){return this.region}hasTokens(){return u.isNotEmptyObject(this.accessTokens)}hasToken(){return!!this.accessTokens[this.region]}getAuthorizationHeader(){return n(this,void 0,void 0,(function*(){return this.accessTokens[this.region]}))}setAccessTokens(e){this.accessTokens=e}loadTokens(){let e=o.resolve(a.homedir(),p),t=this.readCredentialsFile(e);u.isNotEmptyObject(t)&&this.setAccessTokens(Object.assign(Object.assign({},t),t.token&&!t[this.region]&&{[this.region]:t.token})),c.env.REDOCLY_AUTHORIZATION&&this.setAccessTokens(Object.assign(Object.assign({},this.accessTokens),{[this.region]:c.env.REDOCLY_AUTHORIZATION}))}getAllTokens(){return Object.entries(this.accessTokens).filter((([e])=>c.AVAILABLE_REGIONS.includes(e))).map((([e,t])=>({region:e,token:t})))}getValidTokens(){return n(this,void 0,void 0,(function*(){let e=this.getAllTokens(),t=yield Promise.allSettled(e.map((({token:e,region:t})=>this.verifyToken(e,t))));return e.filter(((e,r)=>"fulfilled"===t[r].status)).map((({token:e,region:t})=>({token:e,region:t,valid:!0})))}))}getTokens(){return n(this,void 0,void 0,(function*(){return this.hasTokens()?yield this.getValidTokens():[]}))}isAuthorizedWithRedoclyByRegion(){return n(this,void 0,void 0,(function*(){if(!this.hasTokens())return!1;let e=this.accessTokens[this.region];if(!e)return!1;try{return yield this.verifyToken(e,this.region),!0}catch(e){return!1}}))}isAuthorizedWithRedocly(){return n(this,void 0,void 0,(function*(){return this.hasTokens()&&u.isNotEmptyObject(yield this.getValidTokens())}))}readCredentialsFile(e){return i.existsSync(e)?JSON.parse(i.readFileSync(e,"utf-8")):{}}verifyToken(e,t,r=!1){return n(this,void 0,void 0,(function*(){return this.registryApi.authStatus(e,t,r)}))}login(e,t=!1){return n(this,void 0,void 0,(function*(){let r=o.resolve(a.homedir(),p);try{yield this.verifyToken(e,this.region,t)}catch(e){throw Error("Authorization failed. Please check if you entered a valid API key.")}let n=Object.assign(Object.assign({},this.readCredentialsFile(r)),{[this.region]:e,token:e});this.accessTokens=n,this.registryApi.setAccessTokens(n),i.writeFileSync(r,JSON.stringify(n,null,2))}))}logout(){let e=o.resolve(a.homedir(),p);i.existsSync(e)&&i.unlinkSync(e)}},t.isRedoclyRegistryURL=function(e){let t=c.env.REDOCLY_DOMAIN||c.DOMAINS[c.DEFAULT_REGION];return!(!e.startsWith(`https://api.${t}/registry/`)&&!e.startsWith(`https://api.${"redocly.com"===t?"redoc.ly":t}/registry/`))}},1390:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.RegistryApi=void 0;let i=r(8150),o=r(3777),a=r(771),s=r(3244).i8;t.RegistryApi=class{constructor(e,t){this.accessTokens=e,this.region=t}get accessToken(){return a.isNotEmptyObject(this.accessTokens)&&this.accessTokens[this.region]}getBaseUrl(e=o.DEFAULT_REGION){return`https://api.${o.DOMAINS[e]}/registry`}setAccessTokens(e){return this.accessTokens=e,this}request(e="",t={},r){return n(this,void 0,void 0,(function*(){let n=Object.assign({},t.headers||{},{"x-redocly-cli-version":s});if(!n.hasOwnProperty("authorization"))throw Error("Unauthorized");let o=yield i.default(`${this.getBaseUrl(r)}${e}`,Object.assign({},t,{headers:n}));if(401===o.status)throw Error("Unauthorized");if(404===o.status){let e=yield o.json();throw Error(e.code)}return o}))}authStatus(e,t,r=!1){return n(this,void 0,void 0,(function*(){try{let r=yield this.request("",{headers:{authorization:e}},t);return yield r.json()}catch(e){throw r&&console.log(e),e}}))}prepareFileUpload({organizationId:e,name:t,version:r,filesHash:i,filename:o,isUpsert:a}){return n(this,void 0,void 0,(function*(){let n=yield this.request(`/${e}/${t}/${r}/prepare-file-upload`,{method:"POST",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({filesHash:i,filename:o,isUpsert:a})},this.region);if(n.ok)return n.json();throw Error("Could not prepare file upload")}))}pushApi({organizationId:e,name:t,version:r,rootFilePath:i,filePaths:o,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u}){return n(this,void 0,void 0,(function*(){if(!(yield this.request(`/${e}/${t}/${r}`,{method:"PUT",headers:{"content-type":"application/json",authorization:this.accessToken},body:JSON.stringify({rootFilePath:i,filePaths:o,branch:a,isUpsert:s,isPublic:l,batchId:c,batchSize:u})},this.region)).ok)throw Error("Could not push api")}))}}},7468:function(e,t){"use strict";function r(e,t){return""===e&&(e="#/"),"/"===e[e.length-1]?e+t:e+"/"+t}Object.defineProperty(t,"__esModule",{value:!0}),t.isMappingRef=t.isAbsoluteUrl=t.refBaseName=t.pointerBaseName=t.parsePointer=t.parseRef=t.escapePointer=t.unescapePointer=t.Location=t.isRef=t.joinPointer=void 0,t.joinPointer=r,t.isRef=function(e){return e&&"string"==typeof e.$ref};class n{constructor(e,t){this.source=e,this.pointer=t}child(e){return new n(this.source,r(this.pointer,(Array.isArray(e)?e:[e]).map(o).join("/")))}key(){return Object.assign(Object.assign({},this),{reportOnKey:!0})}get absolutePointer(){return this.source.absoluteRef+("#/"===this.pointer?"":this.pointer)}}function i(e){return decodeURIComponent(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function o(e){return"number"==typeof e?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}t.Location=n,t.unescapePointer=i,t.escapePointer=o,t.parseRef=function(e){let[t,r]=e.split("#/");return{uri:t||null,pointer:r?r.split("/").map(i).filter(Boolean):[]}},t.parsePointer=function(e){return e.substr(2).split("/").map(i)},t.pointerBaseName=function(e){let t=e.split("/");return t[t.length-1]},t.refBaseName=function(e){let t=e.split(/[\/\\]/);return t[t.length-1].replace(/\.[^.]+$/,"")},t.isAbsoluteUrl=function(e){return e.startsWith("http://")||e.startsWith("https://")},t.isMappingRef=function(e){return e.startsWith("#")||e.startsWith("https://")||e.startsWith("http://")||e.startsWith("./")||e.startsWith("../")||e.indexOf("/")>-1}},4182:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.resolveDocument=t.BaseResolver=t.makeDocumentFromString=t.makeRefId=t.YamlParseError=t.ResolveError=t.Source=void 0;let i=r(3197),o=r(6470),a=r(7468),s=r(5220),l=r(771);class c{constructor(e,t,r){this.absoluteRef=e,this.body=t,this.mimeType=r}getAst(e){var t;return void 0===this._ast&&(this._ast=null!==(t=e(this.body,{filename:this.absoluteRef}))&&void 0!==t?t:void 0,this._ast&&0===this._ast.kind&&""===this._ast.value&&1!==this._ast.startPosition&&(this._ast.startPosition=1,this._ast.endPosition=1)),this._ast}getLines(){return void 0===this._lines&&(this._lines=this.body.split(/\r\n|[\n\r]/g)),this._lines}}t.Source=c;class u extends Error{constructor(e){super(e.message),this.originalError=e,Object.setPrototypeOf(this,u.prototype)}}t.ResolveError=u;class p extends Error{constructor(e,t){super(e.message.split("\n")[0]),this.originalError=e,this.source=t,Object.setPrototypeOf(this,p.prototype);let[,r,n]=this.message.match(/\((\d+):(\d+)\)$/)||[];this.line=parseInt(r,10),this.col=parseInt(n,10)}}function d(e,t){return e+"::"+t}function f(e,t){return{prev:e,node:t}}t.YamlParseError=p,t.makeRefId=d,t.makeDocumentFromString=function(e,t){let r=new c(t,e);try{return{source:r,parsed:l.parseYaml(e,{filename:t})}}catch(e){throw new p(e,r)}},t.BaseResolver=class{constructor(e={http:{headers:[]}}){this.config=e,this.cache=new Map}getFiles(){return new Set(Array.from(this.cache.keys()))}resolveExternalRef(e,t){return a.isAbsoluteUrl(t)?t:e&&a.isAbsoluteUrl(e)?new URL(t,e).href:o.resolve(e?o.dirname(e):process.cwd(),t)}loadExternalRef(e){return n(this,void 0,void 0,(function*(){try{if(a.isAbsoluteUrl(e)){let{body:t,mimeType:r}=yield l.readFileFromUrl(e,this.config.http);return new c(e,t,r)}return new c(e,yield i.promises.readFile(e,"utf-8"))}catch(e){throw new u(e)}}))}parseDocument(e,t=!1){var r;let n=e.absoluteRef.substr(e.absoluteRef.lastIndexOf("."));if(![".json",".json",".yml",".yaml"].includes(n)&&!(null===(r=e.mimeType)||void 0===r?void 0:r.match(/(json|yaml|openapi)/))&&!t)return{source:e,parsed:e.body};try{return{source:e,parsed:l.parseYaml(e.body,{filename:e.absoluteRef})}}catch(t){throw new p(t,e)}}resolveDocument(e,t,r=!1){return n(this,void 0,void 0,(function*(){let n=this.resolveExternalRef(e,t),i=this.cache.get(n);if(i)return i;let o=this.loadExternalRef(n).then((e=>this.parseDocument(e,r)));return this.cache.set(n,o),o}))}};let h={name:"unknown",properties:{}},m={name:"scalar",properties:{}};t.resolveDocument=function(e){return n(this,void 0,void 0,(function*(){let t,{rootDocument:r,externalRefResolver:i,rootType:o}=e,l=new Map,c=new Set,u=[];!function e(t,r,o,p){!function t(o,p,g){if("object"!=typeof o||null===o)return;let y=`${p.name}::${g}`;if(!c.has(y))if(c.add(y),Array.isArray(o)){let e=p.items;if(p!==h&&void 0===e)return;for(let r=0;r{t.resolved&&e(t.node,t.document,t.nodePointer,p)}));u.push(t)}}}(t,p,r.source.absoluteRef+"#/")}(r.parsed,r,0,o);do{t=yield Promise.all(u)}while(u.length!==t.length);return l}))}},7275:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateJsonSchema=t.releaseAjvInstance=void 0;let n=r(5499),i=r(7468),o=null;t.releaseAjvInstance=function(){o=null},t.validateJsonSchema=function(e,t,r,a,s,l){let c=function(e,t,r,i){var a,s;let l=(a=r,s=i,o||(o=new n.default({schemaId:"$id",meta:!0,allErrors:!0,strictSchema:!1,inlineRefs:!1,validateSchema:!1,discriminator:!0,allowUnionTypes:!0,validateFormats:!1,defaultAdditionalProperties:!s,loadSchemaSync(e,t){let r=a({$ref:t},e.split("#")[0]);return!(!r||!r.location)&&Object.assign({$id:r.location.absolutePointer},r.node)},logger:!1})),o);return l.getSchema(t.absolutePointer)||l.addSchema(Object.assign({$id:t.absolutePointer},e),t.absolutePointer),l.getSchema(t.absolutePointer)}(t,r,s,l);return c?{valid:!!c(e,{instancePath:a,parentData:{fake:{}},parentDataProperty:"fake",rootData:{},dynamicAnchors:{}}),errors:(c.errors||[]).map((function(e){let t=e.message,r="enum"===e.keyword?e.params.allowedValues:void 0;r&&(t+=` ${r.map((e=>`"${e}"`)).join(", ")}`),"type"===e.keyword&&(t=`type ${t}`);let n=e.instancePath.substring(a.length+1),o=n.substring(n.lastIndexOf("/")+1);if(o&&(t=`\`${o}\` property ${t}`),"additionalProperties"===e.keyword){let r=e.params.additionalProperty;t=`${t} \`${r}\``,e.instancePath+="/"+i.escapePointer(r)}return Object.assign(Object.assign({},e),{message:t,suggest:r})}))}:{valid:!0,errors:[]}}},9740:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.asserts=t.runOnValuesSet=t.runOnKeysSet=void 0;let n=r(771),i=r(5738);t.runOnKeysSet=new Set(["mutuallyExclusive","mutuallyRequired","enum","pattern","minLength","maxLength","casing","sortOrder","disallowed","required","requireAny","ref"]),t.runOnValuesSet=new Set(["pattern","enum","defined","undefined","nonEmpty","minLength","maxLength","casing","sortOrder","ref"]),t.asserts={pattern(e,t,r){if(void 0===e)return{isValid:!0};let o=n.isString(e)?[e]:e,a=i.regexFromString(t);for(let t of o)if(!(null==a?void 0:a.test(t)))return{isValid:!1,location:n.isString(e)?r:r.key()};return{isValid:!0}},enum(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i)if(!t.includes(o))return{isValid:!1,location:n.isString(e)?r:r.child(o).key()};return{isValid:!0}},defined(e,t=!0,r){let n=void 0!==e;return{isValid:t?n:!n,location:r}},required(e,t,r){for(let n of t)if(!e.includes(n))return{isValid:!1,location:r.key()};return{isValid:!0}},disallowed(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i)if(t.includes(o))return{isValid:!1,location:n.isString(e)?r:r.child(o).key()};return{isValid:!0}},undefined(e,t=!0,r){let n=void 0===e;return{isValid:t?n:!n,location:r}},nonEmpty(e,t=!0,r){let n=null==e||""===e;return{isValid:t?!n:n,location:r}},minLength:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:e.length>=t,location:r},maxLength:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:e.length<=t,location:r},casing(e,t,r){if(void 0===e)return{isValid:!0};let i=n.isString(e)?[e]:e;for(let o of i){let i=!1;switch(t){case"camelCase":i=!!o.match(/^[a-z][a-zA-Z0-9]+$/g);break;case"kebab-case":i=!!o.match(/^([a-z][a-z0-9]*)(-[a-z0-9]+)*$/g);break;case"snake_case":i=!!o.match(/^([a-z][a-z0-9]*)(_[a-z0-9]+)*$/g);break;case"PascalCase":i=!!o.match(/^[A-Z][a-zA-Z0-9]+$/g);break;case"MACRO_CASE":i=!!o.match(/^([A-Z][A-Z0-9]*)(_[A-Z0-9]+)*$/g);break;case"COBOL-CASE":i=!!o.match(/^([A-Z][A-Z0-9]*)(-[A-Z0-9]+)*$/g);break;case"flatcase":i=!!o.match(/^[a-z][a-z0-9]+$/g)}if(!i)return{isValid:!1,location:n.isString(e)?r:r.child(o).key()}}return{isValid:!0}},sortOrder:(e,t,r)=>void 0===e?{isValid:!0}:{isValid:i.isOrdered(e,t),location:r},mutuallyExclusive:(e,t,r)=>({isValid:2>i.getIntersectionLength(e,t),location:r.key()}),mutuallyRequired:(e,t,r)=>({isValid:!(i.getIntersectionLength(e,t)>0)||i.getIntersectionLength(e,t)===t.length,location:r.key()}),requireAny:(e,t,r)=>({isValid:i.getIntersectionLength(e,t)>=1,location:r.key()}),ref(e,t,r,n){if(void 0===n)return{isValid:!0};let o=n.hasOwnProperty("$ref");if("boolean"==typeof t)return{isValid:t?o:!o,location:o?r:r.key()};let a=i.regexFromString(t);return{isValid:o&&(null==a?void 0:a.test(n.$ref)),location:o?r:r.key()}}}},4015:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Assertions=void 0;let n=r(9740),i=r(5738);t.Assertions=e=>{let t=[],r=Object.values(e).filter((e=>"object"==typeof e&&null!==e));for(let[e,o]of r.entries()){let r=o.assertionId&&`${o.assertionId} assertion`||`assertion #${e+1}`;if(!o.subject)throw Error(`${r}: 'subject' is required`);let a=Array.isArray(o.subject)?o.subject:[o.subject],s=Object.keys(n.asserts).filter((e=>void 0!==o[e])).map((e=>({assertId:r,name:e,conditions:o[e],message:o.message,severity:o.severity||"error",suggest:o.suggest||[],runsOnKeys:n.runOnKeysSet.has(e),runsOnValues:n.runOnValuesSet.has(e)}))),l=s.find((e=>e.runsOnKeys&&!e.runsOnValues)),c=s.find((e=>e.runsOnValues&&!e.runsOnKeys));if(c&&!o.property)throw Error(`${c.name} can't be used on all keys. Please provide a single property.`);if(l&&o.property)throw Error(`${l.name} can't be used on a single property. Please use 'property'.`);for(let e of a){let r=i.buildSubjectVisitor(o.property,s,o.context),n=i.buildVisitorObject(e,o.context,r);t.push(n)}}return t}},5738:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.regexFromString=t.isOrdered=t.getIntersectionLength=t.buildSubjectVisitor=t.buildVisitorObject=void 0;let n=r(7468),i=r(9740);function o({values:e,rawValues:t,assert:r,location:n,report:o}){let a=i.asserts[r.name](e,r.conditions,n,t);a.isValid||o({message:r.message||`The ${r.assertId} doesn't meet required conditions`,location:a.location||n,forceSeverity:r.severity,suggest:r.suggest,ruleId:r.assertId})}t.buildVisitorObject=function(e,t,r){if(!t)return{[e]:r};let n={},i=n;for(let r=0;ro?!o.includes(t):a?a.includes(t):void 0}:{},n=n[i.type]}return n[e]=r,i},t.buildSubjectVisitor=function(e,t,r){return(i,{report:a,location:s,rawLocation:l,key:c,type:u,resolve:p,rawNode:d})=>{var f;if(r){let e=r[r.length-1];if(e.type===u.name){let t=e.matchParentKeys,r=e.excludeParentKeys;if(t&&!t.includes(c)||r&&r.includes(c))return}}for(let r of(e&&(e=Array.isArray(e)?e:[e]),t)){let t="ref"===r.name?l:s;if(e)for(let s of e)o({values:n.isRef(i[s])?null===(f=p(i[s]))||void 0===f?void 0:f.node:i[s],rawValues:d[s],assert:r,location:t.child(s),report:a});else{let e="ref"===r.name?d:Object.keys(i);o({values:Object.keys(i),rawValues:e,assert:r,location:t,report:a})}}}},t.getIntersectionLength=function(e,t){let r=new Set(t),n=0;for(let t of e)r.has(t)&&n++;return n},t.isOrdered=function(e,t){let r=t.direction||t,n=t.property;for(let t=1;t=o:i<=o))return!1}return!0},t.regexFromString=function(e){let t=e.match(/^\/(.*)\/(.*)|(.*)/);return t&&RegExp(t[1]||t[3],t[2])}},8265:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoContact=void 0;let n=r(780);t.InfoContact=()=>({Info(e,{report:t,location:r}){e.contact||t({message:n.missingRequiredField("Info","contact"),location:r.child("contact").key()})}})},8675:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoDescription=void 0;let n=r(780);t.InfoDescription=()=>({Info(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},9622:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicense=void 0;let n=r(780);t.InfoLicense=()=>({Info(e,{report:t}){e.license||t({message:n.missingRequiredField("Info","license"),location:{reportOnKey:!0}})}})},476:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.InfoLicenseUrl=void 0;let n=r(780);t.InfoLicenseUrl=()=>({License(e,t){n.validateDefinedAndNonEmpty("url",e,t)}})},3467:function(e,t){"use strict";function r(e,t){let r=e.split("/"),n=t.split("/");if(r.length!==n.length)return!1;let i=0,o=0,a=!0;for(let e=0;e({PathMap(e,{report:t,location:n}){let i=[];for(let o of Object.keys(e)){let e=i.find((e=>r(e,o)));e&&t({message:`Paths should resolve unambiguously. Found two ambiguous paths: \`${e}\` and \`${o}\`.`,location:n.child([o]).key()}),i.push(o)}}})},2319:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEnumTypeMismatch=void 0;let n=r(780);t.NoEnumTypeMismatch=()=>({Schema(e,{report:t,location:r}){if(!e.enum||Array.isArray(e.enum)){if(e.enum&&e.type&&!Array.isArray(e.type)){let i=e.enum.filter((t=>!n.matchesJsonSchemaType(t,e.type,e.nullable)));for(let o of i)t({message:`All values of \`enum\` field must be of the same type as the \`type\` field: expected "${e.type}" but received "${n.oasTypeOf(o)}".`,location:r.child(["enum",e.enum.indexOf(o)])})}if(e.enum&&e.type&&Array.isArray(e.type)){let i={};for(let t of e.enum){for(let r of(i[t]=[],e.type))n.matchesJsonSchemaType(t,r,e.nullable)||i[t].push(r);i[t].length!==e.type.length&&delete i[t]}for(let n of Object.keys(i))t({message:`Enum value \`${n}\` must be of one type. Allowed types: \`${e.type}\`.`,location:r.child(["enum",e.enum.indexOf(n)])})}}}})},525:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoHttpVerbsInPaths=void 0;let n=r(771),i=["get","head","post","put","patch","delete","options","trace"];t.NoHttpVerbsInPaths=({splitIntoWords:e})=>({PathItem(t,{key:r,report:o,location:a}){let s=r.toString();if(!s.startsWith("/"))return;let l=s.split("/");for(let t of l){if(!t||n.isPathParameter(t))continue;let r=r=>e?n.splitCamelCaseIntoWords(t).has(r):t.toLocaleLowerCase().includes(r);for(let e of i)r(e)&&o({message:`path \`${s}\` should not contain http verb ${e}`,location:a.key()})}}})},4628:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoIdenticalPaths=void 0,t.NoIdenticalPaths=()=>({PathMap(e,{report:t,location:r}){let n=new Map;for(let i of Object.keys(e)){let e=i.replace(/{.+?}/g,"{VARIABLE}"),o=n.get(e);o?t({message:`The path already exists which differs only by path parameter name(s): \`${o}\` and \`${i}\`.`,location:r.child([i]).key()}):n.set(e,i)}}})},1562:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidParameterExamples=void 0;let n=r(780);t.NoInvalidParameterExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Parameter:{leave(e,t){if(e.example&&n.validateExample(e.example,e.schema,t.location.child("example"),t,r),e.examples)for(let[r,i]of Object.entries(e.examples))"value"in i&&n.validateExample(i.value,e.schema,t.location.child(["examples",r]),t,!1)}}}}},78:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoInvalidSchemaExamples=void 0;let n=r(780);t.NoInvalidSchemaExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{Schema:{leave(e,t){if(e.examples)for(let i of e.examples)n.validateExample(i,e,t.location.child(["examples",e.examples.indexOf(i)]),t,r);e.example&&n.validateExample(e.example,e,t.location.child("example"),t,!1)}}}}},700:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoPathTrailingSlash=void 0,t.NoPathTrailingSlash=()=>({PathItem(e,{report:t,key:r,location:n}){r.endsWith("/")&&"/"!==r&&t({message:`\`${r}\` should not have a trailing slash.`,location:n.key()})}})},5946:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation2xxResponse=void 0,t.Operation2xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>"default"===e||/2[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `2xx` response.",location:{reportOnKey:!0}})}})},5281:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Operation4xxResponse=void 0,t.Operation4xxResponse=()=>({ResponsesMap(e,{report:t}){Object.keys(e).some((e=>/4[Xx0-9]{2}/.test(e)))||t({message:"Operation must have at least one `4xx` response.",location:{reportOnKey:!0}})}})},3408:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationDescription=void 0;let n=r(780);t.OperationDescription=()=>({Operation(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},8742:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUnique=void 0,t.OperationIdUnique=()=>{let e=new Set;return{Operation(t,{report:r,location:n}){t.operationId&&(e.has(t.operationId)&&r({message:"Every operation must have a unique `operationId`.",location:n.child([t.operationId])}),e.add(t.operationId))}}}},5064:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationIdUrlSafe=void 0;let r=/^[A-Za-z0-9-._~:/?#\[\]@!\$&'()*+,;=]*$/;t.OperationIdUrlSafe=()=>({Operation(e,{report:t,location:n}){e.operationId&&!r.test(e.operationId)&&t({message:"Operation `operationId` should not have URL invalid characters.",location:n.child(["operationId"])})}})},8786:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationOperationId=void 0;let n=r(780);t.OperationOperationId=()=>({DefinitionRoot:{PathItem:{Operation(e,t){n.validateDefinedAndNonEmpty("operationId",e,t)}}}})},4112:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationParametersUnique=void 0,t.OperationParametersUnique=()=>{let e,t;return{PathItem:{enter(){e=new Set},Parameter(t,{report:r,key:n,parentLocations:i}){let o=`${t.in}___${t.name}`;e.has(o)&&r({message:`Paths must have unique \`name\` + \`in\` parameters.\nRepeats of \`in:${t.in}\` + \`name:${t.name}\`.`,location:i.PathItem.child(["parameters",n])}),e.add(`${t.in}___${t.name}`)},Operation:{enter(){t=new Set},Parameter(e,{report:r,key:n,parentLocations:i}){let o=`${e.in}___${e.name}`;t.has(o)&&r({message:`Operations must have unique \`name\` + \`in\` parameters. Repeats of \`in:${e.in}\` + \`name:${e.name}\`.`,location:i.Operation.child(["parameters",n])}),t.add(o)}}}}}},7892:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSecurityDefined=void 0,t.OperationSecurityDefined=()=>{let e=new Map;return{DefinitionRoot:{leave(t,{report:r}){for(let[t,n]of e.entries())if(!n.defined)for(let e of n.from)r({message:`There is no \`${t}\` security scheme defined.`,location:e.key()})}},SecurityScheme(t,{key:r}){e.set(r.toString(),{defined:!0,from:[]})},SecurityRequirement(t,{location:r}){for(let n of Object.keys(t)){let t=e.get(n),i=r.child([n]);t?t.from.push(i):e.set(n,{from:[i]})}}}}},8613:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSingularTag=void 0,t.OperationSingularTag=()=>({Operation(e,{report:t,location:r}){e.tags&&e.tags.length>1&&t({message:"Operation `tags` object should have only one tag.",location:r.child(["tags"]).key()})}})},9578:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationSummary=void 0;let n=r(780);t.OperationSummary=()=>({Operation(e,t){n.validateDefinedAndNonEmpty("summary",e,t)}})},5097:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OperationTagDefined=void 0,t.OperationTagDefined=()=>{let e;return{DefinitionRoot(t){var r;e=new Set((null!==(r=t.tags)&&void 0!==r?r:[]).map((e=>e.name)))},Operation(t,{report:r,location:n}){if(t.tags)for(let i=0;i({Parameter(e,{report:t,location:r}){void 0===e.description?t({message:"Parameter object description must be present.",location:{reportOnKey:!0}}):e.description||t({message:"Parameter object description must be non-empty string.",location:r.child(["description"])})}})},7890:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathDeclarationMustExist=void 0,t.PathDeclarationMustExist=()=>({PathItem(e,{report:t,key:r}){-1!==r.indexOf("{}")&&t({message:"Path parameter declarations must be non-empty. `{}` is invalid.",location:{reportOnKey:!0}})}})},3689:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathExcludesPatterns=void 0,t.PathExcludesPatterns=({patterns:e})=>({PathItem(t,{report:r,key:n,location:i}){if(!e)throw Error('Parameter "patterns" is not provided for "path-excludes-patterns" rule');let o=n.toString();if(o.startsWith("/")){let t=e.filter((e=>o.match(e)));for(let e of t)r({message:`path \`${o}\` should not match regex pattern: \`${e}\``,location:i.key()})}}})},2332:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathHttpVerbsOrder=void 0;let r=["get","head","post","put","patch","delete","options","trace"];t.PathHttpVerbsOrder=e=>{let t=e&&e.order||r;if(!Array.isArray(t))throw Error("path-http-verbs-order `order` option must be an array");return{PathItem(e,{report:r,location:n}){let i=Object.keys(e).filter((e=>t.includes(e)));for(let e=0;e({PathMap:{PathItem(e,{report:t,key:r}){r.toString().includes("?")&&t({message:"Don't put query string items in the path, they belong in parameters with `in: query`.",location:{reportOnKey:!0}})}}})},7421:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathParamsDefined=void 0;let r=/\{([a-zA-Z0-9_.-]+)\}+/g;t.PathParamsDefined=()=>{let e,t,n;return{PathItem:{enter(i,{key:o}){t=new Set,n=o,e=new Set(Array.from(o.toString().matchAll(r)).map((e=>e[1])))},Parameter(r,{report:i,location:o}){"path"===r.in&&r.name&&(t.add(r.name),e.has(r.name)||i({message:`Path parameter \`${r.name}\` is not used in the path \`${n}\`.`,location:o.child(["name"])}))},Operation:{leave(r,{report:i,location:o}){for(let r of Array.from(e.keys()))t.has(r)||i({message:`The operation does not define the path parameter \`{${r}}\` expected by path \`${n}\`.`,location:o.child(["parameters"]).key()})},Parameter(r,{report:i,location:o}){"path"===r.in&&r.name&&(t.add(r.name),e.has(r.name)||i({message:`Path parameter \`${r.name}\` is not used in the path \`${n}\`.`,location:o.child(["name"])}))}}}}}},3807:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathSegmentPlural=void 0;let n=r(771);t.PathSegmentPlural=e=>{let{ignoreLastPathSegment:t,exceptions:r}=e;return{PathItem:{leave(e,{report:i,key:o,location:a}){let s=o.toString();if(s.startsWith("/")){let e=s.split("/");for(let o of(e.shift(),t&&e.length>1&&e.pop(),e))r&&r.includes(o)||!n.isPathParameter(o)&&n.isSingular(o)&&i({message:`path segment \`${o}\` should be plural.`,location:a.key()})}}}}}},9527:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.PathsKebabCase=void 0,t.PathsKebabCase=()=>({PathItem(e,{report:t,key:r}){r.substr(1).split("/").filter((e=>""!==e)).every((e=>/^{.+}$/.test(e)||/^[a-z0-9-.]+$/.test(e)))||t({message:`\`${r}\` does not use kebab-case.`,location:{reportOnKey:!0}})}})},5839:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsHeader=void 0;let n=r(771);t.ResponseContainsHeader=e=>{let t=e.names||{};return{Operation:{Response:{enter(e,{report:r,location:i,key:o}){var a;let s=t[o]||t[n.getMatchingStatusCodeRange(o)]||t[n.getMatchingStatusCodeRange(o).toLowerCase()]||[];for(let t of s)(null===(a=e.headers)||void 0===a?void 0:a[t])||r({message:`Response object must contain a "${t}" header.`,location:i.child("headers").key()})}}}}}},5669:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ScalarPropertyMissingExample=void 0;let n=r(1510),i=["string","integer","number","boolean","null"];t.ScalarPropertyMissingExample=()=>({SchemaProperties(e,{report:t,location:r,oasVersion:o,resolve:a}){var s;for(let l of Object.keys(e)){let c=a(e[l]).node;c&&(s=c).type&&!(s.allOf||s.anyOf||s.oneOf)&&"binary"!==s.format&&(Array.isArray(s.type)?s.type.every((e=>i.includes(e))):i.includes(s.type))&&void 0===c.example&&void 0===c.examples&&t({message:`Scalar property should have "example"${o===n.OasVersion.Version3_1?' or "examples"':""} defined.`,location:r.child(l).key()})}}})},6471:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.OasSpec=void 0;let n=r(5220),i=r(780),o=r(7468),a=r(771);t.OasSpec=()=>({any(e,{report:t,type:r,location:s,key:l,resolve:c,ignoreNextVisitorsOnNode:u}){var p,d,f,h;let m=i.oasTypeOf(e);if(r.items)return void("array"!==m&&(t({message:`Expected type \`${r.name}\` (array) but got \`${m}\``}),u()));if("object"!==m)return t({message:`Expected type \`${r.name}\` (object) but got \`${m}\``}),void u();let g="function"==typeof r.required?r.required(e,l):r.required;for(let r of g||[])e.hasOwnProperty(r)||t({message:`The field \`${r}\` must be present on this level.`,location:[{reportOnKey:!0}]});let y=null===(p=r.allowed)||void 0===p?void 0:p.call(r,e);if(y&&a.isPlainObject(e))for(let n in e)y.includes(n)||r.extensionsPrefix&&n.startsWith(r.extensionsPrefix)||!Object.keys(r.properties).includes(n)||t({message:`The field \`${n}\` is not allowed here.`,location:s.child([n]).key()});let v=r.requiredOneOf||null;if(v){let n=!1;for(let t of v||[])e.hasOwnProperty(t)&&(n=!0);n||t({message:`Must contain at least one of the following fields: ${null===(d=r.requiredOneOf)||void 0===d?void 0:d.join(", ")}.`,location:[{reportOnKey:!0}]})}for(let a of Object.keys(e)){let l=s.child([a]),u=e[a],p=r.properties[a];if(void 0===p&&(p=r.additionalProperties),"function"==typeof p&&(p=p(u,a)),n.isNamedType(p))continue;let d=p,m=i.oasTypeOf(u);if(void 0!==d){if(null!==d){if(!1!==d.resolvable&&o.isRef(u)&&(u=c(u).node),d.enum)d.enum.includes(u)||t({location:l,message:`\`${a}\` can be one of the following only: ${d.enum.map((e=>`"${e}"`)).join(", ")}.`,suggest:i.getSuggest(u,d.enum)});else if(d.type&&!i.matchesJsonSchemaType(u,d.type,!1))t({message:`Expected type \`${d.type}\` but got \`${m}\`.`,location:l});else if("array"===m&&(null===(f=d.items)||void 0===f?void 0:f.type)){let e=null===(h=d.items)||void 0===h?void 0:h.type;for(let r=0;re[a]&&t({message:`The value of the ${a} field must be greater than or equal to ${d.minimum}`,location:s.child([a])})}}else{if(a.startsWith("x-"))continue;t({message:`Property \`${a}\` is not expected here.`,suggest:i.getSuggest(a,Object.keys(r.properties)),location:l.key()})}}}})},7281:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagDescription=void 0;let n=r(780);t.TagDescription=()=>({Tag(e,t){n.validateDefinedAndNonEmpty("description",e,t)}})},6855:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TagsAlphabetical=void 0,t.TagsAlphabetical=()=>({DefinitionRoot(e,{report:t,location:r}){if(e.tags)for(let n=0;ne.tags[n+1].name&&t({message:"The `tags` array should be in alphabetical order.",location:r.child(["tags",n])})}})},348:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.reportUnresolvedRef=t.NoUnresolvedRefs=void 0;let n=r(4182);function i(e,t,r){var i;let o=e.error;o instanceof n.YamlParseError&&t({message:"Failed to parse: "+o.message,location:{source:o.source,pointer:void 0,start:{col:o.col,line:o.line}}});let a=null===(i=e.error)||void 0===i?void 0:i.message;t({location:r,message:"Can't resolve $ref"+(a?": "+a:"")})}t.NoUnresolvedRefs=()=>({ref:{leave(e,{report:t,location:r},n){void 0===n.node&&i(n,t,r)}},DiscriminatorMapping(e,{report:t,resolve:r,location:n}){for(let o of Object.keys(e)){let a=r({$ref:e[o]});if(void 0!==a.node)return;i(a,t,n.child(o))}}}),t.reportUnresolvedRef=i},9566:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{let t=e.prefixes||["is","has"],r=RegExp(`^(${t.join("|")})[A-Z-_]`),n=t.map((e=>`\`${e}\``)),i=1===n.length?n[0]:n.slice(0,-1).join(", ")+" or "+n[t.length-1];return{Parameter(e,{report:t,location:n}){"boolean"!==e.type||r.test(e.name)||t({message:`Boolean parameter \`${e.name}\` should have ${i} prefix.`,location:n.child("name")})}}}},7523:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;let n=r(6471),i=r(78),o=r(1562),a=r(8675),s=r(8265),l=r(9622),c=r(476),u=r(9566),p=r(7281),d=r(6855),f=r(9527),h=r(2319),m=r(700),g=r(5946),y=r(5281),v=r(4015),b=r(8742),x=r(4112),w=r(7421),k=r(5097),_=r(7890),O=r(5064),S=r(3408),E=r(5023),P=r(3529),A=r(8613),$=r(7892),C=r(348),R=r(2332),j=r(4628),T=r(8786),I=r(9578),N=r(3467),L=r(525),D=r(3689),M=r(7028),F=r(1750),z=r(3807),U=r(5839),V=r(7899),B=r(5669);t.rules={spec:n.OasSpec,"no-invalid-schema-examples":i.NoInvalidSchemaExamples,"no-invalid-parameter-examples":o.NoInvalidParameterExamples,"info-description":a.InfoDescription,"info-contact":s.InfoContact,"info-license":l.InfoLicense,"info-license-url":c.InfoLicenseUrl,"tag-description":p.TagDescription,"tags-alphabetical":d.TagsAlphabetical,"paths-kebab-case":f.PathsKebabCase,"no-enum-type-mismatch":h.NoEnumTypeMismatch,"boolean-parameter-prefixes":u.BooleanParameterPrefixes,"no-path-trailing-slash":m.NoPathTrailingSlash,"operation-2xx-response":g.Operation2xxResponse,"operation-4xx-response":y.Operation4xxResponse,assertions:v.Assertions,"operation-operationId-unique":b.OperationIdUnique,"operation-parameters-unique":x.OperationParametersUnique,"path-parameters-defined":w.PathParamsDefined,"operation-tag-defined":k.OperationTagDefined,"path-declaration-must-exist":_.PathDeclarationMustExist,"operation-operationId-url-safe":O.OperationIdUrlSafe,"operation-operationId":T.OperationOperationId,"operation-summary":I.OperationSummary,"operation-description":S.OperationDescription,"path-not-include-query":E.PathNotIncludeQuery,"path-params-defined":w.PathParamsDefined,"parameter-description":P.ParameterDescription,"operation-singular-tag":A.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"no-identical-paths":j.NoIdenticalPaths,"no-ambiguous-paths":N.NoAmbiguousPaths,"path-http-verbs-order":R.PathHttpVerbsOrder,"no-http-verbs-in-paths":L.NoHttpVerbsInPaths,"path-excludes-patterns":D.PathExcludesPatterns,"request-mime-type":M.RequestMimeType,"response-mime-type":F.ResponseMimeType,"path-segment-plural":z.PathSegmentPlural,"response-contains-header":U.ResponseContainsHeader,"response-contains-property":V.ResponseContainsProperty,"scalar-property-missing-example":B.ScalarPropertyMissingExample},t.preprocessors={}},4508:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;let n=r(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,r,n){var i;e.set(t.absolutePointer,{used:(null===(i=e.get(t.absolutePointer))||void 0===i?void 0:i.used)||!1,componentType:r,name:n})}return{ref:{leave(t,{type:r,resolve:n,key:i}){if(["Schema","Parameter","Response","SecurityScheme"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString()})}}},DefinitionRoot:{leave(t,r){let i=r.getVisitorData();i.removedCount=0;let o=new Set;for(let r of(e.forEach((e=>{let{used:r,name:n,componentType:a}=e;!r&&a&&(o.add(a),delete t[a][n],i.removedCount++)})),o))n.isEmptyObject(t[r])&&delete t[r]}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,"definitions",n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,"parameters",n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,"responses",n.toString())}},NamedSecuritySchemes:{SecurityScheme(e,{location:r,key:n}){t(r,"securityDefinitions",n.toString())}}}}},7028:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;let n=r(771);t.RequestMimeType=({allowedValues:e})=>({DefinitionRoot(t,r){n.validateMimeType({type:"consumes",value:t},r,e)},Operation:{leave(t,r){n.validateMimeType({type:"consumes",value:t},r,e)}}})},7899:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;let n=r(771);t.ResponseContainsProperty=e=>{let t,r=e.names||{};return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter(e,r){t=r.key},Schema(e,{report:i,location:o}){var a;if("object"!==e.type)return;let s=r[t]||r[n.getMatchingStatusCodeRange(t)]||r[n.getMatchingStatusCodeRange(t).toLowerCase()]||[];for(let t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||i({message:`Response object must contain a top-level "${t}" property.`,location:o.child("properties").key()})}}}}}},1750:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;let n=r(771);t.ResponseMimeType=({allowedValues:e})=>({DefinitionRoot(t,r){n.validateMimeType({type:"produces",value:t},r,e)},Operation:{leave(t,r){n.validateMimeType({type:"produces",value:t},r,e)}}})},962:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BooleanParameterPrefixes=void 0,t.BooleanParameterPrefixes=e=>{let t=e.prefixes||["is","has"],r=RegExp(`^(${t.join("|")})[A-Z-_]`),n=t.map((e=>`\`${e}\``)),i=1===n.length?n[0]:n.slice(0,-1).join(", ")+" or "+n[t.length-1];return{Parameter:{Schema(e,{report:t,parentLocations:n},o){"boolean"!==e.type||r.test(o.Parameter.name)||t({message:`Boolean parameter \`${o.Parameter.name}\` should have ${i} prefix.`,location:n.Parameter.child(["name"])})}}}}},226:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.preprocessors=t.rules=void 0;let n=r(6471),i=r(5946),o=r(5281),a=r(4015),s=r(8742),l=r(4112),c=r(7421),u=r(5097),p=r(1265),d=r(2319),f=r(700),h=r(7890),m=r(5064),g=r(6855),y=r(5486),v=r(2947),b=r(8675),x=r(7281),w=r(8265),k=r(9622),_=r(3408),O=r(897),S=r(5023),E=r(3529),P=r(8613),A=r(476),$=r(7892),C=r(348),R=r(962),j=r(9527),T=r(2332),I=r(7020),N=r(9336),L=r(4628),D=r(6208),M=r(8786),F=r(9578),z=r(3467),U=r(472),V=r(525),B=r(3736),q=r(503),W=r(3807),H=r(3689),Y=r(78),K=r(1562),G=r(5839),Q=r(7557),X=r(5669);t.rules={spec:n.OasSpec,"info-description":b.InfoDescription,"info-contact":w.InfoContact,"info-license":k.InfoLicense,"info-license-url":A.InfoLicenseUrl,"operation-2xx-response":i.Operation2xxResponse,"operation-4xx-response":o.Operation4xxResponse,assertions:a.Assertions,"operation-operationId-unique":s.OperationIdUnique,"operation-parameters-unique":l.OperationParametersUnique,"path-parameters-defined":c.PathParamsDefined,"operation-tag-defined":u.OperationTagDefined,"no-example-value-and-externalValue":p.NoExampleValueAndExternalValue,"no-enum-type-mismatch":d.NoEnumTypeMismatch,"no-path-trailing-slash":f.NoPathTrailingSlash,"no-empty-servers":I.NoEmptyServers,"path-declaration-must-exist":h.PathDeclarationMustExist,"operation-operationId-url-safe":m.OperationIdUrlSafe,"operation-operationId":M.OperationOperationId,"operation-summary":F.OperationSummary,"tags-alphabetical":g.TagsAlphabetical,"no-server-example.com":y.NoServerExample,"no-server-trailing-slash":v.NoServerTrailingSlash,"tag-description":x.TagDescription,"operation-description":_.OperationDescription,"no-unused-components":O.NoUnusedComponents,"path-not-include-query":S.PathNotIncludeQuery,"path-params-defined":c.PathParamsDefined,"parameter-description":E.ParameterDescription,"operation-singular-tag":P.OperationSingularTag,"operation-security-defined":$.OperationSecurityDefined,"no-unresolved-refs":C.NoUnresolvedRefs,"paths-kebab-case":j.PathsKebabCase,"boolean-parameter-prefixes":R.BooleanParameterPrefixes,"path-http-verbs-order":T.PathHttpVerbsOrder,"no-invalid-media-type-examples":N.ValidContentExamples,"no-identical-paths":L.NoIdenticalPaths,"no-ambiguous-paths":z.NoAmbiguousPaths,"no-undefined-server-variable":D.NoUndefinedServerVariable,"no-servers-empty-enum":U.NoEmptyEnumServers,"no-http-verbs-in-paths":V.NoHttpVerbsInPaths,"path-excludes-patterns":H.PathExcludesPatterns,"request-mime-type":B.RequestMimeType,"response-mime-type":q.ResponseMimeType,"path-segment-plural":W.PathSegmentPlural,"no-invalid-schema-examples":Y.NoInvalidSchemaExamples,"no-invalid-parameter-examples":K.NoInvalidParameterExamples,"response-contains-header":G.ResponseContainsHeader,"response-contains-property":Q.ResponseContainsProperty,"scalar-property-missing-example":X.ScalarPropertyMissingExample},t.preprocessors={}},7020:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyServers=void 0,t.NoEmptyServers=()=>({DefinitionRoot(e,{report:t,location:r}){e.hasOwnProperty("servers")?Array.isArray(e.servers)&&0!==e.servers.length||t({message:"Servers must be a non-empty array.",location:r.child(["servers"]).key()}):t({message:"Servers must be present.",location:r.child(["openapi"]).key()})}})},1265:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoExampleValueAndExternalValue=void 0,t.NoExampleValueAndExternalValue=()=>({Example(e,{report:t,location:r}){e.value&&e.externalValue&&t({message:"Example object can have either `value` or `externalValue` fields.",location:r.child(["value"]).key()})}})},9336:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ValidContentExamples=void 0;let n=r(7468),i=r(780);t.ValidContentExamples=e=>{var t;let r=null===(t=e.disallowAdditionalProperties)||void 0===t||t;return{MediaType:{leave(e,t){let{location:o,resolve:a}=t;if(e.schema)if(e.example)s(e.example,o.child("example"));else if(e.examples)for(let t of Object.keys(e.examples))s(e.examples[t],o.child(["examples",t,"value"]),!0);function s(o,s,l){if(n.isRef(o)){let e=a(o);if(!e.location)return;s=l?e.location.child("value"):e.location,o=e.node}i.validateExample(l?o.value:o,e.schema,s,t,r)}}}}}},5486:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerExample=void 0,t.NoServerExample=()=>({Server(e,{report:t,location:r}){-1!==["example.com","localhost"].indexOf(e.url)&&t({message:"Server `url` should not point at example.com.",location:r.child(["url"])})}})},2947:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoServerTrailingSlash=void 0,t.NoServerTrailingSlash=()=>({Server(e,{report:t,location:r}){e.url&&e.url.endsWith("/")&&"/"!==e.url&&t({message:"Server `url` should not have a trailing slash.",location:r.child(["url"])})}})},472:function(e,t){"use strict";var r,n;function i(e){var t;if(e.variables&&0===Object.keys(e.variables).length)return;let n=[];for(var i in e.variables){let o=e.variables[i];if(!o.enum||(Array.isArray(o.enum)&&0===(null===(t=o.enum)||void 0===t?void 0:t.length)&&n.push(r.empty),!o.default))continue;let a=e.variables[i].default;o.enum&&!o.enum.includes(a)&&n.push(r.invalidDefaultValue)}return n.length?n:void 0}Object.defineProperty(t,"__esModule",{value:!0}),t.NoEmptyEnumServers=void 0,(n=r||(r={})).empty="empty",n.invalidDefaultValue="invalidDefaultValue",t.NoEmptyEnumServers=()=>({DefinitionRoot(e,{report:t,location:n}){if(!e.servers||0===e.servers.length)return;let o=[];if(Array.isArray(e.servers))for(let t of e.servers){let e=i(t);e&&o.push(...e)}else{let t=i(e.servers);if(!t)return;o.push(...t)}for(let e of o)e===r.empty&&t({message:"Server variable with `enum` must be a non-empty array.",location:n.child(["servers"]).key()}),e===r.invalidDefaultValue&&t({message:"Server variable define `enum` and `default`. `enum` must include default value",location:n.child(["servers"]).key()})}})},6208:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUndefinedServerVariable=void 0,t.NoUndefinedServerVariable=()=>({Server(e,{report:t,location:r}){var n;if(!e.url)return;let i=(null===(n=e.url.match(/{[^}]+}/g))||void 0===n?void 0:n.map((e=>e.slice(1,e.length-1))))||[],o=(null==e?void 0:e.variables)&&Object.keys(e.variables)||[];for(let e of i)o.includes(e)||t({message:`The \`${e}\` variable is not defined in the \`variables\` objects.`,location:r.child(["url"])});for(let e of o)i.includes(e)||t({message:`The \`${e}\` variable is not used in the server's \`url\` field.`,location:r.child(["variables",e]).key(),from:r.child("url")})}})},897:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.NoUnusedComponents=void 0,t.NoUnusedComponents=()=>{let e=new Map;function t(t,r){var n;e.set(t.absolutePointer,{used:(null===(n=e.get(t.absolutePointer))||void 0===n?void 0:n.used)||!1,location:t,name:r})}return{ref(t,{type:r,resolve:n,key:i,location:o}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString(),location:o})}},DefinitionRoot:{leave(t,{report:r}){e.forEach((e=>{e.used||r({message:`Component: "${e.name}" is never used.`,location:e.location.key()})}))}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,n.toString())}},NamedExamples:{Example(e,{location:r,key:n}){t(r,n.toString())}},NamedRequestBodies:{RequestBody(e,{location:r,key:n}){t(r,n.toString())}},NamedHeaders:{Header(e,{location:r,key:n}){t(r,n.toString())}}}}},6350:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RemoveUnusedComponents=void 0;let n=r(771);t.RemoveUnusedComponents=()=>{let e=new Map;function t(t,r,n){var i;e.set(t.absolutePointer,{used:(null===(i=e.get(t.absolutePointer))||void 0===i?void 0:i.used)||!1,componentType:r,name:n})}return{ref:{leave(t,{type:r,resolve:n,key:i}){if(["Schema","Header","Parameter","Response","Example","RequestBody"].includes(r.name)){let r=n(t);r.location&&e.set(r.location.absolutePointer,{used:!0,name:i.toString()})}}},DefinitionRoot:{leave(t,r){let i=r.getVisitorData();i.removedCount=0,e.forEach((e=>{let{used:r,componentType:o,name:a}=e;if(!r&&o){let e=t.components[o];delete e[a],i.removedCount++,n.isEmptyObject(e)&&delete t.components[o]}})),n.isEmptyObject(t.components)&&delete t.components}},NamedSchemas:{Schema(e,{location:r,key:n}){e.allOf||t(r,"schemas",n.toString())}},NamedParameters:{Parameter(e,{location:r,key:n}){t(r,"parameters",n.toString())}},NamedResponses:{Response(e,{location:r,key:n}){t(r,"responses",n.toString())}},NamedExamples:{Example(e,{location:r,key:n}){t(r,"examples",n.toString())}},NamedRequestBodies:{RequestBody(e,{location:r,key:n}){t(r,"requestBodies",n.toString())}},NamedHeaders:{Header(e,{location:r,key:n}){t(r,"headers",n.toString())}}}}},3736:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.RequestMimeType=void 0;let n=r(771);t.RequestMimeType=({allowedValues:e})=>({PathMap:{RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}},Callback:{RequestBody(){},Response:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}}}},WebhooksMap:{Response:{leave(t,r){n.validateMimeTypeOAS3({type:"consumes",value:t},r,e)}}}})},7557:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseContainsProperty=void 0;let n=r(771);t.ResponseContainsProperty=e=>{let t,r=e.names||{};return{Operation:{Response:{skip:(e,t)=>"204"==`${t}`,enter(e,r){t=r.key},MediaType:{Schema(e,{report:i,location:o}){var a;if("object"!==e.type)return;let s=r[t]||r[n.getMatchingStatusCodeRange(t)]||r[n.getMatchingStatusCodeRange(t).toLowerCase()]||[];for(let t of s)(null===(a=e.properties)||void 0===a?void 0:a[t])||i({message:`Response object must contain a top-level "${t}" property.`,location:o.child("properties").key()})}}}}}}},503:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ResponseMimeType=void 0;let n=r(771);t.ResponseMimeType=({allowedValues:e})=>({PathMap:{Response:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}},Callback:{Response(){},RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}}}},WebhooksMap:{RequestBody:{leave(t,r){n.validateMimeTypeOAS3({type:"produces",value:t},r,e)}}}})},780:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.validateExample=t.getSuggest=t.validateDefinedAndNonEmpty=t.fieldNonEmpty=t.missingRequiredField=t.matchesJsonSchemaType=t.oasTypeOf=void 0;let n=r(9991),i=r(7468),o=r(7275);function a(e,t){return`${e} object should contain \`${t}\` field.`}function s(e,t){return`${e} object \`${t}\` must be non-empty string.`}t.oasTypeOf=function(e){return Array.isArray(e)?"array":null===e?"null":typeof e},t.matchesJsonSchemaType=function(e,t,r){if(r&&null===e)return null===e;switch(t){case"array":return Array.isArray(e);case"object":return"object"==typeof e&&null!==e&&!Array.isArray(e);case"null":return null===e;case"integer":return Number.isInteger(e);default:return typeof e===t}},t.missingRequiredField=a,t.fieldNonEmpty=s,t.validateDefinedAndNonEmpty=function(e,t,r){"object"==typeof t&&(void 0===t[e]?r.report({message:a(r.type.name,e),location:r.location.child([e]).key()}):t[e]||r.report({message:s(r.type.name,e),location:r.location.child([e]).key()}))},t.getSuggest=function(e,t){if("string"!=typeof e||!t.length)return[];let r=[];for(let i=0;ie.distance-t.distance)),r.map((e=>e.variant))},t.validateExample=function(e,t,r,{resolve:n,location:a,report:s},l){try{let{valid:c,errors:u}=o.validateJsonSchema(e,t,a.child("schema"),r.pointer,n,l);if(!c)for(let e of u)s({message:`Example value must conform to the schema: ${e.message}.`,location:Object.assign(Object.assign({},new i.Location(r.source,e.instancePath)),{reportOnKey:"additionalProperties"===e.keyword}),from:a,suggest:e.suggest})}catch(e){s({message:`Example validation errored: ${e.message}.`,location:a.child("schema"),from:a})}}},5220:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isNamedType=t.normalizeTypes=t.mapOf=t.listOf=void 0,t.listOf=function(e){return{name:`${e}List`,properties:{},items:e}},t.mapOf=function(e){return{name:`${e}Map`,properties:{},additionalProperties:()=>e}},t.normalizeTypes=function(e,t={}){let r={};for(let t of Object.keys(e))r[t]=Object.assign(Object.assign({},e[t]),{name:t});for(let e of Object.values(r))n(e);return r;function n(e){if(e.additionalProperties&&(e.additionalProperties=i(e.additionalProperties)),e.items&&(e.items=i(e.items)),e.properties){let r={};for(let[n,o]of Object.entries(e.properties))r[n]=i(o),t.doNotResolveExamples&&o&&o.isExample&&(r[n]=Object.assign(Object.assign({},o),{resolvable:!1}));e.properties=r}}function i(e){if("string"==typeof e){if(!r[e])throw Error(`Unknown type name found: ${e}`);return r[e]}return"function"==typeof e?(t,r)=>i(e(t,r)):e&&e.name?(n(e=Object.assign({},e)),e):e&&e.directResolveAs?Object.assign(Object.assign({},e),{directResolveAs:i(e.directResolveAs)}):e}},t.isNamedType=function(e){return"string"==typeof(null==e?void 0:e.name)}},388:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas2Types=void 0;let n=r(5220),i=/^[0-9][0-9Xx]{2}$/,o={properties:{swagger:{type:"string"},info:"Info",host:{type:"string"},basePath:{type:"string"},schemes:{type:"array",items:{type:"string"}},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},paths:"PathMap",definitions:"NamedSchemas",parameters:"NamedParameters",responses:"NamedResponses",securityDefinitions:"NamedSecuritySchemes",security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs"},required:["swagger","paths","info"]},a={properties:{$ref:{type:"string"},parameters:n.listOf("Parameter"),get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation"}},s={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},consumes:{type:"array",items:{type:"string"}},produces:{type:"array",items:{type:"string"}},parameters:n.listOf("Parameter"),responses:"ResponsesMap",schemes:{type:"array",items:{type:"string"}},deprecated:{type:"boolean"},security:n.listOf("SecurityRequirement"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},l={properties:{description:{type:"string"},schema:"Schema",headers:n.mapOf("Header"),examples:"Examples"},required:["description"]},c={properties:{format:{type:"string"},title:{type:"string"},description:{type:"string"},default:null,multipleOf:{type:"number"},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"number"},minLength:{type:"number"},pattern:{type:"string"},maxItems:{type:"number"},minItems:{type:"number"},uniqueItems:{type:"boolean"},maxProperties:{type:"number"},minProperties:{type:"number"},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{type:"string",enum:["object","array","string","number","integer","boolean","null"]},items:e=>Array.isArray(e)?n.listOf("Schema"):"Schema",allOf:n.listOf("Schema"),properties:"SchemaProperties",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",discriminator:{type:"string"},readOnly:{type:"boolean"},xml:"Xml",externalDocs:"ExternalDocs",example:{isExample:!0},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas2Types={DefinitionRoot:o,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License",version:{type:"string"}},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:a,Parameter:{properties:{name:{type:"string"},in:{type:"string",enum:["query","header","path","formData","body"]},description:{type:"string"},required:{type:"boolean"},schema:"Schema",type:{type:"string",enum:["string","number","integer","boolean","array","file"]},format:{type:"string"},allowEmptyValue:{type:"boolean"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&e.in?"body"===e.in?["name","in","schema"]:"array"===e.type?["name","in","type","items"]:["name","in","type"]:["name","in"]},ParameterItems:{properties:{type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},Operation:s,Examples:{properties:{},additionalProperties:{isExample:!0}},Header:{properties:{description:{type:"string"},type:{type:"string",enum:["string","number","integer","boolean","array"]},format:{type:"string"},items:"ParameterItems",collectionFormat:{type:"string",enum:["csv","ssv","tsv","pipes","multi"]},default:null,maximum:{type:"integer"},exclusiveMaximum:{type:"boolean"},minimum:{type:"integer"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer"},minLength:{type:"integer"},pattern:{type:"string"},maxItems:{type:"integer"},minItems:{type:"integer"},uniqueItems:{type:"boolean"},enum:{type:"array"},multipleOf:{type:"number"}},required:e=>e&&"array"===e.type?["type","items"]:["type"]},ResponsesMap:{properties:{default:"Response"},additionalProperties:(e,t)=>i.test(t)?"Response":void 0},Response:l,Schema:c,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},NamedSchemas:n.mapOf("Schema"),NamedResponses:n.mapOf("Response"),NamedParameters:n.mapOf("Parameter"),NamedSecuritySchemes:n.mapOf("SecurityScheme"),SecurityScheme:{properties:{type:{enum:["basic","apiKey","oauth2"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header"]},flow:{enum:["implicit","password","application","accessCode"]},authorizationUrl:{type:"string"},tokenUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","scopes"];case"application":case"password":return["type","flow","tokenUrl","scopes"];default:return["type","flow","scopes"]}default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"basic":return["type","description"];case"apiKey":return["type","name","in","description"];case"oauth2":switch(null==e?void 0:e.flow){case"implicit":return["type","flow","authorizationUrl","description","scopes"];case"accessCode":return["type","flow","authorizationUrl","tokenUrl","description","scopes"];case"application":case"password":return["type","flow","tokenUrl","description","scopes"];default:return["type","flow","tokenUrl","authorizationUrl","description","scopes"]}default:return["type","description"]}},extensionsPrefix:"x-"},XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}}}},5241:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3Types=void 0;let n=r(5220),i=r(7468),o=/^[0-9][0-9Xx]{2}$/,a={properties:{openapi:null,info:"Info",servers:n.listOf("Server"),security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",components:"Components","x-webhooks":"WebhooksMap"},required:["openapi","paths","info"]},s={properties:{url:{type:"string"},description:{type:"string"},variables:n.mapOf("ServerVariable")},required:["url"]},l={properties:{$ref:{type:"string"},servers:n.listOf("Server"),parameters:n.listOf("Parameter"),summary:{type:"string"},description:{type:"string"},get:"Operation",put:"Operation",post:"Operation",delete:"Operation",options:"Operation",head:"Operation",patch:"Operation",trace:"Operation"}},c={properties:{name:{type:"string"},in:{enum:["query","header","path","cookie"]},description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),content:"MediaTypeMap"},required:["name","in"],requiredOneOf:["schema","content"]},u={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:n.listOf("Parameter"),security:n.listOf("SecurityRequirement"),servers:n.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:n.mapOf("Callback"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}},required:["responses"]},p={properties:{schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),encoding:n.mapOf("Encoding")}},d={properties:{contentType:{type:"string"},headers:n.mapOf("Header"),style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"}}},f={properties:{description:{type:"string"},required:{type:"boolean"},deprecated:{type:"boolean"},allowEmptyValue:{type:"boolean"},style:{enum:["form","simple","label","matrix","spaceDelimited","pipeDelimited","deepObject"]},explode:{type:"boolean"},allowReserved:{type:"boolean"},schema:"Schema",example:{isExample:!0},examples:n.mapOf("Example"),content:"MediaTypeMap"}},h={properties:{description:{type:"string"},headers:n.mapOf("Header"),content:"MediaTypeMap",links:n.mapOf("Link")},required:["description"]},m={properties:{externalDocs:"ExternalDocs",discriminator:"Discriminator",title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"boolean"},exclusiveMinimum:{type:"boolean"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:{enum:["object","array","string","number","integer","boolean","null"]},allOf:n.listOf("Schema"),anyOf:n.listOf("Schema"),oneOf:n.listOf("Schema"),not:"Schema",properties:"SchemaProperties",items:e=>Array.isArray(e)?n.listOf("Schema"):"Schema",additionalItems:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},default:null,nullable:{type:"boolean"},readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",example:{isExample:!0},deprecated:{type:"boolean"},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas3Types={DefinitionRoot:a,Tag:{properties:{name:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs"},required:["name"]},ExternalDocs:{properties:{description:{type:"string"},url:{type:"string"}},required:["url"]},Server:s,ServerVariable:{properties:{enum:{type:"array",items:{type:"string"}},default:{type:"string"},description:null},required:["default"]},SecurityRequirement:{properties:{},additionalProperties:{type:"array",items:{type:"string"}}},Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},Contact:{properties:{name:{type:"string"},url:{type:"string"},email:{type:"string"}}},License:{properties:{name:{type:"string"},url:{type:"string"}},required:["name"]},PathMap:{properties:{},additionalProperties:(e,t)=>t.startsWith("/")?"PathItem":void 0},PathItem:l,Parameter:c,Operation:u,Callback:n.mapOf("PathItem"),RequestBody:{properties:{description:{type:"string"},required:{type:"boolean"},content:"MediaTypeMap"},required:["content"]},MediaTypeMap:{properties:{},additionalProperties:"MediaType"},MediaType:p,Example:{properties:{value:{isExample:!0},summary:{type:"string"},description:{type:"string"},externalValue:{type:"string"}}},Encoding:d,Header:f,ResponsesMap:{properties:{default:"Response"},additionalProperties:(e,t)=>o.test(t)?"Response":void 0},Response:h,Link:{properties:{operationRef:{type:"string"},operationId:{type:"string"},parameters:null,requestBody:null,description:{type:"string"},server:"Server"}},Schema:m,Xml:{properties:{name:{type:"string"},namespace:{type:"string"},prefix:{type:"string"},attribute:{type:"boolean"},wrapped:{type:"boolean"}}},SchemaProperties:{properties:{},additionalProperties:"Schema"},DiscriminatorMapping:{properties:{},additionalProperties:e=>i.isMappingRef(e)?{type:"string",directResolveAs:"Schema"}:{type:"string"}},Discriminator:{properties:{propertyName:{type:"string"},mapping:"DiscriminatorMapping"},required:["propertyName"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks"}},NamedSchemas:n.mapOf("Schema"),NamedResponses:n.mapOf("Response"),NamedParameters:n.mapOf("Parameter"),NamedExamples:n.mapOf("Example"),NamedRequestBodies:n.mapOf("RequestBody"),NamedHeaders:n.mapOf("Header"),NamedSecuritySchemes:n.mapOf("SecurityScheme"),NamedLinks:n.mapOf("Link"),NamedCallbacks:n.mapOf("Callback"),ImplicitFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},authorizationUrl:{type:"string"}},required:["authorizationUrl","scopes"]},PasswordFlow:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},ClientCredentials:{properties:{refreshUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["tokenUrl","scopes"]},AuthorizationCode:{properties:{refreshUrl:{type:"string"},authorizationUrl:{type:"string"},scopes:{type:"object",additionalProperties:{type:"string"}},tokenUrl:{type:"string"}},required:["authorizationUrl","tokenUrl","scopes"]},SecuritySchemeFlows:{properties:{implicit:"ImplicitFlow",password:"PasswordFlow",clientCredentials:"ClientCredentials",authorizationCode:"AuthorizationCode"}},SecurityScheme:{properties:{type:{enum:["apiKey","http","oauth2","openIdConnect"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":return["type","flows","description"];case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"},XCodeSample:{properties:{lang:{type:"string"},label:{type:"string"},source:{type:"string"}}},WebhooksMap:{properties:{},additionalProperties:()=>"PathItem"}}},2608:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Oas3_1Types=void 0;let n=r(5220),i=r(5241),o={properties:{openapi:null,info:"Info",servers:n.listOf("Server"),security:n.listOf("SecurityRequirement"),tags:n.listOf("Tag"),externalDocs:"ExternalDocs",paths:"PathMap",webhooks:"WebhooksMap",components:"Components",jsonSchemaDialect:{type:"string"}},required:["openapi","info"],requiredOneOf:["paths","components","webhooks"]},a={properties:{tags:{type:"array",items:{type:"string"}},summary:{type:"string"},description:{type:"string"},externalDocs:"ExternalDocs",operationId:{type:"string"},parameters:n.listOf("Parameter"),security:n.listOf("SecurityRequirement"),servers:n.listOf("Server"),requestBody:"RequestBody",responses:"ResponsesMap",deprecated:{type:"boolean"},callbacks:n.mapOf("Callback"),"x-codeSamples":n.listOf("XCodeSample"),"x-code-samples":n.listOf("XCodeSample"),"x-hideTryItPanel":{type:"boolean"}}},s={properties:{$id:{type:"string"},id:{type:"string"},$schema:{type:"string"},definitions:"NamedSchemas",$defs:"NamedSchemas",$vocabulary:{type:"string"},externalDocs:"ExternalDocs",discriminator:"Discriminator",myArbitraryKeyword:{type:"boolean"},title:{type:"string"},multipleOf:{type:"number",minimum:0},maximum:{type:"number"},minimum:{type:"number"},exclusiveMaximum:{type:"number"},exclusiveMinimum:{type:"number"},maxLength:{type:"integer",minimum:0},minLength:{type:"integer",minimum:0},pattern:{type:"string"},maxItems:{type:"integer",minimum:0},minItems:{type:"integer",minimum:0},uniqueItems:{type:"boolean"},maxProperties:{type:"integer",minimum:0},minProperties:{type:"integer",minimum:0},required:{type:"array",items:{type:"string"}},enum:{type:"array"},type:e=>Array.isArray(e)?{type:"array",items:{enum:["object","array","string","number","integer","boolean","null"]}}:{enum:["object","array","string","number","integer","boolean","null"]},allOf:n.listOf("Schema"),anyOf:n.listOf("Schema"),oneOf:n.listOf("Schema"),not:"Schema",if:"Schema",then:"Schema",else:"Schema",dependentSchemas:n.listOf("Schema"),prefixItems:n.listOf("Schema"),contains:"Schema",minContains:{type:"integer",minimum:0},maxContains:{type:"integer",minimum:0},patternProperties:{type:"object"},propertyNames:"Schema",unevaluatedItems:"Schema",unevaluatedProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",summary:{type:"string"},properties:"SchemaProperties",items:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",additionalProperties:e=>"boolean"==typeof e?{type:"boolean"}:"Schema",description:{type:"string"},format:{type:"string"},contentEncoding:{type:"string"},contentMediaType:{type:"string"},default:null,readOnly:{type:"boolean"},writeOnly:{type:"boolean"},xml:"Xml",examples:{type:"array"},example:{isExample:!0},deprecated:{type:"boolean"},const:null,$comment:{type:"string"},"x-tags":{type:"array",items:{type:"string"}}}};t.Oas3_1Types=Object.assign(Object.assign({},i.Oas3Types),{Info:{properties:{title:{type:"string"},version:{type:"string"},description:{type:"string"},termsOfService:{type:"string"},summary:{type:"string"},contact:"Contact",license:"License"},required:["title","version"]},DefinitionRoot:o,Schema:s,License:{properties:{name:{type:"string"},url:{type:"string"},identifier:{type:"string"}},required:["name"]},Components:{properties:{parameters:"NamedParameters",schemas:"NamedSchemas",responses:"NamedResponses",examples:"NamedExamples",requestBodies:"NamedRequestBodies",headers:"NamedHeaders",securitySchemes:"NamedSecuritySchemes",links:"NamedLinks",callbacks:"NamedCallbacks",pathItems:"NamedPathItems"}},NamedPathItems:n.mapOf("PathItem"),SecurityScheme:{properties:{type:{enum:["apiKey","http","oauth2","openIdConnect","mutualTLS"]},description:{type:"string"},name:{type:"string"},in:{type:"string",enum:["query","header","cookie"]},scheme:{type:"string"},bearerFormat:{type:"string"},flows:"SecuritySchemeFlows",openIdConnectUrl:{type:"string"}},required(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in"];case"http":return["type","scheme"];case"oauth2":return["type","flows"];case"openIdConnect":return["type","openIdConnectUrl"];default:return["type"]}},allowed(e){switch(null==e?void 0:e.type){case"apiKey":return["type","name","in","description"];case"http":return["type","scheme","bearerFormat","description"];case"oauth2":switch(null==e?void 0:e.flows){case"implicit":return["type","flows","authorizationUrl","refreshUrl","description","scopes"];case"password":case"clientCredentials":return["type","flows","tokenUrl","refreshUrl","description","scopes"];default:return["type","flows","authorizationUrl","refreshUrl","tokenUrl","description","scopes"]}case"openIdConnect":return["type","openIdConnectUrl","description"];default:return["type","description"]}},extensionsPrefix:"x-"},Operation:a})},771:function(e,t,r){"use strict";var n=this&&this.__awaiter||function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function a(e){try{l(n.next(e))}catch(e){o(e)}}function s(e){try{l(n.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?i(e.value):((t=e.value)instanceof r?t:new r((function(e){e(t)}))).then(a,s)}l((n=n.apply(e,t||[])).next())}))};Object.defineProperty(t,"__esModule",{value:!0}),t.isCustomRuleId=t.getMatchingStatusCodeRange=t.assignExisting=t.isNotString=t.isString=t.isNotEmptyObject=t.slash=t.isPathParameter=t.readFileAsStringSync=t.isSingular=t.validateMimeTypeOAS3=t.validateMimeType=t.splitCamelCaseIntoWords=t.omitObjectProps=t.pickObjectProps=t.readFileFromUrl=t.isEmptyArray=t.isEmptyObject=t.isPlainObject=t.notUndefined=t.loadYaml=t.popStack=t.pushStack=t.stringifyYaml=t.parseYaml=void 0;let i=r(3197),o=r(4099),a=r(8150),s=r(3450),l=r(5273),c=r(8698);var u=r(5273);function p(e){return null!==e&&"object"==typeof e&&!Array.isArray(e)}function d(e,t){return t.match(/^https?:\/\//)||(e=e.replace(/^https?:\/\//,"")),o(e,t)}function f(e){return"string"==typeof e}Object.defineProperty(t,"parseYaml",{enumerable:!0,get:function(){return u.parseYaml}}),Object.defineProperty(t,"stringifyYaml",{enumerable:!0,get:function(){return u.stringifyYaml}}),t.pushStack=function(e,t){return{prev:e,value:t}},t.popStack=function(e){var t;return null!==(t=null==e?void 0:e.prev)&&void 0!==t?t:null},t.loadYaml=function(e){return n(this,void 0,void 0,(function*(){let t=yield i.promises.readFile(e,"utf-8");return l.parseYaml(t)}))},t.notUndefined=function(e){return void 0!==e},t.isPlainObject=p,t.isEmptyObject=function(e){return p(e)&&0===Object.keys(e).length},t.isEmptyArray=function(e){return Array.isArray(e)&&0===e.length},t.readFileFromUrl=function(e,t){return n(this,void 0,void 0,(function*(){let r={};for(let n of t.headers)d(e,n.matches)&&(r[n.name]=void 0!==n.envVariable?c.env[n.envVariable]||"":n.value);let n=yield(t.customFetch||a.default)(e,{headers:r});if(!n.ok)throw Error(`Failed to load ${e}: ${n.status} ${n.statusText}`);return{body:yield n.text(),mimeType:n.headers.get("content-type")}}))},t.pickObjectProps=function(e,t){return Object.fromEntries(t.filter((t=>t in e)).map((t=>[t,e[t]])))},t.omitObjectProps=function(e,t){return Object.fromEntries(Object.entries(e).filter((([e])=>!t.includes(e))))},t.splitCamelCaseIntoWords=function(e){let t=e.split(/(?:[-._])|([A-Z][a-z]+)/).filter(Boolean).map((e=>e.toLocaleLowerCase())),r=e.split(/([A-Z]{2,})/).filter((e=>e&&e===e.toUpperCase())).map((e=>e.toLocaleLowerCase()));return new Set([...t,...r])},t.validateMimeType=function({type:e,value:t},{report:r,location:n},i){if(!i)throw Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t[e])for(let o of t[e])i.includes(o)||r({message:`Mime type "${o}" is not allowed`,location:n.child(t[e].indexOf(o)).key()})},t.validateMimeTypeOAS3=function({type:e,value:t},{report:r,location:n},i){if(!i)throw Error(`Parameter "allowedValues" is not provided for "${"consumes"===e?"request":"response"}-mime-type" rule`);if(t.content)for(let e of Object.keys(t.content))i.includes(e)||r({message:`Mime type "${e}" is not allowed`,location:n.child("content").child(e).key()})},t.isSingular=function(e){return s.isSingular(e)},t.readFileAsStringSync=function(e){return i.readFileSync(e,"utf-8")},t.isPathParameter=function(e){return e.startsWith("{")&&e.endsWith("}")},t.slash=function(e){return/^\\\\\?\\/.test(e)?e:e.replace(/\\/g,"/")},t.isNotEmptyObject=function(e){return!!e&&Object.keys(e).length>0},t.isString=f,t.isNotString=function(e){return!f(e)},t.assignExisting=function(e,t){for(let r of Object.keys(t))e.hasOwnProperty(r)&&(e[r]=t[r])},t.getMatchingStatusCodeRange=e=>`${e}`.replace(/^(\d)\d\d$/,((e,t)=>`${t}XX`)),t.isCustomRuleId=function(e){return e.includes("/")}},8065:function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.normalizeVisitors=void 0,t.normalizeVisitors=function(e,t){let r={any:{enter:[],leave:[]}};for(let e of Object.keys(t))r[e]={enter:[],leave:[]};for(let{ruleId:t,severity:n,visitor:o}of(r.ref={enter:[],leave:[]},e))i({ruleId:t,severity:n},o,null);for(let e of Object.keys(r))r[e].enter.sort(((e,t)=>t.depth-e.depth)),r[e].leave.sort(((e,t)=>e.depth-t.depth));return r;function n(e,t,i,o,a=[]){if(a.includes(t))return;a=[...a,t];let s=new Set;for(let r of Object.values(t.properties))r!==i?"object"==typeof r&&null!==r&&r.name&&s.add(r):l(e,a);for(let r of(t.additionalProperties&&"function"!=typeof t.additionalProperties&&(t.additionalProperties===i?l(e,a):void 0!==t.additionalProperties.name&&s.add(t.additionalProperties)),t.items&&(t.items===i?l(e,a):void 0!==t.items.name&&s.add(t.items)),Array.from(s.values())))n(e,r,i,o,a);function l(e,t){for(let n of t.slice(1))r[n.name]=r[n.name]||{enter:[],leave:[]},r[n.name].enter.push(Object.assign(Object.assign({},e),{visit(){},depth:0,context:{isSkippedLevel:!0,seen:new Set,parent:o}}))}}function i(e,o,a,s=0){let l=Object.keys(t);if(0===s)l.push("any"),l.push("ref");else{if(o.any)throw Error("any() is allowed only on top level");if(o.ref)throw Error("ref() is allowed only on top level")}for(let c of l){let l=o[c],u=r[c];if(!l)continue;let p,d,f,h="object"==typeof l;if("ref"===c&&h&&l.skip)throw Error("ref() visitor does not support skip");"function"==typeof l?p=l:h&&(p=l.enter,d=l.leave,f=l.skip);let m={activatedOn:null,type:t[c],parent:a,isSkippedLevel:!1};if("object"==typeof l&&i(e,l,m,s+1),a&&n(e,a.type,t[c],a),p||h){if(p&&"function"!=typeof p)throw Error("DEV: should be function");u.enter.push(Object.assign(Object.assign({},e),{visit:p||(()=>{}),skip:f,depth:s,context:m}))}if(d){if("function"!=typeof d)throw Error("DEV: should be function");u.leave.push(Object.assign(Object.assign({},e),{visit:d,depth:s,context:m}))}}}}},9443:function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.walkDocument=void 0;let n=r(7468),i=r(4182),o=r(771),a=r(5220);t.walkDocument=function(e){let{document:t,rootType:r,normalizedVisitors:s,resolvedRefMap:l,ctx:c}=e,u={},p=new Set;!function e(t,r,d,f,h){var m,g,y,v,b,x,w,k,_,O,S;let E=(e,t=A.source.absoluteRef)=>{if(!n.isRef(e))return{location:d,node:e};let r=i.makeRefId(t,e.$ref),o=l.get(r);if(!o)return{location:void 0,node:void 0};let{resolved:a,node:s,document:c,nodePointer:u,error:p}=o;return{location:a?new n.Location(c.source,u):p instanceof i.YamlParseError?new n.Location(p.source,""):void 0,node:s,error:p}},P=d,A=d,{node:$,location:C,error:R}=E(t),j=new Set;if(n.isRef(t)){let e=s.ref.enter;for(let{visit:n,ruleId:i,severity:o,context:a}of e)!p.has(t)&&(j.add(a),n(t,{report:I.bind(void 0,i,o),resolve:E,rawNode:t,rawLocation:P,location:d,type:r,parent:f,key:h,parentLocations:{},oasVersion:c.oasVersion,getVisitorData:N.bind(void 0,i)},{node:$,location:C,error:R}),(null==C?void 0:C.source.absoluteRef)&&c.refTypes&&c.refTypes.set(null==C?void 0:C.source.absoluteRef,r))}if(void 0!==$&&C&&"scalar"!==r.name){A=C;let i=null===(g=null===(m=u[r.name])||void 0===m?void 0:m.has)||void 0===g?void 0:g.call(m,$),l=!1,c=s.any.enter.concat((null===(y=s[r.name])||void 0===y?void 0:y.enter)||[]),p=[];for(let{context:e,visit:n,skip:a,ruleId:s,severity:u}of c)if(e.isSkippedLevel)!e.parent.activatedOn||e.parent.activatedOn.value.nextLevelTypeActivated||e.seen.has(t)||(e.seen.add(t),l=!0,p.push(e));else if(e.parent&&e.parent.activatedOn&&(null===(v=e.activatedOn)||void 0===v?void 0:v.value.withParentNode)!==e.parent.activatedOn.value.node&&(null===(b=e.parent.activatedOn.value.nextLevelTypeActivated)||void 0===b?void 0:b.value)!==r||!e.parent&&!i){p.push(e);let i={node:$,location:C,nextLevelTypeActivated:null,withParentNode:null===(w=null===(x=e.parent)||void 0===x?void 0:x.activatedOn)||void 0===w?void 0:w.value.node,skipped:null!==(O=(null===(_=null===(k=e.parent)||void 0===k?void 0:k.activatedOn)||void 0===_?void 0:_.value.skipped)||(null==a?void 0:a($,h)))&&void 0!==O&&O};e.activatedOn=o.pushStack(e.activatedOn,i);let c=e.parent;for(;c;)c.activatedOn.value.nextLevelTypeActivated=o.pushStack(c.activatedOn.value.nextLevelTypeActivated,r),c=c.parent;if(!i.skipped){l=!0,j.add(e);let{ignoreNextVisitorsOnNode:r}=T(n,$,t,e,s,u);if(r)break}}if(l||!i)if(u[r.name]=u[r.name]||new Set,u[r.name].add($),Array.isArray($)){let t=r.items;if(void 0!==t)for(let r=0;r<$.length;r++)e($[r],t,C.child([r]),$,r)}else if("object"==typeof $&&null!==$){let i=Object.keys(r.properties);for(let o of(r.additionalProperties&&i.push(...Object.keys($).filter((e=>!i.includes(e)))),n.isRef(t)&&i.push(...Object.keys(t).filter((e=>"$ref"!==e&&!i.includes(e)))),i)){let i=$[o],s=C;void 0===i&&(i=t[o],s=d);let l=r.properties[o];void 0===l&&(l=r.additionalProperties),"function"==typeof l&&(l=l(i,o)),!a.isNamedType(l)&&(null==l?void 0:l.directResolveAs)&&(l=l.directResolveAs,i={$ref:i}),l&&void 0===l.name&&!1!==l.resolvable&&(l={name:"scalar",properties:{}}),a.isNamedType(l)&&("scalar"!==l.name||n.isRef(i))&&e(i,l,s.child([o]),$,o)}}let f=s.any.leave,E=((null===(S=s[r.name])||void 0===S?void 0:S.leave)||[]).concat(f);for(let e of p.reverse())if(e.isSkippedLevel)e.seen.delete($);else if(e.activatedOn=o.popStack(e.activatedOn),e.parent){let t=e.parent;for(;t;)t.activatedOn.value.nextLevelTypeActivated=o.popStack(t.activatedOn.value.nextLevelTypeActivated),t=t.parent}for(let{context:e,visit:r,ruleId:n,severity:i}of E)!e.isSkippedLevel&&j.has(e)&&T(r,$,t,e,n,i)}if(A=d,n.isRef(t)){let e=s.ref.leave;for(let{visit:n,ruleId:i,severity:o,context:a}of e)j.has(a)&&n(t,{report:I.bind(void 0,i,o),resolve:E,rawNode:t,rawLocation:P,location:d,type:r,parent:f,key:h,parentLocations:{},oasVersion:c.oasVersion,getVisitorData:N.bind(void 0,i)},{node:$,location:C,error:R})}function T(e,t,n,i,o,a){let s=I.bind(void 0,o,a),l=!1;return e(t,{report:s,resolve:E,rawNode:n,location:A,rawLocation:P,type:r,parent:f,key:h,parentLocations:function(e){var t,r;let n={};for(;e.parent;)(null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.location)&&(n[e.parent.type.name]=null===(r=e.parent.activatedOn)||void 0===r?void 0:r.value.location),e=e.parent;return n}(i),oasVersion:c.oasVersion,ignoreNextVisitorsOnNode(){l=!0},getVisitorData:N.bind(void 0,o)},function(e){var t;let r={};for(;e.parent;)r[e.parent.type.name]=null===(t=e.parent.activatedOn)||void 0===t?void 0:t.value.node,e=e.parent;return r}(i),i),{ignoreNextVisitorsOnNode:l}}function I(e,t,r){let n=r.location?Array.isArray(r.location)?r.location:[r.location]:[Object.assign(Object.assign({},A),{reportOnKey:!1})];c.problems.push(Object.assign(Object.assign({ruleId:r.ruleId||e,severity:r.forceSeverity||t},r),{suggest:r.suggest||[],location:n.map((e=>Object.assign(Object.assign(Object.assign({},A),{reportOnKey:!1}),e)))}))}function N(e){return c.visitorsData[e]=c.visitorsData[e]||{},c.visitorsData[e]}}(t.parsed,r,new n.Location(t.source,"#/"),void 0,"")}},5019:function(e,t,r){var n=r(5623);e.exports=function(e){return e?("{}"===e.substr(0,2)&&(e="\\{\\}"+e.substr(2)),function e(t,r){var i=[],o=n("{","}",t);if(!o)return[t];var s=o.pre,l=o.post.length?e(o.post,!1):[""];if(/\$$/.test(o.pre))for(var u=0;u=0;if(!x&&!w)return o.post.match(/,.*\}/)?e(t=o.pre+"{"+o.body+a+o.post):[t];if(x)g=o.body.split(/\.\./);else if(1===(g=function e(t){if(!t)return[""];var r=[],i=n("{","}",t);if(!i)return t.split(",");var o=i.pre,a=i.body,s=i.post,l=o.split(",");l[l.length-1]+="{"+a+"}";var c=e(s);return s.length&&(l[l.length-1]+=c.shift(),l.push.apply(l,c)),r.push.apply(r,l),r}(o.body)).length&&1===(g=e(g[0],!1).map(p)).length)return l.map((function(e){return o.pre+g[0]+e}));if(x){var k,_=c(g[0]),O=c(g[1]),S=Math.max(g[0].length,g[1].length),E=3==g.length?Math.abs(c(g[2])):1,P=f;O<_&&(E*=-1,P=h);var A=g.some(d);y=[];for(var $=_;P($,O);$+=E){if(b)"\\"===(k=String.fromCharCode($))&&(k="");else if(k=String($),A){var C=S-k.length;if(C>0){var R=Array(C+1).join("0");k=$<0?"-"+R+k.slice(1):R+k}}y.push(k)}}else{y=[];for(var j=0;j=t}},5751:function(e){let t="object"==typeof process&&process&&!1;e.exports=t?{sep:"\\"}:{sep:"/"}},4099:function(e,t,r){let n=e.exports=(e,t,r={})=>(g(t),!(!r.nocomment&&"#"===t.charAt(0))&&new v(t,r).match(e));e.exports=n;let i=r(5751);n.sep=i.sep;let o=Symbol("globstar **");n.GLOBSTAR=o;let a=r(5019),s={"!":{open:"(?:(?!(?:",close:"))[^/]*?)"},"?":{open:"(?:",close:")?"},"+":{open:"(?:",close:")+"},"*":{open:"(?:",close:")*"},"@":{open:"(?:",close:")"}},l="[^/]",c="[^/]*?",u=e=>e.split("").reduce(((e,t)=>(e[t]=!0,e)),{}),p=u("().*{}+?[]^$\\!"),d=u("[.("),f=/\/+/;n.filter=(e,t={})=>(r,i,o)=>n(r,e,t);let h=(e,t={})=>{let r={};return Object.keys(e).forEach((t=>r[t]=e[t])),Object.keys(t).forEach((e=>r[e]=t[e])),r};n.defaults=e=>{if(!e||"object"!=typeof e||!Object.keys(e).length)return n;let t=n,r=(r,n,i)=>t(r,n,h(e,i));return(r.Minimatch=class extends t.Minimatch{constructor(t,r){super(t,h(e,r))}}).defaults=r=>t.defaults(h(e,r)).Minimatch,r.filter=(r,n)=>t.filter(r,h(e,n)),r.defaults=r=>t.defaults(h(e,r)),r.makeRe=(r,n)=>t.makeRe(r,h(e,n)),r.braceExpand=(r,n)=>t.braceExpand(r,h(e,n)),r.match=(r,n,i)=>t.match(r,n,h(e,i)),r},n.braceExpand=(e,t)=>m(e,t);let m=(e,t={})=>(g(e),t.nobrace||!/\{(?:(?!\{).)*\}/.test(e)?[e]:a(e)),g=e=>{if("string"!=typeof e)throw TypeError("invalid pattern");if(e.length>65536)throw TypeError("pattern is too long")},y=Symbol("subparse");n.makeRe=(e,t)=>new v(e,t||{}).makeRe(),n.match=(e,t,r={})=>{let n=new v(t,r);return e=e.filter((e=>n.match(e))),n.options.nonull&&!e.length&&e.push(t),e};class v{constructor(e,t){g(e),t||(t={}),this.options=t,this.set=[],this.pattern=e,this.regexp=null,this.negate=!1,this.comment=!1,this.empty=!1,this.partial=!!t.partial,this.make()}debug(){}make(){let e=this.pattern,t=this.options;if(!t.nocomment&&"#"===e.charAt(0))return void(this.comment=!0);if(!e)return void(this.empty=!0);this.parseNegate();let r=this.globSet=this.braceExpand();t.debug&&(this.debug=(...e)=>console.error(...e)),this.debug(this.pattern,r),r=this.globParts=r.map((e=>e.split(f))),this.debug(this.pattern,r),r=r.map(((e,t,r)=>e.map(this.parse,this))),this.debug(this.pattern,r),r=r.filter((e=>-1===e.indexOf(!1))),this.debug(this.pattern,r),this.set=r}parseNegate(){if(this.options.nonegate)return;let e=this.pattern,t=!1,r=0;for(let n=0;n>> no match, partial?",e,d,t,f),d!==s))}if("string"==typeof u?(c=p===u,this.debug("string match",u,p,c)):(c=p.match(u),this.debug("pattern match",u,p,c)),!c)return!1}if(i===s&&a===l)return!0;if(i===s)return r;if(a===l)return i===s-1&&""===e[i];throw Error("wtf?")}braceExpand(){return m(this.pattern,this.options)}parse(e,t){g(e);let r=this.options;if("**"===e){if(!r.noglobstar)return o;e="*"}if(""===e)return"";let n,i,a,u,f="",h=!!r.nocase,m=!1,v=[],b=[],x=!1,w=-1,k=-1,_="."===e.charAt(0)?"":r.dot?"(?!(?:^|\\/)\\.{1,2}(?:$|\\/))":"(?!\\.)",O=()=>{if(n){switch(n){case"*":f+=c,h=!0;break;case"?":f+=l,h=!0;break;default:f+="\\"+n}this.debug("clearStateChar %j %j",n,f),n=!1}};for(let t,o=0;o(r||(r="\\"),t+t+r+"|"))),this.debug("tail=%j\n %s",e,e,a,f);let t="*"===a.type?c:"?"===a.type?l:"\\"+a.type;h=!0,f=f.slice(0,a.reStart)+t+"\\("+e}O(),m&&(f+="\\\\");let S=d[f.charAt(0)];for(let e=b.length-1;e>-1;e--){let r=b[e],n=f.slice(0,r.reStart),i=f.slice(r.reStart,r.reEnd-8),o=f.slice(r.reEnd),a=f.slice(r.reEnd-8,r.reEnd)+o,s=n.split("(").length-1,l=o;for(let e=0;e((e=e.map((e=>"string"==typeof e?e.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"):e===o?o:e._src)).reduce(((e,t)=>(e[e.length-1]===o&&t===o||e.push(t),e)),[])).forEach(((t,n)=>{t===o&&e[n-1]!==o&&(0===n?e.length>1?e[n+1]="(?:\\/|"+r+"\\/)?"+e[n+1]:e[n]=r:n===e.length-1?e[n-1]+="(?:\\/|"+r+")?":(e[n-1]+="(?:\\/|\\/"+r+"\\/)"+e[n+1],e[n+1]=o))})),e.filter((e=>e!==o)).join("/")))).join("|");i="^(?:"+i+")$",this.negate&&(i="^(?!"+i+").*$");try{this.regexp=RegExp(i,n)}catch(e){this.regexp=!1}return this.regexp}match(e,t=this.partial){if(this.debug("match",e,this.pattern),this.comment)return!1;if(this.empty)return""===e;if("/"===e&&t)return!0;let r=this.options;"/"!==i.sep&&(e=e.split(i.sep).join("/")),e=e.split(f),this.debug(this.pattern,"split",e);let n,o=this.set;this.debug(this.pattern,"set",o);for(let t=e.length-1;t>=0&&!(n=e[t]);t--);for(let i=0;i=0&&c>0){if(e===t)return[l,c];for(n=[],o=r.length;u>=0&&!s;)u==l?(n.push(u),l=r.indexOf(e,u+1)):1==n.length?s=[n.pop(),c]:((i=n.pop())=0?l:c;n.length&&(s=[o,a])}return s}e.exports=t,t.range=n},4480:function(e,t,r){"use strict";var n=r.g.process&&process.nextTick||r.g.setImmediate||function(e){setTimeout(e,0)};e.exports=function(e,t){return e?void t.then((function(t){n((function(){e(null,t)}))}),(function(t){n((function(){e(t)}))})):t}},4184:function(e,t){var r;!function(){"use strict";var n={}.hasOwnProperty;function i(){for(var e=[],t=0;tu;)if((s=l[u++])!=s)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===r)return e||u||0;return!e&&-1}};e.exports={includes:a(!0),indexOf:a(!1)}},2092:function(e,t,r){var n=r(9974),i=r(8361),o=r(7908),a=r(7466),s=r(5417),l=[].push,c=function(e){var t=1==e,r=2==e,c=3==e,u=4==e,p=6==e,d=7==e,f=5==e||p;return function(h,m,g,y){for(var v,b,x=o(h),w=i(x),k=n(m,g,3),_=a(w.length),O=0,S=y||s,E=t?S(h,_):r||d?S(h,0):void 0;_>O;O++)if((f||O in w)&&(b=k(v=w[O],O,x),e))if(t)E[O]=b;else if(b)switch(e){case 3:return!0;case 5:return v;case 6:return O;case 2:l.call(E,v)}else switch(e){case 4:return!1;case 7:l.call(E,v)}return p?-1:c||u?u:E}};e.exports={forEach:c(0),map:c(1),filter:c(2),some:c(3),every:c(4),find:c(5),findIndex:c(6),filterOut:c(7)}},1194:function(e,t,r){var n=r(7293),i=r(5112),o=r(7392),a=i("species");e.exports=function(e){return o>=51||!n((function(){var t=[];return(t.constructor={})[a]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},5417:function(e,t,r){var n=r(111),i=r(3157),o=r(5112)("species");e.exports=function(e,t){var r;return i(e)&&("function"!=typeof(r=e.constructor)||r!==Array&&!i(r.prototype)?n(r)&&null===(r=r[o])&&(r=void 0):r=void 0),new(void 0===r?Array:r)(0===t?0:t)}},4326:function(e){var t={}.toString;e.exports=function(e){return t.call(e).slice(8,-1)}},648:function(e,t,r){var n=r(1694),i=r(4326),o=r(5112)("toStringTag"),a="Arguments"==i(function(){return arguments}());e.exports=n?i:function(e){var t,r,n;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(r=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?r:a?i(t):"Object"==(n=i(t))&&"function"==typeof t.callee?"Arguments":n}},9920:function(e,t,r){var n=r(6656),i=r(3887),o=r(1236),a=r(3070);e.exports=function(e,t){for(var r=i(t),s=a.f,l=o.f,c=0;c=74)&&(n=a.match(/Chrome\/(\d+)/))&&(i=n[1]),e.exports=i&&+i},748:function(e){e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},2109:function(e,t,r){var n=r(7854),i=r(1236).f,o=r(8880),a=r(1320),s=r(3505),l=r(9920),c=r(4705);e.exports=function(e,t){var r,u,p,d,f,h=e.target,m=e.global,g=e.stat;if(r=m?n:g?n[h]||s(h,{}):(n[h]||{}).prototype)for(u in t){if(d=t[u],p=e.noTargetGet?(f=i(r,u))&&f.value:r[u],!c(m?u:h+(g?".":"#")+u,e.forced)&&void 0!==p){if(typeof d==typeof p)continue;l(d,p)}(e.sham||p&&p.sham)&&o(d,"sham",!0),a(r,u,d,e)}}},7293:function(e){e.exports=function(e){try{return!!e()}catch(e){return!0}}},9974:function(e,t,r){var n=r(3099);e.exports=function(e,t,r){if(n(e),void 0===t)return e;switch(r){case 0:return function(){return e.call(t)};case 1:return function(r){return e.call(t,r)};case 2:return function(r,n){return e.call(t,r,n)};case 3:return function(r,n,i){return e.call(t,r,n,i)}}return function(){return e.apply(t,arguments)}}},5005:function(e,t,r){var n=r(857),i=r(7854),o=function(e){return"function"==typeof e?e:void 0};e.exports=function(e,t){return arguments.length<2?o(n[e])||o(i[e]):n[e]&&n[e][t]||i[e]&&i[e][t]}},7854:function(e,t,r){var n=function(e){return e&&e.Math==Math&&e};e.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof r.g&&r.g)||function(){return this}()||Function("return this")()},6656:function(e,t,r){var n=r(7908),i={}.hasOwnProperty;e.exports=Object.hasOwn||function(e,t){return i.call(n(e),t)}},3501:function(e){e.exports={}},490:function(e,t,r){var n=r(5005);e.exports=n("document","documentElement")},4664:function(e,t,r){var n=r(9781),i=r(7293),o=r(317);e.exports=!n&&!i((function(){return 7!=Object.defineProperty(o("div"),"a",{get:function(){return 7}}).a}))},8361:function(e,t,r){var n=r(7293),i=r(4326),o="".split;e.exports=n((function(){return!Object("z").propertyIsEnumerable(0)}))?function(e){return"String"==i(e)?o.call(e,""):Object(e)}:Object},2788:function(e,t,r){var n=r(5465),i=Function.toString;"function"!=typeof n.inspectSource&&(n.inspectSource=function(e){return i.call(e)}),e.exports=n.inspectSource},9909:function(e,t,r){var n,i,o,a=r(8536),s=r(7854),l=r(111),c=r(8880),u=r(6656),p=r(5465),d=r(6200),f=r(3501),h="Object already initialized",m=s.WeakMap;if(a||p.state){var g=p.state||(p.state=new m),y=g.get,v=g.has,b=g.set;n=function(e,t){if(v.call(g,e))throw TypeError(h);return t.facade=e,b.call(g,e,t),t},i=function(e){return y.call(g,e)||{}},o=function(e){return v.call(g,e)}}else{var x=d("state");f[x]=!0,n=function(e,t){if(u(e,x))throw TypeError(h);return t.facade=e,c(e,x,t),t},i=function(e){return u(e,x)?e[x]:{}},o=function(e){return u(e,x)}}e.exports={set:n,get:i,has:o,enforce:function(e){return o(e)?i(e):n(e,{})},getterFor:function(e){return function(t){var r;if(!l(t)||(r=i(t)).type!==e)throw TypeError("Incompatible receiver, "+e+" required");return r}}}},3157:function(e,t,r){var n=r(4326);e.exports=Array.isArray||function(e){return"Array"==n(e)}},4705:function(e,t,r){var n=r(7293),i=/#|\.prototype\./,o=function(e,t){var r=s[a(e)];return r==c||r!=l&&("function"==typeof t?n(t):!!t)},a=o.normalize=function(e){return String(e).replace(i,".").toLowerCase()},s=o.data={},l=o.NATIVE="N",c=o.POLYFILL="P";e.exports=o},111:function(e){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},1913:function(e){e.exports=!1},133:function(e,t,r){var n=r(7392),i=r(7293);e.exports=!!Object.getOwnPropertySymbols&&!i((function(){var e=Symbol();return!String(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&n&&n<41}))},8536:function(e,t,r){var n=r(7854),i=r(2788),o=n.WeakMap;e.exports="function"==typeof o&&/native code/.test(i(o))},30:function(e,t,r){var n,i=r(9670),o=r(6048),a=r(748),s=r(3501),l=r(490),c=r(317),u=r(6200)("IE_PROTO"),p=function(){},d=function(e){return" + Stremio-Jackett + + + + +
+
+ logo +
+

Stremio-Jackett Rework

+

+ Addon configuration

+ +
+

Streaming

+

You will enter information + about streaming here

+
+
+
+ +
+
+ +

Enable debrid service for faster streaming

+
+
+
+
+ +
+
+ +

(ATTENTION: this is illegal in some + countries)

+
+
+
+ +
+

Torrent Providers

+

You will enter information about + your torrent providers here

+
+
+
+ +
+
+ +

Enable Jackett for more results

+
+
+
+
+ +
+
+ +

Enable caching for faster results

+
+
+
+
+ +
+
+ +

Zilean's api - DMM hashlist scraper for more + results

+
+
+
+ +
+

Redis Information

+

You will enter information about your Redis server here

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ + +
+

Jackett Information

+

You will enter information about + your + Jackett server here

+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+

Debrid Information

+

You will enter your debrid + provider + and its API key here

+ +
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+

Metadata Provider

+

You will enter information about + your metadata provider here

+
+
+
+ +
+
+ +

Use TMDB for more accurate metadata in different + languages

+
+
+
+
+ +
+
+ +

Use Cinemeta for simple metadata

+
+
+
+ +
+

TMDB Informations

+

You will enter information + about TMDB here. You can + find your API key in "Settings" after registering here

+ +
+
+ +
+ +
+
+
+
+ +
+

Zilean Cache + DMM API

+

Please fill your Zilean DMM API + Cache + informations here.

+ +
+
+ +
+ +
+
+
+
+ +
+

Shared Public Cache

+

Please fill your Public + Cache URL + informations here.

+ +
+
+ +
+ +
+
+
+
+ +
+

Filtering

+

Set-up here your filtering + parameters. Suiting results + is a must :)

+ +
+
+ Sorting + +

Choose the sorting that + suits + you the best.

+ +
+
+
+ +
+
+ +

Get the best quality on top

+
+
+
+
+ +
+
+ +

Filter results by size descending +

+
+
+
+
+ +
+
+ +

Filter results by size ascending

+
+
+
+
+ +
+
+ +

Filter results by quality then size + descending

+
+
+
+
+ +
+ Quality + Exclusion + +

Check qualities that you + want + to exclude

+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+
+
+
+
+ +

Choose your languages

+ +
+
+ +
+
+ +
+ + + + + + + + + + + + + +
+
+
+
+
+ +

Set a maximum size + for + your results in + GB.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + maximum + results you want + per + quality.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + maximum + results you + want.

+ +
+ +
+
+ +
+ +
+
+ +

Set the amount of + minimum cached results you want befor a new search.

+ +
+ +
+
+ +
+
+
+ +
+ Install + Copy +
+
+
+
+ + + + \ No newline at end of file diff --git a/stream_fusion/utils/cache/__init__.py b/stream_fusion/utils/cache/__init__.py new file mode 100644 index 0000000..0f11285 --- /dev/null +++ b/stream_fusion/utils/cache/__init__.py @@ -0,0 +1,4 @@ +from stream_fusion.utils.cache.cache_base import CacheBase +from stream_fusion.utils.cache.local_redis import RedisCache + +__all__ = ["CacheBase", "RedisCache"] \ No newline at end of file diff --git a/stream_fusion/utils/cache/cache.py b/stream_fusion/utils/cache/cache.py new file mode 100644 index 0000000..dc85947 --- /dev/null +++ b/stream_fusion/utils/cache/cache.py @@ -0,0 +1,142 @@ +import os +import json +import copy +import redis +import pickle +import hashlib +import requests + +from typing import List + +from stream_fusion.logging_config import logger +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.constants import CACHER_URL, EXCLUDED_TRACKERS +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + +# Redis connection initialization +redis_client = redis.Redis(host="redis", port=6379) + +CACHE_EXPIRATION_TIME = 48 * 60 * 60 + +def cache_key(media): + if isinstance(media, Movie): + key_string = f"movie:{media.titles[0]}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"series:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode('utf-8')).hexdigest() + return hashed_key[:16] + +def search_redis(media): + try: + logger.info(f"Searching for cached {media.type} results") + hash_key = cache_key(media) + cached_result = redis_client.hgetall(hash_key) + + if cached_result: + return [pickle.loads(value) for value in cached_result.values()] + else: + return [] + except Exception as e: + logger.error(f"Error in search_cache: {str(e)}") + return [] + +def cache_redis(torrents: List[JackettResult], media): + if os.getenv("NODE_ENV") == "development": + return + + logger.info("Started caching results") + + cached_torrents = {} + for index, torrent in enumerate(torrents): + try: + cached_torrent = copy.deepcopy(torrent) + cached_torrent.indexer = "Locally cached" + cached_torrents[str(index)] = pickle.dumps(cached_torrent) + except Exception as e: + logger.error(f"Failed to create cached copy of torrent: {str(e)}") + + try: + hash_key = cache_key(media) + redis_client.hmset(hash_key, cached_torrents) + redis_client.expire(hash_key, CACHE_EXPIRATION_TIME) + + logger.info(f"Cached {len(cached_torrents)} {media.type} results") + except Exception as e: + logger.error(f"Failed to cache results: {str(e)}") + + +def search_public(media): + logger.info("Searching for public cached " + media.type + " results") + url = CACHER_URL + "getResult/" + media.type + "/" + # Without that, the cache doesn't return results. Maybe make multiple requests? One for each language, just like jackett? + cache_search = media.__dict__ + cache_search["title"] = cache_search["titles"][0] + cache_search["language"] = cache_search["languages"][0] + # TODO: Wtf, why do we need to use __dict__ here? And also, why is it stuck when we use media directly? + response = requests.get(url, json=cache_search) + return response.json() + + +def cache_public(torrents: List[TorrentItem], media): + if os.getenv("NODE_ENV") == "development": + return + + logger.info("Started Public Caching results") + + cache_items = [] + for torrent in torrents: + if torrent.indexer in EXCLUDED_TRACKERS: + continue + + try: + cache_item = dict() + + cache_item["title"] = torrent.raw_title + cache_item["trackers"] = "tracker:".join(torrent.trackers) + cache_item["magnet"] = torrent.magnet + cache_item["files"] = [] # I guess keep it empty? + cache_item["hash"] = torrent.info_hash + cache_item["indexer"] = torrent.indexer + cache_item["quality"] = ( + torrent.parsed_data.resolution[0] + if len(torrent.parsed_data.resolution) > 0 + else "Unknown" + ) + cache_item["qualitySpec"] = ";".join(torrent.parsed_data.quality) + cache_item["seeders"] = torrent.seeders + cache_item["size"] = torrent.size + cache_item["language"] = ";".join(torrent.languages) + cache_item["type"] = media.type + cache_item["availability"] = torrent.availability + + if media.type == "movie": + cache_item["year"] = media.year + elif media.type == "series": + cache_item["season"] = media.season + cache_item["episode"] = media.episode + cache_item["seasonfile"] = ( + False # I guess keep it false to not mess up results? + ) + + cache_items.append(cache_item) + except: + logger.exception("An exception occured durring public cache parsing") + pass + + try: + url = f"{CACHER_URL}pushResult/{media.type}" + cache_data = json.dumps(cache_items, indent=4) + response = requests.post(url, data=cache_data) + response.raise_for_status() + + if response.status_code == 200: + logger.info(f"Cached {str(len(cache_items))} {media.type} results on Public Cache") + else: + logger.error(f"Failed to public cache {media.type} results: {str(response)}") + except: + logger.error("Failed to public cache results") + pass diff --git a/stream_fusion/utils/cache/cache_base.py b/stream_fusion/utils/cache/cache_base.py new file mode 100644 index 0000000..480af1e --- /dev/null +++ b/stream_fusion/utils/cache/cache_base.py @@ -0,0 +1,85 @@ +import hashlib +from abc import ABC, abstractmethod +from typing import Any, Callable + +from stream_fusion.logging_config import logger + +class CacheBase(ABC): + def __init__(self, config: dict): + """ + Initialize the cache base class. + Args: + config (dict): Configuration dictionary for the cache. + """ + self.logger = logger + self.config = config + + @abstractmethod + def can_cache(self) -> bool: + """ + Check if caching is possible. + Returns: + bool: True if caching is possible, False otherwise. + """ + pass + + @abstractmethod + def get(self, key: str) -> Any: + """ + Retrieve a value from the cache. + Args: + key (str): The cache key. + Returns: + Any: The cached value if found, None otherwise. + """ + pass + + @abstractmethod + def set(self, key: str, value: Any) -> None: + """ + Store a value in the cache. + Args: + key (str): The cache key. + value (Any): The value to be cached. + """ + pass + + def __call__(self, func: Callable) -> Callable: + """ + Decorator for caching function results. + Args: + func (Callable): The function to be cached. + Returns: + Callable: The wrapped function with caching functionality. + """ + def wrapper(*args, **kwargs): + if not self.can_cache(): + self.logger.error("Cache is not available or cannot be used.") + return func(*args, **kwargs) + + key = self.generate_key(func.__name__, *args, **kwargs) + cached_result = self.get(key) + + if cached_result is not None: + self.logger.info(f"Result found in cache for key: {key}") + return cached_result + + result = func(*args, **kwargs) + self.set(key, result) + return result + + return wrapper + + def generate_key(self, func_name: str, *args, **kwargs) -> str: + """ + Generate a cache key based on function name and arguments. + Args: + func_name (str): Name of the function being cached. + *args: Positional arguments of the function. + **kwargs: Keyword arguments of the function. + Returns: + str: A hashed key string. + """ + arg_string = f"{func_name}:{str(args)}:{str(kwargs)}" + hashed_key = hashlib.sha256(arg_string.encode("utf-8")).hexdigest() + return hashed_key[:16] \ No newline at end of file diff --git a/stream_fusion/utils/cache/local_redis.py b/stream_fusion/utils/cache/local_redis.py new file mode 100644 index 0000000..3ccb1f6 --- /dev/null +++ b/stream_fusion/utils/cache/local_redis.py @@ -0,0 +1,219 @@ +import asyncio +import inspect +import jsonpickle +import time +from typing import Any, List +import hashlib +import redis +from stream_fusion.settings import settings +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.cache.cache_base import CacheBase + +class RedisCache(CacheBase): + def __init__(self, config): + super().__init__(config) + self.redis_host = settings.redis_host + self.redis_port = settings.redis_port + self.redis_url = f"redis://{self.redis_host}:{self.redis_port}" + self._redis_client = None + self.media_expiration = settings.redis_expiration + + def get_redis_client(self): + if not self._redis_client: + try: + self._redis_client = redis.Redis.from_url( + self.redis_url, + max_connections=10 + ) + except redis.ConnectionError: + self.logger.error("Failed to create Redis client") + self._redis_client = None + return self._redis_client + + def get_list(self, key: str) -> List[Any]: + result = self.get(key) + if isinstance(result, list): + return result + elif result is not None: + return [result] + return None + + def generate_key(self, func_name: str, *args, **kwargs) -> str: + media = kwargs.get('media') + if media is None and args: + media = next((arg for arg in args if isinstance(arg, (Movie, Series))), None) + + if media: + if isinstance(media, Movie): + key_string = f"movie:{media.titles[0]}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"series:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + else: + key_string = f"{func_name}:{str(args)}:{str(kwargs)}" + + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + def clear(self) -> None: + try: + client = self.get_redis_client() + client.flushdb() + self.logger.info("Cache cleared successfully") + except Exception as e: + self.logger.error(f"Error clearing cache: {e}") + + def get_or_set(self, func: callable, *args, **kwargs) -> Any: + self.logger.debug(f"Entering get_or_set for function: {func.__name__}") + start_time = time.time() + + if not self.can_cache(): + self.logger.debug("Cache is not available, executing function directly") + return self._execute_func(func, *args, **kwargs) + + key = self.generate_key(func.__name__, *args, **kwargs) + self.logger.debug(f"Generated cache key: {key}") + + cached_result = self.get(key) + self.logger.debug(f"Attempted to get result from cache. Found: {cached_result is not None}") + + if cached_result is not None: + self.logger.debug(f"Returning cached result for key: {key}") + return cached_result + + self.logger.debug(f"Cache miss for key: {key}. Executing function.") + result = self._execute_func(func, *args, **kwargs) + self.logger.debug(f"Function execution completed. Setting result in cache.") + self.set(key, result) + + end_time = time.time() + self.logger.debug(f"get_or_set completed in {end_time - start_time:.2f} seconds") + return result + + def _execute_func(self, func, *args, **kwargs): + self.logger.debug(f"Executing function: {func.__name__}") + start_time = time.time() + + sig = inspect.signature(func) + params = sig.parameters + + self.logger.debug(f"Function {func.__name__} expects {len(params)} arguments") + self.logger.debug(f"Received args: {args}, kwargs: {kwargs}") + + call_args = {} + for i, (param_name, param) in enumerate(params.items()): + if i < len(args): + call_args[param_name] = args[i] + elif param_name in kwargs: + call_args[param_name] = kwargs[param_name] + elif param.default is not param.empty: + call_args[param_name] = param.default + else: + self.logger.error(f"Missing required argument: {param_name}") + raise TypeError(f"Missing required argument: {param_name}") + + self.logger.debug(f"Prepared arguments for {func.__name__}: {call_args}") + + if asyncio.iscoroutinefunction(func): + self.logger.debug(f"Function {func.__name__} is asynchronous") + loop = asyncio.get_event_loop() + try: + result = loop.run_until_complete(func(**call_args)) + self.logger.debug(f"Asynchronous function {func.__name__} completed successfully") + except Exception as e: + self.logger.error(f"Error executing asynchronous function {func.__name__}: {str(e)}") + raise + else: + self.logger.debug(f"Function {func.__name__} is synchronous") + try: + result = func(**call_args) + self.logger.debug(f"Synchronous function {func.__name__} completed successfully") + except Exception as e: + self.logger.error(f"Error executing synchronous function {func.__name__}: {str(e)}") + raise + + end_time = time.time() + self.logger.debug(f"Function {func.__name__} executed in {end_time - start_time:.2f} seconds") + return result + + def can_cache(self) -> bool: + self.logger.debug("Checking if caching is possible") + try: + client = self.get_redis_client() + result = client.ping() + self.logger.debug(f"Redis ping result: {result}") + return result + except redis.ConnectionError: + self.logger.error("Unable to connect to Redis") + return False + + def get(self, key: str) -> Any: + self.logger.debug(f"Attempting to get value for key: {key}") + try: + client = self.get_redis_client() + cached_result = client.get(key) + if cached_result: + self.logger.debug(f"Value found in cache for key: {key}") + return jsonpickle.decode(cached_result) + self.logger.debug(f"No value found in cache for key: {key}") + return None + except Exception as e: + self.logger.error(f"Error retrieving from Redis: {e}") + return None + + def set(self, key: str, value: Any, expiration: int = None) -> None: + self.logger.debug(f"Attempting to set value for key: {key}") + if expiration is None: + expiration = self.media_expiration + + try: + client = self.get_redis_client() + cached_data = jsonpickle.encode(value) + result = client.set(key, cached_data, ex=expiration) + self.logger.debug(f"Set operation result for key {key}: {result}") + except Exception as e: + self.logger.error(f"Error storing in Redis: {e}") + + def delete(self, key: str) -> bool: + try: + client = self.get_redis_client() + return bool(client.delete(key)) + except Exception as e: + self.logger.error(f"Error deleting key from Redis: {e}") + return False + + def exists(self, key: str) -> bool: + try: + client = self.get_redis_client() + return bool(client.exists(key)) + except Exception as e: + self.logger.error(f"Error checking key existence in Redis: {e}") + return False + + def get_ttl(self, key: str) -> int: + try: + client = self.get_redis_client() + return client.ttl(key) + except Exception as e: + self.logger.error(f"Error getting TTL from Redis: {e}") + return -1 + + def update_expiration(self, key: str, expiration: int) -> bool: + try: + client = self.get_redis_client() + return bool(client.expire(key, expiration)) + except Exception as e: + self.logger.error(f"Error updating expiration in Redis: {e}") + return False + + def close(self): + if self._redis_client: + self._redis_client.close() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() diff --git a/stream_fusion/utils/debrid/__init__.py b/stream_fusion/utils/debrid/__init__.py new file mode 100644 index 0000000..764eb18 --- /dev/null +++ b/stream_fusion/utils/debrid/__init__.py @@ -0,0 +1,9 @@ +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.debrid.realdebrid import RealDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service + + + +__all__ = ["AllDebrid", "BaseDebrid", "RealDebrid", "Premiumize", "get_debrid_service"] \ No newline at end of file diff --git a/stream_fusion/utils/debrid/alldebrid.py b/stream_fusion/utils/debrid/alldebrid.py new file mode 100644 index 0000000..1c2cc40 --- /dev/null +++ b/stream_fusion/utils/debrid/alldebrid.py @@ -0,0 +1,126 @@ +# alldebrid.py +import json +import uuid +from urllib.parse import unquote + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + + +class AllDebrid(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://api.alldebrid.com/v4/" + + def add_magnet(self, magnet, ip): + url = f"{self.base_url}magnet/upload?agent=jackett&apikey={self.config['debridKey']}&magnet={magnet}&ip={ip}" + return self.get_json_response(url) + + def add_torrent(self, torrent_file, ip): + url = f"{self.base_url}magnet/upload/file?agent=jackett&apikey={self.config['debridKey']}&ip={ip}" + files = {"files[0]": (str(uuid.uuid4()) + ".torrent", torrent_file, 'application/x-bittorrent')} + return self.get_json_response(url, method='post', files=files) + + def check_magnet_status(self, id, ip): + url = f"{self.base_url}magnet/status?agent=jackett&apikey={self.config['debridKey']}&id={id}&ip={ip}" + return self.get_json_response(url) + + def unrestrict_link(self, link, ip): + url = f"{self.base_url}link/unlock?agent=jackett&apikey={self.config['debridKey']}&link={link}&ip={ip}" + return self.get_json_response(url) + + def get_stream_link(self, query_string, config, ip): + query = json.loads(query_string) + + magnet = query['magnet'] + stream_type = query['type'] + torrent_download = unquote(query["torrent_download"]) if query["torrent_download"] is not None else None + + torrent_id = self.__add_magnet_or_torrent(magnet, torrent_download, ip) + logger.info(f"Torrent ID: {torrent_id}") + + if not self.wait_for_ready_status( + lambda: self.check_magnet_status(torrent_id, ip)["data"]["magnets"]["status"] == "Ready"): + logger.error("Torrent not ready, caching in progress.") + return NO_CACHE_VIDEO_URL + logger.info("Torrent is ready.") + + logger.info(f"Getting data for torrent id: {torrent_id}") + data = self.check_magnet_status(torrent_id, ip)["data"] + logger.info(f"Retrieved data for torrent id") + + link = NO_CACHE_VIDEO_URL + if stream_type == "movie": + logger.info("Getting link for movie") + link = max(data["magnets"]['links'], key=lambda x: x['size'])['link'] + elif stream_type == "series": + season = query['season'] + episode = query['episode'] + logger.info(f"Getting link for series {season}, {episode}") + + matching_files = [] + for file in data["magnets"]["links"]: + if season_episode_in_filename(file["filename"], season, episode): + matching_files.append(file) + + if len(matching_files) == 0: + logger.error(f"No matching files for {season} {episode} in torrent.") + return f"Error: No matching files for {season} {episode} in torrent." + + link = max(matching_files, key=lambda x: x["size"])["link"] + else: + logger.error("Unsupported stream type.") + return "Error: Unsupported stream type." + + if link == NO_CACHE_VIDEO_URL: + return link + + logger.info(f"Alldebrid link: {link}") + + unlocked_link_data = self.unrestrict_link(link, ip) + + if not unlocked_link_data: + logger.error("Failed to unlock link.") + return "Error: Failed to unlock link." + + logger.info(f"Unrestricted link: {unlocked_link_data['data']['link']}") + + return unlocked_link_data["data"]["link"] + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + if len(hashes_or_magnets) == 0: + logger.info("No hashes to be sent to All-Debrid.") + return dict() + + url = f"{self.base_url}magnet/instant?agent=jackett&apikey={self.config['debridKey']}&magnets[]={'&magnets[]='.join(hashes_or_magnets)}&ip={ip}" + return self.get_json_response(url) + + def __add_magnet_or_torrent(self, magnet, torrent_download=None, ip=None): + torrent_id = "" + if torrent_download is None: + logger.info(f"Adding magnet to AllDebrid") + magnet_response = self.add_magnet(magnet, ip) + logger.info(f"AllDebrid add magnet response: {magnet_response}") + + if not magnet_response or "status" not in magnet_response or magnet_response["status"] != "success": + return "Error: Failed to add magnet." + + torrent_id = magnet_response["data"]["magnets"][0]["id"] + else: + logger.info(f"Downloading torrent file from Jackett") + torrent_file = self.donwload_torrent_file(torrent_download) + logger.info(f"Torrent file downloaded from Jackett") + + logger.info(f"Adding torrent file to AllDebrid") + upload_response = self.add_torrent(torrent_file, ip) + logger.info(f"AllDebrid add torrent file response: {upload_response}") + + if not upload_response or "status" not in upload_response or upload_response["status"] != "success": + return "Error: Failed to add torrent file." + + torrent_id = upload_response["data"]["files"][0]["id"] + + logger.info(f"New torrent ID: {torrent_id}") + return torrent_id diff --git a/stream_fusion/utils/debrid/base_debrid.py b/stream_fusion/utils/debrid/base_debrid.py new file mode 100644 index 0000000..a0c065a --- /dev/null +++ b/stream_fusion/utils/debrid/base_debrid.py @@ -0,0 +1,61 @@ +import time + +import requests + +from stream_fusion.logging_config import logger + + +class BaseDebrid: + def __init__(self, config): + self.config = config + self.logger = logger + self.__session = requests.Session() + + def get_json_response(self, url, method='get', data=None, headers=None, files=None): + if method == 'get': + response = self.__session.get(url, headers=headers) + elif method == 'post': + response = self.__session.post(url, data=data, headers=headers, files=files) + elif method == 'put': + response = self.__session.put(url, data=data, headers=headers) + elif method == 'delete': + response = self.__session.delete(url, headers=headers) + else: + raise ValueError(f"Unsupported HTTP method: {method}") + + # Check if the request was successful + if response.ok: + try: + return response.json() + except ValueError: + self.logger.error(f"Failed to parse response as JSON: {response.text}") + return None + else: + self.logger.error(f"Request failed with status code {response.status_code}") + return None + + def wait_for_ready_status(self, check_status_func, timeout=30, interval=5): + self.logger.info(f"Waiting for {timeout} seconds to cache.") + start_time = time.time() + while time.time() - start_time < timeout: + if check_status_func(): + self.logger.info("File is ready!") + return True + time.sleep(interval) + self.logger.info(f"Waiting timed out.") + return False + + def donwload_torrent_file(self, download_url): + response = requests.get(download_url) + response.raise_for_status() + + return response.content + + def get_stream_link(self, query, ip=None): + raise NotImplementedError + + def add_magnet(self, magnet, ip=None): + raise NotImplementedError + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + raise NotImplementedError diff --git a/stream_fusion/utils/debrid/get_debrid_service.py b/stream_fusion/utils/debrid/get_debrid_service.py new file mode 100644 index 0000000..a0fa877 --- /dev/null +++ b/stream_fusion/utils/debrid/get_debrid_service.py @@ -0,0 +1,19 @@ +from fastapi.exceptions import HTTPException + +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.realdebrid import RealDebrid + + +def get_debrid_service(config): + service_name = config['service'] + if service_name == "realdebrid": + debrid_service = RealDebrid(config) + elif service_name == "alldebrid": + debrid_service = AllDebrid(config) + elif service_name == "premiumize": + debrid_service = Premiumize(config) + else: + raise HTTPException(status_code=500, detail="Invalid service configuration.") + + return debrid_service diff --git a/stream_fusion/utils/debrid/premiumize.py b/stream_fusion/utils/debrid/premiumize.py new file mode 100644 index 0000000..b84ff71 --- /dev/null +++ b/stream_fusion/utils/debrid/premiumize.py @@ -0,0 +1,120 @@ +# Assuming the BaseDebrid class and necessary imports are already defined as shown previously +import json + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import get_info_hash_from_magnet, season_episode_in_filename +from stream_fusion.logging_config import logger + + +class Premiumize(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://www.premiumize.me/api" + + def add_magnet(self, magnet, ip=None): + url = f"{self.base_url}/transfer/create?apikey={self.config['debridKey']}" + form = {'src': magnet} + return self.get_json_response(url, method='post', data=form) + + # Doesn't work for the time being. Premiumize does not support torrent file torrents + def add_torrent(self, torrent_file): + url = f"{self.base_url}/transfer/create?apikey={self.config['debridKey']}" + form = {'file': torrent_file} + return self.get_json_response(url, method='post', data=form) + + def list_transfers(self): + url = f"{self.base_url}/transfer/list?apikey={self.config['debridKey']}" + return self.get_json_response(url) + + def get_folder_or_file_details(self, item_id, is_folder=True): + if is_folder: + logger.info(f"Getting folder details with id: {item_id}") + url = f"{self.base_url}/folder/list?id={item_id}&apikey={self.config['debridKey']}" + else: + logger.info(f"Getting file details with id: {item_id}") + url = f"{self.base_url}/item/details?id={item_id}&apikey={self.config['debridKey']}" + return self.get_json_response(url) + + def get_availability(self, hash): + url = f"{self.base_url}/cache/check?apikey={self.config['debridKey']}&items[]={hash}" + return self.get_json_response(url) + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + url = f"{self.base_url}/cache/check?apikey={self.config['debridKey']}&items[]=" + "&items[]=".join( + hashes_or_magnets) + return self.get_json_response(url) + + def get_stream_link(self, query, config, ip=None): + query = json.loads(query) + magnet = query['magnet'] + logger.info(f"Received query for magnet: {magnet}") + info_hash = get_info_hash_from_magnet(magnet) + logger.info(f"Info hash extracted: {info_hash}") + stream_type = query['type'] + logger.info(f"Stream type: {stream_type}") + + transfer_data = self.add_magnet(magnet) + if not transfer_data or 'id' not in transfer_data: + logger.error("Failed to create transfer.") + return "Error: Failed to create transfer." + transfer_id = transfer_data['id'] + logger.info(f"Transfer created with ID: {transfer_id}") + + if not self.wait_for_ready_status(lambda: self.get_availability(info_hash)["transcoded"][0] is True): + logger.info("Torrent not ready, caching in progress") + return NO_CACHE_VIDEO_URL + + logger.info("Torrent is ready.") + + # Assuming the transfer is complete, we need to find whether it's a file or a folder + transfers = self.list_transfers() + item_id, is_folder = None, False + for item in transfers.get('transfers', []): + if item['id'] == transfer_id: + if item.get('folder_id'): + item_id = item['folder_id'] + is_folder = True + else: + item_id = item['file_id'] + break + + if not item_id: + logger.error("Transfer completed but no item ID found.") + return "Error: Transfer completed but no item ID found." + + details = self.get_folder_or_file_details(item_id, is_folder) + logger.info(f"Got details") + + if stream_type == "movie": + logger.info("Getting link for movie") + # For movies, we pick the largest file in the folder or the file itself + if is_folder: + link = max(details.get("content", []), key=lambda x: x["size"])["link"] + else: + link = details.get('link') + elif stream_type == "series": + logger.info("Getting link for series") + if is_folder: + season = query["season"] + episode = query["episode"] + files = details.get("content", []) + matching_files = [] + + for file in files: + if season_episode_in_filename(file["name"], season, episode): + matching_files.append(file) + + if len(matching_files) == 0: + logger.error(f"No matching files for {season} {episode} in torrent.") + return f"Error: No matching files for {season} {episode} in torrent." + + link = max(matching_files, key=lambda x: x["size"])["link"] + else: + link = details.get('link') + else: + logger.error("Unsupported stream type.") + return "Error: Unsupported stream type." + + logger.info(f"Link generated: {link}") + return link diff --git a/stream_fusion/utils/debrid/realdebrid.py b/stream_fusion/utils/debrid/realdebrid.py new file mode 100644 index 0000000..6df8bec --- /dev/null +++ b/stream_fusion/utils/debrid/realdebrid.py @@ -0,0 +1,285 @@ +import re +import json +import time +from urllib.parse import unquote + +import requests + +from stream_fusion.constants import NO_CACHE_VIDEO_URL +from stream_fusion.utils.debrid.base_debrid import BaseDebrid +from stream_fusion.utils.general import get_info_hash_from_magnet +from stream_fusion.utils.general import is_video_file +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + + +class RealDebrid(BaseDebrid): + def __init__(self, config): + super().__init__(config) + self.base_url = "https://api.real-debrid.com" + self.headers = {"Authorization": f"Bearer {self.config['debridKey']}"} + + def add_magnet(self, magnet, ip=None): + url = f"{self.base_url}/rest/1.0/torrents/addMagnet" + data = {"magnet": magnet} + logger.info(f"Adding magnet to RD: {magnet}") + return self.get_json_response(url, method='post', headers=self.headers, data=data) + + def add_torrent(self, torrent_file): + url = f"{self.base_url}/rest/1.0/torrents/addTorrent" + return self.get_json_response(url, method='put', headers=self.headers, data=torrent_file) + + def delete_torrent(self, id): + url = f"{self.base_url}/rest/1.0/torrents/delete/{id}" + return self.get_json_response(url, method='delete', headers=self.headers) + + def get_torrent_info(self, torrent_id): + logger.info(f"Getting torrent info for: {torrent_id}") + url = f"{self.base_url}/rest/1.0/torrents/info/{torrent_id}" + torrent_info = self.get_json_response(url, headers=self.headers) + + if not torrent_info or 'files' not in torrent_info: + return None + + return torrent_info + + def select_files(self, torrent_id, file_id): + logger.info(f"Selecting file(s): {file_id}") + url = f"{self.base_url}/rest/1.0/torrents/selectFiles/{torrent_id}" + data = {"files": str(file_id)} + requests.post(url, headers=self.headers, data=data) + + def unrestrict_link(self, link): + url = f"{self.base_url}/rest/1.0/unrestrict/link" + data = {"link": link} + return self.get_json_response(url, method='post', headers=self.headers, data=data) + + def is_already_added(self, magnet): + hash = magnet.split("urn:btih:")[1].split("&")[0].lower() + url = f"{self.base_url}/rest/1.0/torrents" + torrents = self.get_json_response(url, headers=self.headers) + for torrent in torrents: + if torrent['hash'].lower() == hash: + return torrent['id'] + return False + + def wait_for_link(self, torrent_id, timeout=30, interval=5): + start_time = time.time() + while time.time() - start_time < timeout: + torrent_info = self.get_torrent_info(torrent_id) + if torrent_info and 'links' in torrent_info and len(torrent_info['links']) > 0: + return torrent_info['links'] + time.sleep(interval) + + return None + + def get_availability_bulk(self, hashes_or_magnets, ip=None): + if len(hashes_or_magnets) == 0: + logger.info("No hashes to be sent to Real-Debrid.") + return dict() + + url = f"{self.base_url}/rest/1.0/torrents/instantAvailability/{'/'.join(hashes_or_magnets)}" + return self.get_json_response(url, headers=self.headers) + + def get_stream_link(self, query_string, config, ip=None): + query = json.loads(query_string) + + magnet = query['magnet'] + stream_type = query['type'] + file_index = int(query['file_index']) if query['file_index'] is not None else None + season = query['season'] + episode = query['episode'] + torrent_download = unquote(query["torrent_download"]) if query["torrent_download"] is not None else None + info_hash = get_info_hash_from_magnet(magnet) + logger.info(f"RealDebrid get stream link for {stream_type} with hash: {info_hash}") + + cached_torrent_ids = self.__get_cached_torrent_ids(info_hash) + logger.info(f"Found {len(cached_torrent_ids)} cached torrents with hash: {info_hash}") + + torrent_info = None + if len(cached_torrent_ids) > 0: + if stream_type == "movie": + torrent_info = self.get_torrent_info(cached_torrent_ids[0]) + elif stream_type == "series": + torrent_info = self.__get_cached_torrent_info(cached_torrent_ids, file_index, season, episode) + else: + return "Error: Unsupported stream type." + + # The torrent is not yet added + if torrent_info is None: + torrent_info = self.__add_magnet_or_torrent(magnet, torrent_download, anon_magnet=config["anonymizeMagnets"]) + if not torrent_info or 'files' not in torrent_info: + return "Error: Failed to get torrent info." + + logger.info("Selecting file") + self.__select_file(torrent_info, stream_type, file_index, season, episode) + + # == operator, to avoid adding the season pack twice and setting 5 as season pack treshold + if len(cached_torrent_ids) == 0 and stream_type == "series" and len(torrent_info["files"]) > 5: + logger.info("Prefetching season pack") + prefetched_torrent_info = self.__prefetch_season_pack(magnet, torrent_download) + if len(prefetched_torrent_info["links"]) > 0: + self.delete_torrent(torrent_info["id"]) + torrent_info = prefetched_torrent_info + + torrent_id = torrent_info["id"] + logger.info(f"Waiting for the link(s) to be ready for torrent ID: {torrent_id}") + # Waiting for the link(s) to be ready + links = self.wait_for_link(torrent_id) + if links is None: + return NO_CACHE_VIDEO_URL + + if len(links) > 1: + logger.info("Finding appropiate link") + download_link = self.__find_appropiate_link(torrent_info, links, file_index, season, episode) + else: + download_link = links[0] + + logger.info(f"Unrestricting the download link: {download_link}") + # Unrestricting the download link + unrestrict_response = self.unrestrict_link(download_link) + if not unrestrict_response or 'download' not in unrestrict_response: + return "Error: Failed to unrestrict link." + + logger.info(f"Got download link: {unrestrict_response['download']}") + return unrestrict_response['download'] + + def __get_cached_torrent_ids(self, info_hash): + url = f"{self.base_url}/rest/1.0/torrents" + torrents = self.get_json_response(url, headers=self.headers) + + logger.info(f"Searching users real-debrid downloads for {info_hash}") + torrent_ids = [] + for torrent in torrents: + if torrent['hash'].lower() == info_hash: + torrent_ids.append(torrent['id']) + + return torrent_ids + + def __get_cached_torrent_info(self, cached_ids, file_index, season, episode): + cached_torrents = [] + for cached_torrent_id in cached_ids: + cached_torrent_info = self.get_torrent_info(cached_torrent_id) + if self.__torrent_contains_file(cached_torrent_info, file_index, season, episode): + if len(cached_torrent_info["links"]) > 0: # If the links are ready + return cached_torrent_info + + cached_torrents.append(cached_torrent_info) + + if len(cached_torrents) == 0: + return None + + return max(cached_torrents, key=lambda x: x['progress']) + + def __torrent_contains_file(self, torrent_info, file_index, season, episode): + if not torrent_info or "files" not in torrent_info: + return False + + if file_index is None: + for file in torrent_info["files"]: + if file["selected"] and season_episode_in_filename(file['path'], season, episode): + return True + else: + for file in torrent_info["files"]: + if file['id'] == file_index: + return file["selected"] == 1 + + return False + + def __add_magnet_or_torrent(self, magnet, torrent_download=None, anon_magnet=False): + torrent_id = "" + if torrent_download is None: + logger.info(f"Adding magnet to RealDebrid") + if anon_magnet: + magnet = re.sub(r'&tr=[^&]*', '', magnet) + magnet_response = self.add_magnet(magnet) + logger.info(f"RealDebrid add magnet response: {magnet_response}") + + if not magnet_response or 'id' not in magnet_response: + return "Error: Failed to add magnet." + + torrent_id = magnet_response['id'] + elif anon_magnet and torrent_download: + logger.info(f"Adding magnet to RealDebrid") + clean_magnet = re.sub(r'&tr=[^&]*', '', magnet) + magnet_response = self.add_magnet(clean_magnet) + logger.info(f"RealDebrid add magnet response: {magnet_response}") + + if not magnet_response or 'id' not in magnet_response: + return "Error: Failed to add magnet." + + torrent_id = magnet_response['id'] + else: + logger.info(f"Downloading torrent file from Jackett") + torrent_file = self.donwload_torrent_file(torrent_download) + logger.info(f"Torrent file downloaded from Jackett") + + logger.info(f"Adding torrent file to RealDebrid") + upload_response = self.add_torrent(torrent_file) + logger.info(f"RealDebrid add torrent file response: {upload_response}") + + if not upload_response or 'id' not in upload_response: + return "Error: Failed to add torrent file." + + torrent_id = upload_response['id'] + + logger.info(f"New torrent ID: {torrent_id}") + return self.get_torrent_info(torrent_id) + + def __prefetch_season_pack(self, magnet, torrent_download): + torrent_info = self.__add_magnet_or_torrent(magnet, torrent_download) + video_file_indexes = [] + + for file in torrent_info["files"]: + if is_video_file(file["path"]): + video_file_indexes.append(str(file["id"])) + + self.select_files(torrent_info["id"], ",".join(video_file_indexes)) + time.sleep(10) + return self.get_torrent_info(torrent_info["id"]) + + def __select_file(self, torrent_info, stream_type, file_index, season, episode): + torrent_id = torrent_info["id"] + if file_index is not None: + logger.info(f"Selecting file_index: {file_index}") + self.select_files(torrent_id, file_index) + return + + files = torrent_info["files"] + if stream_type == "movie": + largest_file_id = max(files, key=lambda x: x['bytes'])['id'] + logger.info(f"Selecting file_index: {largest_file_id}") + self.select_files(torrent_id, largest_file_id) + elif stream_type == "series": + matching_files = [] + for file in files: + if season_episode_in_filename(file["path"], season, episode): + matching_files.append(file) + + largest_file_id = max(matching_files, key=lambda x: x['bytes'])['id'] + logger.info(f"Selecting file_index: {largest_file_id}") + self.select_files(torrent_id, largest_file_id) + + def __find_appropiate_link(self, torrent_info, links, file_index, season, episode): + selected_files = list(filter(lambda file: file["selected"] == 1, torrent_info["files"])) + + index = 0 + if file_index is not None: + for file in selected_files: + if file["id"] == file_index: + break + index += 1 + else: + matching_indexes = [] + for file in selected_files: + if season_episode_in_filename(file["path"], season, episode): + matching_indexes.append({"index": index, "file": file}) + index += 1 + + index = max(matching_indexes, lambda x: x["file"]["bytes"])["index"] + + if len(links) - 1 < index: + logger.debug(f"From selected files {selected_files}, index: {index} is out of range for {links}.") + return NO_CACHE_VIDEO_URL + + return links[index] diff --git a/stream_fusion/utils/detection.py b/stream_fusion/utils/detection.py new file mode 100644 index 0000000..fb20271 --- /dev/null +++ b/stream_fusion/utils/detection.py @@ -0,0 +1,28 @@ +import re + + +def detect_languages(torrent_name): + language_patterns = { + "fr": r"\b(?:FR(?:ench|a|e|anc[eê]s)?|V(?:O?F(?:F|I|i)?|O?Q)|TRUEFRENCH|VOST(?:FR)?|SUBFRENCH)\b", + "en": r"\b(?:EN(?:G(?:LISH)?)?|VOST(?:EN)?|SUBBED)\b", + "es": r"\b(?:ES(?:P(?:ANISH)?)?|VOSE|SUBESP)\b", + "de": r"\b(?:DE(?:UTSCH|RMAN)?|GER(?:MAN)?|SUBGER)\b", + "it": r"\b(?:IT(?:A(?:LIAN)?)?|SUBITA)\b", + "pt": r"\b(?:PT(?:-BR)?|POR(?:TUGUESE)?|LEGENDADO)\b", + "ru": r"\b(?:RU(?:S(?:SIAN)?)?|SUBSRUS)\b", + "in": r"\b(?:INDIAN|HINDI|TELUGU|TAMIL|KANNADA|MALAYALAM|PUNJABI|MARATHI|BENGALI|GUJARATI|URDU|ODIA|ASSAMESE|KONKANI|MANIPURI|NEPALI|SANSKRIT|SINHALA|SINDHI|TIBETAN|BHOJPURI|DHIVEHI|KASHMIRI|KURUKH|MAITHILI|NEWARI|RAJASTHANI|SANTALI|SINDHI|TULU)\b", + "nl": r"\b(?:NL(?:D)?|DUTCH|SUBSNL)\b", + "hu": r"\b(?:HU(?:N(?:GARIAN)?)?|SUBHUN)\b", + "la": r"\b(?:LA(?:TIN(?:O)?)?)\b", + "multi": r"\b(?:MULTI(?:LANG(?:UE)?)?|DUAL(?:AUDIO)?|VF2)\b", + } + + languages = [] + for language, pattern in language_patterns.items(): + if re.search(pattern, torrent_name, re.IGNORECASE): + languages.append(language) + + if len(languages) == 0: + return ["en"] + + return languages diff --git a/stream_fusion/utils/filter/base_filter.py b/stream_fusion/utils/filter/base_filter.py new file mode 100644 index 0000000..148b31c --- /dev/null +++ b/stream_fusion/utils/filter/base_filter.py @@ -0,0 +1,15 @@ +class BaseFilter: + def __init__(self, config, additional_config=None): + self.config = config + self.item_type = additional_config + + def filter(self, data): + raise NotImplementedError + + def can_filter(self): + raise NotImplementedError + + def __call__(self, data): + if self.config is not None and self.can_filter(): + return self.filter(data) + return data diff --git a/stream_fusion/utils/filter/language_filter.py b/stream_fusion/utils/filter/language_filter.py new file mode 100644 index 0000000..a3b79e3 --- /dev/null +++ b/stream_fusion/utils/filter/language_filter.py @@ -0,0 +1,52 @@ +import re + +from stream_fusion.utils.filter.base_filter import BaseFilter + + +class LanguageFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.fr_regex_patterns = [ + r"^(BlackAngel|Choco|HDForever|MAX|ONLY|Psaro|Sicario|Tezcat74|TyrellCorp|Zapax)$", + r"^(BDHD|FtLi|Goldenyann|HeavyWeight|MARBLECAKE|MUSTANG|Obi|PEPiTE|QUEBEC63|QC63|ROMKENT)$", + r"^(FLOP|FRATERNiTY|FoX|Psaro)$", + r"^(DUSTiN|FCK|FrIeNdS|QUALiTY)$", + r"^(BDHD|FoX|FRATERNiTY|FrIeNdS|MAX|Psaro|T3KASHi)$", + r"^(FUJiSAN|HANAMi|HDForever|HeavyWeight|MARBLECAKE|MYSTERiON|NoNE|ONLY|ONLYMOViE|TkHD|UTT)$", + r"^(BONBON|FCK|FW|FoX|FRATERNiTY|FrIeNdS|MOONLY|MTDK|PATOPESTO|Psaro|T3KASHi|TFA)$", + r"^(ALLDAYiN|ARK01|FUJiSAN|HANAMi|HeavyWeight|NEO|NoNe|ONLYMOViE|Slay3R|TkHD)$", + r"^(4FR|AiR3D|AiRDOCS|AiRFORCE|AiRLiNE|AiRTV|AKLHD|AMB3R)$", + r"^(ANMWR|AVON|AYMO|AZR|BANKAi|BAWLS|BiPOLAR|BLACKPANTERS|BODIE|BOOLZ|BRiNK|CARAPiLS|CiELOS)$", + r"^(CiNEMA|CMBHD|CoRa|COUAC|CRYPT0|D4KiD|DEAL|DiEBEX|DUPLI|DUSS|ENJOi|EUBDS|FHD|FiDELiO|FiDO|ForceBleue)$", + r"^(FREAMON|FRENCHDEADPOOL2|FRiES|FUTiL|FWDHD|GHOULS|GiMBAP|GLiMMER|Goatlove|HERC|HiggsBoson|HiRoSHiMa)$", + r"^(HYBRiS|HyDe|JMT|JoKeR|JUSTICELEAGUE|KAZETV|L0SERNiGHT|LaoZi|LeON|LOFiDEL|LOST|LOWIMDB|LYPSG|MAGiCAL)$", + r"^(MANGACiTY|MAXAGAZ|MaxiBeNoul|McNULTY|MELBA|MiND|MORELAND|MUNSTER|MUxHD|NERDHD|NERO|NrZ|NTK|OBSTACLE)$", + r"^(OohLaLa|OOKAMI|PANZeR|PiNKPANTERS|PKPTRS|PRiDEHD|PROPJOE|PURE|PUREWASTEOFBW|ROUGH|RUDE|Ryotox|SAFETY)$", + r"^(SASHiMi|SEiGHT|SESKAPiLE|SHEEEiT|SHiNiGAMi(UHD)?|SiGeRiS|SILVIODANTE|SLEEPINGFOREST|SODAPOP|S4LVE|SPINE)$", + r"^(SPOiLER|STRINGERBELL|SUNRiSE|tFR|THENiGHTMAREiNHD|THiNK|THREESOME|TiMELiNE|TSuNaMi|UKDHD|UKDTV|ULSHD|Ulysse)$", + r"^(USUNSKiLLED|URY|VENUE|VFC|VoMiT|Wednesday29th|ZEST|ZiRCON)$", + ] + self.fr_regex = re.compile("|".join(self.fr_regex_patterns)) + + def filter(self, data): + filtered_data = [] + for torrent in data: + if not torrent.languages: + continue + + languages = torrent.languages.copy() + + if torrent.from_cache and "multi" in languages: + if self.fr_regex.search(torrent.raw_title): + languages.remove("multi") + + if "multi" in languages or any( + lang in self.config["languages"] for lang in languages + ): + torrent.languages = languages + filtered_data.append(torrent) + + return filtered_data + + def can_filter(self): + return self.config["languages"] is not None diff --git a/stream_fusion/utils/filter/max_size_filter.py b/stream_fusion/utils/filter/max_size_filter.py new file mode 100644 index 0000000..cf43f60 --- /dev/null +++ b/stream_fusion/utils/filter/max_size_filter.py @@ -0,0 +1,23 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class MaxSizeFilter(BaseFilter): + def __init__(self, config, additional_config=None): + super().__init__(config, additional_config) + self.max_size_bytes = int(self.config['maxSize']) * 1024 * 1024 * 1024 # Convertir Go en octets + + def filter(self, data): + filtered_data = [] + for torrent in data: + torrent_size = int(torrent.size) if isinstance(torrent.size, str) else torrent.size + if torrent_size <= self.max_size_bytes: + filtered_data.append(torrent) + else: + logger.debug(f"Excluded torrent due to size: {torrent.raw_title}, Size: {torrent_size / (1024*1024*1024):.2f} GB") + logger.info(f"MaxSizeFilter: input {len(data)}, output {len(filtered_data)}") + return filtered_data + + def can_filter(self): + return int(self.config['maxSize']) > 0 and self.item_type == 'movie' + diff --git a/stream_fusion/utils/filter/quality_exclusion_filter.py b/stream_fusion/utils/filter/quality_exclusion_filter.py new file mode 100644 index 0000000..3ad7ac7 --- /dev/null +++ b/stream_fusion/utils/filter/quality_exclusion_filter.py @@ -0,0 +1,43 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class QualityExclusionFilter(BaseFilter): + RIPS = {"HDRIP", "BRRIP", "BDRIP", "WEBRIP", "TVRIP", "VODRIP"} + CAMS = {"CAM", "TS", "TC", "R5", "DVDSCR", "HDTV", "PDTV", "DSR", "WORKPRINT", "VHSRIP", "HDCAM"} + + def __init__(self, config: dict): + super().__init__(config) + self.excluded_qualities = {quality.upper() for quality in self.config.get('exclusion', [])} + self.exclude_rips = "RIPS" in self.excluded_qualities + self.exclude_cams = "CAM" in self.excluded_qualities + + def filter(self, data): + return [ + stream for stream in data + if self._is_stream_allowed(stream) + ] + + def _is_stream_allowed(self, stream) -> bool: + if any(q.upper() in self.excluded_qualities for q in stream.parsed_data.quality): + logger.debug(f"Stream excluded due to main quality: {stream.parsed_data.quality}") + return False + + if stream.parsed_data.resolution: + for item in stream.parsed_data.resolution: + item_upper = item.upper() + if item_upper in self.excluded_qualities: + logger.debug(f"Stream excluded due to quality spec: {item}") + return False + if self.exclude_rips and item_upper in self.RIPS: + logger.debug(f"Stream excluded due to RIP: {item}") + return False + if self.exclude_cams and item_upper in self.CAMS: + logger.debug(f"Stream excluded due to CAM: {item}") + return False + + logger.debug("Stream allowed") + return True + + def can_filter(self) -> bool: + return bool(self.excluded_qualities) diff --git a/stream_fusion/utils/filter/results_per_quality_filter.py b/stream_fusion/utils/filter/results_per_quality_filter.py new file mode 100644 index 0000000..19f4205 --- /dev/null +++ b/stream_fusion/utils/filter/results_per_quality_filter.py @@ -0,0 +1,33 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class ResultsPerQualityFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.max_results_per_quality = int(self.config.get('resultsPerQuality', 5)) + + def filter(self, data): + filtered_items = [] + resolution_count = {} + + for item in data: + resolutions = getattr(item.parsed_data, 'resolution', []) + if not resolutions: + resolutions = ["?.BZH.?"] + + for resolution in resolutions: + if resolution not in resolution_count: + resolution_count[resolution] = 1 + filtered_items.append(item) + break + elif resolution_count[resolution] < self.max_results_per_quality: + resolution_count[resolution] += 1 + filtered_items.append(item) + break + + logger.info(f"ResultsPerQualityFilter: input {len(data)}, output {len(filtered_items)}") + return filtered_items + + def can_filter(self): + return self.max_results_per_quality > 0 diff --git a/stream_fusion/utils/filter/title_exclusion_filter.py b/stream_fusion/utils/filter/title_exclusion_filter.py new file mode 100644 index 0000000..c11c51d --- /dev/null +++ b/stream_fusion/utils/filter/title_exclusion_filter.py @@ -0,0 +1,32 @@ +from stream_fusion.utils.filter.base_filter import BaseFilter +from stream_fusion.logging_config import logger + + +class TitleExclusionFilter(BaseFilter): + def __init__(self, config): + super().__init__(config) + self.excluded_keywords = {keyword.upper() for keyword in self.config.get('exclusionKeywords', [])} + + def filter(self, data): + filtered_items = [] + for stream in data: + if self._should_include_stream(stream): + filtered_items.append(stream) + + logger.info(f"TitleExclusionFilter: input {len(data)}, output {len(filtered_items)}") + return filtered_items + + def _should_include_stream(self, stream): + try: + title_upper = stream.raw_title.upper() + for keyword in self.excluded_keywords: + if keyword in title_upper: + logger.debug(f"Excluded stream: {stream.raw_title} (keyword: {keyword})") + return False + return True + except AttributeError: + logger.warning(f"Stream has no title attribute: {stream}") + return False + + def can_filter(self): + return bool(self.excluded_keywords) diff --git a/stream_fusion/utils/filter_results.py b/stream_fusion/utils/filter_results.py new file mode 100644 index 0000000..d266f2f --- /dev/null +++ b/stream_fusion/utils/filter_results.py @@ -0,0 +1,196 @@ +import re +from typing import List + +from RTN import title_match, RTN, DefaultRanking, SettingsModel, sort_torrents + +from stream_fusion.utils.filter.language_filter import LanguageFilter +from stream_fusion.utils.filter.max_size_filter import MaxSizeFilter +from stream_fusion.utils.filter.quality_exclusion_filter import QualityExclusionFilter +from stream_fusion.utils.filter.results_per_quality_filter import ResultsPerQualityFilter +from stream_fusion.utils.filter.title_exclusion_filter import TitleExclusionFilter +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger + +quality_order = {"4k": 0, "2160p": 0, "1080p": 1, "720p": 2, "480p": 3} + + +def sort_quality(item): + logger.debug(f"Evaluating quality for item: {item.raw_title}") + if len(item.parsed_data.data.resolution) == 0: + return float("inf"), True + resolution = item.parsed_data.data.resolution[0] + priority = quality_order.get(resolution, float("inf")) + return priority, item.parsed_data.data.resolution is None + + +def items_sort(items, config): + logger.info(f"Starting item sorting. Sort method: {config['sort']}") + settings = SettingsModel( + require=[], + exclude=config["exclusionKeywords"] + config["exclusion"], + preferred=[], + ) + + rtn = RTN(settings=settings, ranking_model=DefaultRanking()) + logger.debug("Applying RTN ranking to items") + torrents = [rtn.rank(item.raw_title, item.info_hash) for item in items] + sorted_torrents = sort_torrents(set(torrents)) + + for key, value in sorted_torrents.items(): + index = next((i for i, item in enumerate(items) if item.info_hash == key), None) + if index is not None: + items[index].parsed_data = value + + logger.info(f"Sorting items by method: {config['sort']}") + if config["sort"] == "quality": + sorted_items = sorted(items, key=sort_quality) + elif config["sort"] == "sizeasc": + sorted_items = sorted(items, key=lambda x: int(x.size)) + elif config["sort"] == "sizedesc": + sorted_items = sorted(items, key=lambda x: int(x.size), reverse=True) + elif config["sort"] == "qualitythensize": + sorted_items = sorted(items, key=lambda x: (sort_quality(x), -int(x.size))) + else: + logger.warning( + f"Unrecognized sort method: {config['sort']}. No sorting applied." + ) + sorted_items = items + + logger.info(f"Sorting complete. Number of sorted items: {len(sorted_items)}") + return sorted_items + +def filter_out_non_matching_movies(items, year): + logger.info(f"Filtering non-matching movies for year : {year}") + year_pattern = re.compile(rf'\b{year}\b') + filtered_items = [] + for item in items: + logger.debug(f"Checking item: {item.raw_title}") + if year_pattern.search(item.raw_title): + logger.debug("Match found") + filtered_items.append(item) + else: + logger.debug("No match found") + return filtered_items + +def filter_out_non_matching_series(items, season, episode): + logger.info( + f"Filtering non-matching items for season {season} and episode {episode}" + ) + filtered_items = [] + clean_season = season.replace("S", "") + clean_episode = episode.replace("E", "") + numeric_season = int(clean_season) + numeric_episode = int(clean_episode) + + for item in items: + logger.debug(f"Checking item: {item.raw_title}") + if len(item.parsed_data.season) == 0 and len(item.parsed_data.episode) == 0: + logger.debug("Item with no season and episode, skipped") + continue + if ( + len(item.parsed_data.episode) == 0 + and numeric_season in item.parsed_data.season + ): + logger.debug("Season match found, episode not specified") + filtered_items.append(item) + continue + if ( + numeric_season in item.parsed_data.season + and numeric_episode in item.parsed_data.episode + ): + logger.debug("Exact season and episode match found") + filtered_items.append(item) + continue + + logger.info( + f"Filtering complete. {len(filtered_items)} matching items found out of {len(items)} total" + ) + return filtered_items + + +def remove_non_matching_title(items, titles): + logger.info(f"Removing items not matching titles: {titles}") + filtered_items = [] + for item in items: + for title in titles: + if title_match(title, item.parsed_data.parsed_title): + filtered_items.append(item) + break + else: + logger.debug("No title match found, item skipped") + + logger.info( + f"Title filtering complete. {len(filtered_items)} items kept out of {len(items)} total" + ) + return filtered_items + + +def filter_items(items, media, config): + logger.info(f"Starting item filtering for media: {media.titles[0]}") + filters = { + "languages": LanguageFilter(config), + "maxSize": MaxSizeFilter(config, media.type), + "exclusionKeywords": TitleExclusionFilter(config), + "exclusion": QualityExclusionFilter(config), + "resultsPerQuality": ResultsPerQualityFilter(config), + } + + logger.info(f"Initial item count: {len(items)}") + + if media.type == "series": + logger.info(f"Filtering out non-matching series torrents") + items = filter_out_non_matching_series(items, media.season, media.episode) + logger.info(f"Item count after season/episode filtering: {len(items)}") + + if media.type == "movie": + logger.info(f"Filtering out non-matching movie torrents") + items = filter_out_non_matching_movies(items, media.year) + logger.info(f"Item count after year filtering: {len(items)}") + + items = remove_non_matching_title(items, media.titles) + logger.info(f"Item count after title filtering: {len(items)}") + + for filter_name, filter_instance in filters.items(): + try: + logger.info(f"Applying {filter_name} filter: {config[filter_name]}") + items = filter_instance(items) + logger.info(f"Item count after {filter_name} filter: {len(items)}") + except Exception as e: + logger.error(f"Error while applying {filter_name} filter", exc_info=e) + + logger.info(f"Filtering complete. Final item count: {len(items)}") + return items + + +def sort_items(items, config): + if config["sort"] is not None: + logger.info(f"Sorting items according to config: {config['sort']}") + return items_sort(items, config) + else: + logger.info("No sorting specified, returning items in original order") + return items + + +def merge_items( + cache_items: List[TorrentItem], search_items: List[TorrentItem] +) -> List[TorrentItem]: + logger.info( + f"Merging cached items ({len(cache_items)}) and search items ({len(search_items)})" + ) + merged_dict = {} + + def add_to_merged(item): + if item.raw_title not in merged_dict: + merged_dict[item.raw_title] = item + else: + if item.seeders > merged_dict[item.raw_title].seeders: + merged_dict[item.raw_title] = item + + for item in cache_items: + add_to_merged(item) + for item in search_items: + add_to_merged(item) + + merged_items = list(merged_dict.values()) + logger.info(f"Merging complete. Total unique items: {len(merged_items)}") + return merged_items diff --git a/stream_fusion/utils/general.py b/stream_fusion/utils/general.py new file mode 100644 index 0000000..55db098 --- /dev/null +++ b/stream_fusion/utils/general.py @@ -0,0 +1,42 @@ +from RTN import parse + +from stream_fusion.logging_config import logger + +video_formats = {".mkv", ".mp4", ".avi", ".mov", ".flv", ".wmv", ".webm", ".mpg", ".mpeg", ".m4v", ".3gp", ".3g2", + ".ogv", + ".ogg", ".drc", ".gif", ".gifv", ".mng", ".avi", ".mov", ".qt", ".wmv", ".yuv", ".rm", ".rmvb", ".asf", + ".amv", ".m4p", ".m4v", ".mpg", ".mp2", ".mpeg", ".mpe", ".mpv", ".mpg", ".mpeg", ".m2v", ".m4v", + ".svi", ".3gp", ".3g2", ".mxf", ".roq", ".nsv", ".flv", ".f4v", ".f4p", ".f4a", ".f4b"} + + +def season_episode_in_filename(filename, season, episode): + if not is_video_file(filename): + return False + + parsed_name = parse(filename) + + return season in parsed_name.season and episode in parsed_name.episode + + +def get_info_hash_from_magnet(magnet: str): + exact_topic_index = magnet.find("xt=") + if exact_topic_index == -1: + logger.debug(f"No exact topic in magnet {magnet}") + return None + + exact_topic_substring = magnet[exact_topic_index:] + end_of_exact_topic = exact_topic_substring.find("&") + if end_of_exact_topic != -1: + exact_topic_substring = exact_topic_substring[:end_of_exact_topic] + + info_hash = exact_topic_substring[exact_topic_substring.rfind(":") + 1:] + + return info_hash.lower() + + +def is_video_file(filename): + extension_idx = filename.rfind(".") + if extension_idx == -1: + return False + + return filename[extension_idx:] in video_formats diff --git a/stream_fusion/utils/jackett/__init__.py b/stream_fusion/utils/jackett/__init__.py new file mode 100644 index 0000000..b47c95e --- /dev/null +++ b/stream_fusion/utils/jackett/__init__.py @@ -0,0 +1,6 @@ +from stream_fusion.utils.jackett.jackett_indexer import JackettIndexer +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.jackett.jackett_service import JackettService + + +__all__ = ["JackettIndexer", "JackettResult", "JackettService"] \ No newline at end of file diff --git a/stream_fusion/utils/jackett/jackett_indexer.py b/stream_fusion/utils/jackett/jackett_indexer.py new file mode 100644 index 0000000..12646ba --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_indexer.py @@ -0,0 +1,9 @@ +class JackettIndexer: + def __init__(self): + self.title = None + self.id = None + self.link = None + self.type = None + self.language = None + self.tv_search_capatabilities = None + self.movie_search_capatabilities = None diff --git a/stream_fusion/utils/jackett/jackett_result.py b/stream_fusion/utils/jackett/jackett_result.py new file mode 100644 index 0000000..7375232 --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_result.py @@ -0,0 +1,61 @@ +from RTN import parse + +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger + +class JackettResult: + def __init__(self): + self.raw_title = None # Raw title of the torrent + self.size = None # Size of the torrent + self.link = None # Download link for the torrent file or magnet url + self.indexer = None # Indexer + self.seeders = None # Seeders count + self.magnet = None # Magnet url + self.info_hash = None # infoHash by Jackett + self.privacy = None # public or private + self.from_cache = None + + # Extra processed details for further filtering + self.languages = None # Language of the torrent + self.type = None # series or movie + + self.parsed_data = None # Ranked result + + def convert_to_torrent_item(self): + return TorrentItem( + self.raw_title, + self.size, + self.magnet, + self.info_hash.lower() if self.info_hash is not None else None, + self.link, + self.seeders, + self.languages, + self.indexer, + self.privacy, + self.type, + self.parsed_data + ) + + def from_cached_item(self, cached_item, media): + if type(cached_item) is not dict: + logger.error(cached_item) + + self.info_hash = cached_item['hash'] + + if len(self.info_hash) != 40: + raise ValueError(f"The hash '{self.info_hash}' does not have the expected length of 40 characters.") + + parsed_result = parse(cached_item['title']) + + self.raw_title = cached_item['title'] + self.indexer = "Cache Public" # Cache doesn't return an indexer sadly (It stores it tho) + self.magnet = cached_item['magnet'] + self.link = cached_item['magnet'] + self.languages = cached_item['language'].split(";") if cached_item['language'] is not None else [] + self.seeders = cached_item['seeders'] + self.size = cached_item['size'] + self.type = media.type + self.from_cache = True + self.parsed_data = parsed_result + + return self diff --git a/stream_fusion/utils/jackett/jackett_service.py b/stream_fusion/utils/jackett/jackett_service.py new file mode 100644 index 0000000..c5b086c --- /dev/null +++ b/stream_fusion/utils/jackett/jackett_service.py @@ -0,0 +1,279 @@ +import os +import queue +import threading +import time +import xml.etree.ElementTree as ET + +import requests +from RTN import parse, ParsedData + +from stream_fusion.utils.jackett.jackett_indexer import JackettIndexer +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.detection import detect_languages +from stream_fusion.logging_config import logger + + +class JackettService: + def __init__(self, config): + self.logger = logger + + self.__api_key = config['jackettApiKey'] + self.__base_url = f"{config['jackettHost']}/api/v2.0" + self.__session = requests.Session() + + def search(self, media): + self.logger.info("Started Jackett search for " + media.type + " " + media.titles[0]) + + indexers = self.__get_indexers() + threads = [] + results_queue = queue.Queue() # Create a Queue instance to hold the results + + # Define a wrapper function that calls the actual target function and stores its return value in the queue + def thread_target(media, indexer): + self.logger.info(f"Searching on {indexer.title}") + start_time = time.time() + + # Call the actual function + if isinstance(media, Movie): + result = self.__search_movie_indexer(media, indexer) + elif isinstance(media, Series): + result = self.__search_series_indexer(media, indexer) + else: + raise TypeError("Only Movie and Series is allowed as media!") + + self.logger.info( + f"Search on {indexer.title} took {time.time() - start_time} seconds and found {len([result for sublist in result for result in sublist])} results") + + results_queue.put(result) # Put the result in the queue + + for indexer in indexers: + # Pass the wrapper function as the target to Thread, with necessary arguments + threads.append(threading.Thread(target=thread_target, args=(media, indexer))) + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + results = [] + + # Retrieve results from the queue and append them to the results list + while not results_queue.empty(): + results.extend(results_queue.get()) + + flatten_results = [result for sublist in results for result in sublist] + + return self.__post_process_results(flatten_results, media) + + def __search_movie_indexer(self, movie, indexer): + + # url = f"{self.__base_url}/indexers/all/results/torznab/api?apikey={self.__api_key}&t=movie&cat=2000&q={movie.title}&year={movie.year}" + + has_imdb_search_capability = (os.getenv( + "DISABLE_JACKETT_IMDB_SEARCH") != "true" and indexer.movie_search_capatabilities is not None and 'imdbid' in indexer.movie_search_capatabilities) + + if has_imdb_search_capability: + languages = ['en'] + index_of_language = [index for index, lang in enumerate(movie.languages) if lang == 'en'][0] + titles = [movie.titles[index_of_language]] + elif indexer.language == "en": + languages = movie.languages + titles = movie.titles + else: + index_of_language = [index for index, lang in enumerate(movie.languages) if + lang == indexer.language or lang == 'en'] + languages = [movie.languages[index] for index in index_of_language] + titles = [movie.titles[index] for index in index_of_language] + + results = [] + + for index, lang in enumerate(languages): + params = { + 'apikey': self.__api_key, + 't': 'movie', + 'cat': '2000', + 'q': titles[index], + 'year': movie.year, + } + + if has_imdb_search_capability: + params['imdbid'] = movie.id + + url = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + url += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + try: + response = self.__session.get(url) + response.raise_for_status() + results.append(self.__get_torrent_links_from_xml(response.text)) + except Exception: + self.logger.exception( + f"An exception occured while searching for a movie on Jackett with indexer {indexer.title} and " + f"language {lang}.") + + return results + + def __search_series_indexer(self, series, indexer): + season = str(int(series.season.replace('S', ''))) + episode = str(int(series.episode.replace('E', ''))) + + has_imdb_search_capability = (os.getenv("DISABLE_JACKETT_IMDB_SEARCH") != "true" + and indexer.tv_search_capatabilities is not None + and 'imdbid' in indexer.tv_search_capatabilities) + if has_imdb_search_capability: + languages = ['en'] + index_of_language = [index for index, lang in enumerate(series.languages) if lang == 'en'][0] + titles = [series.titles[index_of_language]] + elif indexer.language == "en": + languages = series.languages + titles = series.titles + else: + index_of_language = [index for index, lang in enumerate(series.languages) if + lang == indexer.language or lang == 'en'] + languages = [series.languages[index] for index in index_of_language] + titles = [series.titles[index] for index in index_of_language] + + results = [] + + for index, lang in enumerate(languages): + params = { + 'apikey': self.__api_key, + 't': 'tvsearch', + 'cat': '5000', + 'q': titles[index], + } + + if has_imdb_search_capability: + params['imdbid'] = series.id + + url_title = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + url_title += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + url_season = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + params['season'] = season + url_season += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + url_ep = f"{self.__base_url}/indexers/{indexer.id}/results/torznab/api" + params['ep'] = episode + url_ep += '?' + '&'.join([f'{k}={v}' for k, v in params.items()]) + + try: + # Current functionality is that it returns if the season, episode search was successful. This is subject to change + # TODO: what should we prioritize? season, episode or title? + response_ep = self.__session.get(url_ep) + response_ep.raise_for_status() + + response_season = self.__session.get(url_season) + response_season.raise_for_status() + + data_ep = self.__get_torrent_links_from_xml(response_ep.text) + data_season = self.__get_torrent_links_from_xml(response_season.text) + + if data_ep: + results.append(data_ep) + if data_season: + results.append(data_season) + + if not data_ep and not data_season: + response_title = self.__session.get(url_title) + response_title.raise_for_status() + data_title = self.__get_torrent_links_from_xml(response_title.text) + if data_title: + results.append(data_title) + except Exception: + self.logger.exception( + f"An exception occured while searching for a series on Jackett with indexer {indexer.title} and language {lang}.") + + return results + + def __get_indexers(self): + url = f"{self.__base_url}/indexers/all/results/torznab/api?apikey={self.__api_key}&t=indexers&configured=true" + + try: + response = self.__session.get(url) + response.raise_for_status() + return self.__get_indexer_from_xml(response.text) + except Exception: + self.logger.exception("An exception occured while getting indexers from Jackett.") + return [] + + def __get_indexer_from_xml(self, xml_content): + xml_root = ET.fromstring(xml_content) + + indexer_list = [] + for item in xml_root.findall('.//indexer'): + indexer = JackettIndexer() + + indexer.title = item.find('title').text + indexer.id = item.attrib['id'] + indexer.link = item.find('link').text + indexer.type = item.find('type').text + indexer.language = item.find('language').text.split('-')[0] + + self.logger.info(f"Indexer: {indexer.title} - {indexer.link} - {indexer.type}") + + movie_search = item.find('.//searching/movie-search[@available="yes"]') + tv_search = item.find('.//searching/tv-search[@available="yes"]') + + if movie_search is not None: + indexer.movie_search_capatabilities = movie_search.attrib['supportedParams'].split(',') + else: + self.logger.info(f"Movie search not available for {indexer.title}") + + if tv_search is not None: + indexer.tv_search_capatabilities = tv_search.attrib['supportedParams'].split(',') + else: + self.logger.info(f"TV search not available for {indexer.title}") + + indexer_list.append(indexer) + + return indexer_list + + def __get_torrent_links_from_xml(self, xml_content): + xml_root = ET.fromstring(xml_content) + + result_list = [] + for item in xml_root.findall('.//item'): + result = JackettResult() + + result.seeders = item.find('.//torznab:attr[@name="seeders"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}).attrib['value'] + if int(result.seeders) <= 0: + continue + + result.raw_title = item.find('title').text + result.size = item.find('size').text + result.link = item.find('link').text + result.indexer = item.find('jackettindexer').text + result.privacy = item.find('type').text + + # TODO: I haven't seen this in the Jackett XML response. Is this still relevant? + # Or which indexers provide this? + magnet = item.find('.//torznab:attr[@name="magneturl"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}) + result.magnet = magnet.attrib['value'] if magnet is not None else None + + infoHash = item.find('.//torznab:attr[@name="infohash"]', + namespaces={'torznab': 'http://torznab.com/schemas/2015/feed'}) + result.info_hash = infoHash.attrib['value'] if infoHash is not None else None + + result_list.append(result) + + return result_list + + def __post_process_results(self, results, media): + for result in results: + parsed_result = parse(result.raw_title) + + result.parsed_data = parsed_result + result.languages = detect_languages(result.raw_title) + result.type = media.type + + if isinstance(media, Series): + result.season = media.season + result.episode = media.episode + + return results diff --git a/stream_fusion/utils/metdata/cinemeta.py b/stream_fusion/utils/metdata/cinemeta.py new file mode 100644 index 0000000..b8dd5f2 --- /dev/null +++ b/stream_fusion/utils/metdata/cinemeta.py @@ -0,0 +1,35 @@ +import requests + +from stream_fusion.utils.metdata.metadata_provider_base import MetadataProvider +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + + +class Cinemeta(MetadataProvider): + def get_metadata(self, id, type): + self.logger.info("Getting metadata for " + type + " with id " + id) + + full_id = id.split(":") + + url = f"https://v3-cinemeta.strem.io/meta/{type}/{full_id[0]}.json" + response = requests.get(url) + data = response.json() + + if type == "movie": + result = Movie( + id=id, + titles=[self.replace_weird_characters(data["meta"]["name"])], + year=data["meta"]["year"], + languages=["en"] + ) + else: + result = Series( + id=id, + titles=[self.replace_weird_characters(data["meta"]["name"])], + season="S{:02d}".format(int(full_id[1])), + episode="E{:02d}".format(int(full_id[2])), + languages=["en"] + ) + + self.logger.info("Got metadata for " + type + " with id " + id) + return result diff --git a/stream_fusion/utils/metdata/metadata_provider_base.py b/stream_fusion/utils/metdata/metadata_provider_base.py new file mode 100644 index 0000000..c43f93e --- /dev/null +++ b/stream_fusion/utils/metdata/metadata_provider_base.py @@ -0,0 +1,35 @@ +from stream_fusion.logging_config import logger + + +class MetadataProvider: + + def __init__(self, config): + self.config = config + self.logger = logger + + def replace_weird_characters(self, string): + corresp = { + 'ā': 'a', 'ă': 'a', 'ą': 'a', 'ć': 'c', 'č': 'c', 'ç': 'c', + 'ĉ': 'c', 'ċ': 'c', 'ď': 'd', 'đ': 'd', 'è': 'e', 'é': 'e', + 'ê': 'e', 'ë': 'e', 'ē': 'e', 'ĕ': 'e', 'ę': 'e', 'ě': 'e', + 'ĝ': 'g', 'ğ': 'g', 'ġ': 'g', 'ģ': 'g', 'ĥ': 'h', 'î': 'i', + 'ï': 'i', 'ì': 'i', 'í': 'i', 'ī': 'i', 'ĩ': 'i', 'ĭ': 'i', + 'ı': 'i', 'ĵ': 'j', 'ķ': 'k', 'ĺ': 'l', 'ļ': 'l', 'ł': 'l', + 'ń': 'n', 'ň': 'n', 'ñ': 'n', 'ņ': 'n', 'ʼn': 'n', 'ó': 'o', + 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ø': 'o', 'ō': 'o', 'ő': 'o', + 'œ': 'oe', 'ŕ': 'r', 'ř': 'r', 'ŗ': 'r', 'š': 's', 'ş': 's', + 'ś': 's', 'ș': 's', 'ß': 'ss', 'ť': 't', 'ţ': 't', 'ū': 'u', + 'ŭ': 'u', 'ũ': 'u', 'û': 'u', 'ü': 'u', 'ù': 'u', 'ú': 'u', + 'ų': 'u', 'ű': 'u', 'ŵ': 'w', 'ý': 'y', 'ÿ': 'y', 'ŷ': 'y', + 'ž': 'z', 'ż': 'z', 'ź': 'z', 'æ': 'ae', 'ǎ': 'a', 'ǧ': 'g', + 'ə': 'e', 'ƒ': 'f', 'ǐ': 'i', 'ǒ': 'o', 'ǔ': 'u', 'ǚ': 'u', + 'ǜ': 'u', 'ǹ': 'n', 'ǻ': 'a', 'ǽ': 'ae', 'ǿ': 'o', + } + + for weird_char in corresp: + string = string.replace(weird_char, corresp[weird_char]) + + return string + + def get_metadata(self, id, type): + raise NotImplementedError diff --git a/stream_fusion/utils/metdata/tmdb.py b/stream_fusion/utils/metdata/tmdb.py new file mode 100644 index 0000000..0aed92d --- /dev/null +++ b/stream_fusion/utils/metdata/tmdb.py @@ -0,0 +1,43 @@ +import requests + +from stream_fusion.utils.metdata.metadata_provider_base import MetadataProvider +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + +class TMDB(MetadataProvider): + def get_metadata(self, id, type): + self.logger.info("Getting metadata for " + type + " with id " + id) + + full_id = id.split(":") + + result = None + + for lang in self.config['languages']: + url = f"https://api.themoviedb.org/3/find/{full_id[0]}?api_key={self.config['tmdbApi']}&external_source=imdb_id&language={lang}" + response = requests.get(url) + data = response.json() + + if lang == self.config['languages'][0]: + if type == "movie": + result = Movie( + id=id, + titles=[self.replace_weird_characters(data["movie_results"][0]["title"])], + year=data["movie_results"][0]["release_date"][:4], + languages=self.config['languages'] + ) + else: + result = Series( + id=id, + titles=[self.replace_weird_characters(data["tv_results"][0]["name"])], + season="S{:02d}".format(int(full_id[1])), + episode="E{:02d}".format(int(full_id[2])), + languages=self.config['languages'] + ) + else: + if type == "movie": + result.titles.append(self.replace_weird_characters(data["movie_results"][0]["title"])) + else: + result.titles.append(self.replace_weird_characters(data["tv_results"][0]["name"])) + + self.logger.info("Got metadata for " + type + " with id " + id) + return result diff --git a/stream_fusion/utils/models/media.py b/stream_fusion/utils/models/media.py new file mode 100644 index 0000000..43398b3 --- /dev/null +++ b/stream_fusion/utils/models/media.py @@ -0,0 +1,6 @@ +class Media: + def __init__(self, id, titles, languages, type): + self.id = id + self.titles = titles + self.languages = languages + self.type = type diff --git a/stream_fusion/utils/models/movie.py b/stream_fusion/utils/models/movie.py new file mode 100644 index 0000000..121c458 --- /dev/null +++ b/stream_fusion/utils/models/movie.py @@ -0,0 +1,7 @@ +from stream_fusion.utils.models.media import Media + + +class Movie(Media): + def __init__(self, id, titles, year, languages): + super().__init__(id, titles, languages, "movie") + self.year = year diff --git a/stream_fusion/utils/models/series.py b/stream_fusion/utils/models/series.py new file mode 100644 index 0000000..c348de1 --- /dev/null +++ b/stream_fusion/utils/models/series.py @@ -0,0 +1,9 @@ +from stream_fusion.utils.models.media import Media + + +class Series(Media): + def __init__(self, id, titles, season, episode, languages): + super().__init__(id, titles, languages, "series") + self.season = season + self.episode = episode + self.seasonfile = None diff --git a/stream_fusion/utils/parse_config.py b/stream_fusion/utils/parse_config.py new file mode 100644 index 0000000..54389ef --- /dev/null +++ b/stream_fusion/utils/parse_config.py @@ -0,0 +1,16 @@ +import json + +from stream_fusion.utils.string_encoding import decodeb64 + + +def parse_config(b64config): + config = json.loads(decodeb64(b64config)) + + # For backwards compatibility + if "languages" not in config: + config["languages"] = [config["language"]] + + if "anonymizeMagnets" not in config: + config["anonymizeMagnets"] = False # Default to False if not specified + + return config diff --git a/stream_fusion/utils/security/__init__.py b/stream_fusion/utils/security/__init__.py new file mode 100644 index 0000000..931a7f7 --- /dev/null +++ b/stream_fusion/utils/security/__init__.py @@ -0,0 +1,5 @@ +"""Security utils.""" +from stream_fusion.utils.security.security_api_key import api_key_security +from stream_fusion.utils.security.security_secret import secret_based_security + +__all__ = ["secret_based_security", "api_key_security"] diff --git a/stream_fusion/utils/security/security_api_key.py b/stream_fusion/utils/security/security_api_key.py new file mode 100644 index 0000000..f225e3c --- /dev/null +++ b/stream_fusion/utils/security/security_api_key.py @@ -0,0 +1,43 @@ +"""Main dependency for other endpoints.""" +from fastapi import Security +from fastapi.security import APIKeyHeader, APIKeyQuery +from starlette.exceptions import HTTPException +from starlette.status import HTTP_403_FORBIDDEN + +from stream_fusion.services.security_db import security_db_access + +API_KEY_NAME = "api-key" + +api_key_query = APIKeyQuery( + name=API_KEY_NAME, + scheme_name="API key query", + auto_error=False, +) +api_key_header = APIKeyHeader( + name=API_KEY_NAME, + scheme_name="API key header", + auto_error=False, +) + + +async def api_key_security( + query_param: str = Security(api_key_query), + header_param: str = Security(api_key_header), +): + if not query_param and not header_param: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="An API key must be passed as query or header", + ) + + elif query_param and security_db_access.check_key(query_param): + return query_param + + elif header_param and security_db_access.check_key(header_param): + return header_param + + else: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="Wrong, revoked, or expired API key.", + ) diff --git a/stream_fusion/utils/security/security_secret.py b/stream_fusion/utils/security/security_secret.py new file mode 100644 index 0000000..956c688 --- /dev/null +++ b/stream_fusion/utils/security/security_secret.py @@ -0,0 +1,99 @@ +"""Secret dependency. +""" +import secrets +import uuid +from typing import Optional + +from fastapi import Security +from fastapi.security import APIKeyHeader +from starlette.exceptions import HTTPException +from starlette.status import HTTP_403_FORBIDDEN + +from stream_fusion.logging_config import logger +from stream_fusion.settings import settings + + +class GhostLoadedSecret: + """Ghost-loaded secret handler""" + + def __init__(self) -> None: + self._secret = None + + @property + def value(self): + if self._secret: + return self._secret + + else: + self._secret = self.get_secret_value() + return self.value + + def get_secret_value(self): + """Get secret value from environment variable or generate a new one.""" + if settings.secret_api_key: + try: + secret_value = settings.secret_api_key + except KeyError: + secret_value = str(uuid.uuid4()) + logger.warning( + ( + "ENVIRONMENT VARIABLE 'RD_SYNCRR_secret_api_key' FORMAT" + " INCORECT\n\tGenerated a single-use secret key for this" + f" session:\n\t{secret_value=}" + ), + ) + else: + secret_value = str(uuid.uuid4()) + logger.warning( + ( + "ENVIRONMENT VARIABLE 'RD_SYNCRR_secret_api_key' NOT FOUND\n" + "\tGenerated a single-use secret key for this session:\n" + f"\t{secret_value=}" + ), + ) + + return secret_value + + +secret = GhostLoadedSecret() + +SECRET_KEY_NAME = "secret-key" # noqa: S105 + +secret_header = APIKeyHeader( + name=SECRET_KEY_NAME, + scheme_name="Secret header", + auto_error=False, +) + + +async def secret_based_security(header_param: Optional[str] = Security(secret_header)): + """ + Args: + header_param: parsed header field secret_header + + Returns: + True if the authentication was successful + + Raises: + HTTPException if the authentication failed + """ + + if not header_param: + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail="secret_key must be passed as a header field", + ) + + # We simply return True if the given secret-key has the right value + if not secrets.compare_digest(header_param, secret.value): + raise HTTPException( + status_code=HTTP_403_FORBIDDEN, + detail=( + "Wrong secret key. If not set through environment variable" + " 'secret_api_key', it was generated automatically at" + " startup and appears in the server logs." + ), + ) + + else: + return True diff --git a/stream_fusion/utils/string_encoding.py b/stream_fusion/utils/string_encoding.py new file mode 100644 index 0000000..0f182fb --- /dev/null +++ b/stream_fusion/utils/string_encoding.py @@ -0,0 +1,9 @@ +import base64 + + +def encodeb64(data): + return base64.b64encode(data.encode('utf-8')).decode('utf-8') + + +def decodeb64(data): + return base64.b64decode(data).decode('utf-8') diff --git a/stream_fusion/utils/torrent/torrent_item.py b/stream_fusion/utils/torrent/torrent_item.py new file mode 100644 index 0000000..b1737d5 --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_item.py @@ -0,0 +1,93 @@ +from RTN import parse +from urllib.parse import quote + +from stream_fusion.utils.models.media import Media +from stream_fusion.utils.models.series import Series +from stream_fusion.logging_config import logger + + +class TorrentItem: + def __init__(self, raw_title, size, magnet, info_hash, link, seeders, languages, indexer, + privacy, type=None, parsed_data=None): + self.logger = logger + + self.raw_title = raw_title # Raw title of the torrent + self.size = size # Size of the video file inside the torrent - it may be updated during __process_torrent() + self.magnet = magnet # Magnet to torrent + self.info_hash = info_hash # Hash of the torrent + self.link = link # Link to download torrent file or magnet link + self.seeders = seeders # The number of seeders + self.languages = languages # Language of the torrent + self.indexer = indexer # Indexer of the torrent + self.type = type # "series" or "movie" + self.privacy = privacy # "public" or "private" + + self.file_name = None # it may be updated during __process_torrent() + self.files = None # The files inside of the torrent. If it's None, it means that there is only one file inside of the torrent + self.torrent_download = None # The torrent jackett download url if its None, it means that there is only a magnet link provided by Jackett. It also means, that we cant do series file filtering before debrid. + self.trackers = [] # Trackers of the torrent + self.file_index = None # Index of the file inside of the torrent - it may be updated durring __process_torrent() and update_availability(). If the index is None and torrent is not None, it means that the series episode is not inside of the torrent. + + self.availability = False # If it's instantly available on the debrid service + + self.parsed_data = parsed_data # Ranked result + + def to_debrid_stream_query(self, media: Media) -> dict: + return { + "magnet": self.magnet, + "type": self.type, + "file_index": self.file_index, + "season": media.season if isinstance(media, Series) else None, + "episode": media.episode if isinstance(media, Series) else None, + "torrent_download": quote(self.torrent_download) if self.torrent_download is not None else None + } + + def to_dict(self): + return { + 'raw_title': self.raw_title, + 'size': self.size, + 'magnet': self.magnet, + 'info_hash': self.info_hash, + 'link': self.link, + 'seeders': self.seeders, + 'languages': self.languages, + 'indexer': self.indexer, + 'type': self.type, + 'privacy': self.privacy, + 'file_name': self.file_name, + 'files': self.files, + 'torrent_download': self.torrent_download, + 'trackers': self.trackers, + 'file_index': self.file_index, + 'availability': self.availability, + } + + @classmethod + def from_dict(cls, data): + if not isinstance(data, dict): + logger.error(f"Expected dict, got {type(data)}") + return None + + instance = cls( + raw_title=data['raw_title'], + size=data['size'], + magnet=data['magnet'], + info_hash=data['info_hash'], + link=data['link'], + seeders=data['seeders'], + languages=data['languages'], + indexer=data['indexer'], + privacy=data['privacy'], + type=data['type'] + ) + + instance.file_name = data['file_name'] + instance.files = data['files'] + instance.torrent_download = data['torrent_download'] + instance.trackers = data['trackers'] + instance.file_index = data['file_index'] + instance.availability = data['availability'] + + instance.parsed_data = parse(instance.raw_title) + + return instance diff --git a/stream_fusion/utils/torrent/torrent_service.py b/stream_fusion/utils/torrent/torrent_service.py new file mode 100644 index 0000000..9017f1c --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_service.py @@ -0,0 +1,195 @@ +import hashlib +import queue +import threading +import time +import urllib.parse +from typing import List + +import bencode +import requests +from RTN import parse + +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.zilean.zilean_result import ZileanResult +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.general import get_info_hash_from_magnet +from stream_fusion.logging_config import logger + +class TorrentService: + def __init__(self): + self.logger = logger + self.__session = requests.Session() + + def convert_and_process(self, results: List[JackettResult | ZileanResult]): + threads = [] + torrent_items_queue = queue.Queue() + + def thread_target(result: JackettResult | ZileanResult): + torrent_item = result.convert_to_torrent_item() + + if torrent_item.link.startswith("magnet:"): + processed_torrent_item = self.__process_magnet(torrent_item) + else: + processed_torrent_item = self.__process_web_url(torrent_item) + + torrent_items_queue.put(processed_torrent_item) + + for result in results: + threads.append(threading.Thread(target=thread_target, args=(result,))) + + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + torrent_items_result = [] + + while not torrent_items_queue.empty(): + torrent_items_result.append(torrent_items_queue.get()) + + return torrent_items_result + + def __process_web_url(self, result: TorrentItem): + try: + # TODO: is the timeout enough? + time.sleep(0.2) + response = self.__session.get(result.link, allow_redirects=False, timeout=40) + except requests.exceptions.RequestException: + self.logger.error(f"Error while processing url: {result.link}") + return result + except requests.exceptions.ReadTimeout: + self.logger.error(f"Timeout while processing url: {result.link}") + return result + + if response.status_code == 200: + return self.__process_torrent(result, response.content) + elif response.status_code == 302: + result.magnet = response.headers['Location'] + return self.__process_magnet(result) + else: + self.logger.error(f"Error code {response.status_code} while processing url: {result.link}") + + return result + + def __process_torrent(self, result: TorrentItem, torrent_file): + metadata = bencode.bdecode(torrent_file) + + result.torrent_download = result.link + result.trackers = self.__get_trackers_from_torrent(metadata) + result.info_hash = self.__convert_torrent_to_hash(metadata["info"]) + result.magnet = self.__build_magnet(result.info_hash, metadata["info"]["name"], result.trackers) + + if "files" not in metadata["info"]: + result.file_index = 1 + return result + + result.files = metadata["info"]["files"] + + if result.type == "series": + file_details = self.__find_episode_file(result.files, result.parsed_data.season, result.parsed_data.episode) + + if file_details is not None: + self.logger.info("File details") + self.logger.info(file_details) + result.file_index = file_details["file_index"] + result.file_name = file_details["title"] + result.size = file_details["size"] + else: + result.file_index = self.__find_movie_file(result.files) + + return result + + def __process_magnet(self, result: TorrentItem): + if result.magnet is None: + result.magnet = result.link + + if result.info_hash is None: + result.info_hash = get_info_hash_from_magnet(result.magnet) + + result.trackers = self.__get_trackers_from_magnet(result.magnet) + + return result + + def __convert_torrent_to_hash(self, torrent_contents): + hashcontents = bencode.bencode(torrent_contents) + hexHash = hashlib.sha1(hashcontents).hexdigest() + return hexHash.lower() + + def __build_magnet(self, hash, display_name, trackers): + magnet_base = "magnet:?xt=urn:btih:" + magnet = f"{magnet_base}{hash}&dn={display_name}" + + if len(trackers) > 0: + magnet = f"{magnet}&tr={'&tr='.join(trackers)}" + + return magnet + + def __get_trackers_from_torrent(self, torrent_metadata): + # Sometimes list, sometimes string + announce = torrent_metadata["announce"] if "announce" in torrent_metadata else [] + # Sometimes 2D array, sometimes 1D array + announce_list = torrent_metadata["announce-list"] if "announce-list" in torrent_metadata else [] + + trackers = set() + if isinstance(announce, str): + trackers.add(announce) + elif isinstance(announce, list): + for tracker in announce: + trackers.add(tracker) + + for announce_list_item in announce_list: + if isinstance(announce_list_item, list): + for tracker in announce_list_item: + trackers.add(tracker) + if isinstance(announce_list_item, str): + trackers.add(announce_list_item) + + return list(trackers) + + def __get_trackers_from_magnet(self, magnet: str): + url_parts = urllib.parse.urlparse(magnet) + query_parts = urllib.parse.parse_qs(url_parts.query) + + trackers = [] + if "tr" in query_parts: + trackers = query_parts["tr"] + + return trackers + + def __find_episode_file(self, file_structure, season, episode): + + if len(season) == 0 or len(episode) == 0: + return None + + file_index = 1 + strict_episode_files = [] + episode_files = [] + for files in file_structure: + for file in files["path"]: + + parsed_file = parse(file) + + if season[0] in parsed_file.season and episode[0] in parsed_file.episode: + episode_files.append({ + "file_index": file_index, + "title": file, + "size": files["length"] + }) + + # Doesn't that need to be indented? + file_index += 1 + + return max(episode_files, key=lambda file: file["size"]) + + def __find_movie_file(self, file_structure): + max_size = 0 + max_file_index = 1 + current_file_index = 1 + for files in file_structure: + if files["length"] > max_size: + max_file_index = current_file_index + max_size = files["length"] + current_file_index += 1 + + return max_file_index diff --git a/stream_fusion/utils/torrent/torrent_smart_container.py b/stream_fusion/utils/torrent/torrent_smart_container.py new file mode 100644 index 0000000..b150d79 --- /dev/null +++ b/stream_fusion/utils/torrent/torrent_smart_container.py @@ -0,0 +1,212 @@ +import threading + +from typing import List, Dict +from RTN import parse + +from stream_fusion.utils.debrid.alldebrid import AllDebrid +from stream_fusion.utils.debrid.premiumize import Premiumize +from stream_fusion.utils.debrid.realdebrid import RealDebrid +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.cache.cache import cache_public +from stream_fusion.utils.general import season_episode_in_filename +from stream_fusion.logging_config import logger + +class TorrentSmartContainer: + def __init__(self, torrent_items: List[TorrentItem], media): + self.logger = logger + self.logger.info(f"Initializing TorrentSmartContainer with {len(torrent_items)} items") + self.__itemsDict: Dict[TorrentItem] = self.__build_items_dict_by_infohash(torrent_items) + self.__media = media + + def get_hashes(self): + hashes = list(self.__itemsDict.keys()) + self.logger.debug(f"Retrieved {len(hashes)} hashes") + return hashes + + def get_items(self): + items = list(self.__itemsDict.values()) + self.logger.debug(f"Retrieved {len(items)} items") + return items + + def get_direct_torrentable(self): + self.logger.info("Retrieving direct torrentable items") + direct_torrentable_items = [] + for torrent_item in self.__itemsDict.values(): + if torrent_item.privacy == "public" and torrent_item.file_index is not None: + direct_torrentable_items.append(torrent_item) + self.logger.info(f"Found {len(direct_torrentable_items)} direct torrentable items") + return direct_torrentable_items + + def get_best_matching(self): + self.logger.info("Finding best matching items") + best_matching = [] + self.logger.debug(f"Total items to process: {len(self.__itemsDict)}") + for torrent_item in self.__itemsDict.values(): + self.logger.debug(f"Processing item: {torrent_item.raw_title} - Has torrent: {torrent_item.torrent_download is not None}") + if torrent_item.torrent_download is not None: + self.logger.debug(f"Has file index: {torrent_item.file_index is not None}") + if torrent_item.file_index is not None: + best_matching.append(torrent_item) + self.logger.debug("Item added to best matching (has file index)") + else: + best_matching.append(torrent_item) + self.logger.debug("Item added to best matching (magnet link)") + self.logger.info(f"Found {len(best_matching)} best matching items") + return best_matching + + def cache_container_items(self): + self.logger.info("Starting cache process for container items") + threading.Thread(target=self.__save_to_cache).start() + + def __save_to_cache(self): + self.logger.info("Saving public items to cache") + public_torrents = list(filter(lambda x: x.privacy == "public", self.get_items())) + self.logger.debug(f"Found {len(public_torrents)} public torrents to cache") + cache_public(public_torrents, self.__media) + self.logger.info("Caching process completed") + + def update_availability(self, debrid_response, debrid_type, media): + self.logger.info(f"Updating availability for {debrid_type.__name__}") + if debrid_type is RealDebrid: + self.__update_availability_realdebrid(debrid_response, media) + elif debrid_type is AllDebrid: + self.__update_availability_alldebrid(debrid_response, media) + elif debrid_type is Premiumize: + self.__update_availability_premiumize(debrid_response) + else: + self.logger.error(f"Unsupported debrid type: {debrid_type.__name__}") + raise NotImplementedError(f"Debrid type {debrid_type.__name__} not implemented") + + def __update_availability_realdebrid(self, response, media): + self.logger.info("Updating availability for RealDebrid") + for info_hash, details in response.items(): + if "rd" not in details: + self.logger.debug(f"Skipping hash {info_hash}: no RealDebrid data") + continue + torrent_item: TorrentItem = self.__itemsDict[info_hash] + self.logger.debug(f"Processing {torrent_item.type}: {torrent_item.raw_title}") + files = [] + if torrent_item.type == "series": + self.__process_series_files(details, media, torrent_item, files) + else: + self.__process_movie_files(details, files) + self.__update_file_details(torrent_item, files) + self.logger.info("RealDebrid availability update completed") + + def __process_series_files(self, details, media, torrent_item, files): + for variants in details["rd"]: + file_found = False + for file_index, file in variants.items(): + clean_season = media.season.replace("S", "") + clean_episode = media.episode.replace("E", "") + numeric_season = int(clean_season) + numeric_episode = int(clean_episode) + if season_episode_in_filename(file["filename"], numeric_season, numeric_episode): + self.logger.debug(f"Matching file found: {file['filename']}") + torrent_item.file_index = file_index + torrent_item.file_name = file["filename"] + torrent_item.size = file["filesize"] + torrent_item.availability = True + file_found = True + files.append({ + "file_index": file_index, + "title": file["filename"], + "size": file["filesize"] + }) + break + if file_found: + break + + def __process_movie_files(self, details, files): + for variants in details["rd"]: + for file_index, file in variants.items(): + self.logger.debug(f"Adding movie file: {file['filename']}") + files.append({ + "file_index": file_index, + "title": file["filename"], + "size": file["filesize"] + }) + + def __update_availability_alldebrid(self, response, media): + self.logger.info("Updating availability for AllDebrid") + if response["status"] != "success": + self.logger.error(f"AllDebrid API error: {response}") + return + for data in response["data"]["magnets"]: + if not data["instant"]: + self.logger.debug(f"Skipping non-instant magnet: {data['hash']}") + continue + torrent_item: TorrentItem = self.__itemsDict[data["hash"]] + files = [] + self.__explore_folders(data["files"], files, 1, torrent_item.type, media.season, media.episode) + self.__update_file_details(torrent_item, files) + self.logger.info("AllDebrid availability update completed") + + def __update_availability_premiumize(self, response): + self.logger.info("Updating availability for Premiumize") + if response["status"] != "success": + self.logger.error(f"Premiumize API error: {response}") + return + torrent_items = self.get_items() + for i, is_available in enumerate(response["response"]): + if bool(is_available): + torrent_items[i].availability = response["transcoded"][i] + self.logger.debug(f"Updated availability for item {i}: {torrent_items[i].availability}") + self.logger.info("Premiumize availability update completed") + + def __update_file_details(self, torrent_item, files): + if not files: + self.logger.debug(f"No files to update for {torrent_item.raw_title}") + return + file = max(files, key=lambda file: file["size"]) + torrent_item.availability = True + torrent_item.file_index = file["file_index"] + torrent_item.file_name = file["title"] + torrent_item.size = file["size"] + self.logger.debug(f"Updated file details for {torrent_item.raw_title}: {file['title']}") + + def __build_items_dict_by_infohash(self, items: List[TorrentItem]): + self.logger.info(f"Building items dictionary by infohash ({len(items)} items)") + items_dict = {} + for item in items: + if item.info_hash is not None: + if item.info_hash not in items_dict: + self.logger.debug(f"Adding {item.info_hash} to items dict") + items_dict[item.info_hash] = item + else: + self.logger.debug(f"Skipping duplicate info hash: {item.info_hash}") + self.logger.info(f"Built dictionary with {len(items_dict)} unique items") + return items_dict + + def __explore_folders(self, folder, files, file_index, type, season=None, episode=None): + if episode is None or season is None: + return file_index + + if type == "series": + for file in folder: + if "e" in file: + file_index = self.__explore_folders(file["e"], files, file_index, type, season, episode) + continue + parsed_file = parse(file["n"]) + if season in parsed_file.season and episode in parsed_file.episode: + self.logger.debug(f"Matching series file found: {file['n']}") + files.append({ + "file_index": file_index, + "title": file["n"], + "size": file["s"] if "s" in file else 0 + }) + file_index += 1 + elif type == "movie": + file_index = 1 + for file in folder: + if "e" in file: + file_index = self.__explore_folders(file["e"], files, file_index, type) + continue + self.logger.debug(f"Adding movie file: {file['n']}") + files.append({ + "file_index": file_index, + "title": file["n"], + "size": file["s"] if "s" in file else 0 + }) + file_index += 1 + return file_index \ No newline at end of file diff --git a/stream_fusion/utils/zilean/zilean_result.py b/stream_fusion/utils/zilean/zilean_result.py new file mode 100644 index 0000000..14da751 --- /dev/null +++ b/stream_fusion/utils/zilean/zilean_result.py @@ -0,0 +1,63 @@ +from RTN import parse + +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.logging_config import logger +from stream_fusion.utils.detection import detect_languages + + +class ZileanResult: + def __init__(self): + self.raw_title = None # Raw title of the torrent + self.size = None # Size of the torrent + self.link = None # Download link for the torrent file or magnet url + self.indexer = None # Indexer + self.seeders = None # Seeders count + self.magnet = None # Magnet url + self.info_hash = None # infoHash by Jackett + self.privacy = None # public or private + self.from_cache = None + + # Extra processed details for further filtering + self.languages = None # Language of the torrent + self.type = None # series or movie + + self.parsed_data = None # Ranked result + + def convert_to_torrent_item(self): + return TorrentItem( + self.raw_title, + self.size, + self.magnet, + self.info_hash.lower() if self.info_hash is not None else None, + self.link, + self.seeders, + self.languages, + self.indexer, + self.privacy, + self.type, + self.parsed_data + ) + + def from_api_cached_item(self, api_cached_item, media): + if type(api_cached_item) is not dict: + logger.error(api_cached_item) + + self.info_hash = api_cached_item['infoHash'] + if len(self.info_hash) != 40: + raise ValueError(f"The hash '{self.info_hash}' does not have the expected length of 40 characters.") + + parsed_result = parse(api_cached_item['filename']) + + self.raw_title = parsed_result.raw_title + self.indexer = "DMM API" + self.magnet = "magnet:?xt=urn:btih:" + self.info_hash + self.link = self.magnet + self.languages = detect_languages(self.raw_title) + self.seeders = 0 + self.size = api_cached_item['filesize'] + self.type = media.type + self.privacy = "private" + self.from_cache = True + self.parsed_data = parsed_result + + return self diff --git a/stream_fusion/utils/zilean/zilean_service.py b/stream_fusion/utils/zilean/zilean_service.py new file mode 100644 index 0000000..a65ab7e --- /dev/null +++ b/stream_fusion/utils/zilean/zilean_service.py @@ -0,0 +1,99 @@ +import re +import requests + +from concurrent.futures import ThreadPoolExecutor, as_completed + +from stream_fusion.logging_config import logger +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series + + +class ZileanService: + def __init__(self, config): + self.base_url = config["zileanUrl"] + self.search_endpoint = "/dmm/search" + self.session = requests.Session() + self.logger = logger + self.max_workers = config.get("max_workers", 4) + + def search(self, media): + if isinstance(media, Movie): + return self.__search_movie(media) + elif isinstance(media, Series): + return self.__search_series(media) + else: + raise TypeError("Only Movie and Series are allowed as media!") + + # TODO: remove duplicate title from titles list + def __clean_title(self, title): + pronouns_to_remove = [ + 'le', 'la', 'les', 'l\'', 'un', 'une', 'des', 'du', 'de', 'à', 'au', 'aux', + 'the', 'a', 'an', 'some', 'of', 'to', 'at', 'in', 'on', 'for', + 'he', 'she', 'it', 'they', 'we', 'you', 'i', 'me', 'him', 'her', 'them', 'us', + 'il', 'elle', 'on', 'nous', 'vous', 'ils', 'elles', 'je', 'tu', 'moi', 'toi', 'lui' + ] + title = title.lower() + title = re.sub(r'[^a-zA-Z0-9\s]', ' ', title) + words = title.split() + words = [word for word in words if word not in pronouns_to_remove] + cleaned_title = ' '.join(words) + cleaned_title = re.sub(r'\s+', ' ', cleaned_title).strip() + return cleaned_title + + def __deduplicate_api_results(self, api_results): + unique_results = set() + deduplicated_results = [] + + for result in api_results: + result_tuple = tuple(sorted(result.items())) + + if result_tuple not in unique_results: + unique_results.add(result_tuple) + deduplicated_results.append(result) + + return deduplicated_results + + def __remove_duplicate_titles(self, titles): + seen = set() + return [title for title in titles if not (title.lower() in seen or seen.add(title.lower()))] + + def __search_movie(self, movie): + unique_titles = self.__remove_duplicate_titles(movie.titles) + clean_titles = [self.__clean_title(title) for title in unique_titles] + return self.__threaded_search(clean_titles) + + def __search_series(self, series): + unique_titles = self.__remove_duplicate_titles(series.titles) + clean_titles = [self.__clean_title(title) for title in unique_titles] + search_texts = clean_titles.copy() + + if hasattr(series, 'season') and hasattr(series, 'episode'): + search_texts.extend([f"{title} {series.season}{series.episode}" for title in clean_titles]) + + return self.__threaded_search(search_texts) + + def __threaded_search(self, search_texts): + results = [] + with ThreadPoolExecutor(max_workers=self.max_workers) as executor: + future_to_text = {executor.submit(self.__make_request, text): text for text in search_texts} + for future in as_completed(future_to_text): + results.extend(future.result()) + return self.__deduplicate_api_results(results) + + def __make_request(self, query_text): + payload = {"queryText": query_text} + headers = {"Content-Type": "application/json"} + url = self.base_url + self.search_endpoint + + try: + response = self.session.post(url, json=payload, headers=headers, timeout=10) + response.raise_for_status() + json_response = response.json() + if isinstance(json_response, list): + return json_response + else: + self.logger.warning(f"Unexpected response format for query: {query_text}") + return [] + except Exception as e: + self.logger.exception(f"An exception occurred while searching for '{query_text}' on Zilean: {str(e)}") + return [] \ No newline at end of file diff --git a/stream_fusion/version.py b/stream_fusion/version.py new file mode 100644 index 0000000..1e086b7 --- /dev/null +++ b/stream_fusion/version.py @@ -0,0 +1,12 @@ +import toml +from stream_fusion.settings import settings + + +def get_version(): + with open(settings.version_path, "r") as f: + pyproject_data = toml.load(f) + return pyproject_data["tool"]["poetry"]["version"] + + +if __name__ == "__main__": + pass \ No newline at end of file diff --git a/stream_fusion/videos/nocache.mp4 b/stream_fusion/videos/nocache.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..0e24b788b2631dd8e6ce0de6f292f19c50a16eb1 GIT binary patch literal 240125 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

Ex=;)#iPW&fSv~`Q(@*o2o5zIR&ri~ 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

vutAk75;HG{$z;4HkUN=NcsZJj|Y{xF)y=gu8 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\qz)pA$1VrO!smQ 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

p}vtv@6V44d!zaB6q(-zARM@HgUA zp#EcU{ze?V@7VMn{8iW(d?U^>?H>^5U$^P}DRH_jz7a>~A7k?aag6T40k#9=jG#o` z>rbP)<(72>o3@z#8{i3ckB9@S?j@dfmmv_cfR{+!-gs+)Om6CZt)JW^ z2oSOfJlebaTTOt`yBff8yenxa6^|ck1GfAC`Asdi8Gtz$Qq`-M(O_ryU5~7TZHqUP ztN|;vk>6DXERh@Q4=voc+U|RALxj}Wpv}$UMAw^?ao+$0(YIuqBj+J)OwciYqU%`k zT;XOtMl$WbmGV@VDu{*q^1#Y{oyS0D&fO6-Tb29ZSn2(Y~jAN@4lM{(SPg{;`Ut4pIZd}BKecie{T5SxLV3B zLJ!YVP3{c`(5}BM4Gekva++@sevv_FhA}v366HzF16b?S2LXWyp&S9@>L~6yW=$?3 z?a=Hic_!V=o!6Bmoqj~Plg-WU9b_yQq8Qa#a8N1d5@JXqEn)}Sk9(d0(V4^T<4W~NZkQl(@2-3- zin4d5FXh#C=bOf_%Jt|`dh*=ho!Y?o{KqCCC|BQLsrnTmX^~K9O0qz}#iWQYO0yx8 z_Ll`d0ov}_aw-J7m0`(RxRJ5~Y%;8?)8JFftOL-)caT)A6+Gv} zBLt`|_FX)O5c{$>|sl>1>{UK|D{`spjYbMtiUSoJ|S%9-wRNf{dPkDWC9#S z)x-O?0QmN`6xK8-q%a3}wE)?JFf%cpqWz#vUoh_ku*P0A@DX<@AP6TD$)}=#8j|gK zooQ^SPh$5{Mo1d?<%AdaX*T&wX~(nS9lj$5Fty}$zB?hB)6-WipHdzloDtq-;emJr zV*T$12I~0mdFWH>T?#PHh;i{XB6&s<s8gWdb`*;TSG=viZ0-B|9ff`yWrgKQtQY17kfly|E1;GS z>$z0q$K20_!n7Z?FtTSGGP^lOry7JYTqGLjmqT`p`K0vdLFDcEpFZhGH%1jCkhI}L zf$E*i?C2_i&cHpyi5gv~Ru8}As8&rc(WMPFWU(;sJSJT3CX7wq?CrQ*wp8qloMJ~% zh@iWG%%BZRhL@&Y(E+abs08ZkjC_Xotn^$rx~U3?v$jxGmNg$Fn+9D8j0I?JA$Ar9 zZ%mXm*TW&rx)b#uFBS;(H|?z+jL!v-v#j_R8lTyyy>iaoyZ|9KDjTVHDOZonSYoFj z3c|Y##(d4~vCTW)-glX-@#ePqk#k_Y;=3GZ9Qk=Q)f*G_1E_kIa=rT+uQ=(c9PjNB z2)0DN*p^)Xm2b430X3Zbs7atK+vB@-P^n!)l%R~Nbx@*Ye0KtllL2~HlICylOm*lO z=JGfIdhW^q7}Ed*;G3r=cNrkonarYh_bCYf?tvgJ7ju}eeJytaMyrYY01d9L=Dz$- zEKz#)ewr@#pLad}M=wzJ@n0GV{v!F0_WIwizU(ck502gD!3;>RaW&B;m% zW2!fr$mi3JB}hHfVcPQ0XQw*z?+)}I7t!t82I!fwwo8nXfn6p{#m2%%`JFa{2nejc zj@KmZ?A*PPgLO?Cvfv!X?9>4AUxTpd;*=OQ4AGO%S%4QKVRYjo66w*BiK?;Dp!-^9 z4^`S_Ru+E^G2geyswdf$;#s(v!Xmsg74a6N8>;|p9Nv>rGi6gmr57^j^BeL%e5CSw zoI!@a>1g+0Y-WI*t!ICovB4{%`QB57DE02E{o;&L=K1emAm5S{ zjrmhd(^#In9P~JNWvxhO)Eh8S;lVor$-E9Q(^a$-F#0Ea>hsK#wv8kRqI^yzeu}b!2^*HjHQw&*#vVp|^Bo1?29E$gL)e zlKJjZ&_fX98vY;J%m!{B*WGsTU^Q5*cK5m8qj*<-cLAWB?h=62zfi+?Zo((<6>9& zggiaPVdwY`pN; z6Ii4B-iQ6h5pGYSGSr2;3?CssK57Q&%w1JryA@7uN?z?n@M~ z8DlwyocH%#LJ^|%MrPoh-j%{rR&E=;xVOIB9Q4JzmS{r$&oOsJK*)HCy8$Rr_Y%@Z_Pzcm9syJeO`R;&ycn5s)Fp?f890uql-!JUatJdrGMDuCn1On<1dZbT7D7z z(cnMz^xr!Bx5rRU-z{n2S1;9S_KTN-jr-=MLYeNol;ahGkJBe= z1J0Z8Jr5A5l(|ZenchdEdyawzDP0l?wcQszN;oU3OTBA=02OS(RS89asdtydpS=5o zn5((_`m^hyJD|Kjt?*|Jzste*4Ss2C?fr}B&!_Ocr~lRo;al7ve(t$`H>90kU6SL_ zuP&*Z^Ea2ob?=fq6aK&@=}NtfGo_?1gSdA|5VT@-nYS*f$LJ@Q6d(J~xg==%JC|fl z_@8x2?hl4!3djW+`3nt6-dOFPDSzK3@hdl~vzdAmm^I&32CgK-)r_^$4ULi?y1$_S zc{1T08X|t*QLdYtEa>8!>DTBl*@k36>rD$87WwYDs4*uzd3AP_^I!~BFDwLJ9k zN1vc%4+a?jM}_~KIS6{_{oELo;b+NzYqR-h^aQ;G`pvXFznRtw;;%mH-z1Xq{uW8) z!W{gCruBc3)Ze#@|8{Ti>)g+jrdbZ@n00b5N=aFWUS44PM2T-|{LCcYgPQ zK+u}GV7w6I%{U=y@YTw08nk`2<}AY7j2j%BJ}bvPs%uuB@+_kDIH<0DWTBl`TcMsl zrphb?FN(^A#m-PE2JKF;f8kDjy~!+hj_K$URKe!XOY za{{%Xdu>~D!$d)0Mh!;-0W!5WpLyU3zDsFWbL|*lOn&o`xJkSfxUzIsyCQk*$pPD> z$Bp+VJH?wtzww~O3YHr~DRDeEJe52kZy@L|K-5a7Pp6({2EO<_w2jB(c#t4`?1smC zL7w7JCFL3C?Fat3Pl~r%Zg1A2qxFW`r%Avzk5m=uNTAk1Vn@Kc=FJCcLBsLHs|+DT z(`KnzWNCuU3S>O=)4bvesL12mIK}c(Z5&(J3KLsFQh}hyQh2A2Mnt(|(O%ZRC=(2=iu zaKi<6U*?*xPv<}Ozz#29&V)qEg6E~VX+W2Vsap)FNs+1#6&vh87e7%71}inN%4?O-oczt zEX#;3VD=+7Uw4fldtUx^z)LhTEMPn(+TJsj8N8!|aw-y`Ce}pyS;kD{n`uqsY^sYS z1XZla?f6FM4%~h`HZ%b*G5?fnaIAah~pU};ES`()*srCh@flJx(+*GD_x72 zvI3-<$NTt=tY$s2tH?9Y->=Q7=?rafzo^(T;o7?REYh1j3r?ZG0o>tAC`4t@#Cj5; zn%Q1U!Ty#np)>ACbAHEIEiv<>1KJjc8Ax<*gw{x~`dl*4If@J)iJwv4_jC_w<@Vd*j)jn&fY);hygp(Cnst zLFhnGpu9w25bbUaAoJ`ZkpGn5pxqCGeVo1WSnyZ0`=ri(92lAfm}K&HbFA}a;Z!cup#F6ZYVu|oGyU`7=RlG=Gh zL<-oS>ZM%l)Io7&a77P%5dCTsGAL^*SBl-((ep*uH>_HwsLan=u87PjHQC<^6f05 zSnNHj0g~)+(V2M`jSsiZz$>2BEPX-#0ij!~1>RsbXR-ZN8sDQ&FOcU$yB+tdZ_fig zZ^k(0AFzj{Y)--oGbN8u8o1{6x^yICkVliYZC%8sE6jr;sP ztmR14{OE>nE8^?tMD*QednyPGHH^~>SEm_+WO1307^P3*ebXkGb(Qvqrxnjc6r4Pi zBlW_v$lRF8bG77{RG61-(-Fj}Aq2+zj;AT9wmqZkd~1mXnyuDA+-*DA=lQm1PX>FT#=qhu2)}rjR0UKBC283xw{Xy}5~jX&{;Q#B(QMTS6Lp1@4sG9BGu6 z%Ru9XQ;)km-F01ufFL(5BfqXrXZWR@B7V19)mRfVe0#Ug{}~=ICbQsGr&uyOiWX>? zD=@pDe;KGpy&6NN<=Qx5pGly0o--=iW;xbdU=HWZ2@W>@?e#EBl$z1xue>xPjcp{p z1m!-cNOiJsk9!X%&$Vc@Ui2@q6xTM;+?Ywr2c8{Eo1m*OHk!Y?{*dChezD5k1TZHKzALVP4i-TKI>WsLYXZc%ziJ2yDf^-Bhd(J;Xfv;M67&#kU= zYH6E zBzFa|Y};Om1;2CcM-ZkpHQ;zf8A`t>>+XfZl~h_nVA74u!pXNA;l3Az#vL}yCj}SI z4`~;eQnb5*>1`VS+3N)?)@$y>K*<@fkzaT2D~Yn>*-AeQur65yU=t&#bvD^za$ zUqp@GJ}HBPIt6$BxRs^gQB?gR9pcmGtJGX%Y8LlZHAH#~Z1nL)RK5?4&dK`bFC!-y z+Q~njDO$|v+h^ck7?nG_8Wflv&&lyb}ONN>-D1HI`0WP6q5CC3Y{hBS+AprLR;VIoCBvfozR6{JH3TSaY$qi1gUK#HCzG;6eB18xX!3>< z=q;pp-EIxV=7&oz57jvOr>gE6T+JUr%wOy%*G2{hac?JSiDh{TCyPanJ$)9Ma5=Fw zx()QQrZtwlQw-A946jJ=NtnD0{~W|h6gK?3pr<30)tya*x#rJ2kYoL}O%zU2=yfQ3 zOZoAz9rwH6J9l?x^=e7r$Mo~Hy$G3oMnjJ_5ZF?l$AW6G%>nnhaXv8fAY2w&N_-3P|GK8+XpI5+H134N*n#`H$q{4 z_-H?Sf1OXlF6Kr@S%=D&3C!8mxc|{W7pw-!{x0GTnk1c;%#%>|$wV!bS@F15NDlfK zGJP!gg~rvw7gtON0)0I-RqQW8AJ|tMQ@O(&@?vFVwv>`bj#g-qVmezaE>hp-bA^}sv6^=!7qW1i zasXLg6q11@iPw#;UU(=RRsSxXJq3rA zq`O9*+`QZ&>C+^MD~s_0<8uw(xa#2OIx|t=<413rd@U5xw2X2mkfPt&8y#8^Nv>KH zwn{5L&Ta}r*cVS~y}>_c)h`(Any#E?-d?MZIqIf?^QRGi>2T2IJVz>!y!T!iCS2M) z3q5p(-&Qks<))j*=iFV8!ny7%yIl`And$Nx2URnlhSbHbHja?SD{QHT%^gp#PPakN zn~S${DL2@guQ?)Hkg+c4vhDE7;$oguMZ{95a^o1mFeIZ zptf819B>P#88nGL1hFrsY9cpDioM=4{EkZ3TH5SKqm{4y`HB(Y4@9}eEWpLLq znb~JEsAQX{3128x zuF}HT>v>IC0$)#ig&dude&#z{Me47WRA+(h^sL)^wdOmib5|0QIzE2PwzR6-QdOSU zjr7=g>+;Hl$@py&u}|l0r<1Yv@VLJygP2L_V`xQYA_0GqiW#VLoQ&J_yT*;l^2vDe z1+w6*>P7W%v)yK0spTV**{Y7vQjkAZ!yy7ICS z2UNlaT6yO>5Er^9f0gz9w&_M_4wuFWgSi|x>icy%A6E@?)-lC~c=gXk)}v0U)@RA- zQYYjV0(B!{QIO#-UpCt>88nhCgF{fYDvgC`D>X3`&x+FKI1$O=agb1k@#~}pDsVk} zvH4mqeWHMqRka&}6g;zMM19F65WUy*T1&z$a`<(wcu{}KX+Hl!!Lc5+$=Ed+T;;Qv@a>2z z^bmaY)1G1AwZ*5qPmO$@15fepYz`f5xK-nu0>zIM8^G7^KIL=+$SPciBTwLhj*T_K zNLtgW#BzaxKdaPEMBO2M>nNV1Fx4SzR81}v3rjH^#Pu8&@W@a|8*I51%)LGH1I#jg z%Ji&-ZWi;3IK$;K5P?OdXWcQz^;jcm@_cu>4?VdOK6nYCAYzxZ=R9IHUe9cW2*<)PB*^upz^_1AJ zTRx4E9q0_&=+0{Nv^Re4%1@S*`6!K4C8HUZU?mlSb&l1JQXX?2Co>Q(PPXZ$-i;2c zoZ(Vz(Cp*UCN2@wt@geKO={Z;s|fshD}hf~Oyk zzyCMLFTZ`R@JhYxVWyxVnQ%xk@#tp!WKK^kvbxygnpH z*7}wu?AV=U7m!%Y4>21PwhZicKas?}5)D^Y)+V?j`K-JYpV+JdZ0j#4~&W%Yp zr|q>aWGFp+`MP76AV(OJZBCM@R+Jd83S_Q5#_j=udrp4wgO=*RO5p zs#*siEa71K7Y3!vjRx!;evpCEy6`)hW}QW0()yAalByUuc%Do(XVU8PsVjD0g^+F2 zK>P_8s??fV`T0VyuVK=BuEzEwtA?IhoyU_sseDab{scvwc~|`8>9Kz48gEfA5n*49 z&4F{w50p9fRT;4YC^$V36jR4qKR%*C4E3&Z6d5Jm3asfd4Ia;U^m=brFLsUn$TgT)8B54M5Bs)W_pL;&X0)gKFsED@&;cz8{+YLCe6ZPQyH~ z4J60_pK*7UmD*jE6LxluH7VtaW$iPaENe!3W=$gfMcU~zH*taSgR_xLb;oB0l0#DX z3_fyH4bt!PvpIlb(xTU1mbINB2$0Vp4HG}UJSzL$BEjSGxh^y2@*S zIMf$U;MZ;}5pinT2v=*Z!@O2`B&6-@A8vN~!+2TLTwX*UrYColIl2P_TO0wF7U}?P zM%*lXW^IgwODUv8TjsbHug*{U!?t%nTS0fO5Jzww@5L@<`4DtXtI}Q);~#DW29Q^k zDMY^_Q06YS@4#b2VQ11nJvg@IPf9~Lu1&O_c{SWRyJq7ZD96?p0PQ5osdc$K9O-U2 z(l>)K`s{LPPnQO;%+M)4p5)cG!zqXq%8zl9Xe&aUQO0Y6%uw%FvDbKwnE4_GQ zw?Wn;ZDpHr3U;YVo`5tqR1M@8_rdmg9Wv}Me(h>%zFa$~*-> zInkI(TgfQrd5tL^?&y8lthBQ}Ub{8K+h`{?AsSn+E!jSc8hNpEdQ5NYAPngS)=-QD ztjvnFRXM0QvxMNN>;&p6JpZ0Tlh}`xJsA$uui+_!2hOgOzjv7|6X$`X5>RX-9pz!F z+*vls>OPSdzRZjgmrGw!ybKrGNJg!wY41f7r(N>98{zhoTDf63UN5jO9eC=~c#LecTsh1fDb-^y1nqreCoJ(@0DBx{s-$^rm*VOB9g z0z0=_9{58S*-T9^kZ+>hTp_?Jup5R8okXR`&&{Eb#f;{R@`_FnVI^r1T(O5@iRFAH z7O&bACM5Rsl81z)lLUk>V)VXjti%p&?TV;3_D0MW{qny2IYd!g>0(EmBM-*J4taOg z`C80=RnLbaE3v+6n=K5eSJS%hANk}7@kC_Mu1nsu*t-8_=Z{B_40HUhM5sN_@u1Ok zHG8v&LH#vc+twP1ZLLLN;5el(+6?66P&0ex#0W{clD2D(YSCgw^ciR^k|ie8nk~ca z0bS`s5)+Cm5F$BgR4Q-)Go49n+P;hDOm#(xm1g;o%kc%ZL0~jD_?i zKy7?LhwxdkW;5?qyjY|bo+xJ`VhdG^xrMfeT>BtvF->h#6a+1p*7*W&>Q3SX$jAWq z@)RM|jJ_nR*aK!)q6hL(e0}n9TPL4uoX1;xs=Ys2M~k_r!k92>p=`>~kk@r(X#_e$ zG@Khj*dY|($i;par9q(*(MV9Hdaf=x-HDu=f$jg6V*t-draFeXyW> zDMQRs>(iP;8C^8c60R4q6GqEBN;LRD7~oVQ4Y0xt;ygo=vwZj*KvD%bDU*u?QS5t2 z_zp@Uq`2c2W@~&nsiSBTc^nx;bOZ;TI)qo#MPJGqx-oC$w`DU2q$P!`dfKu!$D2o@ zdAcJ2ZVR$xYBcxbVQ!(~$PmxLSjNTbu z=+`8WE6@=7B;M{m-BjnUs^uBZ(05XJ;LfJ@$S7YCV?>>+|DreW11=_3@}Z$Lef0!w zS@ki&$wj3TFN`oH8?~dyy91@tHi8pxboZ8R)em~Y(g!mE>92A!Bd*MNiF+!cr=`Xj zbXa!VPu-Rq=#D>@Q&4$bnTL&pz|^tPzpP1AWGz)E>VH|SUKvWjUhLuGK&INd`l)ST zNSGAwSkCLuotJ%GoxYkm}cNV2j~ zuP+SXBw*S#^uOls?!-z76&bmN;Dl|x>^){Yi~%W2dEwX>XC9a=jNcok2 zHs3@&3#;*l*%mLDoEL!PV$A8QFVZYp(&7s+3k__SG1Iib61LuxRn@`cBf>E0^QB|U z4#vWKW4A9@kchUIsgQF$5Rh47VFJTQcW@pGJKfdhnbLLXU`K`|_xv-|4R}H)Ets}L zMkSM<>!~Ib#wztSbzw7S@?*~zk|*%g^&P_AZ-uoo_1qSEhOW(W#x1V$lGZ`;-J@a= z@3~mAkPbG8@$vCTM5`KZKf1O>J*Wmb9Mn#Wq?inZi9MuD5jfYCMZ+r1btnClo&7G- ztE4_*5TvxB{h>TQvsD1q#JZ5Uz}rwW1A1NBh4$bCua~2*;3BDUXx|Yi7cz@{P{kXi zq>1hI-m_Dt1o(|Q!UolyNx5^X;e~8)rtxHJ!tE7qEqI>$ni`v?X6#ThT$R3XLz&Y7 z)!2_2ed6RDR?{@OUQAGSU4Dto*kz3&{KD#G0F4_$gW*^NIhc_tGQ1Vd#K*Vpby2+P zTZa-EWVT#i&-q%2yY*3=({`}pX#`KL_Edir{Q%Xua}x8pAzGsta~~DN#csddwK}0gmNrE56Kc!pB6cdzaP^;G2ti2<( zqnV?o^dd;AqjZp!Ni9`0{j|;2FRDa1K0EXk9!RO-8}igi8l-J(>cb8+hmyb~fec4N ze&y;NQ_GuRgYXweK(bWFoIRXNA(hI+l!Q&0G+k@t-_D3wNR>}+p{ zHC?Ps7g*tLdu%<}GdNsfVoA<@G05!6(apDZVBMx0Z&XM7DZY}O>n{1x9#!&oUAZ0& zOK%-Ia?0Z`Y_MLp+NXOHZ~8SKy9-7c)}aTocoxv>T!@IbBA!@!b9ai56h(?yA??p& zc_SOWSoXdn5U80zpdIyigu3o_d=jmJkeWX?T_=9mNs%^ypEAvAcet38eMlaL_ zYx&R{3NsCNi8XYkocAiB2k(8^{S{fTggts{t0|G8?E?o4GGne>&Fd?W7ECY3O=NSj zgOAI&nH(Dj8#P|R&w_EV9peuaY7GZ))VJ8>u#h%2u8Ij_@ur)j%TF*D+ z>+)@%&OIbi^jpp)HnS;AG4OQ}$-d@7eJ!p|lZH&^KPa!PaYVToSG%V}Gpg@j;XaM# z4HU+Pp*zmM)YTtO_jxL#7w8<=JDI1Qhxt|8q_khMhyq1Z&(LlKWFX9b1hAtMQ`rSn z$|0`rd=^AN;v!pRd<~;y)y#U<=gl{h$!|Oayj|>ABpBDPwY)~pS9Zui9WxScq6Z#R zjPrwFyzK2E)8ctzUqAz2nDJg<`zHJobl}7jgawcK8VJKDID#fy;6|S1fbb*|T}-oU zm(-JJ_Dz$)HF11$>r!#3qHAtt93XDkbd0BVi0;h2J}B;>Wa88?Yo&rpH}L%1Oemga z-f`kw$H#UF18+1K1=h)V90lz2g@8*09?vv3MCwHe;O6p~;3{it*+kOO4Lrv8wNta8 zI(`>WmXMgAk-wjFZiB>~Htdysbdn0Hg|aGozfn+LeOzY_7O5mk-44L_9y?ix4V6_= z3^Fn{;fNRZLgMzR4QAB!cQ3ynY(Tcby;j1&CL%IT7`v(iQ@j1a-*wnz0ImdS$;X|R zWIi@F@>V}>qdqGK`;y5F1$$ewy3H%e>n3(MnmIss-Z^>$n!ksP_Ud$6>e@HYniuT_ zow2p%E3^2p2GL##I6HE$*9GP?R7j+OC_-aHaxyk;s0k;Lh9{~;sUT%OL+_T!*u*+= zY^YzMsz7V(Kd+2xCFa&YzOr8pp)B!sC^8DTeRWcd`WXv53QIY6*w(Or>6^aSEH#}1 z0Xd-odL8rUmpBnr^LC)2&u9Gx8R?nmo!YZBC;0L7U%)+K&~t~=av0~3eu^LT=I!oT z?bm$wXj0MfR8tMSY|-thn0&NEj-dLY%Ev_6{UotbiEPk^7@xs}?d>IJD}v!9ol9|> zikN3*mxYJr)>Mq2c1fG$cuk1eV&x+_jwL^ll)Ev{A%q>CbP?MhOYyL4^YCuf1HOr^ zCq--6=7S$`g^xLC#Vj#tNqy?04R69^!|3y}v!;$yeO{X0u6D1v{zqo*Aimj>#URfr8qOFKE&eB*OMD#4xIexD*b;S`M>oQL= zO7Ag09GXC3=xrJL9{KYbQl5phwVddeQJoo2U~++3Ue{_n)j|(x#i;eq(Ak8Ukna$k zLy&>%si3Ici>^%X*{j4j9xo8kddd%NMlhY2u*fu5*JUnfLG2h!TutKLp zw|V*vVifr#E7cN*An6w&Rb_hGtu3jj?U*NS>5Ll1!rE>&qWP2wcCXWYAo{>bz5r?S zbR54;1kyOG-H3ce^0*Qm!1Y)jKk)o%OK7JLc38{tbmw^9s|X=47BMBl$!lF*aObJy zX{ilRWiKCbnGtND5R0Zj{K%!9Rhm@^o2506kBcFFWH=bCIIqdvan&=NzaI4o-QHh2 zg&luiaRW#9_MklS>ZNpry|YhV5Pi;2zsYnF#~O5M{W3Cr|5l~8$CD1lVQArf-eg(( z^%l4%EN@nw>I2v;zXqWl=97FfQ^bZeJwem%kfH72~p!yJ?Zw3(=+TM zO9^Sq`GC?-Qw%~@b`4FXc{Cop+;)odoQImda5HdeoST-Uy!(WoPw=!1I5Et@lUDm# zVL9^DVFo^+U~pNcJXsG})6xG;b;^ZOHcJVNZhKpZKILneXGs$+YJ^MzSqa^3b?5%6 zN{Ps!F-`(J3i@#oVr;D&_yH!3nTTrx2hT^O?HDtJcOv9-t6xQCb8{V~Bo4wT9*=yH zJ-DYbw?5i2F`>&dr?25t@GtXFM98s-GY@qe-OuNSV8{~Qc!#@a<*aA!T4x)?amygh z&yiZ9H5Uey84vNcFG~&d2Xal}*<0PXhnef1!w34WS}dITsn9=rrhPRt>_If(i6*tL z16D4e=>6tRHdTmWwc6fgp$~DJcI(J1?8OPvt(=6AHCn~7$U((R8d=};K^1+@$kXRFNEfS)(*2Q*_TyZ#2QPj+C^TSvdo(0E&euuOFyK0M!UMyrRrdz8o|`ayD+&Kr5-l( zrQh?;LblB?UXMEcUPs)gU)Ef_^`1C2?N$XAFSX{ZGJ1nhm{O7q&Ny$f5Vz`+jcx7b zHiKsjc=OIsyy)BDfOfZJGyJgV)Oa0LugihMBvIir`YJ(K;o}zqvl{BoPEZY+T<|xo zzK3ce*A*+LGdV6j1Dapnz>%EQs2ii_4pEgIu?bE&QrRAqm!-w7SkA>XlW%^ARvsSO z^*ZBhCMoXqlhiZQCTceDpv=PxzhDtm)QMx%GZYj;)u}HAh8Fwe@Sa~t-oJp67VlIneA=jjwkm(1d~4UKWT{HyNR->?iM>*R-eBKk zFbGevJOXC^2Le2ATOVB&>rdqx&jsk;%$O=(bdYD{zun{HHYI|5;p>0&d?e%cMsRK8 z0@TnEICr3W)Yr>4XKN^_^FCLf3C3|x`fdg6#Px|FBMM2ZnLdG>vW6}U(h{djCP|&B zMD8Yz8xG>n(B3RFkMGlu?!T z87ykNpS$&)4K-HZlsGz&dg6FnVs6QAo{N!VwYKpme~_c-?Y&M(?CzdskL7<$;&qtt z&Yy8(Rn|jToA9ZKhNEE=$4fG2E&I3u)@Oj!y1R+AT_&x_x8!qs@&O8dSK zk_3!e)2m5F)-TwvzpW0~BZ77(vI)Uav1 zS@!Ww`ulo38E7sXKK>@MOLb}9OEGI-_SbNjJzzT@+IAZP%X+rclZb3dlx^H7k5g6R z=|qs%@tN?*+&sVT*%x9gZfNLN&t2mN9_j|qBOnl|XCCuV#W8C-F zwl1SAPM4FsI?cswombi)X_t-Z0J#`4nXCHx!8qrlv9Jd!VD1RkSff}V{yMm3T2Z(1 z16A%^?CRdKALsI?iHT$nPk|9ly+}`q=cmCfSm-_4&3zgI@uBTATW1QLH_6ZbA7k&J zqzlt?fsSq4wzw)U$6ksKZ+XNBVz) zZXl-$Au`0-2A{Z06j#U<{E;z1dvmfqGk0{pls(FTX8(lB9S>tVBhfC7oud$i@SZjJ zck{l{CuQqiE5a0}#wbIEwJ zY!&PUC8?~zkblEmdJm;%r+Yu1AMlG8OhPt_yMc5$9FRrhh)}26qWp8XfP%0?5AQ^i zR@N;Wpnun39O6 ziTD>-q2lDUU(D2gB`>*#Q2V;Mplwq{V-aS_icXbQxCG|wTAV17@(26q0DskOpRz%` zF$!VI4FIooxRMBUxp4Hi*FlF5)M5mFTjwk6+Bz#@uo>XL7VyvtLY#TArT!%%_P){r zV-Kg+nd!y;LZhcW4tYZ68pyR1ea^05H0{Fklp6?z1*-lRzV-Ky=JF&@j2EGM8F`23 z>Z``fZJQ45(ma$C*w|9S=`C6Tkq2>IcmqHMM~{Wb0q&18g>TnC9Si;n9+FsjP2)W} zEG@B0NMgTjs!(qaU;7a4zZH&=Z-X|GEk{nQ3HV4nRpKQ4`UD7=t8TOyzx5bWDOzGN z^dtfcX-~oMORrg?7D2cfNxDGpfQz~BPF67&z^NsDZr&4^>H1Ax$H6%}+4~CYjkkZ~ zEKNZ}kqfPY&;wa+|Nr~q0S7~sN`Vy5rDr$|wjQ?&G&c_c)e2{}T|x>sQqMa-8(6ld z?M=~(-DFj`ymjIzDJx+JjG4>#WK1jSv~q1FdI8ibk#6-9(-Z;xuZ@&#`_7 z8jMs|^XV>;2JURf32UX#^+KR&9?Eia(YyT)ZA?y4*W8`Sdg~8vt3d)ab#^&(Gl-Nm zPoHZ}x{>H6H=qn7<}a8$FPo6(|Ma{~7x&BF3AnoVPyn-|OkWME6fw3lx`ru)<)i8( zZdd4yiu(muzJl#wiab0skhRZXD`7)K$Go1u@n`W2(sdBWM#_iG#jYPL#_~e;2~$f> z5$3L<+fYp>czM?o-Waa_0<$?mK_x?tUANilh)D`Q%_#{Bz?R4txhGQV-{4%^+t$U0 zoPSSyY3(FDRsq&_Nc%4JcewS4Xp_xOTD@OR!dTe%ldp$Ys>poQnd<$4E$%tq$TC3_ z3j}mda>S^aB2r!_jhePytDv0ibpm5aj0UE2f?Y1es866FGVkJV6cJwhtAkNL4sJll zHM=FQoU1wyyK6|CBW60gkwjBtF%-dx8WrgkY&uHCkT%ic#nye)dHPPmi|NoE{xz=hWN>hliZlLp z=`sXAVxcD_I}gVRkjRl-KKjOp;s;wPeao8ZiKqv^UCH$yGlH92P~?Uajg~l!3~1!2 z7(fY>#?4cH?7pqtuGX(SttnAbQBJZbo1B}ZG$Q_Oc-E29BP2dJ4gsUX2s1npoXsUkTyDf@9v!BN+yBrD)J} zgG7&><5Fgz^^JB8Sw6W(s0(gJn#&P9~(eB;3tk&jLi5 z>U_%SLIoKwYQBE5GOH!iH~W|_jsNVZ*kXCN{sK`vCgpGd`L(Bxkh6Y;l|5QAzM0Z( z&B-`(eyuvu9cWp-dm;NO8qyJMZ&dJ3hhhnP-9+(p<#5dbZA)wS z_BkKQ?tyY2F|Q(X>yqzw0q;)X!bTF@Q{ErJ!UOmLw=f$*%1I)@Pv|3RJn2F~LD(w2 zBVW=7f^id@=z)*v3jrmRvA*wNxft693=_uY@F{0q$=YwXz zroWM#NRc6&Q~}@8ElHj=?o!UFDoO}2&%WfFaSiB?N!#=M#~Vtd7EHPR$FNxSvb zJwEep(bZR0=|_@ANAZ80;vlL8%#Y>upsQ@Mvt60=waHRnCean_cprPgQOiW$x#ldQ z{`H`ZRB$BgK(Cp(H=zBLK5bnFZ(|9Mj0Fgk@;2LoB2C=b^qk_I+_m^Fpf(%oc5! zVyQy#v7qn_&*f$2i|`9vAeg5v6R;@N_vf2;9$S}S60dpaAjWrsA+G-;1oQEGq}uj~ zV~#d)KBJ=;DsPuYLgHJadXyB+6OzA07{Q0#w?Zzfc*|gOq_e6bvZmsJH7I zfu8qa4374U-7!(|u?M3#0@u~pDe2c^cb*7!-EV*<=8QjF>HU$`i9acC`~zx^F=nvt zy@you37ztXIGQzTx!bdJt}hUG8UoO!o|6qbWDG*7=;S!&+^PdAIm!BEvv`Ug5?p9s z)rRR^=B!HDQ?LNG$`o<3LOTioJf#cYG2F`k8-|sm@x+s}P^64C@Jj+Y;+_0>pP>5U zXLi6lac({y;SE>l=~B0vzM&_2Wjx(H`9TG0ffX?yJXuS)Sg#;4C`mhF0>UzT<=sG} zL6*lvWne?(f*&v*QjW6)6s9?G0*OcliH!sX(C9u@ub`>a$vhYuKs^4PveFijI(2l} z0cuWy@z=P$0RkHf614IqsJrpG?J|XoG(88iiA;3h`)sKi2n|2lwqWr_4wbtvXCV~R zxVnP50{|Xg{+Htl0pO(&h9+#RW(qxe|15&(hiHl^u8TL?EZZ)%LWyC%-fOYV-lzPq za@G6PBp)ttzo+cfo;7Z%F3C8G`J9z!DCS_a`0WkX_qpReTGWAS37TYSorJ%SKJ|@B z#AMSBBl<{<{xmCdBv*3!Gp;e1w=eR4wHq#mpR{w`-H4fyBQ&}~_ku&|WL@;G=*s_q ztgHL{LLxYY#|Wa&TWTtuwh>__n^C>l>qq(Vl&=51G4@Tx^XcWb?z~bo+JY`gQYmmX z^p~#m{hGKkkW|CLAfh#z@ZKGqS{Ipi4=;|<)#%l(K*C$pPZD5!0`H4J%aYEywxUU;;NSy29rLiD$8PwZ!FK!SR5ZR-ULGbh0{tv^_2o zprG9<>Fp8B3!m+}-P7#ujwDo*H1(>J3Eu8P#Q#{rwW~UEG&U4>UHhWQoHfLC?6#hi zNtDsk4u_>{I4vVI`HRO-K~{jNj%P_PUAb5EuF8Vwufw;im?QAgHvTnE`^ZnPXvD`u zpie_6u+CI-UM4C`NDJA1LA`*FVjnH6QY;_up_4cEY7YW|77VcPkxv>^!F5}OWb~yz zzk870BlAlM9F6*JS~G`EEcia?nKl9Sa*#Ir4EOk!K?+` zWCjxay@d$Lz30uu(18sX&|?&goRsKjzgJ*$Pktz3u^gz+DoBh3))aeLp#BY}5lzkr zOUZ73FB=;)Ri@29XtT7wjdwl*ILhgo06M~C&QK<6o3aNUSN$k)l8v3L|JLHyr>S~~ z{2Zi;KFnXEe}BLz{w09#$pEai$#Cr>&*_uae*@qIs5M4-Mva z3!S^5CiQjA3cmjlP&!30M=7l?D`TuH^$E%Tmsl~{6-98mlBH7DF|kTYB;0+SU^xH% zBG2yuLdZ#h0qk>t=%O6cYjL&OcR#c`$@h|b3&l=%&usYscoUf;(gQHgz@-jHI@6No ztW|aDR2kPN^HYRFJl~ScM{y468Wo8C;frMvT{39wSD$Nj4+in|*ZT0RZ_~KjOEP(> zzRk?gpbTN7ZYvO5;xt-ujvL;=3TN@~%*4yF&U*gFBpbJ9vpx<$F6)M!xk<6sI7d#r z6B-=rt48XoG}kaPQ?W+aNH03u2DUsL!Ez0P@h8g_jp{K?D;w|(Ii95v-k6YcmqE_11xd%43?2(Y$}gF~uctQoMq_={WDLzc~2Hw!owc(^-?vtAz^SqZ0b<+ zzAIW4*`LUYp2{g0$0J`VoV)RJr!5Lg<;@RAg)=6W`@7!8A$c+(PWfoMu-RzqD!sPt z431FJ?(9>jKL})*j^lsCZZOXS%tBLc*|j;JFva~I`tXj&)n>%%fKP(VStEuntom`0<678mUWSdtnB-A= zjN6cTV)Qx*AtUWkJX1{Tjhc2>}IF*V&Gx|{3Xmd>kC1Mbsj zZ>_1{FaJ{vPq@{AhVg|Ngk?_4ao*vNy-3NDH&W^R-Jza+r22_XlbZONUx3ctK*8X2 zU()`v8Hr)eqjSigB4pG%daw~NnNl%l@CG%~R%>A8?E01X$}&Y#)T8}ugd!=ipLxT{ z0oqp+D=(|(O#sHNVDIFo7Vq*0kIpP(WFzC;wSaZ0^{hDc&RuEW3GwGQkCbK_wV=vuvz3W*~8|okCxz!w7bE0Hfv@cDdbz>g&Q$#V2``q~c^mu(4 z0td1LA9i<4?*GaKn`}$l=3d8|cYR+{JIGXrGKA!|ee~g-T|`7cma#xA%Pe#!%Ki3? zw5*r58$2F{t*Jy>eB;{y8aCJaa_UHcZURWSmzh%D5De|Fm%A|5z(7C)_j@oE!=~eA zDxq$fm6mV%zZ3?OyY}ywGrJ*0{2P1u^bxZ0QiJ78+E26HS~>V1%ayB1rsYE#6;**p zi(bd`C=q6|%a5R&dO|)&)`M&bH!0xJShuxUEy|p^SJv@r{>Qo**vI}!at14^j=xV8 z!W^WTuLk^9$1n+~5e|2#&1Rgy6$@W@NygZcf?0j30Rd#BKPhd{{m}#%|IR_3TbsZF z?_(zNDP#GQjxU2T7PI?uo|$^-I%U!K!Sb*!k+%4~eQWqoYDP8?u1(8nl^Y6F>=#(G z*hU)>E9+4x1nDYxvbRAYv56(V4DXqt6!9$cV+o*@v@n$mQ| z&k&rW>l6ffY_bUMjQG1PxPILyxU<;$na)^`8D3*^E3SfZhC@*QN^GM8K~t*5l>t0k zBQot4uQWj&0!xR^wPkPFFDE$-4*%>&WyoVG{$dx~h}>$oB&jle&6^kj7%K3{85L9q zW7^Vo=XGviAgAiT0gX_3ne;jkKh;RPyH7X?Xi`X01fK+u~=f`}2?QyW0sLfetJm5fki~XN{de6czm<5)SJ88JO~uu9NaVMZqv?O1G>PlAcR0`Cq`+T zI5uSXo8Qb^lw3=CEAbH{&S3&asE#E9m&ovpD2@0@^%sC*8EyjoF`e_5o-H9M*jJ}|f%DZ~}n&rsWMBx~}Osqhk z62h&tyoFqjsw2xnxUo0rsF57X+@7(xC5=_v1Y>p=00gX=4_sRBG&T=sFBUSGGOJ`y zjdq0@?x}~K<6p+f-l$Sz!|o;e22Nyy2A;*JLGLE&o#BZAg3#Qxk=aRr)`~jE1{FOT z`-F~=(W|heQQmU&75woC_=xNUyf{4W5NJDUK?RU^6G0+*Nphvx938oZoEE(Pf$v;> z#I3Y!?LK{0E9G|7ju!=y9bSSCfBt4MR>g42`yGP$EBABjUhdN{*F}y8LFnu#1r0Bl z84KRstI$vCIjIc`aoxQdX5)eq+ALD$`ksip)USv&8HXsLla?Jz1_Ymbk{e5RbivBM z*>a4`n8yb}s(Sv=fak4J>yHAnmW(gezz!m%05zUAEzy8%C#+Y3;68YH6Bd&>nX0T$ zggScLpE%$qXa|Ar&O%sS=k8-uS-rtkIHP{03+x{=M8;OglDbUPYjlGgz|%8O8>xcUTcm?7^HipbzokvBQila zhIoAmo3&)v!fh)`)?Zm=z$Yn@sh{;$?+W|uInmYL&yp4GB4?7wTRhkYMsM#nZu{Z( zk78wDr+88Dmp|MIr}X<)0!>?_Ce2|0_wQ7v(|Bmm6ykN?MFMZM^5+(zuaqG)R0;!- z9arqrpm(^q>lTUe8@)3M-S16LAM@H8WXjbJ&9Q@_W1$EG{B!1EtJbT4eT;4z)R?|30hwJVi=+^I;}LoR@A5H6 zT%U%cX{zjWO;Xm=mGVX`BAzAgM*&hCKCB?hFA$h(@3?_zqTd#XnV$|Xc+uvX(#XmIKY`$wtE^IV z5IPc7+h^C6te?bnp!X2Tx)tMJ3W}WGL!#Ld!WEZWMMDQkF2Tc znfA#{3?*6i&MaJZYojqL-68a@Ji(2fxF8O3hM2M_t&jAeAE_ z{Q~3#xuiZ`R$Tt}c*ckX=xpxzV-Nmc%9j6xpH872h9ow{3cMZ#I@|JDtok#KInu%p?~^pF`1@Vi)P4+$ z;PzduRxNgvoj9r>E0CxxGkQ>UBCn%0EgJ@c;A0Y>j4^B)4FQmC((%oph19;ackiM} z$rI|ylE#{|k5t3QqcvoO{%EibSaivIU!d!$n8#t{gN&PdEgJAX0t9A<80-o;a{c3| zW|wifNPAY38s3T-n2+i>2M37gMhg>lH{or#E|CO@cqsp&d-08MOo1g_SZ*}r65KS1hw-BLS=+7 zcx(%Jxt?1$=JhT5?cW@xrH<>HhIQ%pdcltz9W0G`?9@VX2TnYndegpyJiAa8U?Yi? z-Izto&yOv=N9f}RnA;iAm~N`lrev7w!GM?O@w;7v(nO@|29`z!+li4ia*U3<5Ia~FcAVj49MWHicj}cDZ;|f?OW<2@>fC~ghr!k=5duyoH?(l@^CnOesl9zjA{~o#tlwH!fT_>wChk>YeC+MU zc>eDytXz^bN-{<)qFLjF+0q#~qq4~4d`Dny8B;7Mf+IQbIEeTNB7%~7Qg&HEf)AW3 z28m?WOlZ{3nUv}}hHK9QTjS|rQ<9WE9f1-=Ds}eUu=UUi=H1iPR2~}WudwOOYX*^PBuH<|n&nWeX`SFG1u7KC zPnM?m%8pu>G_LMA=31JSKVHbqY;XAGx%(@~1&@LO-9{SA9JKx-0qaGpZr1CreC7XkoVkXa9q=&K|ZoK|1vamS=_PJB2WqE+RH>;>H z`#&vDCqo1JaYZ`sHJrc=Whsb4nd(9L{d}Nqs-C3NRdEE>Upxq?OvN0mt~jv0t4gcm?^5$nG2AbJa6eNHE77 zXbOIyK6+8LYW{uxR|4G#jeFze`&{?KeWdzH443?CJ$pR@b*MyJ?OO!3=rW79vFmjQ z_Tn#CZ_aO+Q6TShR>#Q+9j>Ki(;Dk#1D|<}E1`#1deFRFjZpK#K!P9A2$FF2dDIqY zTcAkqCH`uo0t&`V7DJLZOuyaD+28Ru36KSYk<5Ma(({ke6eeOyuxwX4znQ@$n!_* zgS4=sM*^9{WeVHspN&q*d$Ya+mn%~EzY0JKTqG~e$hrn#%kO@W?kMGB6r-;5Q}2QZ z#0%{B9>v^=<>fZvGC5qPcqR-t+Cf4^1U(M>oU3$51;3}!7A-g>Tearpab2lciW!eP zGA7+H*BwOk+GKbJ70g_4d@@KV5udcL8zNatg>L>*?}dPuTF*|uued1BB)$3Ro+#8X zr1TQP%#{L%;ghT7g{pJsmK&8gRtAhEb*~fN-^47t^OS<|R1YRZrN*`ut(t7oL+zDs zJiG7x2ML|WAaI8rnp8LJANhO}GRNZu2EQ`s*vr@S*A%4t1wSKRy?&yG2>;rwzd)#! zI;vGDmmj~ehhE25vA#JcoLfl#JIuCC%9d%;aYNC6&{&Wa;+l_SKpD6NuBmf$=Ja)+ z%Gkm~9s#UQjk$GI6HM9J?5NpGJpDY2336?tV_et2#^9%Ar-x!~#){0=1m+W>m2TrL zplC1;XS7{5H?yIS5m_iKZxLE`@n~w9A&5H&;2a=TD+ufbIYp(wB<@Rn4^6daFx8oI zG-vHaeB|)(;-4K7?+nN89N*?roz#5M+F7T4b!`FMN3+l^LIOSB{aMNcYnSm8x1QZV zAPGPxQS?O%$p%mL;AihY?n&CC>@6YI6cG2_*#y^+%{ZZuYQ}aA`N70=2zTX#fgNmw z06@p2!0!9jd80%ifGv0Wav zO1EaTfB%0?D*K?$>dm8iXIf%spWd8cE&bTT3?B)?8ZDPqDtk@Bc(69BSd7(*lRr0}NP5{S9#}L3)dCey z3pUc+{1MJFyTR_2Ea*rX*Yg8u|U z(tl<QxgC4u8l&BgXwG;$t zKJqA07gCTiAp66?mZsuvI@@-}rSC`b0w*vzrdEP3VnH88_XnfR8KVe#b@6*E?~2A$_> zcS@I!5TFbNW{y zn;5#HOLFJX6wYG^6nB2*^{9r#-?bjqYT(g)GKcCohU^csp8vI+27WC@)tclt6U-OR zkAsXE896&aPSJd?*<)~#G_s+o`HU)KMD>DV`D`w6ZqC47TSKZAHS{Ups-<&UC+=gW z;qe;^XV*;)21So}QK%H;hAdv!=WwGu9-}ARnez(U=o{OKd^f5A!_Fv6U_~1P)q>nz zfhH6ti*Xxd&Zr^MZQP*vcHh#76DPI2k$&MP)WU?RwfVfr4Eu&P`H-K^7>mq9s1txx zprVE0@WJ1)2|?ZNS@r3a{2D~m@lcs7U0ZiNnZwQj{vr2qhgu(3TFspL)LGJ=N-|-B zarkWsBtOy)ld=Jr7>grzx2OvAbgNHq%aECTRot{ke8jexLK7xHkXCSXG*Zwmx9D0L zpsZsW`wj%BwM*sIu&ao!gzBmiXPoSTq(V}ZKB&`CZx0yCr=%<#4fnPgRftaq zgIS1q`Go9B3|p~CzzrB8cC2W?dLk4`D6|4z;I47F^}aYy*=vGOqO0M#(YKGaYjs{( zlE#6?kW7b4DDe_h!P@%BQ9R^0APEY|tTOE?#Rk&L%!E6yLkg!4KU+8~vCL}W(l&Ug zpAN9wF{9TF5&u=CjUBdQ7yr3wIr!zDo{xY#Lj7sbyiu}>E?;KgE1TiOV2(DH?LUsW zN(m2_4Wg}HlzDoUtIt@+j;;Ut3ECDl^I;C2zxek9t3_imJK?~*ELwK8>3!vve4OWR z5vEx<8>d@$=BJ}R^7h5lS<&4hZ6~<)C!uz|@e;Z4$AA%uvmxwOCRpo`nL%)Q5_7e? zvA_Lak_VYj_hCyc=}YZc;X0#NQ>&U2aB9_PjJ<^+dexZnjQqKLQ-JZ!5)0e@I&UV6 zk#3Frjq@Y;9m5Yy_--gzc2~XzWoA7;Lq#;H!+pdNU=YIecU{%a;kTAn$>wSEfbl^c zzFB3)h>3=kL5@8Co*ThVPg2~!D{XKXL*nx;SjVQP0psS~rc-J6YDA&&$K)+vxSZX1 zmNQ}{M-&s839UIbrAp+OF-U;mNudh@S7Cg*mfafrQM+}Yu9wwYs=w-M@j88TNvA`4 zs?D(ECGl2Cl2*2^k9hg7e3wkaY_&xjO2q=kur+Gr33A)|N+(J2OS~Ci$%yziaQlVz zfvEgu-YMwkXZaZX(f*!+@OSFiu(PJ--;+$MSb5_f-vjRcuNTE@^ux5I!NEOA85zunhh=2s;Z|DdbV4`J~h(ayYgfB&Mah zv6WsXkaDX>!JzTv{;q}3MLp^-JDQ=jf)sTg_fR3m=Wnmj!%zSO4RX(y!jagTjKm?{ z7{7cJzYs}TrR=-)hEm1&{rX?zSomc{#CJ8n!b_%)Ny#*$3H)Wi#SQ;C`&FEIyYia5 z`w|XC=cPaX6ly4|yL8Zwbv?h$jlZl;Ywn?q^SjN0lUD)boibNV6IQbJ*+fQpGy|js1|UP9K6X48*|S#;Y-B zbI{UsaGZtjNgvgBcAngvt-J^`%-&L2lby^e$J)wqp(2xC>jn@PlfaqqYt<$pZI6)d z?7CBkqc*gW`$`+3-xD5Zri*n6qQ#n)k-}5#GVSLeBU~2``~DU_aRgs|1iWGo1Xk|J2}DikD7!1d|i)mgiO!srLxorwrq}RggpZN!rI~6^A}=rxV)h>nq;? z_W+l+Dpsq^$QpJbM9;6iBfgGy*5UaqVF=R1^pJd%#F5n({Yh{_3`BdI^TeMw){uPM zD7xwhcp`vgRgX{S$)>i?D%3}sa&3(x$S0)?>%@PJ;HS!Aqa)AdcS2^BxnG&p4Uw_y zsAm|%QI|Lf6rEGD3X^tGpAnU@Dn!vji~qIc0zyu)@vS7Gcj4u5oX85T_(9$;Z) zF%}c2e6R?!p(&%{-v9%F6kD5AJ5w{Uk&Kk|E@&qK(M32KtgCak5#2V<~ zvu3q8ZJ7^I__@H4iU(NI{C0WUa5QlgoaD-BT1>p((c$GF0tX{|fKpPdfgLp#e=1}m zlb}C=H*aKi)uUo>K}bfU7;c0ETD6RBKqy z;z;j#5aLmpM|X@~x9O328r-T1+n-glS2cGD&hOD!&e;K)Emm|%$pJQn@>FEzlA}?& z?6Gsmu{Q@cEG%s3%NkNWu?11d{Nhh-j|e>=w(-^5uiyzuzCDKNTrW6oR)Vpe>o1GB z4G=HZgVQn6sZG`+6>lF>5RW5p@*YTNetVd4S|b7G9V=96NRtrqnw1yN%-<;=d2meb z7IK)f-xPhmofQKOiuC^3Dc_~0-aS^V6$S4d)Z3(#0PfGUA6$bB0ve@ROY`G_({;5@ zD?I2;y{L4@pevFnwhS~+!Mh5}0rB8ZhjT?lCutb;K~MKj*L|qCIIo?wqA8;}_Se8= zIpVw`|3vKjTrcrvYJ@txkPv}z+Tg+;5?IeEwa~fv-OF!pO^xk~Shdt!)dJVSvu6Pl z*G4}l_`0i5HT72XOzceiaHcFx=BNd&A9v+|fm3HkXY6i16nIPbfpuV%HOog38yPaU zwTlW=qC2k?{CnI)9U3WnT9E;3wd@$g#km)L+>f1!l7@3v;=76ye_H2Ddbm>VB_(}M z70l_mgmyGdnjk$=TW`MzDV;|iO8}Z1?3uj+S#s9BJuxp^L66c_j0@9eQ=iQdS8?d~ zU**OR|8|AE@Z?w4OLPhTC^CQV5d?AWZI6*?bemA9zDhjnG&jpi1in6;;>ByRr7d`= zy8$*3P76>$z?a<5CDT~6p^K<28pwpm($zT(woiJ^`0A&)OdqCP^9Vtw6D8j!X(MYQ z+DkmEgw;$#IPvsC|ABp#N_D}Kli}MR5SFRFMaS)^lrHR_Jdc_uIGxwoYs=2- zFyFjp923RwB@;ubd)PCb1tpLu*P<_UA=sWRj8z1SWfn&MpE+tyHlJ0yh>q1((0A}hh#=4SbqoP)T! zZ+0b)6xt)JoQPi&*O~&6jNm@G0tQ>M)`>%t(?P3C@4fA{T;l5mjb5UzT(s#)&tFP} z_<4aj`D2b^t#WS=@z>L1Wmb7Uz7W2-ByCiSefy1A8M&{FmR-4%91o5Ex+u=~lXrbC z6#lVBfd&(fA%DQWGvGkzn*h@b^e=ENS6a${^zv3lQC;aRCzFu;*54pqprDa)-8RQP zEwG3jvo%eDY%!GXb8XiYhz*iDpIgsAJa2Y1dMfm?AY3GP$FPrBg~czp&xNp2pUuf| z`@0Df21#r1p2I$V?BJ(USC0V_tr~dop6oG+lPVqR_6BV#50Ph>Xdw3E*oAp@a*@1y z&z9+JVR654XY!U)AWc))VO}nj$(BZ>=wdW6KziYL=jo;3POY}3>FJn3@G;B5xAkW@ zN0y*|!zyM>r`C?IRqbi-fm^mPNL@6qgv245pA$@E3#A5KM-I|Qo4bs|vqnPi!=2ue z*M%F#%d@7h1liOuFqd|LacL)r7|B6O@6T0G*F+U#$$&|12H0R*zSR;YRWBCFei_pI ztS(%2MPL+zG<*WMk7LRBCxKG(; zJx>uWLp%RoZeB6Lx!k^g}<0_WXpBUiTPCk5R5OF0>)z) zEHMaHG6qf-*4Qu(bZgvuP$);E&&5yJzUro*jq%4@$QWxW|8O0F8Lj60BdsP6eUXMF zF#&^g8%2fHZdbm0ZeS3!<3Hq_d}ou;T?s)Tkoh4J6nClVGp&WKXxO=>kzY`65gQHL zUIIzZkjf>F#_v(bb;!|s!wq4CxE&AxMAqWtp~E5Z!M8ZZTG#jTKz!q*NJfuS!i<{x#~tZ^>9-BTPObi_===I`%V8XjS%8tu zT1Q1u@B;1^g@)f%N~M2y55h>gvg&Lug1@TC z-@Ttcj%m;t}?8c7xh$LB=l|_|=HG2#GmAncd2# zL{EaD43t;M_a*Ti8!iU%3azJWJ%VM6Y%h9}7H`fS9Yv=zI?F@}%OIV3iam~AKwd(f zuvCo~Iv1-YejG)sUUpqNewZ;-VCLgG*mN79yF>!^)j6R385g!~#27j7V z*wV?{Obj1Ycf=htI^Gc{y=N&_1reIW2EaVfX&5U1g^E7fc1N(Q5AvrlZ>vm zx{;;H7?)$w)BQb5iE(8lF|E_N z%f_$rQOU(fY+hN^w_%Cwe26qeH1*0N8Ym8NUko0HE`gOpO#5cmVX$?JI0@4~8`kQ- zW&)i_J=Ds{#$gtlUzLJd#poM~Emnx{#dA)=ox+~t(q`qyS}DyFKe6tc@{Lv<|kKI2y%@io_eH#L_x`?vnv z*+xHLBKCa_A=0vngBwC`eAiy zS$Jz4jL3^nS~qgG-#4Z9jqXJvSh^4{{GA=lwArND;%;clk%g3G!ZV-vs(Co6qBBPM zZ;aJw&)OW+WsgJKV^K%9m4j7mB|)b-dLefc8P#xHio)FsC55PUZCmPnbrAzF05$)G zrAXD{-Ov41l2i*EY&iqkRqRp>O*K9{w{F}a<#1%nV5p@j-~Q4rAnLM}mM0K^!WvG? z!oQ+(?VD4)ll-;n*c8C5#&pcVGt`&6vl(!%wWD&Dm@OzU^Gi@cOgOwGQ}R^=3~x`D z)MZ46p-KptdQW}QzC6Q~4g-Wm%X{(sn*H%U0L=l%f4w^+pyA_#KcZFL^IHrl2P1`M zKbt{{+85qY5a&Vwxy><5R_Z#f7Q_#YSClfRP2&&dlqo+?AR+7wLYh-NUMLVlRVbcb zst+M?BHXn#uHCo#3n%f2_8l;GeQC9EHTv=Wc4yJ3N|v}sJ0ONEXu+koaZ<8u){43I z0&1c=BRGslm!;2w5M!ivrZ!qhNC;Owp3yZu;fU|^stpa6TIL4KwOjT(HD4~7Np1$1 zBVM+v`~spc{BB5kU3!62S||LVbzUY)dGiPSYy|zT!wO)snHF$a7%|1H8g&V|;d&H| z3ENpgIgh0Np_nMo3!MktTC{+nI)~}y&qCf#MVFPEWLg8ir87$h5m#~MY}e~u4YDFX zRg$%0KKIM}L8x(p`MOz0`;HxdzAwi@QyeN4oY%JXyDLpvB%}sQ7l|%qG|w_>y9RdB z%cdgu(AQG+*((9RQ#|W?i$=I^>2%ypv?zoV%IU&u<5j6&Asw9sZIIyA;!1nLP4d%r zCiN5bWfGP;d*!ayOK~$eqwxtx{{@YG2_;TIW7?UxG;E?bgfdwW8)E(fNa7`-ZBHNT zI=i&8e}zzg1gy9o6zvK2(|QU}*UUjMl2V6=Je`)QvjMfw6u&#obxK#ct^jjkkK( z_G~R6abD)6W~kW)dG|Ya7GTr_%78QlH_s@t>WEW9lKhNrHvE>{Ms1}Z{U{owGARDa z=t6-%$UCl%7VRL?5}E1B?67ZqNO04|xXMf3?@T33<8Gb9G=_|q-!D2FuGufJJZIwq zqodJ9(9X(@9gb^hL*O7=XO1<6V5*z-HIW!CDyBfX=}i~BkQkfhQCEM_xu3^Snekh= zRt0Lpcj5j$WV!md-5R}L?Xwuk3EUP-P*hVDu;N6Vn?chx)nW5uZvFOvp%9}NlZR-- zmUYhBo3O#D4O&U3ZzK-RoF^(FNBq;kiELPGIh|&Y41YB0 zxSPnn=%>V^x1v(P8945OEr zc<_4H0{6$Ar}I$m?_uEb;5&IBPUwf6}|Bpej+4 z21AQ~x`Sn?$QFo|19HK9BmH&j1H2)iuB^2Vc~TliRSG~^?)kXhM+FiCfMNObegh?U9 zn`OGN8EAU-#g#VA34g5v9$N(Co7GS3UdY3O-yt53Ds%^9(2SNsAj7&&ReY5}-@1w9 zYah&xHj@$oxY1qW39Jvq9C5j8CcW=nHta>@}q0AaMK|*24AUwHlJL? zXcviV8L0Vy_I-D0&_KuMMh#T;8LqrqZLUUmt3r}6!3;2Ih$zc9kmy))VG4H3^QO<3 zm)T`YX{=SKCAun~1B)IzA@2zy4RifOEXl_x4#!1dn(81JB{>3f8o`3aOg>;&h%$>N zrHTX_i2R1j(?LGM))wNc{11w_t&v~LdRZ-op>Ez5Q0R~wMlRUe=A{ojDQ#BnoQoa( zk~=OBXdv_`3B;K2B`9Z!cwwu_qi||&oQG0=xrhE?cv+MP*n1-`{s5?bPDq9-!)HM+M>ldqbtY|OX&;N(Gw+yZ$$g*|CvY45fnVFfHnVDsg#mvmi%*@Qp7LzSz zS+e-L+ikbIr|-A!4or>X^C~}5DIxiU>x~x<9onu4 zWg844zZS$JAypFsyD+hC+fcL=3n=>_m(>1Q_ocWZq$}oAE`d4tjfOF`ALCd5FV(^K z`_j0Obi1c%4MljF!Lk%yKJU>;L|185OK0he;pS#pEm+r)O`j4`H0~E3WFVbh!=BCo z+0?3NfLPpnqw!G$V04R85=dLOk^u~3yOY+t&5N+!pSGz?Y<*N}bD;k&9@sQOZ(V!rjr630L>{ zbgb{QT?NW+_8rmDximEQdV0(MYu+H33PxbUkhXe^?;tEz@y^HQRi14OFH9Otm$*=R zgYJFoxJnQo=11EvMooI993-O)0@7L1oF*2HA0xX(tYa3OF36OpncQ>EAto3{DBh?u z#ZHT$LFxcbt47zg?6P?aJyxV8QKO~up(@`u%{;$aE{O59 z#$SZRzhK|)99|0`=YZo&q?=-|`vsuh+Jx$!vv#!-@&RrS-hTPA-5^k^>0#<&z$6%% zywqpM7ja%%ywh&zwc_2f4jC9y02`Z&6d5l#z&gowDlDhPDO2dzF9=)ZSK@N^QSs-Eb5`5)=qcD6Vzk4~Y zt6>QT20u!i>gjlY0Tj{!4}xbGl1an4A0SjkW&C3Pus!5a!N0Bru{4#zBf0XC^F zWgNcDxU6UMeV02K6>>zL7Rmc`I>70Ees;-76!WmSER%MGY8_?uxot}6L^BX4h-fDC zN;Ut@B83^VKP zo`D#uFo?`PnI@Sqb;VAub8S>t2TEgpq2_Yc##J)Rv?!ByQh~O8h8lt-<&pX7!dZ(v z?!$TI9p1S8&yw#RG`B>k;S*Rw8QT@@9M*(z$KP{y1-Ar?L}1|`_krC{G#Yu5S^Um< z@E_&7VViKNI(r*KM3J--zwTTPq$n3X9ziH?3$X0?GTh9v?UWYkB!%LNnTx!in)*C{ zR`;)~aHK__fR!H=OmS;jM2v-XK^kIBmpD3~F zOYjKtaF{dHyCOUTs^e(ObTKDAMW=G!ZGDFyo%iL}cD~Ze_nSEYu`nZbzFH=HPU*h> zz0>H{qr>8w9SFrQMPo@g1^FY_M$EX0)y}hM{0wq4tds#R9_&zR*-C2zZbf?^a(imiXZBJGhW zxg%SmxcHv0@@T(kJItN$fs>nxTCpv__GLPk?i%Ni@Bpd24aO?7?^&4T+c0J%Ur4Bb z_xS|lL#LN^S*U!{P7%u21Jtdd#C4oh3oJ{Wqz&;rCRDGNQx}x}27iLg3%=qYwBD5t zk!v~oVu|mUdSi_f_%1nBoavsi_655m)M_ROXVwIvEU1vp%3kt!YUFDyAE5Zj52C|} zZ_Qa|dkXntFW1YG8F3fN1DobSz({!>QX@Yf?ys9IuiKsv}~$rjpBaCo2ejH=K#Cg^f!5BHm8M{Jtk3YP>Y#zV$RYCJ6UVVZrVCa%w>|5$Y0d3M3?Bt|AnmyK%4Z7{9 z?u7vRT`ohT0ykqkZH478wmSGn|ajp4H`Ve&t-qe~{O zMwNC5awzZjZhM5z9h};6SXS2x0t9u#&LcL+emRvOsWPE?#Tq7vkld?5M?Naq;X48r zmK54O3MidMdNJ?chsFxY7?gVJC`{Fh+NO*m7M1ZqN?1IPvQdi!CVlu2;4!>oTK=F? zM8}G2NNnp6f>|k9NquB`Y?@dz#RCHOw32`MN)a?|-GN;~hcpi#PHbBb=NNPa=UF*^ z5=w(TU79H`4J8uEy;3waUiG}Bs6FZBxY3J3ypf{s-X9mmS;c3|pw(B0JV7e%@gbgq zuHXLsJJsFC&qD@8F^AcYB+lBw<)l34Z^MZ$Mp4OET4Ko3t|6Y}eQRKKHPm(mJ$dx7 z>yrGhHDD3A5`|k}BYHnpo3W2U=&WNldw$$8A*^coPOkWNu~w9zL&swMfDV0<6J4gk z6#Sh|YXmp27z#T69FAu|tx?7qs;2tO#-olw^+{2EDR41li8$lrN=!*&-t^w*$CJo{ zHl+CvaQqU~gDw67cyvA=QHT_TY2I%>pS+>iOg5?}HBx8MrWb#>4aLo(nPWhr3nOgW ze&0vc#(o#8(`6O<#D(e0f$*x10v6nmgFHyFzv`t?`i(27z4!*}(tkKFC6Z+fm(HU2 z-pa%0QRN;4IM^_NDXpfxB0UyZNeUWPOZ+y-l`REBz+HNQSJntri9Ke?`Ze|$*Sn{EPFbfZSyZ0ssDGDfPDBJfm zBv#6AvBzGQ>+2syNwGp)RvgUIlLl(PTJ{OTgLH6Or94`pmMkX27cmoBkO*QH$nN@8 zppCD?@bY7sJReL7h`Ja+7!iI%G-ZV$uHQl-HRTZ1v&l*ZNf;_BPxwe*O4&q3%|Fk( zC9riN>y8y*S9~RQUt?GV>&1R(3=TPLWLPq4RRXy!5gn4t*JEAV<+_N&W>@2`qxQ9^ zI!)G*v?YeMNh|VJ2Lz8-TWyL$wcNrcDPpc$QugEh1`^!S@{CpOnYVw5NtJPj_$%## zX)|t?r01?Qlu5GS`&Z$@%sCIkY?obK&yr%Fo~CkU-!sE)ooXVvHth@ZjV4x2oS-&V z?uODURIJx(tTqA8WN|;YWt_4-=YbEF;exzgXoY+u&BbmEAs>AT!U^DD^vi=`B^dL2 zf&%*QLQG_DLqBsWr_D~upeFE86rWtauHomYoxkoozb_w}#WSfm3h2|km|CMc?ZCVF z3QU_l<>Vn_y^R{>cPCG3L9C7BWW|Sj;=SJ!`P_>28_mqcr{~Jx@32)l5wpjs(s0(y zG=5#@ED%6u+~nKC^Br+Z`XoR3wYXT^kq)7v6!+_GnZZ0HP^oaNzK%c+oz8K6WNy^B zgtGnx-Le@GlI-yAtZ+5BQ1y?M04TaNu3=&n^GOBVrN(aF-^%O?M-8_e*zc9)JR%T1 z(d-GqX(frviJiTT$hV_|VtGQMpq^4ddAyi$r7zcCzk&G!%8}3!z~w8P?t@{IDFb_I z7LgNr7jAxUAQB6fLM>(IF7BSId;iL1`>L`B4?ThA4M3k`%J!YhQ!*nSxw#=`k#Qj| zReWIy)u1y`zB=A0H6>O>bdJ`Jy{qqC@(Zei>C-njzN=`fdv5_Ou6DW{B`QyxgI2KU z_P|-oC4O%Umt4i-@nyqeA&9&f+Q3)RL;n5!_HtAxII;IL>$T!he}fyqlD zf1GJ|qQ#Z)V1OFY-%-fQd5^V|M27zDo%$Y#W`}J_wSu>WR-`1up1M2{Sm28BfCAE6 zc(^hW%W-vMpNwjggwv2$OYJL!EAz|S_ay!md` zl}&EjDP6geHJ5hocei9A<#~uRoQ}+FblLe&fdzR|S|ZoJn}`C9BGt2&huV5iLNCbI zLgk+^h|}g5;`$Gd!^N+;W;xPbyjwemm5tnHyW&^7)}C;>jy0YnI%Mzod&qo(5nyVg ziTZ*trNRd7sV&inuA50TCwwSi3|W`8E>n7Jyzy(WtA6Z>X#wF*wuK+AY{^y@WN&0D z@=o@U@MB-kEi;4!i(CHr;mjD2{^06L7jPMU9E0@d^#&G;Ee~me#NY zGgTQ*ih?g>7KE03@^OWJPj%bQrDZK;JI}Z8QFIg=owm&`Ogz5Vq=l%vB0Nk*XqXo&q_{&=&n?b}8k5GzXFU!d^iDU8`N6`J( zqoys}tJ(Jb)e%zgh09g-8HFDoh5+@fa`)`(+sE5_MF&hZ$vAh_>v!xOwYb7{2%I(R zHYc@*NrJxQvol&@F&DJ!I!$_SBCaZ7!L-wLy|44l;`c}a;l);QI^XQ)`zEMM-b3Dm zAs`ggo*=LYrFmUZB=kJ`C~MNH=oG!y_c-&-GCTo4T(9<1iYeTEjyWMcSvselTUvn4 zvYerY)LW3z^iz&bb7u+Ncm&utsgaHOnmz<$721=nf3hE6Ub)2%d=8>}cdv2fX!Y7; zTDsY&(&~Y(;fsy|ur+e>RjeRQ&t>g3rO6lJ1B{!wE^0x^TM*o;{(utp?RdLyn`jN3 z;H#New|Coe3--XlAZd+dCS+G9gcbi6T0lf}X3}=ldpPUZW0R)+PR^OtvKGL(iB;p% zOMgmkVC`5N!(C@;F~aJsK#((s4htfmasB4AX^u>o0!${l&iKx2(o6yNnQ7f5wNv*c zOdE*<+!V4h-Vg83mG&nNx1XrPzEW;QsfRQABfi1}c7cerWS1Nn&z&`J(0|msj-=QP zL-z=t1fyDP0Omf=iRAg#Qr<%;B!kSXSq|Ccg^Y68{8SNpdRylm_{6$p+zsg@Y}&x4 zCyf$!{85?C4X3f%e3a$_37JQrh^Qyz+G~YGm3pJQ%PEWc^4^#66EJz|aL)F8CH*wY zNy^n|%a`w2qX2j-(U1XsdKIcqde=mo;YmH~%2pbSDou)%Y+QG2S zYbFa_&)-Y_KsQky0j()~j zwPul6(hHkugNLg+Pv&oX%s?DqFOdp8%2!jZHxT+K?BpVD=eG_rxuG z;|hn{&O;Kh6@o$A8Ky(lO#8q*OV2<%jOn?xK2td=#3wI@4bX!PHLiex zR;s#N!~&_lH1{N-->nybxW2z`n2Gs<-qYTz^9_Y)qz=cb*jGARW;Lg*jzbsm{j-|J zu=~?gQaE+oHka)jk3;hL$QDg_fd;C zP2Wa*oGFm@?>{CLTnZcZD_{uO!WQ!mN-W>5^zOXvtvHiF9fKx}x3Z5h;W#e~hi})0 zzJi3qzPKOfR}5_28K>p#N6i_NesYc(Z#R{$Rnvq!KjAV3W6yM zn6U^h&n?&1A@u7-FvkfKJi?8NMwSgjCyHr=8&( zx`z{_hCdQ%$ozFUU7AyP?EvP-d<~z-OEl|{`sM4LCfj7?N+u|&7dr%p>FNzhF0!qP zGaW0X*@n(ZNRqdIcrd$OD8g#(jj_7umqNbVYSF1_G%9d0RIabXK|^6oz_bZ)J( z;MBicpF~flavTDjvb&^2E59MMSG`;`D^-N&jSt{kz{P8s7#_bjXXJefAU(B_#FKA& z1Rhu&WF&FB`usUqzb^qw>p=b{tKDW%!n^0o!b1m=zoTC|H!gZzSanjGaB=2#GT{TJ zh~Ft9KWE$NS(kcWv@BIUQ}EcDm(-ZX!gi;U-8A*_uQDMI%DbMM z7|4?uRzT$C@@tW3MCNGTjwTSWI=&mvJ%Sv?LqRoHqh&>90FkmTf!o(#6S&&kZvxgj z0kdl3nr+=(+D|bxV1Q0C zD}@xeQ^sx;=3~(6GP1X8zw=ZUP`uZ=i_G|0$bsQj74S&ADc~N5Q~Z`m{M)c4pNk! zxnaNUy8wF07AK>QMf(pCH&}yrV zDzjAQgQJZ#<-lt?9-O>ttso))0nrQt#0A(~ch zdcEi=EIhF>MT~Wt<0;)nFDNoHV}!lk$G=N!Zq|X?UK)6R_@;~@N6w61ZnAlna>zcX z!eqe_oC@F_>Kq`z+CeZ@vk!XK?|z@E*mwECu8V4hXOO&Y7=3f9$-uh(NO~cOiQlae z^>_u=A*xF5LE!DX)HBJ2O_&}3lJAlv`p{1lTRZ?_`8VnLOXm>vY@vF zUcHt6BJ z>}Wx@4(U8-FDQ;XBuL(_irJJ0B#xR1eNMHhrsfhU2-Wbkffm7~3BAeJ?kyO-^@;62 zhG!iCP#a74b7O0Uz~g1Z4PT4;T1{1sw@B}H7I{SK}yA7?si zv4G_1TTupa=WX(=Rk`!?)Oo##WMtFF;WNXK2MCWiL5K7>L#B>w5XSvdEzF39MBhZE z1>N;9bj+GUW=6~eK+ttdR?|6pKg$Md(YEn+wGkJ{^jbf0m>j^Vge{RPTv{WBU?YZ{ z@OQI&sDpP=(xkX@Wb?NfV-6Oa5&qQIVSH5l$T0ehviKz&h|jcKd8>$2NU8>E>pS=P zEF<>HwY>fCntP$x_kNIIeUE*xjfRa**I^R)=-M@BuD8`CDt1&hP3xTSry)XKU7s2& z-pQ<65#1wcu}5&xrJ)_ChSClLe@w43Af?s4%p~M@v*TSxCpeM!A#KLQnAi!Qf>^-K zc%S=1BL(`VQd@ZeaKz!d$NV{jFH_OPF)pWMeb4XhK^CiMlxZRqK$whtTFL=oOjq?y zV30n^9~Gc~GW2r6+F^bw8*(N-Mel~ac`c0>PO z%7>Q@tX>F)=@sO--a?lDM$SrOzP8CcB{$Bdb2Sln*Pu?!<ma z94H7k>#~AUhh5_}F@WLq_c4Q8;f_fY*Ok1U#t8w`KKPeW6nE0?QF?HL9nS%fbN2sbg_=Jw)ckO#O#XtUAYs zpriq|8S9AkOp3dLO$w{cpZK_@7T#YvoUj6DSh8JWJ9w7@?#!y59$(jtp_=y`yiiqT zX%%i1ynTurDvYL-o%j71BO>bJU}W;zLGUu?Hsq6EKh8sdB_h`WXWAsZKXRA$QjiAI zjeC6ln13c+D*|;ROaH8)9 zYzX`D5BrfNj)gBPpi??fXxrV<_}s00r*iwpg`n%@HcB^0JXWF81%sO=`w-!~06~bA zvrzuaTB}=u9WzhfD9Hg|B^v8keRHC1zZOo)(hz+`#BXT#S=Ngr;WwZu0cB z8c|7GghNGCeN~oaj@dL_x(p8YT|{)D!Mbg{kn@WKJ!-xOX$>z#kB1r)mpAg2ZF|}j zX07*P{FbE>ny^!w;XL=%6~h8hcjsz+-o$2^-rkZ)FxP3@MC5A$HgL9#M~hs~(?ggv zDPSyz8C&Dh-A(G1BLU?El0KjXr|qUHhTE!F>SkZVo1+h3rk<=gVyh|@;N))4)Ry2a z&b~5aucsxbArRQWIEd&1^5R$3b!bkGO4rNmZ*uq!ed_wQb-IM$;Qd}5uDWsNsImhM zb9p3MLpAbJ>UjbKQtRFri)RUjvta-};mQr_TU8RdW;1yyjCw=8#`|vTJ=|u?cDJCb zloU7CHj4x^c0O@=8dQ!62!t8+K15RTHca5jJ1LTubq3AvPNKdByN<(9@QS-us9PILXUs!vJyW3{j>r^h_ z%1nbx47Kq`qVd*>TQSpmWj!P8Vv{ED+eIB3R|p~H+%J{wSJVVsBt~&P_K?jEM)Dz> zZ(exYGnCRJi+NP0)6ygLQ-8_Nnj<&eZU@LOTt2GO(ItS|Bg{@xK6FOBQ0ddmTKJVn0>T(CL8LVu;d1HXOILdj89yyJb z>>TKvcHT#EkEMEqaMyv^Cc$Gc5E8{gYK#qsP;R{aYSQW~@bN4|E55;(1+iPtdI6N_WN8bOFUt=-{^1TBhF>k~ zqoZg~Bg=X_hHHNTKBx|0BGJBUKIlADBtJfb31@S4pHg(=UQGvQ^L=+;IynP;7D-)! zcr3?-EZ*tN;Hz-9jU?sHN#c|QdCg}c2&+WD68LVKe6pK3vX2d5i%aIwcc_S=2tW{j}OVCaqyuRWTGShuCJ;G z<;N>Uve>m$8ogHeq_%GIeOnSL~%!>)@q5eQZh6-IZ~pi&zb4G+l^cZSzyvc!`O z=D=ai%l)ya!>$KdTJ!2K;v%>o-i`-Vv{lnCVwMAk$=bH`i(Cy^1(Zqf-Xm)1-U;Zp zU1F+(M~Yh-($i?@gEY6coOP7gyr)Lhaf@(u;I-)|6?4I?{A5XTSr5!&eWSy} zn={(*=@6iBth!b64@oA=pZhN;&;q+gsQ7s=LeawRvgny)=!XioC5aUaFlGcZ^l6Jw zo`Ijh*KxGvk2JfSY$H9UB^psI8ZTPFluT4nM)BM~w z4TyFc1q{}wdtO$5PAod%|4eSn@X^s(e5EPw+Q$NRz_u-3TuD^eSnas|o<~Lx(VSg4 z;nBh2$L3Jxk5Tigc+Xp)P!ljX*p?H%btBwLV|+DNd6+Q(iTsCKhSufzJi9ES&1?Dc z=Fwh)7b^BOqt(bn0k@)6S)vEnmC2+vue^_}@KY_pV!IA|#p68TI3wk@n>i`QHc@&i zPt#PCprU46&C}$Cicw3{C9jvs+RVo{L5ow&cs|g6Pi?UJt@Zs(5ika*sT#~_{c&K) zsdkd#?Ug5$2)>WXZKo90PvWbmCmQugs`d}VE<5m96=x#f#-3n%O&`xwt; zg-CE*7JWUbd2A=>3Xm0_ot05T5lBMd3_6rwdkza|0}Zcz@+Vr{`}0f=`_t0lQha`;ypdiFGJ-W*ya#HG`VBRkxMz$O8A{?JJN?&6T^1fDodmZ5c-@^|vL3eoHyl zfunBh4L~Sf_e^Z=^3>Ar6q_>JJ1m`G?p4*x60%&j+$MCI>XM+Ht1>sdI;66wY~_Go z`d>fuZr*FInuG~Z>zn>5Pkv^o}#$kzWtwcXnJz zuzO;wi>l$1vmn?_0(eL+Qz|9~2ZHZl{+0b}X^J-Vt$S!0g|^Ez7V2K2{orcBi|6fo zHDKU^{QWv`JRm9Kb5;X3twQq`L%J=ncY_0)b&Coh^j#&R=DpZlEumW@fL2A&x+ulD zek+x-Q{>%|LJ&Qc)JofZ17rE@ys>FjJ0QnZJI}3Hq4k@Y=PbHGVYf&(!m~-@seQzw z;G4N&51+-!C(67NBkqray*lRBiBq3Fx5RQiF_N)NY*b@2q)J6n=d}HrE18mJWuYVe zjBo@+?0~u^ctYx=fDh%Ax3ihYb)R%Znq4Q)gz!FI_TZO$d2QK0o#E!w$}9MyFp4JY z1J*Eogk-@&I)~l=+-N~E_3?{}@5dFcvQtI*KYJ`5{QBt}=seLmK zMb+4h5)_Qd-bz`uT5n++nQfjh@#AGCjcJp%ZUjTzDll_Ksn@a%=K@AK7h~Jz{uqLR zdwOe^UWS5df$8m*lb zkumr)pM&}1I~Uo~@Dg|N2Rn!SEb^65S+cvcu{E27-JCVZ(xTQ&Fp?inrn_wCA?nO| z;Tb1j1%TQVvBbvqQl`>xJ5++vRQz)yS||iNMMGkMyfZJo)!KX+zBN;ATe5c&zK(;3|qr_8hN3Q)%Y&pK7^x)jv4aB*V+SxoSmFI^{MzJtDn zMk`9o-@xOWS$=4SV;X2bRi8~kB#@#jy?z{BZs_?S~*{#HYN>y0Vz|NU=9ZfP2i*%un!qDAC4BK0il(I0x#p*s6jZGl}6Cg z@F4V{eC6(mXL3{b_9ZpIdEBBK(gG2mw|KEMYRcHdHYg8cbUPy(P(Lk}64lf9`)@|L zjq~z+z>jQtSW1(GxOAQb#2s7X7B-isoueOZO_nvWrU^g!mI=ifn8j_#g!OSxGqBcB z7WG`yZ=l(UejWO9%mE!s|5h2AaACK}ylM2~?xHibD{=ny6&olN^RASPj!7z@3IZbi z616#F3)T6tU{}FB1oHZ@FN%^vD`uOro0m(Pm4#gb6+>=Ty%9edb%B!IiPx8Qvm8mf zw)&z%H?z||kfbfyJoLAaS*cL_+^7aXUHKil#o0|mKGUR=c9k-Q=sZQ=ZO`!VhBQ%` z!HRD!G4mq*ydR)=<4YF=O$J5SN@axyS!G1Fka-)dPMKG=_*tGmE*tEu~jb5aE0ASfA6p71tt}6raOCppB z^yLNyY?d?2R4+}muOf->LBWDh%!!g&U^6xC5Fm1gc6j1dJSfzeM1gV8s5OIXo2e^e-{s*4z+_c(6{1S1`X0GaACBTE zOy(%CAj8-@^iYKWCbXHMb{$?{R#5~dl! zYkqR53PPU*quB+a%sHt!N-XTNOmDAr!@=m+_xv~CXYTxfB^moirSk0t_uJMn*ta&* z^t5Q_^dN)WOmcHptF>#*lz^lZP)?l5VXN{_ZY#8KHMG?rC2OW|DT3=y;8!Ov1>c+r zpp8A0rM{`{Jz`Eqmd#`UwqZH=2ov_6P+?uM5+Zph9yh5MiOlC40QLBJ_;TB-E)0T?H#fu zadF|D72W_t{sZ+lBq#5RW}BL1$<$Yvxi8IOR6)`d1eO`uus`11<|%#IEju8#zT9NJ zmOK`Rq%_9_`mtR{H@i;i8BkHOrf-616LT8nYWu0T9H)3t`GZRlQIQ$`5svUkT;j8% z>83D2poUQ%;`bopDa}UpCBRruEo+DfpVteS%O35%fL--+1wHMo z3}*IMhQQBz;~A931zoz1%l)e4;K!SDDzM8j+g%$0ObG`eExXOR-Z5xzw*);ds76+d zNZSrV?9}~zqoQIgJ$kE&Z-8*U`j9vFWH|mH3a`|wKm#x^c+9|-!bw=EJf@G56r%#4 zov3y3-;#oGZb5UWz7;9$7LPfAAJYfN&BP@0A%~Mt$6)AZLvyAkot`vy$bOe ze89iu;XjBjSw~j(i@~GOi@i8#JDpB7AIfumD?hChj&oOh391|S*5oqkm+HxGvy$a! zbSe;=nP~VfDPGy{&nt3Tv#=84X5TZexVU2z-27Nf1G!jJ0iCF2`Ut`GEjR}ax=))Z zg4D}sPaX+yi5UBk_!E6l#jED|;cQj|qj4vq=}`-mo=t@;ze0#A;$W~sj+Bu~nA^AX zi?x}PmQm`R+^}eXJA`ip);wWG_)9m9QQ7*&Dy#Yzk&@Lxc(qf8Z2Pp>(oHQB43<65 z&RX?5^I`H=&V}yD*q?*H*fy_)@WMH7)NGFJvs+~`IQVzYJr)2-o6Ng=qDd?m`YnYo zvZ$#8?LKS7qHtdd&Ei+(l0h1hNd~hZl=nt-MFM)qWitsZ@l_^=jB^rJhw#_Q6>OIX z`rj=*2M_FMabmxcUaourkE!OvTWIlALET0SujmAv^OX6Nq4kt9LSp>lGO6;Aywn3Q zrg`F4tHc~K*V37;hw{PQ^DC;UYRlcc?w4SS5<}bZd;iE|xNiHYXU5riy@gC_&JGLp zM7L0NMb{1`^{$DbEM!_NIx>p0fSOMi?u&`{oG~qdT%|3d7;mQY$C$IDkG966AH?d= z^YlUKt`&P#MZCa6rHat(Q;Qu3J}{?0W`#?(udEn$2SNY=FV%D$<(b4@Rj~b2Vw(PKQlm`Ek1GrO;G0 z{FVl^VR>`oL)DM^)!&fwms1Y_qqpT$1?tpItt_Hn z9@VyBonq3zg@UQKv@1->ap>i+zH@yE9PG6uvdAST<4A&q>umUPsj!?%P^we_M{zbn z-`$<34h71c&WqCgd(1aK>X%ynzOl!#G@0OU>gxcOrOUIL1(vW@64zU>R-p#pc0~zr zSnmm3^g#{~sMXOxYyfFB7{T>*_mv0Yb@yp9Q8bepo#NmCh5cWVoqaSlT-5@NS{VB4 z+3dTCwU0e8bliyW#FPE{jShgBlt9=KsTGGjD@7WVOI`Yr2IJpzm1HwyPO#mEsBDd4(XUAlN2SGr%r!f>-%#1LH|!Qen2pPR^^3GkgvA~53A^1E zDut!_-m~eYod^5uZs2$NOyQ9FsL5eo-(3X!zqwZU|EtrWz2-)<-1fA0 z%6W6e?V5$&|F+!uU*ti#0RRAe00RU80D_1AQ4erqu!Ell^Zv%p4*&p;7*w1OfcgC* zV@A#XxS|g6K!a~qmr8LIYGa8Zrxb7{pJ~&@=`7xUroz(2-U^eVjZt)e!2CnSk`#Kv zQeH4(3UXGC<k^(XTDrAPA#`PE#QYm@MLmESAO>n@}lc5$L(TgiC739=8b&XJFti zTA=;;DK_VCmjgfOSF!>)RB<>ny^dQ}U5`o?(Fqk^>VP>&f~aHh=5>g6(8mS& zbIcOqgO&2$-tQ&1C( zBsrScW<)q?tD`IaB{1B+IZE1j_nd#6kcrp7<(}}4=wEm6XMd_c8v2>w4?pGoSmX+w zDYoXXSAwM+3H-G{>9FHZjU=2O7z&h~`d>Hdo$e#*?40a6sao&F^_(#HjhVy?d zoBWOw`e&T{fTj3Ke~a_~M|(^=ze0rl9il(b|4@kVKSQ(xEG2h}?fDBY@D)qJqI-NN zIvp2E&Qz%b3rsX~sjHDp@7pJM*w|U_uh!Gpmq`82aD7^3>dacH?8LJGfO0bYf3Q9b zGKCWUs@6ZZ(FlkfT&lqt0JQa!$|p*DwEsVq9qiwN_!s{71>yb@vG%Q6Ep)9s|*rw0g9Xy>}*8}*&aOPGT_qyxQ+-J(+X9^qa*iHpD&5=4awacneJi5Zj`K{Zwx7gg<(konUFMD7h z%6=6a{`_etU&L3vO0`s5h9tJ%DC5Y?!Y(f|ch><#$M&4D1sdyhAgvMJG7FNevd1 zbiOIaN!x{21Z7+5G~0PeT8G{#2C z7C=1WgK4mS4nzzag0opt2$u``vQM!LDrAM+B+sYn%{ODOt^k* zPWWLf7(orq9}vsY?~_GewZCJzNdhq~vXe$l37zHmbT7tdzu7nY`<2Whl*BH;1&x?)`f&fGcO33K!Z@7{oJGr`q!Waufgqf&B`A^5;LPE4p2{dSloPl`4)c0HfV;71(O-1WLQj28!04RS7KJ=L3z|T7W5=g4>lKqXK8XwqJ9M_U}z) z5;9lftKYHKr|qG#q5Vn)B=s2emxuo@{zu^d`a`nwOEC3M;+lY^21>pak#% zfs80B+FaO|69CXpV+Zj-Bped|k0!xU-K@7mqhC4VX2)stf3*1@`_t$D)f4Scv>gV! z!GE7&{)%>dp7%q+$k2!+XfdDT4ELO*6+dia5rO5C*W`;+%~uHF5V90@%ElZDi0deZ zwd|K}lfatg6E00#D&|VXI^L{o&2(r8qMINKMZ4k$J3I8fRVexyNG`&BTP-3({s(@p zj{EG^ZN|2*u?IPDM72n~euBPJERgN!>j|9X%qz}>6^8czD)xU;bL{&|3jI%LT7ab% zPX8G+VmLkk@QibNz)Y<_+SdYqN=_A#m!H6&4!8vWtGE%8p~IVhHU0@VH5C~PMxN5*e8#~9w$Np7UCk0s54s=4Hgn3uTa!UH7-($3G#Ku2NhkG0H_p?Rp#bWX$YBQfl2CQeG+Q@6reXd2`PCb7r z1;u$ccKL=q4zzHwk_xDgN#o%N|1V?y$4ceiUkaFi!qNaNbx8QnVEwQH1jJD#puC*# z@1_88IYm#Tk-cil$lB27JAO|QH>!pplO{0+{tsWx4T8O_9!|Iv%5#Dz zse}t*W7hHh2=ni`{pU)V-=x@ol2QjOb#M4@kot#)#3Bjm6F>qRP4vL7*#iDf@b4!9 z()|L(`4d#F!S3NddrDN}A3UY{kDkin|0hpzv?*Ea{_s@6!GH7=ch6Sp4^Qnk{wJPV zTK}u3sJH$e=D(OjezN2H&58RrC%=V%Cnw&YoN5gAKK!$xtU3QQRQl>a8tVF=47Ig( z3uqbD_@Q0I_Op^Me>2evVoy(#w|{d z3P3^6S#)84cdL&&i@3p#e(K|u71l*G2!MhhnNkZ-z{*v`mKQ>`GAo+AhHNt9PS12L z^vcSE-M&S&+ow`giEWr=3yBcq2H*!pg#g9k#Sn;; zi*@zjDc>!=G+b7*9XQ!m3w~OOIld$pJ6J6^UHy7Uaf}X>-Y#DQt>bb%yTrt^Tuxkx zug6tqG^-FY2i^=*rW^9(0erUc=Eu1JIX4y9088H|I0C>HTzyrrE*VupIU00x*J-mc zY=_m*6>3fUi&^F2Z3U#Lw_-Bsv=3fn*GM@-xBWVAC-W!0YhueCP1W>zHt(jpQdnNM z`2ai7QSpX&el@0OV8Bb^WcV%4i=ct%6*t78e$Bg@8Z%&MN-%@*pTS7hS@+ES|KWZ80 z_8@CoThSCMHD^^w%PH`wcZ?W=xxi6IQ_&omtlWsZqu5h7|#_w+eRN{VV_eN$*D)V)Yk5>E8hJmHu4-Wq$%}FxU(K zry=tL;Ho8aM06q)n#nu`T}?tuW7Wg{j%stV7-GJ!2Y*#6=^^|yNY1roZxE9Dt^GLo zfFxZ!sj;6j8mt9%r05({(swM-9D!IJo@WreMWok{J9qoZDMaU%r~)=vD_nOdjF_g# z;J)tr@2}uR>DLigwUaVJV~ImAUg7@@OzbDRP`G52o?Uiao z#K{NBW8{>TvGq08=j$sfEh(Zf1%4JBT6=604&z8^b2V%5RJifo@=`n)@^108cu;w5 zj>38dEMdt{fN}GmK%=epyZg5nkzoa9&15+&9C?@Xj8W&hoO%S52#O8(-9XdrL#WZG zLjyEpy>MRsYd}EbzxXKs=2M^W@8YBSlTWF^-o$^jk*V8!GeKUksPg}NGs75(a%^fV zS41gC5t~YM*d(c>kcJry#t~z3gh}ZnB`VcAIyO3_rAuw(uCx;AUTUiC*iP++wEn-} z@6Ak_q3gdspSJwG-}~Ob_ult?zwi5g?>)R!s7f(@;ccc}Txr}`V*A^^HCENeANCE_ zyIJTQSCMmyYrD%iZo*+n-n$nNsk){`CGC*%?SntW1(IMK5`k zo#;^$UI#Tun0D=2R^*xfZI!USp4PAR*>dB9VT7Qt{6zf1$ z{I-s#drc#JK%?)kCl`z}=Z-x4(=KE{?h~8)#5{|@J=IF6l?Hva=MSgsfz+d8#jBQsA*w8`&2KKICzOW-;N{$ z{y2^B#jhfH_{Dza!?!sM@rg4jn12CZp%8B#S3h5Ai5u%w_}UWLF?aTQRg5b8=WuS> zvXzOKmS$!i({f}pbe#rJgt>Joe4_1b*)o7S9*zvgCcZct4g?eL}*BXL19paYe zwQV?E$1)gJJC@Yg8hko5({>g|XK3KglNR0{>#2_1+9($7YPJ2e^7}h-URvpe<}I)Z zw|$|O6f|;4X-TG$1wKGJZ;b)po-s_}VxBSC(ZJ77?bV2z3+_$Ie?I!kcAqTP6&1SI z!-o}575j&4Y;?LQO_?5+t0|-5s>#tm|2#S_Y4^pF`Z*{1MyirG?R>M;H)qc?yEcb; z&l*nEzsaK;C{^x_l;;*pSuVeHg{pfmwZGG{oW8DFLtSh~Q+sbr|M8Zcsp60-@8e1* zACv9+yo}uE7ulj1+cx85%oyu-W8;)ZUe7GTA5;~3I8S)f9+T%1srOfF96NK+4!4lS z(FMz9RVPd_(Dbm>+pqGhg`Ytk!)E0Ev5x6DOD;_=f_q_+-_K2ympf(jPUnwr)vQ%h zE(%WAP5)37O3x9VQWkMZ3BPw9xRC=?FV=xU+)Kc#EU)N@S)M-cVLj8EgXkVYQzt#B_>d9#j&z83F{brY~zdA-?R^rIlGQ0E> z(&X**@3@ij&(ER6hAFlT3OUreFzM70Z0zv^PWC4I$a{u=H%DqT_IqRU&0d2pO*P4m z;$-@lDS!B6k%tM7o=qfQIG)9y-Hpk|stb0^g8PN94Ve2N17ZOtia+b~7BU;fSBQ zP{!0t7+FhjK6TVDPv0JRl_=~1L>kt)^Vf+LcJt#2g=uqUU2CMDDAw58g%UQJGx->|H?kZfgjZ}S$HA!TLH+Gol|WY+kO zy{KB|j4l6z>RUc`S{-98-65+{-bTwP(o$nerR$6h#{!&3J}wBR9V%!2vgIFYL)9%4 zy6vg-=mQVaM^AowHE}z+qW^xWdiiK0m-L5I=AM;Vc{f$3(^o3*`k8!Dc#J_rva#^2 zMIvI=2O?Pf+R`pIE4=LGt>BGmT zlx;|rnmYLcWN0GA=Jsu(y3$&|L1B_IN_j4+I*+*{nRx_bUhkYd{%@Sg=HB=qv<>!i7)!=m< z7=Xx{{w6_)de@6k4i}upg$RD{Jvtu{^=X(Vu7S&mbA;Rb@AB~pm>{=;MY)wDR^mWz zaerE+5k~&)#Y~+OLjxuSoARUPm_6CHWp%yHn|p+*H+u2UgNY_6}G-#p7?@w(S#N zvme}!-fnW_L8bPLTC1~+X0_y_n!w^nD7`mL~hN+vzj(`V{SS4Ea2pgUv7=}s5``BMI=*Uqk)j?igw6>UmVH#}z=W}QJp($rPXuHQAN8A|1K z4?kh9LfHo>+1ERZlGG>O-3UdP;-J~j;*@A;2afpND|m>kEp+s^0_FWxif0*Xp=U!o4pk|)D!CCeqenwr_WG#9DauAwT@a1*MI)1e z--+A32vH(El`Ki2HIgN%VyvJfS)h^>y|t5)v~c?BP2Rf0exah0MAf$`*bPcjB~z>< zZAt#Pk|b*YOOn6(=a!@;L?U_P+2q=8Oe6=-c_>oeUy>#|{yN{(o2SMqgvzAD+5=sZ z*$d-0IMkpA3No)Q^WxY3hz81zjI^iRO1-}MyVa&O_LLLe2Mljn!dP_vee#tzEq`SZ z$#7(&#gv(L_lY3I(+-$x^)A=UXoNKBr27KDGxw)KoN|7fN-Z(Ta#rVOJ5`<3o*$ir zBm>drn3sx(F*;jQw21`V(wXq%(!n~4E^bUQ6z6==Y4b71Nom6bsTD0utvh`rYt&aV zli~?yQrW$lx-qqSW>W9Zi*Ifw6)VauL&>uBmCU4;3ujW<%1YgsTRk(Wj=A;C&7@X{ zGRZ-*N_{0Wsa3+6RQAA8-Iz%|GpPS*r)RFTI&7}JJ`3DAqmv|W4pUYwb z*rAcZ>c^ZF`3RY%$vMt!8H!c)8dog%j-V2ZXL1KeSD7#~^Bg(ZF`ao7i?b z+b)%dVH7iDIK-352?5wZPj(1+sD>IC0gfvz*yldI{h2=QK%(x?6hC$g32@XxIRozAr9eOKnL2m2Qb;5P)FgIzfXV{K$N-uXwZ(kJ%mZf1~P*^p$=?N zE*BciRwr+18{my0oDks&fjSr5xeV}v2epo4A_kQQ4%-Hxu)`*yL9jclRomME zWerBP#+jI2`z=h@8^HbgyYWVu^v$?0iA3UI_)826H4Fl7EzQR;CB+L1?w+>9F$(KZ zqW+uaW;mV_Baj^&z+i`gjlv($`UJU$02@R=hO!4Im^cZfn>R4nF-$dtgH8}Yp&mX0 zcpT#D;JD$5gN*_@w6_C274*B{*EZ-3ksUsakb@J7^kf7OnnAlBn(#x9KRzQKKrr^i z)o1vvC-2^W-RX%dbXo7YKL5-0sVA;_;tE`@|L?pS2=2caa3Ij&y%*^4-V8!YC6Glx z0ns4;C-ABhhc|BU=GPx|@C6e#8+7m`2ZB%^(n1(5pvB<^aR0Xg$d-cs9CTmM&w&mn z3dNxNfPNZu7U)F+SlEFE_saM>Km&-~6u=SzJSl+31Q2||g7iAjBp$G@1^W;KWd8uT z8o)aMh6$jT0K%Jp$Y1<<@*aPEnm>E|)#I;!Js*`rIE+BxSJ=RH4Fg}GU>>1Njyr(P z{+ str: + """ + Returns: + api_key: a newly generated API key + """ + return security_db_access.create_key(name, never_expires) + + +@router.get( + "/revoke", + dependencies=[Depends(secret_based_security)], + include_in_schema=settings.security_hide_docs, +) +def revoke_api_key( + api_key: str = Query(..., alias="api-key", description="the api_key to revoke"), +): + """ + Revokes the usage of the given API key + + """ + return security_db_access.revoke_key(api_key) + + +@router.get( + "/renew", + dependencies=[Depends(secret_based_security)], + include_in_schema=settings.security_hide_docs, +) +def renew_api_key( + api_key: str = Query(..., alias="api-key", description="the API key to renew"), + expiration_date: str = Query( + None, + alias="expiration-date", + description="the new expiration date in ISO format", + ), +): + """ + Renews the chosen API key, reactivating it if it was revoked. + """ + return security_db_access.renew_key(api_key, expiration_date) + + +@router.get( + "/logs", + dependencies=[Depends(secret_based_security)], + response_model=UsageLogs, + include_in_schema=settings.security_hide_docs, +) +def get_api_key_usage_logs(): + """ + Returns usage information for all API keys + """ + # TODO Add some sort of filtering on older keys/unused keys? + + return UsageLogs( + logs=[ + UsageLog( + api_key=row[0], + is_active=row[1], + never_expire=row[2], + expiration_date=row[3], + latest_query_date=row[4], + total_queries=row[5], + name=row[6], + ) + for row in security_db_access.get_usage_stats() + ], + ) diff --git a/stream_fusion/web/api/docs/__init__.py b/stream_fusion/web/api/docs/__init__.py new file mode 100644 index 0000000..90a0ddd --- /dev/null +++ b/stream_fusion/web/api/docs/__init__.py @@ -0,0 +1,4 @@ +"""Routes for swagger and redoc.""" +from stream_fusion.web.api.docs.views import router + +__all__ = ["router"] diff --git a/stream_fusion/web/api/docs/views.py b/stream_fusion/web/api/docs/views.py new file mode 100644 index 0000000..d75854f --- /dev/null +++ b/stream_fusion/web/api/docs/views.py @@ -0,0 +1,53 @@ +from fastapi import APIRouter, Request +from fastapi.openapi.docs import ( + get_redoc_html, + get_swagger_ui_html, + get_swagger_ui_oauth2_redirect_html, +) +from fastapi.responses import HTMLResponse + +router = APIRouter() + + +@router.get("/docs", include_in_schema=False) +async def swagger_ui_html(request: Request) -> HTMLResponse: + """ + Swagger UI. + + :param request: current request. + :return: rendered swagger UI. + """ + title = request.app.title + return get_swagger_ui_html( + openapi_url=request.app.openapi_url, + title=f"{title} - Swagger UI", + oauth2_redirect_url=str(request.url_for("swagger_ui_redirect")), + swagger_js_url="/static/docs/swagger-ui-bundle.js", + swagger_css_url="/static/docs/swagger-ui.css", + ) + + +@router.get("/swagger-redirect", include_in_schema=False) +async def swagger_ui_redirect() -> HTMLResponse: + """ + Redirect to swagger. + + :return: redirect. + """ + return get_swagger_ui_oauth2_redirect_html() + + +@router.get("/redoc", include_in_schema=False) +async def redoc_html(request: Request) -> HTMLResponse: + """ + Redoc UI. + + :param request: current request. + :return: rendered redoc UI. + """ + title = request.app.title + return get_redoc_html( + openapi_url=request.app.openapi_url, + title=f"{title} - ReDoc", + redoc_js_url="/static/docs/redoc.standalone.js", + ) diff --git a/stream_fusion/web/api/monitoring/__init__.py b/stream_fusion/web/api/monitoring/__init__.py new file mode 100644 index 0000000..3529b58 --- /dev/null +++ b/stream_fusion/web/api/monitoring/__init__.py @@ -0,0 +1,4 @@ +"""API for checking project status.""" +from stream_fusion.web.api.monitoring.views import router + +__all__ = ["router"] diff --git a/stream_fusion/web/api/monitoring/views.py b/stream_fusion/web/api/monitoring/views.py new file mode 100644 index 0000000..1b36caf --- /dev/null +++ b/stream_fusion/web/api/monitoring/views.py @@ -0,0 +1,12 @@ +from fastapi import APIRouter + +router = APIRouter() + + +@router.get("/health") +def health_check() -> None: + """ + Checks the health of a project. + + It returns 200 if the project is healthy. + """ diff --git a/stream_fusion/web/api/router.py b/stream_fusion/web/api/router.py new file mode 100644 index 0000000..1ff73aa --- /dev/null +++ b/stream_fusion/web/api/router.py @@ -0,0 +1,7 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.api import auth, docs + +api_router = APIRouter() +api_router.include_router(auth.router, prefix="/auth", tags=["_auth"]) +api_router.include_router(docs.router) \ No newline at end of file diff --git a/stream_fusion/web/application.py b/stream_fusion/web/application.py new file mode 100644 index 0000000..ace8ccd --- /dev/null +++ b/stream_fusion/web/application.py @@ -0,0 +1,64 @@ +import os +from importlib import metadata +from pathlib import Path + +from fastapi import FastAPI +from fastapi.responses import UJSONResponse +from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles + +from stream_fusion.logging_config import configure_logging + +from stream_fusion.version import get_version +from stream_fusion.web.api.router import api_router +from stream_fusion.web.root.router import root_router +from stream_fusion.web.playback.router import stream_router +from stream_fusion.web.lifetime import register_shutdown_event, register_startup_event + +APP_ROOT = Path(__file__).parent.parent + + +def get_app() -> FastAPI: + """ + Get FastAPI application. + + This is the main constructor of an application. + + :return: application. + """ + if not os.environ.get("RUNNING_UNDER_GUNICORN"): + configure_logging() + app = FastAPI( + title="StreamFusion", + version=str(get_version()), + docs_url=None, + redoc_url=None, + openapi_url="/api/openapi.json", + default_response_class=UJSONResponse, + ) + + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + # Adds startup and shutdown events. + register_startup_event(app) + register_shutdown_event(app) + + # Main router for the API. + app.include_router(router=root_router) + app.include_router(router=stream_router) + app.include_router(router=api_router, prefix="/api") + # Adds static directory. + # This directory is used to access swagger files. + app.mount( + "/static", + StaticFiles(directory=APP_ROOT / "static"), + name="static", + ) + + return app diff --git a/stream_fusion/web/lifetime.py b/stream_fusion/web/lifetime.py new file mode 100644 index 0000000..4a54d95 --- /dev/null +++ b/stream_fusion/web/lifetime.py @@ -0,0 +1,44 @@ +import os +from collections.abc import Awaitable +from typing import Callable + +from fastapi import FastAPI + +from stream_fusion.settings import settings + + +def register_startup_event( + app: FastAPI, +) -> Callable[[], Awaitable[None]]: # pragma: no cover + """ + Actions to run on application startup. + + This function uses fastAPI app to store data + in the state, such as db_engine. + + :param app: the fastAPI application. + :return: function that actually performs actions. + """ + + @app.on_event("startup") + async def _startup() -> None: + app.middleware_stack = None + app.middleware_stack = app.build_middleware_stack() + return _startup + + +def register_shutdown_event( + app: FastAPI, +) -> Callable[[], Awaitable[None]]: # pragma: no cover + """ + Actions to run on application's shutdown. + + :param app: fastAPI application. + :return: function that actually performs actions. + """ + + @app.on_event("shutdown") + async def _shutdown() -> None: + pass + + return _shutdown diff --git a/stream_fusion/web/playback/__init__.py b/stream_fusion/web/playback/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/stream_fusion/web/playback/router.py b/stream_fusion/web/playback/router.py new file mode 100644 index 0000000..a78f8b7 --- /dev/null +++ b/stream_fusion/web/playback/router.py @@ -0,0 +1,6 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.playback import stream + +stream_router = APIRouter() +stream_router.include_router(stream.router, prefix="/playback", tags=["stream"]) \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/__init__.py b/stream_fusion/web/playback/stream/__init__.py new file mode 100644 index 0000000..98080e6 --- /dev/null +++ b/stream_fusion/web/playback/stream/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.playback.stream.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/schemas.py b/stream_fusion/web/playback/stream/schemas.py new file mode 100644 index 0000000..2497869 --- /dev/null +++ b/stream_fusion/web/playback/stream/schemas.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel + +class StreamResponse(BaseModel): + content_range: str | None + content_length: str | None + accept_ranges: str + content_type: str + +class ErrorResponse(BaseModel): + detail: str + +class HeadResponse(BaseModel): + status_code: int \ No newline at end of file diff --git a/stream_fusion/web/playback/stream/views.py b/stream_fusion/web/playback/stream/views.py new file mode 100644 index 0000000..53f71b7 --- /dev/null +++ b/stream_fusion/web/playback/stream/views.py @@ -0,0 +1,169 @@ +import asyncio +from functools import lru_cache +import aiohttp +from fastapi import APIRouter, Depends, HTTPException, Query, Request, Response, status +from fastapi.responses import RedirectResponse, StreamingResponse + +from stream_fusion.services.redis.redis_config import get_redis_cache_dependency +from stream_fusion.utils.cache.local_redis import RedisCache +from stream_fusion.logging_config import logger +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service +from stream_fusion.utils.parse_config import parse_config +from stream_fusion.utils.string_encoding import decodeb64 +from stream_fusion.web.playback.stream.schemas import ( + ErrorResponse, + HeadResponse, + StreamResponse, +) + + +router = APIRouter() + + +@lru_cache(maxsize=128) +def get_adaptive_chunk_size(file_size): + MB = 1024 * 1024 + GB = 1024 * MB + + if file_size < 1 * GB: + return 1 * MB # 1 MB + elif file_size < 3 * GB: + return 2 * MB # 2 MB + elif file_size < 9 * GB: + return 5 * MB # 5 MB + elif file_size < 20 * GB: + return 10 * MB # 10 MB + else: + return 20 * MB # 20 MB + + +async def proxy_stream(url: str, headers: dict, proxy: str = None): + async with aiohttp.ClientSession() as session: + async with session.get(url, headers=headers, proxy=proxy) as response: + file_size = int(response.headers.get("Content-Length", 0)) + chunk_size = get_adaptive_chunk_size(file_size) + + while True: + chunk = await response.content.read(chunk_size) + if not chunk: + break + yield chunk + + +def get_stream_link( + decoded_query: str, config: dict, ip: str, redis_cache: RedisCache +) -> str: + cache_key = f"stream_link:{decoded_query}_{ip}" + + cached_link = redis_cache.get(cache_key) + if cached_link: + logger.info(f"Stream link cached: {cached_link}") + return cached_link + + debrid_service = get_debrid_service(config) + link = debrid_service.get_stream_link(decoded_query, config, ip) + + redis_cache.set(cache_key, link, expiration=3600) # Cache for 1 hour + logger.info(f"Stream link generated and cached: {link}") + + return link + + +@router.get( + "/{config}/{query}", + response_model=StreamResponse, + responses={500: {"model": ErrorResponse}}, +) +async def get_playback( + config: str, + query: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +): + try: + if not query: + raise HTTPException(status_code=400, detail="Query required.") + + config = parse_config(config) + decoded_query = decodeb64(query) + ip = request.client.host + + link = get_stream_link(decoded_query, config, ip, redis_cache) + + range_header = request.headers.get("Range") + headers = {} + if range_header: + headers["Range"] = range_header + + proxy = None # Not yet implemented + + async with aiohttp.ClientSession() as session: + async with session.get(link, headers=headers, proxy=proxy) as response: + if response.status == 206: + return StreamingResponse( + proxy_stream(link, headers, proxy), + status_code=206, + headers=StreamResponse( + content_range=response.headers["Content-Range"], + content_length=response.headers["Content-Length"], + accept_ranges="bytes", + content_type="video/mp4", + ).model_dump(), + ) + elif response.status == 200: + return StreamingResponse( + proxy_stream(link, headers, proxy), + headers=StreamResponse( + content_range=None, + content_length=None, + accept_ranges="bytes", + content_type="video/mp4", + ).model_dump(), + ) + else: + return RedirectResponse(link, status_code=302) + + except Exception as e: + logger.error(f"Playback error: {e}") + raise HTTPException( + status_code=500, + detail=ErrorResponse( + detail="An error occurred while processing the request." + ).model_dump(), + ) + + +@router.head( + "/{config}/{query}", + response_model=HeadResponse, + responses={500: {"model": ErrorResponse}}, +) +async def head_playback( + config: str, + query: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +): + try: + if not query: + raise HTTPException(status_code=400, detail="Query required.") + + config = parse_config(config) + decoded_query = decodeb64(query) + ip = request.client.host + + cache_key = f"stream_link:{decoded_query}_{ip}" + + if redis_cache.exists(cache_key): + return Response(status_code=status.HTTP_200_OK) + else: + await asyncio.sleep(0.2) + return Response(status_code=status.HTTP_200_OK) + except Exception as e: + logger.error(f"HEAD request error: {e}") + return Response( + status=500, + content=ErrorResponse( + detail="An error occurred while processing the request." + ).model_dump_json(), + ) diff --git a/stream_fusion/web/root/__init__.py b/stream_fusion/web/root/__init__.py new file mode 100644 index 0000000..448fd33 --- /dev/null +++ b/stream_fusion/web/root/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.search import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/config/__init__.py b/stream_fusion/web/root/config/__init__.py new file mode 100644 index 0000000..2fbcce3 --- /dev/null +++ b/stream_fusion/web/root/config/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.config.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/config/schemas.py b/stream_fusion/web/root/config/schemas.py new file mode 100644 index 0000000..a57e24d --- /dev/null +++ b/stream_fusion/web/root/config/schemas.py @@ -0,0 +1,22 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class ManifestResponse(BaseModel): + id: str + icon: str + version: str + catalogs: List = Field(default_factory=list) + resources: List[str] + types: List[str] + name: str + description: str + behaviorHints: dict = Field(default_factory=lambda: { + "configurable": True, + }) + config: Optional[List[dict]] = None + +class ConfigureTemplateContext(BaseModel): + request: dict + +class StaticFileResponse(BaseModel): + file_path: str diff --git a/stream_fusion/web/root/config/views.py b/stream_fusion/web/root/config/views.py new file mode 100644 index 0000000..1413eb4 --- /dev/null +++ b/stream_fusion/web/root/config/views.py @@ -0,0 +1,78 @@ +from cachetools import TTLCache +from fastapi import APIRouter, Request +from fastapi.responses import FileResponse, RedirectResponse +from fastapi.templating import Jinja2Templates + +from stream_fusion.logging_config import logger +from stream_fusion.version import get_version +from stream_fusion.web.root.config.schemas import ManifestResponse, StaticFileResponse + +router = APIRouter() + +templates = Jinja2Templates(directory="/app/stream_fusion/templates") +stream_cache = TTLCache(maxsize=1000, ttl=3600) + + +@router.get("/") +async def root(): + logger.info("Redirecting to /configure") + return RedirectResponse(url="/configure") + + +@router.get("/configure") +@router.get("/{config}/configure") +async def configure(request: Request): + logger.info("Serving configuration page") + return templates.TemplateResponse("index.html", {"request": request}) + + +@router.get("/static/{file_path:path}", response_model=StaticFileResponse) +async def serve_static(file_path: str): + logger.debug(f"Serving static file: {file_path}") + return FileResponse(f"/app/stream_fusion/templates/{file_path}") + + +@router.get("/manifest.json") +async def get_manifest(): + logger.info("Serving manifest.json") + return ManifestResponse( + id="community.limedrive.streamfusion", + icon="https://i.imgur.com/tVjqEJP.png", + version=str(get_version()), + resources=["stream"], + types=["movie", "series"], + name="StreamFusion", + description="StreamFusion enhances Stremio by integrating torrent indexers and debrid services," + " providing access to a vast array of cached torrent sources. This plugin seamlessly bridges" + " Stremio with popular indexers and debrid platforms, offering users an expanded content" + " library and a smooth streaming experience.", + behaviorHints={ + "configurable": True, + "configurationRequired": True + }, + config=[ + { + "key": "api_key", + "title": "API Key", + "type": "text", + "required": True + } + ] + ) + + +@router.get("/{params}/manifest.json") +async def get_manifest(): + logger.info("Serving manifest.json") + return ManifestResponse( + id="community.limedrive.streamfusion", + icon="https://i.imgur.com/tVjqEJP.png", + version=str(get_version()), + resources=["stream"], + types=["movie", "series"], + name="StreamFusion", + description="StreamFusion enhances Stremio by integrating torrent indexers and debrid services," + " providing access to a vast array of cached torrent sources. This plugin seamlessly bridges" + " Stremio with popular indexers and debrid platforms, offering users an expanded content" + " library and a smooth streaming experience." + ) diff --git a/stream_fusion/web/root/router.py b/stream_fusion/web/root/router.py new file mode 100644 index 0000000..59d17ec --- /dev/null +++ b/stream_fusion/web/root/router.py @@ -0,0 +1,8 @@ +from fastapi.routing import APIRouter + +from stream_fusion.web.root import config +from stream_fusion.web.root import search + +root_router = APIRouter() +root_router.include_router(config.router, tags=["config"]) +root_router.include_router(search.router, tags=["search"]) \ No newline at end of file diff --git a/stream_fusion/web/root/search/__init__.py b/stream_fusion/web/root/search/__init__.py new file mode 100644 index 0000000..0db0c92 --- /dev/null +++ b/stream_fusion/web/root/search/__init__.py @@ -0,0 +1,3 @@ +from stream_fusion.web.root.search.views import router + +__all__ = ["router"] \ No newline at end of file diff --git a/stream_fusion/web/root/search/schemas.py b/stream_fusion/web/root/search/schemas.py new file mode 100644 index 0000000..f6639c5 --- /dev/null +++ b/stream_fusion/web/root/search/schemas.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, Field +from typing import List, Optional + +class Stream(BaseModel): + name: str + description: str + url: Optional[str] = None + infoHash: Optional[str] = None + fileIdx: Optional[int] = None + behaviorHints: dict = Field(default_factory=dict) + +class SearchResponse(BaseModel): + streams: List[Stream] \ No newline at end of file diff --git a/stream_fusion/web/root/search/stremio_parser.py b/stream_fusion/web/root/search/stremio_parser.py new file mode 100644 index 0000000..1800e0e --- /dev/null +++ b/stream_fusion/web/root/search/stremio_parser.py @@ -0,0 +1,141 @@ +import json +import queue +import threading +from typing import List + +from stream_fusion.utils.models.media import Media +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.utils.string_encoding import encodeb64 + + +INSTANTLY_AVAILABLE = "[⚡]" +DOWNLOAD_REQUIRED = "[⬇️]" +DIRECT_TORRENT = "[🏴‍☠️]" + + +# TODO: Languages +def get_emoji(language): + emoji_dict = { + "fr": "🇫🇷 FRENCH", + "en": "🇬🇧 ENGLISH", + "es": "🇪🇸 SPANISH", + "de": "🇩🇪 GERMAN", + "it": "🇮🇹 ITALIAN", + "pt": "🇵🇹 PORTUGUESE", + "ru": "🇷🇺 RUSSIAN", + "in": "🇮🇳 INDIAN", + "nl": "🇳🇱 DUTCH", + "hu": "🇭🇺 HUNGARIAN", + "la": "🇲🇽 LATINO", + "multi": "🌍 MULTi", + } + return emoji_dict.get(language, "🇬🇧") + + +def filter_by_availability(item): + if item["name"].startswith(INSTANTLY_AVAILABLE): + return 0 + else: + return 1 + + +def filter_by_direct_torrnet(item): + if item["name"].startswith(DIRECT_TORRENT): + return 1 + else: + return 0 + + +def parse_to_debrid_stream(torrent_item: TorrentItem, configb64, host, torrenting, results: queue.Queue, media: Media): + if torrent_item.availability == True: + name = f"{INSTANTLY_AVAILABLE}\n" + else: + name = f"{DOWNLOAD_REQUIRED}\n" + + parsed_data = torrent_item.parsed_data.data + + resolution = parsed_data.resolution[0] if parsed_data.resolution else "Unknow" + name += f"{resolution}" + + if parsed_data.quality: + name += f"\n({'|'.join(parsed_data.quality)})" + + size_in_gb = round(int(torrent_item.size) / 1024 / 1024 / 1024, 2) + + title = f"{torrent_item.raw_title}\n" + + if torrent_item.file_name is not None: + title += f"{torrent_item.file_name}\n" + + title += f"👥 {torrent_item.seeders} 💾 {size_in_gb}GB 🔍 {torrent_item.indexer}\n" + + if parsed_data.codec: + title += f"🎥 {', '.join(parsed_data.codec)} " + if parsed_data.audio: + title += f"🎧 {', '.join(parsed_data.audio)} " + if parsed_data.codec or parsed_data.audio: + title += "\n" + + # Gestion des langues + if torrent_item.languages: + title += "/".join(get_emoji(language) for language in torrent_item.languages) + else: + title += "🌐" + + queryb64 = encodeb64(json.dumps(torrent_item.to_debrid_stream_query(media))).replace('=', '%3D') + + results.put({ + "name": name, + "description": title, + "url": f"{host}/playback/{configb64}/{queryb64}", + "behaviorHints":{ + "bingeGroup": f"stremio-jackett-{torrent_item.info_hash}", + "filename": torrent_item.file_name if torrent_item.file_name is not None else torrent_item.raw_title # TODO: Use parsed title? + } + }) + + if torrenting and torrent_item.privacy == "public": + name = f"{DIRECT_TORRENT}\n{parsed_data.quality}\n" + if len(parsed_data.quality) > 0 and parsed_data.quality[0] != "Unknown" and \ + parsed_data.quality[0] != "": + name += f"({'|'.join(parsed_data.quality)})" + results.put({ + "name": name, + "description": title, + "infoHash": torrent_item.info_hash, + "fileIdx": int(torrent_item.file_index) if torrent_item.file_index else None, + "behaviorHints":{ + "bingeGroup": f"stremio-jackett-{torrent_item.info_hash}", + "filename": torrent_item.file_name if torrent_item.file_name is not None else torrent_item.raw_title # TODO: Use parsed title? + } + # "sources": ["tracker:" + tracker for tracker in torrent_item.trackers] + }) + + +def parse_to_stremio_streams(torrent_items: List[TorrentItem], config, media): + stream_list = [] + threads = [] + thread_results_queue = queue.Queue() + + configb64 = encodeb64(json.dumps(config).replace('=', '%3D')) + for torrent_item in torrent_items[:int(config['maxResults'])]: + thread = threading.Thread(target=parse_to_debrid_stream, + args=(torrent_item, configb64, config['addonHost'], config['torrenting'], + thread_results_queue, media), + daemon=True) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + + while not thread_results_queue.empty(): + stream_list.append(thread_results_queue.get()) + + if len(stream_list) == 0: + return [] + + if config['debrid']: + stream_list = sorted(stream_list, key=filter_by_availability) + stream_list = sorted(stream_list, key=filter_by_direct_torrnet) + return stream_list diff --git a/stream_fusion/web/root/search/views.py b/stream_fusion/web/root/search/views.py new file mode 100644 index 0000000..6f42070 --- /dev/null +++ b/stream_fusion/web/root/search/views.py @@ -0,0 +1,255 @@ +import hashlib +import time +from fastapi import APIRouter, Depends, Request + +from stream_fusion.services.redis.redis_config import get_redis_cache_dependency +from stream_fusion.utils.cache.cache import search_public +from stream_fusion.utils.cache.local_redis import RedisCache +from stream_fusion.logging_config import logger +from stream_fusion.utils.debrid.get_debrid_service import get_debrid_service +from stream_fusion.utils.filter_results import ( + filter_items, + filter_out_non_matching_movies, + filter_out_non_matching_series, + merge_items, + sort_items, +) +from stream_fusion.utils.jackett.jackett_result import JackettResult +from stream_fusion.utils.jackett.jackett_service import JackettService +from stream_fusion.utils.metdata.cinemeta import Cinemeta +from stream_fusion.utils.metdata.tmdb import TMDB +from stream_fusion.utils.models.movie import Movie +from stream_fusion.utils.models.series import Series +from stream_fusion.utils.parse_config import parse_config +from stream_fusion.utils.torrent.torrent_item import TorrentItem +from stream_fusion.web.root.search.schemas import SearchResponse, Stream +from stream_fusion.web.root.search.stremio_parser import parse_to_stremio_streams +from stream_fusion.utils.torrent.torrent_service import TorrentService +from stream_fusion.utils.torrent.torrent_smart_container import TorrentSmartContainer +from stream_fusion.utils.zilean.zilean_result import ZileanResult +from stream_fusion.utils.zilean.zilean_service import ZileanService + + +router = APIRouter() + + +@router.get("/{config}/stream/{stream_type}/{stream_id}", response_model=SearchResponse) +async def get_results( + config: str, + stream_type: str, + stream_id: str, + request: Request, + redis_cache: RedisCache = Depends(get_redis_cache_dependency), +) -> SearchResponse: + start = time.time() + logger.info(f"Stream request: {stream_type} - {stream_id}") + + stream_id = stream_id.replace(".json", "") + config = parse_config(config) + logger.debug(f"Parsed configuration: {config}") + + def get_metadata(): + logger.info(f"Fetching metadata from {config['metadataProvider']}") + if config["metadataProvider"] == "tmdb" and config["tmdbApi"]: + metadata_provider = TMDB(config) + else: + metadata_provider = Cinemeta(config) + return metadata_provider.get_metadata(stream_id, stream_type) + + media = redis_cache.get_or_set( + get_metadata, stream_id, stream_type, config["metadataProvider"] + ) + logger.info(f"Retrieved media metadata: {str(media.titles)}") + + def stream_cache_key(media): + if isinstance(media, Movie): + key_string = f"stream:{media.titles[0]}:{media.year}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"stream:{media.titles[0]}:{media.languages[0]}:{media.season}{media.episode}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + cached_result = redis_cache.get(stream_cache_key(media)) + if cached_result is not None: + logger.info("Returning cached processed results") + total_time = time.time() - start + logger.info(f"Request completed in {total_time:.2f} seconds") + return SearchResponse(streams=cached_result) + + debrid_service = get_debrid_service(config) + + def filter_all_results(results, media): + if media.type == "series": + filtered = filter_out_non_matching_series( + results, media.season, media.episode + ) + logger.info( + f"Filtered series results: {len(filtered)} (from {len(results)})" + ) + return filtered + else: + filtered = filter_out_non_matching_movies(results, media.year) + logger.info( + f"Filtered movie results: {len(filtered)} (from {len(results)})" + ) + return results + + def media_cache_key(media): + if isinstance(media, Movie): + key_string = f"media:{media.titles[0]}:{media.year}:{media.languages[0]}" + elif isinstance(media, Series): + key_string = f"media:{media.titles[0]}:{media.languages[0]}:{media.season}" + else: + raise TypeError("Only Movie and Series are allowed as media!") + hashed_key = hashlib.sha256(key_string.encode("utf-8")).hexdigest() + return hashed_key[:16] + + def get_search_results(media, config): + search_results = [] + torrent_service = TorrentService() + + def perform_search(update_cache=False): + nonlocal search_results + search_results = [] + + if config["cache"] and not update_cache: + public_cached_results = search_public(media) + if public_cached_results: + logger.info( + f"Found {len(public_cached_results)} public cached results" + ) + public_cached_results = [ + JackettResult().from_cached_item(torrent, media) + for torrent in public_cached_results + if len(torrent.get("hash", "")) == 40 + ] + public_cached_results = filter_items( + public_cached_results, media, config=config + ) + public_cached_results = torrent_service.convert_and_process( + public_cached_results + ) + search_results.extend(public_cached_results) + + if config["zilean"] and len(search_results) < int( + config["minCachedResults"] + ): + zilean_service = ZileanService(config) + zilean_search_results = zilean_service.search(media) + if zilean_search_results: + logger.info( + f"Found {len(zilean_search_results)} results from Zilean" + ) + zilean_search_results = [ + ZileanResult().from_api_cached_item(torrent, media) + for torrent in zilean_search_results + if len(torrent.get("infoHash", "")) == 40 + ] + zilean_search_results = filter_items( + zilean_search_results, media, config=config + ) + zilean_search_results = torrent_service.convert_and_process( + zilean_search_results + ) + search_results = merge_items(search_results, zilean_search_results) + + if config["jackett"] and len(search_results) < int( + config["minCachedResults"] + ): + jackett_service = JackettService(config) + jackett_search_results = jackett_service.search(media) + logger.info(f"Found {len(jackett_search_results)} results from Jackett") + filtered_jackett_search_results = filter_items( + jackett_search_results, media, config=config + ) + logger.info( + f"Filtered Jackett results: {len(filtered_jackett_search_results)}" + ) + if filtered_jackett_search_results: + torrent_results = torrent_service.convert_and_process( + filtered_jackett_search_results + ) + logger.debug( + f"Converted {len(torrent_results)} Jackett results to TorrentItems" + ) + search_results = merge_items(search_results, torrent_results) + + if update_cache and search_results: + logger.info(f"Updating cache with {len(search_results)} results") + try: + cache_key = media_cache_key(media) + search_results_dict = [item.to_dict() for item in search_results] + redis_cache.set(cache_key, search_results_dict) + except Exception as e: + logger.error(f"Error updating cache: {e}") + + perform_search() + return search_results + + def get_and_filter_results(media, config): + min_results = int(config.get("minCachedResults", 1)) + cache_key = media_cache_key(media) + + unfiltered_results = redis_cache.get(cache_key) + if unfiltered_results is None: + logger.info("No results in cache. Performing new search.") + nocache_results = get_search_results(media, config) + nocache_results_dict = [item.to_dict() for item in nocache_results] + redis_cache.set(cache_key, nocache_results_dict) + return nocache_results + else: + logger.info(f"Results retrieved from redis cache - {len(unfiltered_results)}.") + unfiltered_results = [TorrentItem.from_dict(item) for item in unfiltered_results] + + filtered_results = filter_all_results(unfiltered_results, media) + + if len(filtered_results) < min_results: + logger.info( + f"Insufficient filtered results ({len(filtered_results)}). Performing new search." + ) + redis_cache.delete(cache_key) + unfiltered_results = get_search_results(media, config) + unfiltered_results_dict = [item.to_dict() for item in unfiltered_results] + redis_cache.set(cache_key, unfiltered_results_dict) + filtered_results = filter_all_results(unfiltered_results, media) + + logger.info(f"Final number of filtered results: {len(filtered_results)}") + return filtered_results + + search_results = get_and_filter_results(media, config) + + def stream_processing(search_results, media, config): + torrent_smart_container = TorrentSmartContainer(search_results, media) + + if config["debrid"]: + hashes = torrent_smart_container.get_hashes() + ip = request.client.host + result = debrid_service.get_availability_bulk(hashes, ip) + torrent_smart_container.update_availability( + result, type(debrid_service), media + ) + logger.info(f"Checked availability for {len(result.items())} items") + + if config["cache"]: + logger.debug("Caching public container items") + torrent_smart_container.cache_container_items() + + best_matching_results = torrent_smart_container.get_best_matching() + best_matching_results = sort_items(best_matching_results, config) + logger.info(f"Found {len(best_matching_results)} best matching results") + + stream_list = parse_to_stremio_streams(best_matching_results, config, media) + logger.info(f"Processed {len(stream_list)} streams for Stremio") + + return stream_list + + stream_list = stream_processing(search_results, media, config) + streams = [Stream(**stream) for stream in stream_list] + redis_cache.set( + stream_cache_key(media), streams, expiration=3600 + ) # Make the cache expire after 1 hour TODO: Make this configurable + total_time = time.time() - start + logger.info(f"Request completed in {total_time:.2f} seconds") + return SearchResponse(streams=streams)