Arshit-Singhal-Official commited on
Commit
7836a85
·
1 Parent(s): 3ca55c5

Implement Real-Debrid speedtest functionality to optimize CDN selection

Browse files
mediaflow_proxy/main.py CHANGED
@@ -1,16 +1,18 @@
1
  import logging
2
  from importlib import resources
 
3
 
4
- from fastapi import FastAPI, Depends, Security, HTTPException
5
  from fastapi.security import APIKeyQuery, APIKeyHeader
6
  from starlette.middleware.cors import CORSMiddleware
7
- from starlette.responses import RedirectResponse
8
  from starlette.staticfiles import StaticFiles
9
 
10
  from mediaflow_proxy.configs import settings
11
  from mediaflow_proxy.routes import proxy_router
12
  from mediaflow_proxy.schemas import GenerateUrlRequest
13
  from mediaflow_proxy.utils.crypto_utils import EncryptionHandler, EncryptionMiddleware
 
14
  from mediaflow_proxy.utils.http_utils import encode_mediaflow_proxy_url
15
 
16
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
@@ -49,6 +51,26 @@ async def health_check():
49
  return {"status": "healthy"}
50
 
51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  @app.get("/favicon.ico")
53
  async def get_favicon():
54
  return RedirectResponse(url="/logo.png")
@@ -86,4 +108,4 @@ def run():
86
 
87
 
88
  if __name__ == "__main__":
89
- run()
 
1
  import logging
2
  from importlib import resources
3
+ import uuid
4
 
5
+ from fastapi import FastAPI, Depends, Security, HTTPException, BackgroundTasks
6
  from fastapi.security import APIKeyQuery, APIKeyHeader
7
  from starlette.middleware.cors import CORSMiddleware
8
+ from starlette.responses import RedirectResponse, JSONResponse
9
  from starlette.staticfiles import StaticFiles
10
 
11
  from mediaflow_proxy.configs import settings
12
  from mediaflow_proxy.routes import proxy_router
13
  from mediaflow_proxy.schemas import GenerateUrlRequest
14
  from mediaflow_proxy.utils.crypto_utils import EncryptionHandler, EncryptionMiddleware
15
+ from mediaflow_proxy.utils.rd_speedtest import run_speedtest, prune_task, results
16
  from mediaflow_proxy.utils.http_utils import encode_mediaflow_proxy_url
17
 
18
  logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
 
51
  return {"status": "healthy"}
52
 
53
 
54
+ @app.get("/speedtest")
55
+ async def trigger_speedtest(background_tasks: BackgroundTasks, api_password: str = Depends(verify_api_key)):
56
+ # Generate a random UUID as task_id
57
+ task_id = str(uuid.uuid4()) # Generate unique task ID
58
+ background_tasks.add_task(run_speedtest, task_id)
59
+
60
+ # Schedule the task to be pruned after 1 hour
61
+ background_tasks.add_task(prune_task, task_id)
62
+
63
+ return RedirectResponse(url=f"/speedtest_progress.html?task_id={task_id}")
64
+
65
+
66
+ @app.get("/speedtest/results/{task_id}", response_class=JSONResponse)
67
+ async def get_speedtest_result(task_id: str):
68
+ if task_id in results:
69
+ return results[task_id]
70
+ else:
71
+ return {"message": "Speedtest is still running, please wait or the task may have expired."}
72
+
73
+
74
  @app.get("/favicon.ico")
75
  async def get_favicon():
76
  return RedirectResponse(url="/logo.png")
 
108
 
109
 
110
  if __name__ == "__main__":
111
+ run()
mediaflow_proxy/static/speedtest_progress.html ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Speedtest</title>
7
+ <style>
8
+ body.light-mode {
9
+ font-family: Arial, sans-serif;
10
+ display: flex;
11
+ justify-content: center;
12
+ align-items: center;
13
+ height: 100vh;
14
+ margin: 0;
15
+ background-color: #f4f4f9;
16
+ color: #333;
17
+ }
18
+ body.dark-mode {
19
+ font-family: Arial, sans-serif;
20
+ display: flex;
21
+ justify-content: center;
22
+ align-items: center;
23
+ height: 100vh;
24
+ margin: 0;
25
+ background-color: #121212;
26
+ color: #ffffff;
27
+ }
28
+ .container {
29
+ text-align: center;
30
+ }
31
+ h1 {
32
+ font-size: 24px;
33
+ }
34
+ .progress-bar {
35
+ width: 80%;
36
+ margin: 20px auto;
37
+ height: 20px;
38
+ background-color: #e0e0e0;
39
+ border-radius: 10px;
40
+ overflow: hidden;
41
+ position: relative;
42
+ }
43
+ .progress-bar::after {
44
+ content: "";
45
+ position: absolute;
46
+ top: 0;
47
+ left: 0;
48
+ width: 0%;
49
+ height: 100%;
50
+ background-color: #3498db;
51
+ animation: progress 180s linear forwards;
52
+ }
53
+ @keyframes progress {
54
+ 0% { width: 0%; }
55
+ 100% { width: 100%; }
56
+ }
57
+ .toggle-switch {
58
+ position: absolute;
59
+ top: 10px;
60
+ right: 10px;
61
+ }
62
+ </style>
63
+ <script>
64
+ const urlParams = new URLSearchParams(window.location.search);
65
+ const taskId = urlParams.get("task_id");
66
+
67
+ async function checkStatus() {
68
+ try {
69
+ const response = await fetch(`/speedtest/results/${taskId}`);
70
+ const data = await response.json();
71
+
72
+ // Check if the test is complete based on the response data
73
+ if (!data.message || !data.message.includes("still running")) {
74
+ // Redirect to the results if the test is done
75
+ window.location.href = `/speedtest/results/${taskId}`;
76
+ } else {
77
+ // Poll again after 5 seconds if the test is still running
78
+ setTimeout(checkStatus, 5000);
79
+ }
80
+ } catch (error) {
81
+ console.error("Error fetching status:", error);
82
+ // Retry after 5 seconds in case of error
83
+ setTimeout(checkStatus, 5000);
84
+ }
85
+ }
86
+
87
+ // Start the first status check after 120 seconds (120000 milliseconds)
88
+ setTimeout(checkStatus, 120000);
89
+
90
+ // Toggle dark mode
91
+ function toggleDarkMode() {
92
+ const body = document.body;
93
+ body.classList.toggle('dark-mode');
94
+ body.classList.toggle('light-mode');
95
+ }
96
+ </script>
97
+ </head>
98
+ <body class="light-mode">
99
+ <div class="toggle-switch">
100
+ <label for="darkModeToggle">Dark Mode</label>
101
+ <input type="checkbox" id="darkModeToggle" onclick="toggleDarkMode()">
102
+ </div>
103
+ <div class="container">
104
+ <h1>Speedtest in progress... Please wait up to 3 minutes.</h1>
105
+ <div class="progress-bar"></div>
106
+ </div>
107
+ </body>
108
+ </html>
mediaflow_proxy/utils/rd_speedtest.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import time
3
+ import random
4
+ import logging
5
+
6
+ logging.basicConfig(level=logging.INFO)
7
+
8
+ results={}
9
+
10
+ def run_speedtest(taskid :str):
11
+ results[taskid] = perform_speed_test() # Run the speedtest and store the results
12
+
13
+ def prune_task(taskid :str):
14
+ time.sleep(3600)
15
+ if taskid in results:
16
+ del results[taskid] # Remove task from the results set
17
+
18
+
19
+ def perform_speed_test():
20
+ test_urls = {
21
+ "AMS": "https://45.download.real-debrid.com/speedtest/testDefault.rar/",
22
+ "RBX": "https://rbx.download.real-debrid.com/speedtest/test.rar/",
23
+ "LON1": "https://lon1.download.real-debrid.com/speedtest/test.rar/",
24
+ "HKG1": "https://hkg1.download.real-debrid.com/speedtest/test.rar/",
25
+ "SGP1": "https://sgp1.download.real-debrid.com/speedtest/test.rar/",
26
+ "SGPO1": "https://sgpo1.download.real-debrid.com/speedtest/test.rar/",
27
+ "TYO1": "https://tyo1.download.real-debrid.com/speedtest/test.rar/",
28
+ "LAX1": "https://lax1.download.real-debrid.com/speedtest/test.rar/",
29
+ "TLV1": "https://tlv1.download.real-debrid.com/speedtest/test.rar/",
30
+ "MUM1": "https://mum1.download.real-debrid.com/speedtest/test.rar/",
31
+ "JKT1": "https://jkt1.download.real-debrid.com/speedtest/test.rar/",
32
+ "Cloudflare": "https://45.download.real-debrid.cloud/speedtest/testCloudflare.rar/"
33
+ }
34
+
35
+ speed = {}
36
+ test_duration = 10 # Duration for each test in seconds
37
+
38
+ for location, base_url in test_urls.items():
39
+ # Generate a random float with 16 decimal places
40
+ random_number = f"{random.uniform(0, 1):.16f}"
41
+ url = f"{base_url}{random_number}"
42
+
43
+ logging.info(f"Testing URL: {url}")
44
+
45
+ start_time = time.time()
46
+ total_bytes = 0
47
+
48
+ try:
49
+ # Stream the response
50
+ with requests.get(url, stream=True, timeout=10) as response:
51
+ response.raise_for_status()
52
+ while time.time() - start_time < test_duration:
53
+ chunk = response.raw.read(8192) # Read in chunks
54
+ if not chunk: # Stop if no more data
55
+ break
56
+ total_bytes += len(chunk)
57
+
58
+ duration = time.time() - start_time
59
+ speed_mbps = (total_bytes * 8) / (duration * 1_000_000)
60
+ speed[location] = {
61
+ "speed_mbps": round(speed_mbps, 2),
62
+ "duration": round(duration, 2)
63
+ }
64
+ logging.info(f"Speed for {location}: {speed_mbps} Mbps in {duration} seconds")
65
+ except requests.RequestException as e:
66
+ speed[location] = {"error": str(e)}
67
+ logging.error(f"Error for {location}: {e}")
68
+
69
+ return speed
poetry.lock CHANGED
@@ -1,4 +1,4 @@
1
- # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
2
 
3
  [[package]]
4
  name = "annotated-types"
@@ -13,13 +13,13 @@ files = [
13
 
14
  [[package]]
15
  name = "anyio"
16
- version = "4.6.0"
17
  description = "High level compatibility layer for multiple asynchronous event loop implementations"
18
  optional = false
19
  python-versions = ">=3.9"
20
  files = [
21
- {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"},
22
- {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"},
23
  ]
24
 
25
  [package.dependencies]
@@ -30,7 +30,7 @@ typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
30
 
31
  [package.extras]
32
  doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
33
- 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.21.0b1)"]
34
  trio = ["trio (>=0.26.1)"]
35
 
36
  [[package]]
@@ -101,6 +101,120 @@ files = [
101
  {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
102
  ]
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  [[package]]
105
  name = "click"
106
  version = "8.1.7"
@@ -469,13 +583,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
469
 
470
  [[package]]
471
  name = "pydantic-settings"
472
- version = "2.5.2"
473
  description = "Settings management using Pydantic"
474
  optional = false
475
  python-versions = ">=3.8"
476
  files = [
477
- {file = "pydantic_settings-2.5.2-py3-none-any.whl", hash = "sha256:2c912e55fd5794a59bf8c832b9de832dcfdf4778d79ff79b708744eed499a907"},
478
- {file = "pydantic_settings-2.5.2.tar.gz", hash = "sha256:f90b139682bee4d2065273d5185d71d37ea46cfe57e1b5ae184fc6a0b2484ca0"},
479
  ]
480
 
481
  [package.dependencies]
@@ -501,6 +615,27 @@ files = [
501
  [package.extras]
502
  cli = ["click (>=5.0)"]
503
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  [[package]]
505
  name = "sniffio"
506
  version = "1.3.1"
@@ -597,6 +732,23 @@ files = [
597
  {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
598
  ]
599
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  [[package]]
601
  name = "uvicorn"
602
  version = "0.31.1"
@@ -618,16 +770,16 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
618
 
619
  [[package]]
620
  name = "xmltodict"
621
- version = "0.14.1"
622
  description = "Makes working with XML feel like you are working with JSON"
623
  optional = false
624
  python-versions = ">=3.6"
625
  files = [
626
- {file = "xmltodict-0.14.1-py2.py3-none-any.whl", hash = "sha256:3ef4a7b71c08f19047fcbea572e1d7f4207ab269da1565b5d40e9823d3894e63"},
627
- {file = "xmltodict-0.14.1.tar.gz", hash = "sha256:338c8431e4fc554517651972d62f06958718f6262b04316917008e8fd677a6b0"},
628
  ]
629
 
630
  [metadata]
631
  lock-version = "2.0"
632
  python-versions = ">=3.10"
633
- content-hash = "8f7497d565d81c39251e2413999975dc89af0404f0205a0f2e6b26b5a4d8a8bd"
 
1
+ # This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
2
 
3
  [[package]]
4
  name = "annotated-types"
 
13
 
14
  [[package]]
15
  name = "anyio"
16
+ version = "4.6.2.post1"
17
  description = "High level compatibility layer for multiple asynchronous event loop implementations"
18
  optional = false
19
  python-versions = ">=3.9"
20
  files = [
21
+ {file = "anyio-4.6.2.post1-py3-none-any.whl", hash = "sha256:6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d"},
22
+ {file = "anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c"},
23
  ]
24
 
25
  [package.dependencies]
 
30
 
31
  [package.extras]
32
  doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
33
+ 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", "truststore (>=0.9.1)", "uvloop (>=0.21.0b1)"]
34
  trio = ["trio (>=0.26.1)"]
35
 
36
  [[package]]
 
101
  {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
102
  ]
103
 
104
+ [[package]]
105
+ name = "charset-normalizer"
106
+ version = "3.4.0"
107
+ description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
108
+ optional = false
109
+ python-versions = ">=3.7.0"
110
+ files = [
111
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"},
112
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"},
113
+ {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"},
114
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"},
115
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"},
116
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"},
117
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"},
118
+ {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"},
119
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"},
120
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"},
121
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"},
122
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"},
123
+ {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"},
124
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"},
125
+ {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"},
126
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"},
127
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"},
128
+ {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"},
129
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"},
130
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"},
131
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"},
132
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"},
133
+ {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"},
134
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"},
135
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"},
136
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"},
137
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"},
138
+ {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"},
139
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"},
140
+ {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"},
141
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"},
142
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"},
143
+ {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"},
144
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"},
145
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"},
146
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"},
147
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"},
148
+ {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"},
149
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"},
150
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"},
151
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"},
152
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"},
153
+ {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"},
154
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"},
155
+ {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"},
156
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"},
157
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"},
158
+ {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"},
159
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"},
160
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"},
161
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"},
162
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"},
163
+ {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"},
164
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"},
165
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"},
166
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"},
167
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"},
168
+ {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"},
169
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"},
170
+ {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"},
171
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"},
172
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"},
173
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"},
174
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"},
175
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"},
176
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"},
177
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"},
178
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"},
179
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"},
180
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"},
181
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"},
182
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"},
183
+ {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"},
184
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"},
185
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"},
186
+ {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"},
187
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"},
188
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"},
189
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"},
190
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"},
191
+ {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"},
192
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"},
193
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"},
194
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"},
195
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"},
196
+ {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"},
197
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"},
198
+ {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"},
199
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"},
200
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"},
201
+ {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"},
202
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"},
203
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"},
204
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"},
205
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"},
206
+ {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"},
207
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"},
208
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"},
209
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"},
210
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"},
211
+ {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"},
212
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"},
213
+ {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"},
214
+ {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"},
215
+ {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"},
216
+ ]
217
+
218
  [[package]]
219
  name = "click"
220
  version = "8.1.7"
 
583
 
584
  [[package]]
585
  name = "pydantic-settings"
586
+ version = "2.6.0"
587
  description = "Settings management using Pydantic"
588
  optional = false
589
  python-versions = ">=3.8"
590
  files = [
591
+ {file = "pydantic_settings-2.6.0-py3-none-any.whl", hash = "sha256:4a819166f119b74d7f8c765196b165f95cc7487ce58ea27dec8a5a26be0970e0"},
592
+ {file = "pydantic_settings-2.6.0.tar.gz", hash = "sha256:44a1804abffac9e6a30372bb45f6cafab945ef5af25e66b1c634c01dd39e0188"},
593
  ]
594
 
595
  [package.dependencies]
 
615
  [package.extras]
616
  cli = ["click (>=5.0)"]
617
 
618
+ [[package]]
619
+ name = "requests"
620
+ version = "2.32.3"
621
+ description = "Python HTTP for Humans."
622
+ optional = false
623
+ python-versions = ">=3.8"
624
+ files = [
625
+ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
626
+ {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
627
+ ]
628
+
629
+ [package.dependencies]
630
+ certifi = ">=2017.4.17"
631
+ charset-normalizer = ">=2,<4"
632
+ idna = ">=2.5,<4"
633
+ urllib3 = ">=1.21.1,<3"
634
+
635
+ [package.extras]
636
+ socks = ["PySocks (>=1.5.6,!=1.5.7)"]
637
+ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
638
+
639
  [[package]]
640
  name = "sniffio"
641
  version = "1.3.1"
 
732
  {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
733
  ]
734
 
735
+ [[package]]
736
+ name = "urllib3"
737
+ version = "2.2.3"
738
+ description = "HTTP library with thread-safe connection pooling, file post, and more."
739
+ optional = false
740
+ python-versions = ">=3.8"
741
+ files = [
742
+ {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"},
743
+ {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"},
744
+ ]
745
+
746
+ [package.extras]
747
+ brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
748
+ h2 = ["h2 (>=4,<5)"]
749
+ socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
750
+ zstd = ["zstandard (>=0.18.0)"]
751
+
752
  [[package]]
753
  name = "uvicorn"
754
  version = "0.31.1"
 
770
 
771
  [[package]]
772
  name = "xmltodict"
773
+ version = "0.14.2"
774
  description = "Makes working with XML feel like you are working with JSON"
775
  optional = false
776
  python-versions = ">=3.6"
777
  files = [
778
+ {file = "xmltodict-0.14.2-py2.py3-none-any.whl", hash = "sha256:20cc7d723ed729276e808f26fb6b3599f786cbc37e06c65e192ba77c40f20aac"},
779
+ {file = "xmltodict-0.14.2.tar.gz", hash = "sha256:201e7c28bb210e374999d1dde6382923ab0ed1a8a5faeece48ab525b7810a553"},
780
  ]
781
 
782
  [metadata]
783
  lock-version = "2.0"
784
  python-versions = ">=3.10"
785
+ content-hash = "3434c9ad02a2a41d9ca49bf311db17b2c81ab6799be7a372eb36ad646beaba1c"
pyproject.toml CHANGED
@@ -33,6 +33,7 @@ gunicorn = "^23.0.0"
33
  pycryptodome = "^3.20.0"
34
  uvicorn = "^0.31.0"
35
  tqdm = "^4.66.5"
 
36
 
37
 
38
  [tool.poetry.group.dev.dependencies]
 
33
  pycryptodome = "^3.20.0"
34
  uvicorn = "^0.31.0"
35
  tqdm = "^4.66.5"
36
+ requests = "^2.31.0"
37
 
38
 
39
  [tool.poetry.group.dev.dependencies]