├── .github ├── FUNDING.yml └── workflows │ └── publish.yml ├── .gitignore ├── .readthedocs.yaml ├── LICENSE ├── README.md ├── docs ├── index.md └── requirements.txt ├── images └── 1337x.png ├── mkdocs.yml ├── py1337x ├── __init__.py ├── config.py ├── models.py ├── parser.py ├── py1337x.py ├── types │ ├── __init__.py │ ├── category.py │ ├── order.py │ └── sort.py └── utils.py ├── pyproject.toml └── uv.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | custom: ['https://buymeacoffee.com/hemantapkh'] 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Package 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | publish: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | id-token: write 12 | steps: 13 | 14 | - name: Clone the repo 15 | uses: actions/checkout@v4 16 | 17 | - name: Set up Python 18 | uses: actions/setup-python@v5 19 | with: 20 | python-version: '3.x' 21 | 22 | - name: Install dependencies 23 | run: | 24 | pip install build 25 | 26 | - name: Build distribution 27 | run: python -m build 28 | 29 | - name: Publish the package 30 | uses: pypa/gh-action-pypi-publish@v1.12.4 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # UV 98 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | #uv.lock 102 | 103 | # poetry 104 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 105 | # This is especially recommended for binary packages to ensure reproducibility, and is more 106 | # commonly ignored for libraries. 107 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 108 | #poetry.lock 109 | 110 | # pdm 111 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 112 | #pdm.lock 113 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 114 | # in version control. 115 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 116 | .pdm.toml 117 | .pdm-python 118 | .pdm-build/ 119 | 120 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 121 | __pypackages__/ 122 | 123 | # Celery stuff 124 | celerybeat-schedule 125 | celerybeat.pid 126 | 127 | # SageMath parsed files 128 | *.sage.py 129 | 130 | # Environments 131 | .env 132 | .venv 133 | env/ 134 | venv/ 135 | ENV/ 136 | env.bak/ 137 | venv.bak/ 138 | 139 | # Spyder project settings 140 | .spyderproject 141 | .spyproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | # mkdocs documentation 147 | /site 148 | 149 | # mypy 150 | .mypy_cache/ 151 | .dmypy.json 152 | dmypy.json 153 | 154 | # Pyre type checker 155 | .pyre/ 156 | 157 | # pytype static type analyzer 158 | .pytype/ 159 | 160 | # Cython debug symbols 161 | cython_debug/ 162 | 163 | # PyCharm 164 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 165 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 166 | # and can be added to the global gitignore or merged into this file. For a more nuclear 167 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 168 | #.idea/ -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | build: 3 | os: ubuntu-22.04 4 | tools: 5 | python: "3.11" 6 | # You can also specify other tool versions: 7 | # nodejs: "16" 8 | 9 | # Build documentation with Mkdocs 10 | mkdocs: 11 | configuration: mkdocs.yml 12 | 13 | # Dependencies required to build your docs 14 | python: 15 | install: 16 | - requirements: docs/requirements.txt 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Hemanta Pokharel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 1337x 3 |

4 |

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Stars 14 | 15 | 16 | Issues 17 | 18 | 19 |

20 | The ultimate Python API wrapper for 1337x. 21 | 22 |

23 | 24 | ## Table of Contents 25 | - [Installation](#installation) 26 | - [Getting Started](#getting-started) 27 | - [Examples](#examples) 28 | - [Asynchronous Usage](#asynchronous-usage) 29 | - [Documentation](#documentation) 30 | - [Contributing](#contributing) 31 | - [License](#license) 32 | 33 | ## Installation 34 | Install via [PyPi](https://www.pypi.org/project/1337x): 35 | ```bash 36 | pip install 1337x 37 | ``` 38 | 39 | Or install from the source: 40 | ```bash 41 | pip install git+https://github.com/hemantapkh/1337x 42 | ``` 43 | 44 | 45 | ## Examples 46 | 47 | ### Searching Torrents 48 | ```python 49 | import py1337x 50 | 51 | torrents = py1337x.Py1337x() 52 | 53 | # Basic search 54 | results = torrents.search('ubuntu', page=1) 55 | for result in results.items: 56 | print(f"Title={result.name} Seeders={result.seeders}") 57 | 58 | # Search with sorting by seeders 59 | results = torrents.search('vlc', sort_by=py1337x.sort.SEEDERS, category=py1337x.category.APPS) 60 | print(results) 61 | 62 | # Get today's trending torrents 63 | results = torrents.trending() 64 | print(results) 65 | ``` 66 | 67 | ### Getting Torrent Information 68 | To get magnetlink and other information of the torrent. 69 | ```python 70 | # Getting info the the first result of the above search 71 | torrent_id = results.items[0].torrent_id 72 | info = torrents.info(torrent_id=torrent_id) 73 | print(info) 74 | 75 | # Convert the result to dictionary 76 | info_dict = info.to_dict() 77 | print(info_dict) 78 | ``` 79 | 80 | ## Asynchronous Usage 81 | For asynchronous usage, all functionalities are the same; use `AsyncPy1337x` instead of `Py1337x`: 82 | 83 | ```python 84 | import asyncio 85 | from py1337x import AsyncPy1337x 86 | 87 | async def main(): 88 | torrents = AsyncPy1337x() 89 | results = await torrents.search('vlc media player') 90 | print(results) 91 | 92 | asyncio.run(main()) 93 | ``` 94 | 95 | ## Documentation 96 | 97 | The detailled documentation of the project is available [here](https://1337x.readthedocs.org/en/latest/). 98 | 99 | ## Contributing 100 | 101 | Any contributions you make are **greatly appreciated**. 102 | 103 | *Thanks to every [contributors](https://github.com/hemantapkh/1337x/graphs/contributors) who have contributed in this project.* 104 | 105 | ## License 106 | 107 | Distributed under the MIT License. See [LICENSE](https://github.com/hemantapkh/1337x/blob/main/LICENSE) for more information. 108 | 109 | ----- 110 | Author/Maintainer: [Hemanta Pokharel](https://github.com/hemantapkh/) 111 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 |

2 | 1337x 3 |

4 |

5 | 6 | Pypi Version 7 | 8 | 9 | Downloads 10 | 11 | 12 | Visitors 13 | 14 | 15 | Stars 16 | 17 | 18 | Issues 19 | 20 |

21 | 22 | 23 | ## Installation 24 | 25 | Install with pip 26 | 27 | ```console 28 | pip install 1337x 29 | ``` 30 | 31 | Install from source 32 | 33 | ```console 34 | pip install git+https://github.com/hemantapkh/1337x 35 | ``` 36 | 37 | ## Detailled Documentation 38 | 39 | ### ::: py1337x.Py1337x 40 | ### ::: py1337x.AsyncPy1337x 41 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | babel==2.17.0 2 | certifi==2025.1.31 3 | charset-normalizer==3.4.1 4 | click==8.1.8 5 | colorama==0.4.6 6 | ghp-import==2.1.0 7 | griffe==1.5.7 8 | idna==3.10 9 | Jinja2==3.1.6 10 | Markdown==3.7 11 | MarkupSafe==3.0.2 12 | mergedeep==1.3.4 13 | mkdocs==1.6.1 14 | mkdocs-autorefs==1.3.1 15 | mkdocs-get-deps==0.2.0 16 | mkdocs-material==9.6.4 17 | mkdocs-material-extensions==1.3.1 18 | mkdocstrings==0.28.1 19 | mkdocstrings-python==1.15.0 20 | packaging==24.2 21 | paginate==0.5.7 22 | pathspec==0.12.1 23 | platformdirs==4.3.6 24 | Pygments==2.19.1 25 | pymdown-extensions==10.14.3 26 | python-dateutil==2.9.0.post0 27 | PyYAML==6.0.2 28 | pyyaml_env_tag==0.1 29 | regex==2024.11.6 30 | requests==2.32.3 31 | six==1.17.0 32 | urllib3==2.3.0 33 | watchdog==6.0.0 34 | -------------------------------------------------------------------------------- /images/1337x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hemantapkh/1337x/3463a2fdbd308b27ed33d339010c2c9799784a23/images/1337x.png -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: PY1337X 2 | site_description: API wrapper of 1337x 3 | site_url: https://hemantapkh.com 4 | repo_name: hemantapkh/1337x 5 | repo_url: https://github.com/hemantapkh/1337x 6 | theme: 7 | name: "material" 8 | palette: 9 | - media: "(prefers-color-scheme)" 10 | toggle: 11 | icon: material/lightbulb-auto 12 | name: Switch to light mode 13 | - media: '(prefers-color-scheme: light)' 14 | scheme: default 15 | primary: teal 16 | accent: amber 17 | toggle: 18 | icon: material/lightbulb 19 | name: Switch to dark mode 20 | - media: '(prefers-color-scheme: dark)' 21 | scheme: slate 22 | primary: teal 23 | accent: amber 24 | toggle: 25 | icon: material/lightbulb-outline 26 | name: Switch to system preference 27 | 28 | icon: 29 | repo: fontawesome/brands/github-alt 30 | language: en 31 | 32 | features: 33 | - content.code.annotate 34 | - content.code.copy 35 | - content.code.select 36 | - content.footnote.tooltips 37 | - content.tabs.link 38 | - content.tooltips 39 | - navigation.footer 40 | - navigation.indexes 41 | - navigation.instant 42 | - navigation.instant.prefetch 43 | - navigation.instant.preview 44 | - navigation.instant.progress 45 | - navigation.path 46 | - navigation.tabs 47 | - navigation.tabs.sticky 48 | - navigation.top 49 | - navigation.tracking 50 | - search.highlight 51 | - search.share 52 | - search.suggest 53 | - toc.follow 54 | 55 | plugins: 56 | - search 57 | - mkdocstrings: 58 | handlers: 59 | python: 60 | options: 61 | members_order: source 62 | merge_init_into_class: true 63 | docstring_options: 64 | ignore_init_summary: true 65 | -------------------------------------------------------------------------------- /py1337x/__init__.py: -------------------------------------------------------------------------------- 1 | from py1337x.py1337x import Py1337x # noqa 2 | from py1337x.py1337x import AsyncPy1337x # noqa 3 | from py1337x.types import category, sort, order # noqa 4 | -------------------------------------------------------------------------------- /py1337x/config.py: -------------------------------------------------------------------------------- 1 | default_base_url: str = "https://www.1337x.to" 2 | -------------------------------------------------------------------------------- /py1337x/models.py: -------------------------------------------------------------------------------- 1 | from dataclasses import asdict, dataclass 2 | from typing import List, Optional 3 | 4 | 5 | @dataclass 6 | class TorrentItem: 7 | """Represents a single torrent item in the search results.""" 8 | 9 | name: str 10 | torrent_id: str 11 | url: str 12 | seeders: str 13 | leechers: str 14 | size: str 15 | time: str 16 | uploader: str 17 | uploader_link: str 18 | 19 | def to_dict(self): 20 | return asdict(self) 21 | 22 | 23 | @dataclass 24 | class TorrentResult: 25 | """Represents the result of a torrent search.""" 26 | 27 | items: List[TorrentItem] 28 | current_page: int 29 | item_count: int 30 | page_count: int 31 | 32 | def to_dict(self): 33 | return asdict(self) 34 | 35 | 36 | @dataclass 37 | class TorrentInfo: 38 | """Represents information about a torrent.""" 39 | 40 | name: Optional[str] 41 | short_name: Optional[str] 42 | description: Optional[str] 43 | category: Optional[str] 44 | type: Optional[str] 45 | genre: Optional[List[str]] 46 | language: Optional[str] 47 | size: Optional[str] 48 | thumbnail: Optional[str] 49 | images: Optional[List[str]] 50 | uploader: Optional[str] 51 | uploader_link: Optional[str] 52 | downloads: Optional[str] 53 | last_checked: Optional[str] 54 | date_uploaded: Optional[str] 55 | seeders: Optional[str] 56 | leechers: Optional[str] 57 | magnet_link: Optional[str] 58 | info_hash: Optional[str] 59 | 60 | def to_dict(self): 61 | return asdict(self) 62 | -------------------------------------------------------------------------------- /py1337x/parser.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup 2 | from requests import Response 3 | 4 | from py1337x import models 5 | 6 | 7 | def torrent_parser(response: Response, base_url: str, page: int = 1) -> models.TorrentResult: 8 | """ 9 | Parse the response from a torrent result page. 10 | 11 | Args: 12 | response (Response): The HTTP response object. 13 | base_url (str): The base URL for the API. 14 | page (int): The current page number. 15 | 16 | Returns: 17 | The parsed search results. 18 | """ 19 | soup = BeautifulSoup(response.content, "html.parser") 20 | 21 | torrent_list = soup.select('a[href*="/torrent/"]') 22 | seeders_list = soup.select("td.coll-2") 23 | leechers_list = soup.select("td.coll-3") 24 | size_list = soup.select("td.coll-4") 25 | time_list = soup.select("td.coll-date") 26 | uploader_list = soup.select("td.coll-5") 27 | 28 | last_page = soup.find("div", {"class": "pagination"}) 29 | 30 | if not last_page: 31 | page_count = page 32 | else: 33 | try: 34 | page_count = int(last_page.findAll("a")[-1]["href"].split("/")[-2]) 35 | except Exception: 36 | page_count = page 37 | 38 | items = [] 39 | 40 | if torrent_list: 41 | for count, torrent in enumerate(torrent_list): 42 | name = torrent.getText().strip() 43 | torrent_id = torrent["href"].split("/")[2] 44 | link = base_url + torrent["href"] 45 | seeders = seeders_list[count].getText() 46 | leechers = leechers_list[count].getText() 47 | size = size_list[count].contents[0] 48 | time = time_list[count].getText() 49 | uploader = uploader_list[count].getText().strip() 50 | uploader_link = base_url + "/" + uploader + "/" 51 | 52 | items.append( 53 | models.TorrentItem( 54 | name=name, 55 | torrent_id=torrent_id, 56 | url=link, 57 | seeders=seeders, 58 | leechers=leechers, 59 | size=size, 60 | time=time, 61 | uploader=uploader, 62 | uploader_link=uploader_link, 63 | ) 64 | ) 65 | 66 | return models.TorrentResult( 67 | items=items, current_page=page or 1, item_count=len(torrent_list), page_count=page_count 68 | ) 69 | 70 | 71 | def info_parser(response: Response, base_url: str) -> models.TorrentInfo: 72 | """ 73 | Parse the response from a torrent information page. 74 | 75 | Args: 76 | response (Response): The HTTP response object. 77 | base_url (str): The base URL for the API. 78 | 79 | Returns: 80 | The parsed torrent information. 81 | """ 82 | soup = BeautifulSoup(response.content, "html.parser") 83 | 84 | name = soup.find("div", {"class": "box-info-heading clearfix"}) 85 | name = name.text.strip() if name else None 86 | 87 | short_name = soup.find("div", {"class": "torrent-detail-info"}) 88 | short_name = short_name.find("h3").getText().strip() if short_name else None 89 | 90 | description = soup.find("div", {"class": "torrent-detail-info"}) 91 | description = description.find("p").getText().strip() if description else None 92 | 93 | genre = soup.find("div", {"class": "torrent-category clearfix"}) 94 | genre = [i.text.strip() for i in genre.find_all("span")] if genre else None 95 | 96 | thumbnail = soup.find("div", {"class": "torrent-image"}) 97 | thumbnail = thumbnail.find("img")["src"] if thumbnail else None 98 | 99 | if thumbnail and not thumbnail.startswith("http"): 100 | if thumbnail.startswith("//"): 101 | thumbnail = "https:" + thumbnail 102 | else: 103 | thumbnail = base_url + thumbnail 104 | 105 | magnet_link = soup.select('a[href^="magnet"]') 106 | magnet_link = magnet_link[0]["href"] if magnet_link else None 107 | 108 | info_hash = soup.find("div", {"class": "infohash-box"}) 109 | info_hash = info_hash.find("span").getText() if info_hash else None 110 | 111 | images = soup.find("div", {"class": "tab-pane active"}) 112 | images = [i["src"] for i in images.find_all("img")] if images else None 113 | 114 | description_list = soup.find_all("ul", {"class": "list"}) 115 | 116 | if len(description_list) > 2: 117 | first_list = description_list[1].find_all("li") 118 | second_list = description_list[2].find_all("li") 119 | 120 | category = first_list[0].find("span").getText() 121 | species = first_list[1].find("span").getText() 122 | language = first_list[2].find("span").getText() 123 | size = first_list[3].find("span").getText() 124 | uploader = first_list[4].find("span").getText().strip() 125 | uploader_link = base_url + "/" + uploader + "/" 126 | 127 | downloads = second_list[0].find("span").getText() 128 | last_checked = second_list[1].find("span").getText() 129 | date_uploaded = second_list[2].find("span").getText() 130 | seeders = second_list[3].find("span").getText() 131 | leechers = second_list[4].find("span").getText() 132 | else: 133 | category = species = language = size = uploader = uploader_link = downloads = last_checked = date_uploaded = ( 134 | seeders 135 | ) = leechers = None 136 | 137 | return models.TorrentInfo( 138 | name=name, 139 | short_name=short_name, 140 | description=description, 141 | category=category, 142 | type=species, 143 | genre=genre, 144 | language=language, 145 | size=size, 146 | thumbnail=thumbnail, 147 | images=images if images else None, 148 | uploader=uploader, 149 | uploader_link=uploader_link, 150 | downloads=downloads, 151 | last_checked=last_checked, 152 | date_uploaded=date_uploaded, 153 | seeders=seeders, 154 | leechers=leechers, 155 | magnet_link=magnet_link, 156 | info_hash=info_hash.strip() if info_hash else None, 157 | ) 158 | -------------------------------------------------------------------------------- /py1337x/py1337x.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Literal, Optional 2 | 3 | import cloudscraper 4 | from asyncer import asyncify 5 | 6 | from py1337x import config, models, parser, utils 7 | 8 | 9 | class Py1337x: 10 | """ 11 | A class to interact with the py1337x API for searching and retrieving torrent information. 12 | 13 | Example: 14 | ```python 15 | from py1337x import Py1337x 16 | 17 | torrents = Py1337x() 18 | 19 | vlc_media = torrents.search('vlc media player') 20 | print(vlc_media) 21 | ``` 22 | """ 23 | 24 | def __init__( 25 | self, 26 | base_url: str = config.default_base_url, 27 | cloudscraper_kwargs: Dict = {}, 28 | ): 29 | """ 30 | Initialize the Py1337x class with base URL, headers, and requests session. 31 | 32 | Args: 33 | base_url (Optional[str]): The base URL for the API. 34 | cloudscraper_kwargs (Optional[dict]): Kwargs to pass in cloudscraper. 35 | """ 36 | self.base_url = base_url 37 | self.requests = cloudscraper.create_scraper(**cloudscraper_kwargs) 38 | self.url_builder = utils.URLBuilder(base_url) 39 | 40 | def search( 41 | self, 42 | query: str, 43 | page: int = 1, 44 | category: Optional[str] = None, 45 | sort_by: Optional[Literal["time", "size", "seeders", "leechers"]] = None, 46 | order: Literal["asc", "desc"] = "desc", 47 | ) -> models.TorrentResult: 48 | """ 49 | Search for torrents based on a query. 50 | 51 | Args: 52 | query (str): The search query. 53 | page (int): The page number. 54 | category (Optional[str]): Category of the torrent. 55 | sort_by (Optional[str]): Sort by "time", "size", "seeders" or "leechers". 56 | order (str): The order string ('asc' or 'desc'). 57 | 58 | Returns: 59 | Result from the query 60 | 61 | Example: 62 | Basic search 63 | ```python 64 | from py1337x import Py1337x 65 | 66 | results = torrents.search('ubuntu') 67 | print(results) 68 | ``` 69 | 70 | Search with sorting by seeders 71 | ```python 72 | from py1337x.types import category, sort, order 73 | 74 | results = torrents.search('ubuntu', sort_by=sort.SEEDERS) 75 | print(results) 76 | ``` 77 | 78 | Search within a category 79 | ```python 80 | results = torrents.search('ubuntu', category=category.APPS) 81 | print(results) 82 | ``` 83 | 84 | Paginated search results, fetching page 2 85 | ```python 86 | results = torrents.search('ubuntu', page=2, order=order.ASC) 87 | print(results) 88 | ``` 89 | """ 90 | query = self.url_builder.sanitize_query(query) 91 | category = self.url_builder.sanitize_category(category) 92 | url = self.url_builder.build_search_url(query, page, category, sort_by, order) 93 | print(url) 94 | 95 | response = self.requests.get(url) 96 | 97 | return parser.torrent_parser(response, base_url=self.base_url, page=page) 98 | 99 | def trending(self, category: Optional[str] = None, weekly: bool = False) -> models.TorrentResult: 100 | """ 101 | Retrieve trending torrents. 102 | 103 | Args: 104 | category (Optional[str]): Category of the torrent. 105 | weekly (bool): Whether to get weekly trending torrents. 106 | 107 | Returns: 108 | Trending torrents 109 | 110 | Example: 111 | Get today's trending torrents 112 | ```python 113 | trending_today = torrents.trending() 114 | print(trending_today) 115 | ``` 116 | 117 | Weekly trending torrents in the applications category 118 | ```python 119 | trending_weekly = torrents.trending(category=category.APPS, weekly=True) 120 | print(trending_weekly) 121 | ``` 122 | """ 123 | url = self.url_builder.build_trending_url(category, weekly) 124 | response = self.requests.get(url) 125 | 126 | return parser.torrent_parser(response, base_url=self.base_url) 127 | 128 | def top(self, category: Optional[str] = None) -> models.TorrentResult: 129 | """ 130 | Retrieve top 100 torrents. 131 | 132 | Args: 133 | category (Optional[str]): Category of the torrent. 134 | 135 | Returns: 136 | Top 100 torrents 137 | 138 | Example: 139 | Get top 100 torrents 140 | ```python 141 | top_torrents = torrents.top() 142 | print(top_torrents) 143 | ``` 144 | 145 | Top torrents in specific category 146 | ```python 147 | top_movies = torrents.top(category=category.MOVIES) 148 | print(top_movies) 149 | ``` 150 | """ 151 | url = self.url_builder.build_top_url(category) 152 | response = self.requests.get(url) 153 | 154 | return parser.torrent_parser(response, base_url=self.base_url) 155 | 156 | def popular(self, category: str, weekly: bool = False) -> models.TorrentResult: 157 | """ 158 | Retrieve popular torrents. 159 | 160 | Args: 161 | category (str): Category of the torrent. 162 | weekly (bool): Whether to get weekly popular torrents. 163 | 164 | Returns: 165 | Popular torrents 166 | 167 | Example: 168 | Get popular torrents 169 | ```python 170 | popular = torrents.popular(category=category.GAMES) 171 | print(popular) 172 | ``` 173 | 174 | Weekly popular torrents 175 | ```python 176 | popular_weekly = torrents.popular(category=category.APPS, weekly=True) 177 | print(popular_weekly) 178 | ``` 179 | """ 180 | url = self.url_builder.build_popular_url(category, weekly) 181 | response = self.requests.get(url) 182 | 183 | return parser.torrent_parser(response, base_url=self.base_url) 184 | 185 | def browse(self, category: str, page: int = 1) -> models.TorrentResult: 186 | """ 187 | Browse torrents by category. 188 | 189 | Args: 190 | category (str): Category of the torrent. 191 | page (int): The page number. 192 | 193 | Returns: 194 | Parsed browse results. 195 | 196 | Example: 197 | Browse torrents under the applications category 198 | ```python 199 | apps = torrents.browse(category=category.APPS, page=2) 200 | print(apps) 201 | ``` 202 | """ 203 | url = self.url_builder.build_browse_url(category, page) 204 | response = self.requests.get(url) 205 | 206 | return parser.torrent_parser(response, base_url=self.base_url, page=page) 207 | 208 | def info(self, link: Optional[str] = None, torrent_id: Optional[str] = None) -> models.TorrentInfo: 209 | """ 210 | Retrieve information of a torrent. 211 | 212 | Args: 213 | link (Optional[str]): The URL to the torrent. 214 | torrent_id (Optional[str]): The torrent ID. 215 | 216 | Returns: 217 | Parsed torrent information. 218 | 219 | Raises: 220 | TypeError: If neither link nor torrent_id is provided, or if both are provided. 221 | """ 222 | url = self.url_builder.build_info_url(link, torrent_id) 223 | response = self.requests.get(url) 224 | 225 | return parser.info_parser(response, base_url=self.base_url) 226 | 227 | 228 | class AsyncPy1337x(Py1337x): 229 | """ 230 | An experimental asynchronous version of the Py1337x class to interact with the py1337x 231 | asynchronously by calling the original methods in a worker thread. 232 | 233 | Example: 234 | ```python 235 | import asyncio 236 | 237 | from py1337x import AsyncPy1337x 238 | 239 | async def main(): 240 | torrents = AsyncPy1337x() 241 | vlc_media = await torrents.search('vlc media player') 242 | print(vlc_media) 243 | 244 | asyncio.run(main()) 245 | ``` 246 | """ 247 | 248 | def __init__(self, *args, **kwargs): 249 | super().__init__(*args, **kwargs) 250 | 251 | def __getattribute__(self, name): 252 | # Get the attribute from the parent class 253 | attr = super().__getattribute__(name) 254 | 255 | if callable(attr): 256 | # If the attribute is callable, return an asyncified version of it 257 | return asyncify(attr) 258 | 259 | # Otherwise, return the attribute as is 260 | return attr 261 | -------------------------------------------------------------------------------- /py1337x/types/__init__.py: -------------------------------------------------------------------------------- 1 | from py1337x.types import category # noqa 2 | from py1337x.types import order # noqa 3 | from py1337x.types import sort # noqa 4 | -------------------------------------------------------------------------------- /py1337x/types/category.py: -------------------------------------------------------------------------------- 1 | MOVIES='movies' 2 | TV='tv' 3 | GAMES='games' 4 | MUSIC='music' 5 | APPS='apps' 6 | ANIME='anime' 7 | DOCUMENTARIES='documentaries' 8 | XXX='xxx' 9 | OTHER='other' 10 | -------------------------------------------------------------------------------- /py1337x/types/order.py: -------------------------------------------------------------------------------- 1 | ASCENDING="asc" 2 | DESCENDING="desc" 3 | -------------------------------------------------------------------------------- /py1337x/types/sort.py: -------------------------------------------------------------------------------- 1 | TIME="time" 2 | SIZE="size" 3 | SEEDERS="seeders" 4 | LEECHERS="leechers" 5 | -------------------------------------------------------------------------------- /py1337x/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | 4 | class URLBuilder: 5 | """ 6 | A class to build and sanitize URLs. 7 | """ 8 | 9 | def __init__(self, base_url: str): 10 | """ 11 | Initialize the URLBuilder with the base URL. 12 | 13 | Args: 14 | base_url (str): The base URL for the API. 15 | """ 16 | self.base_url = base_url 17 | 18 | def sanitize_query(self, query: str) -> str: 19 | """ 20 | Sanitize the search query. 21 | 22 | Args: 23 | query (str): The search query. 24 | 25 | Returns: 26 | str: The sanitized query. 27 | """ 28 | return "+".join(query.split()) 29 | 30 | def sanitize_category(self, category: Optional[str]) -> Optional[str]: 31 | """ 32 | Sanitize the category string. 33 | 34 | Args: 35 | category (Optional[str]): The category string. 36 | 37 | Returns: 38 | Optional[str]: The sanitized category string or None if no category is provided. 39 | """ 40 | if category: 41 | if category.lower() in ["xxx", "tv"]: 42 | return category.upper() 43 | return category.capitalize() 44 | return None 45 | 46 | def build_search_url( 47 | self, query: str, page: int, category: Optional[str], sort_by: Optional[str], order: str 48 | ) -> str: 49 | """ 50 | Build the URL for searching torrents. 51 | 52 | Args: 53 | query (str): The search query. 54 | page (int): The page number. 55 | category (Optional[str]): The category string. 56 | sort_by (Optional[str]): The sort by string. 57 | order (str): The order string ('asc' or 'desc'). 58 | 59 | Returns: 60 | str: The constructed search URL. 61 | """ 62 | query = self.sanitize_query(query) 63 | category_part = "" 64 | category_type = "" 65 | sort_part = "" 66 | sort_type = "" 67 | order_part = "" 68 | 69 | if category: 70 | category_part = "category-" 71 | category_type = f"{self.sanitize_category(category)}/" 72 | 73 | if sort_by: 74 | sort_part = "sort-" 75 | sort_type = f"{sort_by.lower()}/" 76 | order_part = f"{order.lower()}/" 77 | 78 | url = f"{self.base_url}/{sort_part}{category_part}search/{query}/{category_type}{sort_type}{order_part}{page}/" 79 | return url 80 | 81 | def build_trending_url(self, category: Optional[str], weekly: bool) -> str: 82 | """ 83 | Build the URL for trending torrents. 84 | 85 | Args: 86 | category (Optional[str]): The category string. 87 | week (bool): Whether to get weekly trending torrents. 88 | 89 | Returns: 90 | str: The constructed trending URL. 91 | """ 92 | if weekly and category: 93 | return f"{self.base_url}/trending/w/{category.lower()}/" 94 | elif weekly: 95 | return f"{self.base_url}/trending-week/" 96 | elif category: 97 | return f"{self.base_url}/trending/d/{category.lower()}/" 98 | return f"{self.base_url}/trending" 99 | 100 | def build_top_url(self, category: Optional[str]) -> str: 101 | """ 102 | Build the URL for top 100 torrents. 103 | 104 | Args: 105 | category (Optional[str]): The category string. 106 | 107 | Returns: 108 | str: The constructed top 100 URL. 109 | """ 110 | if category: 111 | if category.lower() == "apps": 112 | category = "applications" 113 | elif category.lower() == "tv": 114 | category = "television" 115 | return f"{self.base_url}/top-100-{category.lower()}" 116 | return f"{self.base_url}/top-100" 117 | 118 | def build_popular_url(self, category: str, weekly: bool) -> str: 119 | """ 120 | Build the URL for popular torrents. 121 | 122 | Args: 123 | category (str): The category string. 124 | weekly (bool): Whether to get weekly popular torrents. 125 | 126 | Returns: 127 | str: The constructed popular URL. 128 | """ 129 | return f"{self.base_url}/popular-{category.lower()}{'-week' if weekly else ''}" 130 | 131 | def build_browse_url(self, category: str, page: int) -> str: 132 | """ 133 | Build the URL for browsing torrents by category. 134 | 135 | Args: 136 | category (str): The category string. 137 | page (int): The page number. 138 | 139 | Returns: 140 | str: The constructed browse URL. 141 | """ 142 | if category.lower() in ["xxx", "tv"]: 143 | category = category.upper() 144 | else: 145 | category = category.capitalize() 146 | return f"{self.base_url}/cat/{category}/{page}/" 147 | 148 | def build_info_url(self, link: Optional[str], torrent_id: Optional[str]) -> Optional[str]: 149 | """ 150 | Build the URL for fetching torrent info. 151 | 152 | Args: 153 | link (Optional[str]): The direct link to the torrent. 154 | torrent_id (Optional[str]): The torrent ID. 155 | 156 | Returns: 157 | str: The constructed info URL. 158 | 159 | Raises: 160 | TypeError: If neither link nor torrent_id is provided, or if both are provided. 161 | """ 162 | if not link and not torrent_id: 163 | raise TypeError("Missing 1 required positional argument: link or torrent_id") 164 | elif link and torrent_id: 165 | raise TypeError("Got an unexpected argument: Pass either link or torrent_id") 166 | 167 | return f"{self.base_url}/torrent/{torrent_id}/-/" if torrent_id else link 168 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "1337x" 3 | version = "2.0.1" 4 | description = "Unofficial API of 1337x.to" 5 | authors = [{ name = "Hemanta Pokharel", email = "yo@hemantapkh.com" }] 6 | requires-python = "~=3.8" 7 | readme = "README.md" 8 | license = "MIT" 9 | keywords = [ 10 | "1337x", 11 | "torrents", 12 | ] 13 | classifiers = [ 14 | "Development Status :: 5 - Production/Stable", 15 | "Intended Audience :: Developers", 16 | "License :: OSI Approved :: MIT License", 17 | "Operating System :: OS Independent", 18 | "Topic :: Utilities", 19 | ] 20 | dependencies = [ 21 | "requests>=2.32.3,<3", 22 | "beautifulsoup4>=4.12.3,<5", 23 | "cloudscraper>=1.2.71,<2", 24 | "asyncer==0.0.8", 25 | ] 26 | 27 | [project.urls] 28 | Repository = "https://github.com/hemantapkh/1337x" 29 | Documentation = "https://github.com/hemantapkh/1337x/blob/main/README.md" 30 | "Issue tracker" = "https://github.com/hemantapkh/1337x/issues" 31 | 32 | [tool.hatch.build.targets.sdist] 33 | include = ["py1337x"] 34 | 35 | [tool.hatch.build.targets.wheel] 36 | include = ["py1337x"] 37 | 38 | [build-system] 39 | requires = ["hatchling"] 40 | build-backend = "hatchling.build" 41 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 2 3 | requires-python = ">=3.8" 4 | resolution-markers = [ 5 | "python_full_version >= '3.9'", 6 | "python_full_version < '3.9'", 7 | ] 8 | 9 | [[package]] 10 | name = "1337x" 11 | version = "2.0.0" 12 | source = { editable = "." } 13 | dependencies = [ 14 | { name = "asyncer" }, 15 | { name = "beautifulsoup4" }, 16 | { name = "cloudscraper" }, 17 | { name = "requests" }, 18 | ] 19 | 20 | [package.metadata] 21 | requires-dist = [ 22 | { name = "asyncer", specifier = ">=0.0.8" }, 23 | { name = "beautifulsoup4", specifier = ">=4.12.3" }, 24 | { name = "cloudscraper", specifier = ">=1.2.71" }, 25 | { name = "requests", specifier = ">=2.32.3" }, 26 | ] 27 | 28 | [[package]] 29 | name = "anyio" 30 | version = "4.5.2" 31 | source = { registry = "https://pypi.org/simple" } 32 | resolution-markers = [ 33 | "python_full_version < '3.9'", 34 | ] 35 | dependencies = [ 36 | { name = "exceptiongroup", marker = "python_full_version < '3.9'" }, 37 | { name = "idna", marker = "python_full_version < '3.9'" }, 38 | { name = "sniffio", marker = "python_full_version < '3.9'" }, 39 | { name = "typing-extensions", marker = "python_full_version < '3.9'" }, 40 | ] 41 | sdist = { url = "https://files.pythonhosted.org/packages/4d/f9/9a7ce600ebe7804daf90d4d48b1c0510a4561ddce43a596be46676f82343/anyio-4.5.2.tar.gz", hash = "sha256:23009af4ed04ce05991845451e11ef02fc7c5ed29179ac9a420e5ad0ac7ddc5b", size = 171293, upload-time = "2024-10-13T22:18:03.307Z" } 42 | wheels = [ 43 | { url = "https://files.pythonhosted.org/packages/1b/b4/f7e396030e3b11394436358ca258a81d6010106582422f23443c16ca1873/anyio-4.5.2-py3-none-any.whl", hash = "sha256:c011ee36bc1e8ba40e5a81cb9df91925c218fe9b778554e0b56a21e1b5d4716f", size = 89766, upload-time = "2024-10-13T22:18:01.524Z" }, 44 | ] 45 | 46 | [[package]] 47 | name = "anyio" 48 | version = "4.9.0" 49 | source = { registry = "https://pypi.org/simple" } 50 | resolution-markers = [ 51 | "python_full_version >= '3.9'", 52 | ] 53 | dependencies = [ 54 | { name = "exceptiongroup", marker = "python_full_version >= '3.9' and python_full_version < '3.11'" }, 55 | { name = "idna", marker = "python_full_version >= '3.9'" }, 56 | { name = "sniffio", marker = "python_full_version >= '3.9'" }, 57 | { name = "typing-extensions", marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, 58 | ] 59 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } 60 | wheels = [ 61 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, 62 | ] 63 | 64 | [[package]] 65 | name = "asyncer" 66 | version = "0.0.8" 67 | source = { registry = "https://pypi.org/simple" } 68 | dependencies = [ 69 | { name = "anyio", version = "4.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, 70 | { name = "anyio", version = "4.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 71 | { name = "typing-extensions", marker = "python_full_version < '3.10'" }, 72 | ] 73 | sdist = { url = "https://files.pythonhosted.org/packages/ff/67/7ea59c3e69eaeee42e7fc91a5be67ca5849c8979acac2b920249760c6af2/asyncer-0.0.8.tar.gz", hash = "sha256:a589d980f57e20efb07ed91d0dbe67f1d2fd343e7142c66d3a099f05c620739c", size = 18217, upload-time = "2024-08-24T23:15:36.449Z" } 74 | wheels = [ 75 | { url = "https://files.pythonhosted.org/packages/8a/04/15b6ca6b7842eda2748bda0a0af73f2d054e9344320f8bba01f994294bcb/asyncer-0.0.8-py3-none-any.whl", hash = "sha256:5920d48fc99c8f8f0f1576e1882f5022885589c5fcbc46ce4224ec3e53776eeb", size = 9209, upload-time = "2024-08-24T23:15:35.317Z" }, 76 | ] 77 | 78 | [[package]] 79 | name = "beautifulsoup4" 80 | version = "4.13.4" 81 | source = { registry = "https://pypi.org/simple" } 82 | dependencies = [ 83 | { name = "soupsieve" }, 84 | { name = "typing-extensions" }, 85 | ] 86 | sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067, upload-time = "2025-04-15T17:05:13.836Z" } 87 | wheels = [ 88 | { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285, upload-time = "2025-04-15T17:05:12.221Z" }, 89 | ] 90 | 91 | [[package]] 92 | name = "certifi" 93 | version = "2025.4.26" 94 | source = { registry = "https://pypi.org/simple" } 95 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload-time = "2025-04-26T02:12:29.51Z" } 96 | wheels = [ 97 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload-time = "2025-04-26T02:12:27.662Z" }, 98 | ] 99 | 100 | [[package]] 101 | name = "charset-normalizer" 102 | version = "3.4.2" 103 | source = { registry = "https://pypi.org/simple" } 104 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload-time = "2025-05-02T08:34:42.01Z" } 105 | wheels = [ 106 | { url = "https://files.pythonhosted.org/packages/95/28/9901804da60055b406e1a1c5ba7aac1276fb77f1dde635aabfc7fd84b8ab/charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", size = 201818, upload-time = "2025-05-02T08:31:46.725Z" }, 107 | { url = "https://files.pythonhosted.org/packages/d9/9b/892a8c8af9110935e5adcbb06d9c6fe741b6bb02608c6513983048ba1a18/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", size = 144649, upload-time = "2025-05-02T08:31:48.889Z" }, 108 | { url = "https://files.pythonhosted.org/packages/7b/a5/4179abd063ff6414223575e008593861d62abfc22455b5d1a44995b7c101/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", size = 155045, upload-time = "2025-05-02T08:31:50.757Z" }, 109 | { url = "https://files.pythonhosted.org/packages/3b/95/bc08c7dfeddd26b4be8c8287b9bb055716f31077c8b0ea1cd09553794665/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", size = 147356, upload-time = "2025-05-02T08:31:52.634Z" }, 110 | { url = "https://files.pythonhosted.org/packages/a8/2d/7a5b635aa65284bf3eab7653e8b4151ab420ecbae918d3e359d1947b4d61/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", size = 149471, upload-time = "2025-05-02T08:31:56.207Z" }, 111 | { url = "https://files.pythonhosted.org/packages/ae/38/51fc6ac74251fd331a8cfdb7ec57beba8c23fd5493f1050f71c87ef77ed0/charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", size = 151317, upload-time = "2025-05-02T08:31:57.613Z" }, 112 | { url = "https://files.pythonhosted.org/packages/b7/17/edee1e32215ee6e9e46c3e482645b46575a44a2d72c7dfd49e49f60ce6bf/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", size = 146368, upload-time = "2025-05-02T08:31:59.468Z" }, 113 | { url = "https://files.pythonhosted.org/packages/26/2c/ea3e66f2b5f21fd00b2825c94cafb8c326ea6240cd80a91eb09e4a285830/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", size = 154491, upload-time = "2025-05-02T08:32:01.219Z" }, 114 | { url = "https://files.pythonhosted.org/packages/52/47/7be7fa972422ad062e909fd62460d45c3ef4c141805b7078dbab15904ff7/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", size = 157695, upload-time = "2025-05-02T08:32:03.045Z" }, 115 | { url = "https://files.pythonhosted.org/packages/2f/42/9f02c194da282b2b340f28e5fb60762de1151387a36842a92b533685c61e/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", size = 154849, upload-time = "2025-05-02T08:32:04.651Z" }, 116 | { url = "https://files.pythonhosted.org/packages/67/44/89cacd6628f31fb0b63201a618049be4be2a7435a31b55b5eb1c3674547a/charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", size = 150091, upload-time = "2025-05-02T08:32:06.719Z" }, 117 | { url = "https://files.pythonhosted.org/packages/1f/79/4b8da9f712bc079c0f16b6d67b099b0b8d808c2292c937f267d816ec5ecc/charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", size = 98445, upload-time = "2025-05-02T08:32:08.66Z" }, 118 | { url = "https://files.pythonhosted.org/packages/7d/d7/96970afb4fb66497a40761cdf7bd4f6fca0fc7bafde3a84f836c1f57a926/charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", size = 105782, upload-time = "2025-05-02T08:32:10.46Z" }, 119 | { url = "https://files.pythonhosted.org/packages/05/85/4c40d00dcc6284a1c1ad5de5e0996b06f39d8232f1031cd23c2f5c07ee86/charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", size = 198794, upload-time = "2025-05-02T08:32:11.945Z" }, 120 | { url = "https://files.pythonhosted.org/packages/41/d9/7a6c0b9db952598e97e93cbdfcb91bacd89b9b88c7c983250a77c008703c/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", size = 142846, upload-time = "2025-05-02T08:32:13.946Z" }, 121 | { url = "https://files.pythonhosted.org/packages/66/82/a37989cda2ace7e37f36c1a8ed16c58cf48965a79c2142713244bf945c89/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", size = 153350, upload-time = "2025-05-02T08:32:15.873Z" }, 122 | { url = "https://files.pythonhosted.org/packages/df/68/a576b31b694d07b53807269d05ec3f6f1093e9545e8607121995ba7a8313/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", size = 145657, upload-time = "2025-05-02T08:32:17.283Z" }, 123 | { url = "https://files.pythonhosted.org/packages/92/9b/ad67f03d74554bed3aefd56fe836e1623a50780f7c998d00ca128924a499/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f", size = 147260, upload-time = "2025-05-02T08:32:18.807Z" }, 124 | { url = "https://files.pythonhosted.org/packages/a6/e6/8aebae25e328160b20e31a7e9929b1578bbdc7f42e66f46595a432f8539e/charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", size = 149164, upload-time = "2025-05-02T08:32:20.333Z" }, 125 | { url = "https://files.pythonhosted.org/packages/8b/f2/b3c2f07dbcc248805f10e67a0262c93308cfa149a4cd3d1fe01f593e5fd2/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", size = 144571, upload-time = "2025-05-02T08:32:21.86Z" }, 126 | { url = "https://files.pythonhosted.org/packages/60/5b/c3f3a94bc345bc211622ea59b4bed9ae63c00920e2e8f11824aa5708e8b7/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", size = 151952, upload-time = "2025-05-02T08:32:23.434Z" }, 127 | { url = "https://files.pythonhosted.org/packages/e2/4d/ff460c8b474122334c2fa394a3f99a04cf11c646da895f81402ae54f5c42/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", size = 155959, upload-time = "2025-05-02T08:32:24.993Z" }, 128 | { url = "https://files.pythonhosted.org/packages/a2/2b/b964c6a2fda88611a1fe3d4c400d39c66a42d6c169c924818c848f922415/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", size = 153030, upload-time = "2025-05-02T08:32:26.435Z" }, 129 | { url = "https://files.pythonhosted.org/packages/59/2e/d3b9811db26a5ebf444bc0fa4f4be5aa6d76fc6e1c0fd537b16c14e849b6/charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", size = 148015, upload-time = "2025-05-02T08:32:28.376Z" }, 130 | { url = "https://files.pythonhosted.org/packages/90/07/c5fd7c11eafd561bb51220d600a788f1c8d77c5eef37ee49454cc5c35575/charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", size = 98106, upload-time = "2025-05-02T08:32:30.281Z" }, 131 | { url = "https://files.pythonhosted.org/packages/a8/05/5e33dbef7e2f773d672b6d79f10ec633d4a71cd96db6673625838a4fd532/charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", size = 105402, upload-time = "2025-05-02T08:32:32.191Z" }, 132 | { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload-time = "2025-05-02T08:32:33.712Z" }, 133 | { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload-time = "2025-05-02T08:32:35.768Z" }, 134 | { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload-time = "2025-05-02T08:32:37.284Z" }, 135 | { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload-time = "2025-05-02T08:32:38.803Z" }, 136 | { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload-time = "2025-05-02T08:32:40.251Z" }, 137 | { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload-time = "2025-05-02T08:32:41.705Z" }, 138 | { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload-time = "2025-05-02T08:32:43.709Z" }, 139 | { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload-time = "2025-05-02T08:32:46.197Z" }, 140 | { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload-time = "2025-05-02T08:32:48.105Z" }, 141 | { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload-time = "2025-05-02T08:32:49.719Z" }, 142 | { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload-time = "2025-05-02T08:32:51.404Z" }, 143 | { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload-time = "2025-05-02T08:32:53.079Z" }, 144 | { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload-time = "2025-05-02T08:32:54.573Z" }, 145 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload-time = "2025-05-02T08:32:56.363Z" }, 146 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload-time = "2025-05-02T08:32:58.551Z" }, 147 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload-time = "2025-05-02T08:33:00.342Z" }, 148 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload-time = "2025-05-02T08:33:02.081Z" }, 149 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload-time = "2025-05-02T08:33:04.063Z" }, 150 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload-time = "2025-05-02T08:33:06.418Z" }, 151 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload-time = "2025-05-02T08:33:08.183Z" }, 152 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload-time = "2025-05-02T08:33:09.986Z" }, 153 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload-time = "2025-05-02T08:33:11.814Z" }, 154 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload-time = "2025-05-02T08:33:13.707Z" }, 155 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload-time = "2025-05-02T08:33:15.458Z" }, 156 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload-time = "2025-05-02T08:33:17.06Z" }, 157 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload-time = "2025-05-02T08:33:18.753Z" }, 158 | { url = "https://files.pythonhosted.org/packages/4c/fd/f700cfd4ad876def96d2c769d8a32d808b12d1010b6003dc6639157f99ee/charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb", size = 198257, upload-time = "2025-05-02T08:33:45.511Z" }, 159 | { url = "https://files.pythonhosted.org/packages/3a/95/6eec4cbbbd119e6a402e3bfd16246785cc52ce64cf21af2ecdf7b3a08e91/charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a", size = 143453, upload-time = "2025-05-02T08:33:47.463Z" }, 160 | { url = "https://files.pythonhosted.org/packages/b6/b3/d4f913660383b3d93dbe6f687a312ea9f7e89879ae883c4e8942048174d4/charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45", size = 153130, upload-time = "2025-05-02T08:33:50.568Z" }, 161 | { url = "https://files.pythonhosted.org/packages/e5/69/7540141529eabc55bf19cc05cd9b61c2078bebfcdbd3e799af99b777fc28/charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5", size = 145688, upload-time = "2025-05-02T08:33:52.828Z" }, 162 | { url = "https://files.pythonhosted.org/packages/2e/bb/d76d3d6e340fb0967c43c564101e28a78c9a363ea62f736a68af59ee3683/charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1", size = 147418, upload-time = "2025-05-02T08:33:54.718Z" }, 163 | { url = "https://files.pythonhosted.org/packages/3e/ef/b7c1f39c0dc3808160c8b72e0209c2479393966313bfebc833533cfff9cc/charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027", size = 150066, upload-time = "2025-05-02T08:33:56.597Z" }, 164 | { url = "https://files.pythonhosted.org/packages/20/26/4e47cc23d2a4a5eb6ed7d6f0f8cda87d753e2f8abc936d5cf5ad2aae8518/charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b", size = 144499, upload-time = "2025-05-02T08:33:58.637Z" }, 165 | { url = "https://files.pythonhosted.org/packages/d7/9c/efdf59dd46593cecad0548d36a702683a0bdc056793398a9cd1e1546ad21/charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455", size = 152954, upload-time = "2025-05-02T08:34:00.552Z" }, 166 | { url = "https://files.pythonhosted.org/packages/59/b3/4e8b73f7299d9aaabd7cd26db4a765f741b8e57df97b034bb8de15609002/charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01", size = 155876, upload-time = "2025-05-02T08:34:02.527Z" }, 167 | { url = "https://files.pythonhosted.org/packages/53/cb/6fa0ccf941a069adce3edb8a1e430bc80e4929f4d43b5140fdf8628bdf7d/charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58", size = 153186, upload-time = "2025-05-02T08:34:04.481Z" }, 168 | { url = "https://files.pythonhosted.org/packages/ac/c6/80b93fabc626b75b1665ffe405e28c3cef0aae9237c5c05f15955af4edd8/charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681", size = 148007, upload-time = "2025-05-02T08:34:06.888Z" }, 169 | { url = "https://files.pythonhosted.org/packages/41/eb/c7367ac326a2628e4f05b5c737c86fe4a8eb3ecc597a4243fc65720b3eeb/charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7", size = 97923, upload-time = "2025-05-02T08:34:08.792Z" }, 170 | { url = "https://files.pythonhosted.org/packages/7c/02/1c82646582ccf2c757fa6af69b1a3ea88744b8d2b4ab93b7686b2533e023/charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a", size = 105020, upload-time = "2025-05-02T08:34:10.6Z" }, 171 | { url = "https://files.pythonhosted.org/packages/28/f8/dfb01ff6cc9af38552c69c9027501ff5a5117c4cc18dcd27cb5259fa1888/charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", size = 201671, upload-time = "2025-05-02T08:34:12.696Z" }, 172 | { url = "https://files.pythonhosted.org/packages/32/fb/74e26ee556a9dbfe3bd264289b67be1e6d616329403036f6507bb9f3f29c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", size = 144744, upload-time = "2025-05-02T08:34:14.665Z" }, 173 | { url = "https://files.pythonhosted.org/packages/ad/06/8499ee5aa7addc6f6d72e068691826ff093329fe59891e83b092ae4c851c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", size = 154993, upload-time = "2025-05-02T08:34:17.134Z" }, 174 | { url = "https://files.pythonhosted.org/packages/f1/a2/5e4c187680728219254ef107a6949c60ee0e9a916a5dadb148c7ae82459c/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", size = 147382, upload-time = "2025-05-02T08:34:19.081Z" }, 175 | { url = "https://files.pythonhosted.org/packages/4c/fe/56aca740dda674f0cc1ba1418c4d84534be51f639b5f98f538b332dc9a95/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", size = 149536, upload-time = "2025-05-02T08:34:21.073Z" }, 176 | { url = "https://files.pythonhosted.org/packages/53/13/db2e7779f892386b589173dd689c1b1e304621c5792046edd8a978cbf9e0/charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", size = 151349, upload-time = "2025-05-02T08:34:23.193Z" }, 177 | { url = "https://files.pythonhosted.org/packages/69/35/e52ab9a276186f729bce7a0638585d2982f50402046e4b0faa5d2c3ef2da/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", size = 146365, upload-time = "2025-05-02T08:34:25.187Z" }, 178 | { url = "https://files.pythonhosted.org/packages/a6/d8/af7333f732fc2e7635867d56cb7c349c28c7094910c72267586947561b4b/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", size = 154499, upload-time = "2025-05-02T08:34:27.359Z" }, 179 | { url = "https://files.pythonhosted.org/packages/7a/3d/a5b2e48acef264d71e036ff30bcc49e51bde80219bb628ba3e00cf59baac/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", size = 157735, upload-time = "2025-05-02T08:34:29.798Z" }, 180 | { url = "https://files.pythonhosted.org/packages/85/d8/23e2c112532a29f3eef374375a8684a4f3b8e784f62b01da931186f43494/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", size = 154786, upload-time = "2025-05-02T08:34:31.858Z" }, 181 | { url = "https://files.pythonhosted.org/packages/c7/57/93e0169f08ecc20fe82d12254a200dfaceddc1c12a4077bf454ecc597e33/charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", size = 150203, upload-time = "2025-05-02T08:34:33.88Z" }, 182 | { url = "https://files.pythonhosted.org/packages/2c/9d/9bf2b005138e7e060d7ebdec7503d0ef3240141587651f4b445bdf7286c2/charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", size = 98436, upload-time = "2025-05-02T08:34:35.907Z" }, 183 | { url = "https://files.pythonhosted.org/packages/6d/24/5849d46cf4311bbf21b424c443b09b459f5b436b1558c04e45dbb7cc478b/charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", size = 105772, upload-time = "2025-05-02T08:34:37.935Z" }, 184 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload-time = "2025-05-02T08:34:40.053Z" }, 185 | ] 186 | 187 | [[package]] 188 | name = "cloudscraper" 189 | version = "1.2.71" 190 | source = { registry = "https://pypi.org/simple" } 191 | dependencies = [ 192 | { name = "pyparsing", version = "3.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, 193 | { name = "pyparsing", version = "3.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 194 | { name = "requests" }, 195 | { name = "requests-toolbelt" }, 196 | ] 197 | sdist = { url = "https://files.pythonhosted.org/packages/ac/25/6d0481860583f44953bd791de0b7c4f6d7ead7223f8a17e776247b34a5b4/cloudscraper-1.2.71.tar.gz", hash = "sha256:429c6e8aa6916d5bad5c8a5eac50f3ea53c9ac22616f6cb21b18dcc71517d0d3", size = 93261, upload-time = "2023-04-25T23:20:19.467Z" } 198 | wheels = [ 199 | { url = "https://files.pythonhosted.org/packages/81/97/fc88803a451029688dffd7eb446dc1b529657577aec13aceff1cc9628c5d/cloudscraper-1.2.71-py2.py3-none-any.whl", hash = "sha256:76f50ca529ed2279e220837befdec892626f9511708e200d48d5bb76ded679b0", size = 99652, upload-time = "2023-04-25T23:20:15.974Z" }, 200 | ] 201 | 202 | [[package]] 203 | name = "exceptiongroup" 204 | version = "1.3.0" 205 | source = { registry = "https://pypi.org/simple" } 206 | dependencies = [ 207 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 208 | ] 209 | sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } 210 | wheels = [ 211 | { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, 212 | ] 213 | 214 | [[package]] 215 | name = "idna" 216 | version = "3.10" 217 | source = { registry = "https://pypi.org/simple" } 218 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } 219 | wheels = [ 220 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, 221 | ] 222 | 223 | [[package]] 224 | name = "pyparsing" 225 | version = "3.1.4" 226 | source = { registry = "https://pypi.org/simple" } 227 | resolution-markers = [ 228 | "python_full_version < '3.9'", 229 | ] 230 | sdist = { url = "https://files.pythonhosted.org/packages/83/08/13f3bce01b2061f2bbd582c9df82723de943784cf719a35ac886c652043a/pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032", size = 900231, upload-time = "2024-08-25T15:00:47.416Z" } 231 | wheels = [ 232 | { url = "https://files.pythonhosted.org/packages/e5/0c/0e3c05b1c87bb6a1c76d281b0f35e78d2d80ac91b5f8f524cebf77f51049/pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", size = 104100, upload-time = "2024-08-25T15:00:45.361Z" }, 233 | ] 234 | 235 | [[package]] 236 | name = "pyparsing" 237 | version = "3.2.3" 238 | source = { registry = "https://pypi.org/simple" } 239 | resolution-markers = [ 240 | "python_full_version >= '3.9'", 241 | ] 242 | sdist = { url = "https://files.pythonhosted.org/packages/bb/22/f1129e69d94ffff626bdb5c835506b3a5b4f3d070f17ea295e12c2c6f60f/pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be", size = 1088608, upload-time = "2025-03-25T05:01:28.114Z" } 243 | wheels = [ 244 | { url = "https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf", size = 111120, upload-time = "2025-03-25T05:01:24.908Z" }, 245 | ] 246 | 247 | [[package]] 248 | name = "requests" 249 | version = "2.32.3" 250 | source = { registry = "https://pypi.org/simple" } 251 | dependencies = [ 252 | { name = "certifi" }, 253 | { name = "charset-normalizer" }, 254 | { name = "idna" }, 255 | { name = "urllib3", version = "2.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, 256 | { name = "urllib3", version = "2.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, 257 | ] 258 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } 259 | wheels = [ 260 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, 261 | ] 262 | 263 | [[package]] 264 | name = "requests-toolbelt" 265 | version = "1.0.0" 266 | source = { registry = "https://pypi.org/simple" } 267 | dependencies = [ 268 | { name = "requests" }, 269 | ] 270 | sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } 271 | wheels = [ 272 | { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, 273 | ] 274 | 275 | [[package]] 276 | name = "sniffio" 277 | version = "1.3.1" 278 | source = { registry = "https://pypi.org/simple" } 279 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } 280 | wheels = [ 281 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, 282 | ] 283 | 284 | [[package]] 285 | name = "soupsieve" 286 | version = "2.7" 287 | source = { registry = "https://pypi.org/simple" } 288 | sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418, upload-time = "2025-04-20T18:50:08.518Z" } 289 | wheels = [ 290 | { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677, upload-time = "2025-04-20T18:50:07.196Z" }, 291 | ] 292 | 293 | [[package]] 294 | name = "typing-extensions" 295 | version = "4.13.2" 296 | source = { registry = "https://pypi.org/simple" } 297 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } 298 | wheels = [ 299 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, 300 | ] 301 | 302 | [[package]] 303 | name = "urllib3" 304 | version = "2.2.3" 305 | source = { registry = "https://pypi.org/simple" } 306 | resolution-markers = [ 307 | "python_full_version < '3.9'", 308 | ] 309 | sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677, upload-time = "2024-09-12T10:52:18.401Z" } 310 | wheels = [ 311 | { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338, upload-time = "2024-09-12T10:52:16.589Z" }, 312 | ] 313 | 314 | [[package]] 315 | name = "urllib3" 316 | version = "2.4.0" 317 | source = { registry = "https://pypi.org/simple" } 318 | resolution-markers = [ 319 | "python_full_version >= '3.9'", 320 | ] 321 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload-time = "2025-04-10T15:23:39.232Z" } 322 | wheels = [ 323 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload-time = "2025-04-10T15:23:37.377Z" }, 324 | ] 325 | --------------------------------------------------------------------------------